Compare commits

..

247 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
8258f2b892 curse you abbreviated currency icons 2024-06-01 19:38:17 -05:00
4577213ee9 bug fixes and fix fresh installs 2024-06-01 18:54:10 -05:00
beb002feb9 bug fixes 2024-06-01 12:36:02 -05:00
1a2af482aa bug fixes and remove prettier github action 2024-05-31 20:43:09 -05:00
4bf9139a60 small improvements
- removed tabs permission apparently it was unnecessary

- fixed store main page "owned" tags when the user's inventory is private/friends-only

- fixed "creator" tags in comment sections around the site

- deleted currently-unused currencies.json resource
2024-05-29 17:03:53 -05:00
120f8cc23e
Merge pull request #7 from StarManTheGamer/main
grammar changes readme
2024-05-28 01:38:55 -05:00
StarManTheGamer
273d524fe0
Update README.md 2024-05-28 01:16:31 -05:00
StarManTheGamer
fd940da48a
grammar changes to README 2024-05-28 01:15:59 -05:00
28e483b02e
Merge pull request #6 from StarManTheGamer/main
PRETTIER MY BELOVED!!!!!!!!!!!!!!!!!!!!!!
2024-05-27 20:26:42 -05:00
github-actions[bot]
0ff8fac37b chore: format code with Prettier 2024-05-28 00:23:56 +00:00
StarManTheGamer
e1ace9551b
Merge branch 'indexxing:main' into main 2024-05-27 17:23:39 -07:00
d63760ec50 Revert "Update manifest.json"
This reverts commit 30ebcc854a.
2024-05-27 18:57:01 -05:00
30ebcc854a Update manifest.json 2024-05-27 18:56:50 -05:00
63b55d9883 fixed item wishlist and copy asset id context menu item 2024-05-27 18:55:00 -05:00
StarManTheGamer
ff6c26cb0a Revert "chore: format code with Prettier"
This reverts commit cc39111694.
2024-05-27 17:16:58 -05:00
121676bda8 fix hide user ads 2024-05-27 17:15:02 -05:00
github-actions[bot]
cc39111694 chore: format code with Prettier 2024-05-27 22:04:23 +00:00
StarManTheGamer
d25b2f97c2
Merge branch 'indexxing:main' into main 2024-05-27 15:04:21 -07:00
StarManTheGamer
bf6e9048c0 Revert "chore: format code with Prettier"
This reverts commit 70241eead6.
2024-05-27 17:04:02 -05:00
github-actions[bot]
70241eead6 chore: format code with Prettier 2024-05-27 22:02:41 +00:00
b21af09301 renamed files 2024-05-27 17:02:32 -05:00
StarManTheGamer
8f687c53b1 Merge branch 'main' of https://github.com/StarManTheGamer/PolyPlus 2024-05-27 17:02:13 -05:00
StarManTheGamer
5a425667f2
Merge branch 'indexxing:main' into main 2024-05-27 15:02:02 -07:00
fc230ec3b8 rewrote settings and new features
- rewrote settings

- reorganized several settings as nested objects

- added avatars enabled sub-setting of collectibles hoarder list

- added default min copies

- new feature: "Timed-Item Owner Check"

- new feature: "Hide User Ads"
2024-05-27 17:01:25 -05:00
StarManTheGamer
28881e45a1 prettier automatically work 2024-05-27 16:52:48 -05:00
StarManTheGamer
e5e5721085 prettier 2024-05-27 16:42:16 -05:00
40f09022fb remove messy switch/checkbox setting thing 2024-05-22 17:20:35 -05:00
c1bee43f08 prepare for next update
i don't feel like writing github commit description right now
2024-05-22 17:18:55 -05:00
6ebc9cc596 expand hoarders list to up to 300 owners 2024-05-05 22:30:31 -05:00
55ad153d2b 33 files changed - update + hoarders list
- Added new feature: "Collectibles' Hoarders List"

[ IMPROVEMENTS ]

- Reorganized resources folder to be in the root directory rather than a sub-folder of the javascript folder

- Added a new feature: "Quick Library Downloads"

- Basically finished the "Event Items Store Category" feature

- Added more manifest.json information
2024-04-25 17:04:38 -05:00
8b20599662 Fix "Try-On" Store Items
- Improved detection for the purchase button on the store item view page AGAIN (hopefully it works 100% of the time now for all situations)

- Fixed "Try-On" store items again (the code was working it was just the adding of the mesh to the list of items that broke)
2024-04-20 15:54:29 -05:00
eeaafeb35a Updates and Bug Fixes
- Experimental Feature: Event Items Store Category

- Fixed "Try On" store items

- Deleted "Try On" store items code and moved it to the main item view page script

- Fixed Transactions page Bricks to IRL Price converter

- Transactions page bricks to IRL Price converter now doesn't say "$NaN" when the input is erased

- Deleted popup and installation HTML files as they aren't used and haven't been used for a while. I am thinking of adding a new popup when you click the extension icon in your browser toolbar but that will be different than the code that was there.

- New Feature: Show "Owners" instead of "Sales"

- Updated theme code to run on all pages, even when the "Theme Creator" feature is turned off so that CSS such as modal CSS can still apply without other scripts having to apply their own CSS blob to the page

- Updated context menu background code to remove all context menus created by the extension before creating the context menus so the error for duplicate context menu IDs won't keep appearing (hopefully, but I haven't gotten the error yet so...)

- Fixed "Trending Items" IRL Brick price breaking if any of the trending items are free

- Improved detection for the purchase button on item view pages

- Fixed the item view page not checking if the item isn't owned before trying to add the IRL Brick price

- Removed old setTimeout() code at the start of some files
2024-04-20 11:58:00 -05:00
9478b85f49
fix the fix that added random characters??? 2024-04-18 16:55:48 -05:00
b15c39f861 Update store.js
accidentally selected line of code I didn't mean to GitHub commit (event items category on store, I unchecked all the lines but the last line in the function)
2024-04-18 16:55:13 -05:00
706467c98b Fix Store "Owned" Tags 2024-04-18 16:53:27 -05:00
04bac6431f "Quick Place Downloads" Feature and Fixed Achievements 2024-04-06 14:15:14 -05:00
4566b7d031 Add "Achievement Owned Percentage" Feature
- Added "Achievement Owned Percentage" feature, all achievements now say what percentage of all players on Polytoria own the achievement (percentage fixed to 2 decimal places)

- The achievement progress bar no longer shows if the place has no achievements. This also applies to the new feature "Achievement Owned Percentage"
2024-03-27 18:16:42 -05:00
f3077da95f Progress on Avatar Sandbox
The + and - button shown on item card hover now updates depending on if the item is being worn or not.
2024-03-27 16:44:55 -05:00
a8cf983e26 Add "Place Achievements Progress Bar" Feature 2024-03-27 12:31:51 -05:00
066babda56 Add "Show Approximate Place Revenue" Feature
- Add "Show Approximate Place Revenue" feature

- Link roadmap in the README.md
2024-03-26 20:29:39 -05:00
5fea28c467 Improved "Show Outfit Cost on Profiles" Design
- Improved "Show Outfit Cost on Profiles" design

- Improved Debug Page
2024-03-26 18:29:15 -05:00
2e146391b1 Improvements
- "Run Update Notifier" context menu item now only runs on the developer debug page and nowhere else

- Settings are automatically saved whenever you edit the value of a select field

- Added accurate names for element IDs on the settings page so I can link to them

- If there is a hash/anchor URL specified in the settings page redirect URL it will carry the hash/anchor URL over to the local page URL
2024-03-07 14:59:37 -06:00
f4a8163745 Add IRL prices to Guild Stores and Improved Theme Loading
- Updated the way the Theme Creator loads themes, it now loads instantly on page load! (credit to SK-Fast's old Poly+ extension for the realization)

- Guild stores now have IRL prices listed if the "Show IRL Price with Brick Count" setting is enabled.
2024-03-07 14:41:37 -06:00
251e28edeb Update Notification Banner & Improvements
- Update banner on settings page with 2 options: go to releases page on this GitHub repository or to skip this update (unrecommended)

- You can now skip updates (again, unrecommended)

- Added "Reset to Default" button on all options modals on the settings page which will reset that setting's specific options to their defaults.

- Removed element IDs from the "Modify Navbar" feature options modal (they were there due to the old way options modals worked before release)

- Renamed polyplus-settings.js to settings.js

- Updated all "IRL Price with Brick Count" display code to have different variable names

- "IRL Price with Brick Count" is more accurate by parsing abbreviated numbers into their full number (fixing odd bugs that would happen with things such as a user's networth) - it is still not super accurate when it comes to users' networth but it's way better than before

 - You can now clear specific data locations (chrome.storage.sync and chrome.storage.local) on the extension's debug page

- Updated update notifier code

- The profile page now uses the utilities to calculate the "IRL Price with Brick Count" result rather than using the old repetitive code

- Added another extension icon for when the extension has an update available - it currently isn't used anywhere due to the code for it not working for some reason
2024-03-07 10:38:11 -06:00
79f6b3a237 Improved Settings Page Footer
You are now able to see the version number, build type (stable or pre-release), and quickly check for updates

The text has been changed to say "made by Index with the help of several contributors" instead of just "made by Index"
2024-02-22 22:33:36 -06:00
Index
7af0a628b0
Update README.md 2024-02-21 21:58:04 -06:00
b87c899256 forgot to add changes from inspect element to vscode
lol
2024-02-21 21:54:55 -06:00
948c9b8fae Improved Settings Page and other Improvements
- Improved settings page ("*unsaved" title when not saved yet, redesigned toggle indicators, redesigned toggle buttons, and small changes)

- data-URL property of the body HTML tag is now the pathname rather than the full URL

- Work in progress places integration for Pinned Games (on the actual places page, debating deleting this or not)

- Code cleanup

- Code improvements

- Added .gitignore

- Updated specific.css to use pathnames rather than the full URL

- Updated data_structure.txt reference for developers

- Changed manifest version name to pre-release build so you can tell what build you are using
2024-02-21 21:48:06 -06:00
62 changed files with 25530 additions and 5144 deletions

2
.gitignore vendored Normal file
View file

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

View file

@ -2,44 +2,65 @@
> Poly+ is in no way affiliated with the Polytoria team. This is a project I started for the community - and I hope some community members are willing to help with development so the extension is better for everyone ❤️. > Poly+ is in no way affiliated with the Polytoria team. This is a project I started for the community - and I hope some community members are willing to help with development so the extension is better for everyone ❤️.
> [!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 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.
# 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)
^ shields.io for the badges ❤️
# 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 in the future. 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
- Google Chrome - Google Chrome
- Chromium - Chromium
- Edge - Edge
@ -51,24 +72,24 @@ 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:
3. 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)
4. Extract the .ZIP - Firefox: [Firefox Add-ons Store](https://addons.mozilla.org/en-US/firefox/addon/polytoriaplus/)
5. Enable developer mode on your browser (developer mode allows you to load unpacked extensions)
6. 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!
7. When the file selector comes up, open the unzipped folder from this GitHub repository
8. 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?
> Pre-Release builds may have work in progress features or bugs! > Pre-release builds may have work-in-progress features or bugs!
1. Go to the top of this GitHub repository 1. Go to the top of this GitHub repository
2. Click on the "Code" button 2. Click on the "Code" button
3. Click on the "Download ZIP" button in the dropdown menu 3. Click on the "Download ZIP" button in the dropdown menu
4. Extract the .ZIP 4. Extract the .ZIP
5. Enable developer mode on your browser (developer mode allows you to load unpacked extensions) 5. Enable developer mode on your browser (developer mode allows you to load unpacked extensions)
6. Go to your browsers extensions page (usually `browserName://extensions`) 6. Go to your browser extensions page (usually `browserName://extensions`)
8. Click the "Load Unpacked" button 7. Click the "Load Unpacked" button
9. When the file selector comes up, open the unzipped folder of this repository's source code 8. When the file selector comes up, open the unzipped folder of this repository's source code
10. Go to Polytoria and you should see some changes! 9. Go to Polytoria and you should see some changes!

15664
css/polytoria.css Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,60 +0,0 @@
@font-face {
font-family: LexendRegular;
src: url('Lexend-Regular.ttf');
}
@font-face {
font-family: LexendLight;
src: url('Lexend-Light.ttf');
}
@font-face {
font-family: LexendBold;
src: url('Lexend-Bold.ttf');
}
@font-face {
font-family: LexendBlack;
src: url('Lexend-Black.ttf');
}
body {
margin: 0;
padding: 12px;
font-family: LexendRegular;
background-color: #242424;
color: white;
}
.button {
border-radius: 20px;
background-color: #fff;
color: #424242;
width: 95%;
padding: 10px;
border: none;
font-weight: bold;
text-decoration: none;
}
input {
border-radius: 20px;
background-color: #fff;
color: #007bff;
width: 95%;
padding: 10px;
border: none;
font-weight: bold;
}
h1,.h1 {
font-family: LexendBlack;
}
h2,.h2 {
font-family: LexendBold;
}
hr {
border-color: transparent;
}

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

@ -1,17 +1,140 @@
body[data-URL^="https://polytoria.com/my/friends"] .col-lg-3 { /*
FRONTEND FIXES / SPACING IMPROVEMENTS
*/
body[data-URL^='/my/friends'] .col-lg-3 {
margin-bottom: 20px; margin-bottom: 20px;
} }
body[data-URL^="https://polytoria.com/create/"] .mt-2.mt-lg-0.col-lg.d-flex.align-content-between.flex-wrap { body[data-URL^='/create/'] .mt-2.mt-lg-0.col-lg.d-flex.align-content-between.flex-wrap {
width: 50%; width: 50%;
} }
body[data-URL^="https://polytoria.com/create/"] .col.d-flex.align-content-between.flex-wrap { 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;
}
/* ------------------------------------------ */
/*
INLINE EDITING
*/
body[data-polyplus-inlineEditing='true'] .polyplus-inlineEditing-visible {
display: block !important;
}
.polyplus-inlineEditing-visible {
display: none;
}
body[data-polyplus-inlineEditing='true'] .polyplus-inlineEditing-hidden {
display: none !important;
}
.polyplus-inlineEditing-hidden {
display: block;
}
/* ------------------------------------------ */
/*
MODALS
*/
html:has(.polyplus-modal[open]),
body:has(.polyplus-modal[open]) {
overflow: hidden;
}
.polyplus-modal::backdrop {
background: rgba(0, 0, 0, 0.73);
}
/* ------------------------------------------ */
/*
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 @@
PolyPlus_Settings: [] Sync:
------------------
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

BIN
images/icon-update.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View file

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

@ -1,8 +0,0 @@
<html>
<head>
<title>Poly+</title>
</head>
<body>
<p>Thank you for installing Poly+!</p>
</body>
</html>

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

File diff suppressed because it is too large Load diff

View file

@ -1,456 +0,0 @@
setTimeout(function () {}, 100)
const UserID = JSON.parse(window.localStorage.getItem('account_info')).ID
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"
}
let ItemCardContents = `
<div style="max-width: 150px;">
<div class="card mb-2 avatar-item-container">
<div class="p-2">
<img src=":ItemThumbnail" class="img-fluid">
<span class="position-absolute" style="top: 5px; left: 5px; z-index: 1;">
<span class="badge bg-secondary">:ItemType</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/:ItemID" class="text-reset">
<h6 class="text-truncate mb-0"> :ItemName</h6>
</a>
<small class="text-muted d-block text-truncate">
by <a href="/users/:CreatorID" class="text-reset">:CreatorName</a>
</small>
</div>
`
if (new URLSearchParams(new URL(window.location).search).get('sandbox') === 'true') {
console.log('Avatar Sandbox!')
LoadFile(chrome.runtime.getURL('js/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()
});
});
}
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.classList = 'col-auto'
NewItemCard.innerHTML = ItemCardContents
.replace(':ItemName', item.name)
.replace(':ItemID', item.id)
.replace(':ItemType', item.type)
.replace(item.type.charAt(0), item.type.charAt(0).toUpperCase())
.replace(':CreatorName', item.creator.name)
.replace(':CreatorID', item.creator.id)
.replace(':ItemThumbnail', item.thumbnail)
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 && 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')
switch(info.type) {
case 'hat':
Avatar.items.push(info.id)
break
default:
Avatar[info.type] = info.id
break
}
} else {
console.log('unequip')
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
}
}
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.classList = 'col-auto';
NewItemCard.innerHTML = ItemCardContents
.replace(':ItemName', item.name)
.replace(':ItemID', item.id)
.replace(':ItemType', item.type.charAt(0).toUpperCase() + item.type.substring(1))
.replace(':CreatorName', item.creator.name)
.replace(':CreatorID', item.creator.id)
.replace(':ItemThumbnail', item.thumbnail);
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.'
}
}
/*
function LoadWearing() {
const WearingItems = [
...Avatar.items,
Avatar.shirt,
Avatar.pants,
Avatar.face
].filter(item => item !== null)
Array.from(Wearing.children).forEach(element => {
console.log('AAAAAAAAAAAA', element)
})
}
*/
/*
function LoadWearing() {
const AllItems = structuredClone(Avatar.items)
AllItems.push(Avatar.shirt)
AllItems.push(Avatar.pants)
AllItems.push(Avatar.face)
AllItems.forEach(item => {
if (item !== null) {
let Element = document.querySelector(`a[href="/store/${item}"]`)
if (Element !== null) {
console.log('exists - load wearing')
Element = Element.parentElement.parentElement
Wearing.appendChild(Element)
} else if (Element === null) {
console.log('doesn\' exist - load wearing')
fetch('https://api.polytoria.com/v1/store/:id'.replace(':id', item))
.then(response => {
if (!response.ok) {
throw new Error('Network not ok')
}
return response.json()
})
.then(item => {
let NewItemCard = document.createElement('div')
NewItemCard.classList = 'col-auto'
NewItemCard.innerHTML = ItemCardContents
.replace(':ItemName', item.name)
.replace(':ItemID', item.id)
.replace(':ItemType', item.type.charAt(0).toUpperCase() + item.type.substring(1))
.replace(':CreatorName', item.creator.name)
.replace(':CreatorID', item.creator.id)
.replace(':ItemThumbnail', item.thumbnail)
Wearing.appendChild(NewItemCard)
NewItemCard.getElementsByClassName('p-2')[0].addEventListener('click', function(){
WearAsset(NewItemCard, item)
});
})
.catch(error => {
console.log('Fetch error: ' + error)
});
} else if (item.type === TabSelected) {
console.log('item type is selected tab - load wearing')
ItemGrid.appendChild(Element)
} else {
console.log('remove item - load wearing')
Element.remove()
}
}
});
}
*/

View file

@ -1,39 +1,37 @@
var SelectedFriends = [] var SelectedFriends = [];
setTimeout(function () {
chrome.storage.sync.get(['PolyPlus_Settings'], function (result) { chrome.storage.sync.get(['PolyPlus_Settings'], function (result) {
Settings = result.PolyPlus_Settings; Settings = result.PolyPlus_Settings;
if (Settings.ImprovedFrListsOn === true) { if (Settings.ImprovedFrListsOn === true) {
var Tab = "requests" var Tab = 'requests';
var FriendsContainer = document.getElementById('friends-container') var FriendsContainer = document.getElementById('friends-container');
var Container = document.createElement('div') var Container = document.createElement('div');
Container.classList = 'row mb-3' Container.classList = 'row mb-3';
Container.innerHTML = ` Container.innerHTML = `
<div class="col"><button id="AccAllFrBtn" class="btn btn-success w-100">Accept all Friend Request(s)</button></div> <div class="col"><button id="AccAllFrBtn" class="btn btn-success w-100">Accept all Friend Request(s)</button></div>
<div class="col"><button id="DelAllFrBtn" class="btn btn-danger w-100">Decline all Friend Request(s)</button></div> <div class="col"><button id="DelAllFrBtn" class="btn btn-danger w-100">Decline all Friend Request(s)</button></div>
` `;
FriendsContainer.parentElement.insertBefore(Container, FriendsContainer) FriendsContainer.parentElement.insertBefore(Container, FriendsContainer);
var AccAllFrBtn = document.getElementById('AccAllFrBtn') var AccAllFrBtn = document.getElementById('AccAllFrBtn');
var DelAllFrBtn = document.getElementById('DelAllFrBtn') var DelAllFrBtn = document.getElementById('DelAllFrBtn');
var AccBtns = document.querySelectorAll('[onclick="acceptFriendRequest(this)"]') var AccBtns = document.querySelectorAll('[onclick="acceptFriendRequest(this)"]');
var DelBtns = document.querySelectorAll('[onclick="declineFriendRequest(this)"]') var DelBtns = document.querySelectorAll('[onclick="declineFriendRequest(this)"]');
if (!(AccBtns.length === 0)) { if (!(AccBtns.length === 0)) {
AccAllFrBtn.addEventListener('click', function () { AccAllFrBtn.addEventListener('click', function () {
AccBtns.forEach(element => { AccBtns.forEach((element) => {
setTimeout(function () {}, 145) setTimeout(function () {}, 145);
fetch('https://polytoria.com/api/friends/send', { fetch('https://polytoria.com/api/friends/send', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('input[name="_csrf"]').value 'X-CSRF-Token': document.querySelector('input[name="_csrf"]').value
}, },
body: JSON.stringify({ userID: parseInt(element.getAttribute('data-user-id')) }), body: JSON.stringify({userID: parseInt(element.getAttribute('data-user-id'))})
}) }).catch((error) => {
.catch(error => {
// Handle any errors // Handle any errors
console.error('Error:', error); console.error('Error:', error);
Success = false Success = false;
}); });
/* /*
@ -45,27 +43,26 @@ setTimeout(function () {
FriendsContainer.appendChild(NewAcceptBtn) FriendsContainer.appendChild(NewAcceptBtn)
NewAcceptBtn.click(); NewAcceptBtn.click();
*/ */
}) });
}); });
} else { } else {
AccAllFrBtn.setAttribute('disabled', 'true') AccAllFrBtn.setAttribute('disabled', 'true');
} }
if (!(DelBtns.length === 0)) { if (!(DelBtns.length === 0)) {
DelAllFrBtn.addEventListener('click', function () { DelAllFrBtn.addEventListener('click', function () {
DelBtns.forEach(element => { DelBtns.forEach((element) => {
setTimeout(function () {}, 110) setTimeout(function () {}, 110);
fetch('https://polytoria.com/api/friends/remove', { fetch('https://polytoria.com/api/friends/remove', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('input[name="_csrf"]').value 'X-CSRF-Token': document.querySelector('input[name="_csrf"]').value
}, },
body: JSON.stringify({ userID: parseInt(element.getAttribute('data-user-id')) }), body: JSON.stringify({userID: parseInt(element.getAttribute('data-user-id'))})
}) }).catch((error) => {
.catch(error => {
// Handle any errors // Handle any errors
console.error('Error:', error, document.querySelector('input[name="_csrf"]').value); console.error('Error:', error, document.querySelector('input[name="_csrf"]').value);
Success = false Success = false;
}); });
/* /*
@ -77,136 +74,137 @@ setTimeout(function () {
FriendsContainer.appendChild(NewDeclineBtn) FriendsContainer.appendChild(NewDeclineBtn)
NewDeclineBtn.click(); NewDeclineBtn.click();
*/ */
}) });
}); });
} else { } else {
DelAllFrBtn.setAttribute('disabled', 'true') DelAllFrBtn.setAttribute('disabled', 'true');
} }
let Text = document.createElement('p') let Text = document.createElement('p');
Text.classList = 'mx-auto' Text.classList = 'mx-auto';
Text.style.textAlign = 'center' Text.style.textAlign = 'center';
Text.style.fontSize = '1.3rem' Text.style.fontSize = '1.3rem';
Text.style.display = 'none' Text.style.display = 'none';
Text.innerHTML = ` Text.innerHTML = `
<span>0</span> friends selected! <span>0</span> friends selected!
<br> <br>
<button id="viewSelectionBtn" class="btn btn-primary">View Selection</button> <button id="viewSelectionBtn" class="btn btn-primary">View Selection</button>
<button id="clearSelectionBtn" class="btn btn-warning">Clear Selection</button> <button id="clearSelectionBtn" class="btn btn-warning">Clear Selection</button>
<button id="removeSelectionBtn" class="btn btn-danger">Remove Selected Friends</button> <button id="removeSelectionBtn" class="btn btn-danger">Remove Selected Friends</button>
` `;
FriendsContainer.parentElement.insertBefore(Text, FriendsContainer) FriendsContainer.parentElement.insertBefore(Text, FriendsContainer);
let Text_Span = Text.querySelector('span'); let Text_Span = Text.querySelector('span');
let Text_View = document.getElementById('viewSelectionBtn'); let Text_View = document.getElementById('viewSelectionBtn');
let Text_Clear = document.getElementById('clearSelectionBtn'); let Text_Clear = document.getElementById('clearSelectionBtn');
let Text_Remove = document.getElementById('removeSelectionBtn'); let Text_Remove = document.getElementById('removeSelectionBtn');
document.querySelector('[data-friends-tab="requests"]').addEventListener('click', function () { document.querySelector('[data-friends-tab="requests"]').addEventListener('click', function () {
Tab = "requests" Tab = 'requests';
Container.style.display = ''; Container.style.display = '';
Text.style.display = 'none'; Text.style.display = 'none';
document.querySelectorAll('input[type="check"]').forEach(element => {element.remove();}); document.querySelectorAll('input[type="check"]').forEach((element) => {
element.remove();
});
}); });
document.querySelector('[data-friends-tab="friends"]').addEventListener('click', function () { document.querySelector('[data-friends-tab="friends"]').addEventListener('click', function () {
Tab = "friends" Tab = 'friends';
Container.style.display = 'none'; Container.style.display = 'none';
Text.style.display = ''; Text.style.display = '';
}); });
var ConfirmRemove = 0 var ConfirmRemove = 0;
Text_View.addEventListener('click', function () {}); Text_View.addEventListener('click', function () {});
Text_Clear.addEventListener('click', function () { Text_Clear.addEventListener('click', function () {
SelectedFriends = [] SelectedFriends = [];
UpdateCheckboxes(); UpdateCheckboxes();
Text_Span.innerText = SelectedFriends.length Text_Span.innerText = SelectedFriends.length;
}); });
Text_Remove.addEventListener('click', function () { Text_Remove.addEventListener('click', function () {
ConfirmRemove = ConfirmRemove + 1 ConfirmRemove = ConfirmRemove + 1;
switch (ConfirmRemove) { switch (ConfirmRemove) {
case 0: case 0:
Text_Remove.innerText = 'Remove Selected Friends' Text_Remove.innerText = 'Remove Selected Friends';
break break;
case 1: case 1:
Text_Remove.innerText = 'Are you sure?' Text_Remove.innerText = 'Are you sure?';
break break;
case 2: case 2:
for (let i = 0; i < SelectedFriends.length; i++) { for (let i = 0; i < SelectedFriends.length; i++) {
setTimeout(function () {}, 110) setTimeout(function () {}, 110);
let NewDeclineBtn = document.createElement('a') let NewDeclineBtn = document.createElement('a');
NewDeclineBtn.style.display = 'none' NewDeclineBtn.style.display = 'none';
NewDeclineBtn.classList = 'btn btn-danger' NewDeclineBtn.classList = 'btn btn-danger';
NewDeclineBtn.setAttribute('data-user-id', SelectedFriends[i]) NewDeclineBtn.setAttribute('data-user-id', SelectedFriends[i]);
NewDeclineBtn.setAttribute('onclick', 'declineFriendRequest(this)') NewDeclineBtn.setAttribute('onclick', 'declineFriendRequest(this)');
FriendsContainer.appendChild(NewDeclineBtn) FriendsContainer.appendChild(NewDeclineBtn);
NewDeclineBtn.click(); NewDeclineBtn.click();
} }
SelectedFriends = [] SelectedFriends = [];
UpdateCheckboxes(); UpdateCheckboxes();
Text_Remove.innerText = 'Remove Selected Friends' Text_Remove.innerText = 'Remove Selected Friends';
ConfirmRemove = 0 ConfirmRemove = 0;
break break;
} }
}); });
const observer = new MutationObserver(function () { const observer = new MutationObserver(function () {
if (FriendsContainer.children.length > 0 && Tab === "friends") { if (FriendsContainer.children.length > 0 && Tab === 'friends') {
LoadCheckBoxes(); LoadCheckBoxes();
} }
}); });
observer.observe(FriendsContainer, {childList: true, subtree: false}); observer.observe(FriendsContainer, {childList: true, subtree: false});
function LoadCheckBoxes() { function LoadCheckBoxes() {
Array.from(FriendsContainer.children).forEach(element => { Array.from(FriendsContainer.children).forEach((element) => {
let DeclineBtn = element.querySelector('a.btn.btn-danger') let DeclineBtn = element.querySelector('a.btn.btn-danger');
let UserID = DeclineBtn.getAttribute('data-user-id') let UserID = DeclineBtn.getAttribute('data-user-id');
let Column = document.createElement('div') let Column = document.createElement('div');
let EditColumn = element.querySelector('.col-9') let EditColumn = element.querySelector('.col-9');
Column.classList = 'col-auto' Column.classList = 'col-auto';
var NewCheckBox = document.createElement('button') var NewCheckBox = document.createElement('button');
NewCheckBox.classList = 'polyplus-multiremovefr-checkbox' NewCheckBox.classList = 'polyplus-multiremovefr-checkbox';
NewCheckBox.setAttribute('style', 'padding: 20px; background-color: #191919; border: 1px solid #393939; border-radius: 1rem;') NewCheckBox.setAttribute('style', 'padding: 20px; background-color: #191919; border: 1px solid #393939; border-radius: 1rem;');
var Index = SelectedFriends.indexOf(UserID) var Index = SelectedFriends.indexOf(UserID);
if (Index !== -1) { if (Index !== -1) {
DeclineBtn.classList.add('disabled') DeclineBtn.classList.add('disabled');
NewCheckBox.style.borderColor = 'lime' NewCheckBox.style.borderColor = 'lime';
} }
EditColumn.classList.remove('col-9') EditColumn.classList.remove('col-9');
EditColumn.classList.add('col') EditColumn.classList.add('col');
Column.appendChild(NewCheckBox) Column.appendChild(NewCheckBox);
EditColumn.parentElement.appendChild(Column) EditColumn.parentElement.appendChild(Column);
NewCheckBox.addEventListener('click', function () { NewCheckBox.addEventListener('click', function () {
var Index = SelectedFriends.indexOf(UserID) var Index = SelectedFriends.indexOf(UserID);
if (Index === -1) { if (Index === -1) {
DeclineBtn.classList.add('disabled') DeclineBtn.classList.add('disabled');
SelectedFriends.push(UserID) SelectedFriends.push(UserID);
NewCheckBox.style.borderColor = 'lime' NewCheckBox.style.borderColor = 'lime';
} else { } else {
SelectedFriends.splice(Index, 1) SelectedFriends.splice(Index, 1);
NewCheckBox.style.borderColor = '#393939' NewCheckBox.style.borderColor = '#393939';
DeclineBtn.classList.remove('disabled') DeclineBtn.classList.remove('disabled');
} }
Text_Span.innerText = SelectedFriends.length Text_Span.innerText = SelectedFriends.length;
UpdateCheckboxes(); UpdateCheckboxes();
}); });
}); });
} }
function UpdateCheckboxes() { function UpdateCheckboxes() {
document.querySelectorAll('.polyplus-multiremovefr-checkbox').forEach(element => { document.querySelectorAll('.polyplus-multiremovefr-checkbox').forEach((element) => {
let Parent = element.parentElement.parentElement.parentElement.parentElement.parentElement let Parent = element.parentElement.parentElement.parentElement.parentElement.parentElement;
let DeclineBtn = Parent.querySelector('a.btn.btn-danger') let DeclineBtn = Parent.querySelector('a.btn.btn-danger');
if (element.getAttribute('disabled')) { if (element.getAttribute('disabled')) {
element.removeAttribute('disabled') element.removeAttribute('disabled');
} }
if (SelectedFriends.IndexOf(DeclineBtn.getAttribute('data-user-id')) === -1) { if (SelectedFriends.IndexOf(DeclineBtn.getAttribute('data-user-id')) === -1) {
element.style.borderColor = '#393939' element.style.borderColor = '#393939';
DeclineBtn.classList.remove('disabled') DeclineBtn.classList.remove('disabled');
if (SelectedFriends.length >= 25) { if (SelectedFriends.length >= 25) {
element.setAttribute('disabled', true) element.setAttribute('disabled', true);
} }
} else { } else {
DeclineBtn.classList.add('disabled') DeclineBtn.classList.add('disabled');
element.style.borderColor = 'lime' element.style.borderColor = 'lime';
} }
}) });
} }
} }
}); });
}, 100);

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

@ -1,190 +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
chrome.storage.sync.get(['PolyPlus_Settings'], async function(result) {
Settings = result.PolyPlus_Settings || {}
if (Settings.IRLPriceWithCurrencyOn === true) {
IRLPrice()
} }
if (Settings.PinnedGamesOn === true || Settings.BestFriendsOn === true) { if (Settings.PinnedGamesOn === true) {
Update() const PlaceIDs = result.PolyPlus_PinnedGames || [];
} chrome.storage.local.get(['PolyPlus_PinnedGamesData'], async function(result){
}); let PinnedGamesData = result['PolyPlus_PinnedGamesData'] || {data:undefined};
let ContainerElement = ` const PinnedGamesContainer = document.createElement('div')
<div class="card-body p-0 m-1 scrollFadeContainer d-flex"></div>`; PinnedGamesContainer.innerHTML = `
let GameContainerElement = ` <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="scrollFade card me-2 place-card force-desktop text-center mb-2" style="opacity: 1;">
<div class="card-body"> <div class="card-body">
<div class="ratings-header"> <div class="ratings-header">
<img src=":Thumbnail" class="place-card-image" style="position: relative;"> <img src="${PlaceDetails.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;"> <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> <span>
<i id="thumbup-icn" class="thumb-icon far fa-thumbs-up"></i> ${PlaceDetails.playing}
:Likes Playing
</span>
|
<span>
<i id="thumbdown-icn" class="thumb-icon far fa-thumbs-down"></i>
:Dislikes
</span> </span>
</div> </div>
</div> </div>
<div> <div>
<div class="mt-2 mb-1 place-card-title"> <div class="mt-2 mb-1 place-card-title">
:GameName ${PlaceDetails.name}
</div> </div>
</div> </div>
</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'); if (!PlaceDetails.isActive) {
NewContainer.style.display = 'none' const PlayerCountText = PlaceCard.getElementsByClassName('p+pinned_games_playing')[0]
NewContainer.classList = 'card card-dash mcard'; PlayerCountText.children[0].classList = 'text-warning fa-duotone fa-lock'
NewContainer.style.animationDelay = '0.18s'; PlayerCountText.children[1].remove()
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) { PinnedGamesCard.appendChild(PlaceCard)
BestFriendsData = result.PolyPlus_BestFriends || [];
if (Settings.BestFriendsOn === true) {
BestFriends();
} else {
BestFriendsContainer.style.display = 'none'
Spacer.style.display = 'none'
} }
PinnedGamesCard.children[0].remove()
PinnedGamesCard.classList.add('d-flex')
Array.from(PinnedGamesCard.children).forEach((place) => {place.classList.remove('d-none')})
}); });
} }
function PinnedGames() { if (Settings.IRLPriceWithCurrency && Settings.IRLPriceWithCurrency.Enabled === true) {
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 () => { (async () => {
let Utilities = await import(chrome.runtime.getURL('/js/resources/utils.js')); Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default Utilities = Utilities.default;
const TrendingItems = document.getElementById('home-trendingItems') const TrendingItems = document.getElementById('home-trendingItems');
console.log(TrendingItems.children[1].getElementsByClassName('d-flex')[0].children)
for (let item of TrendingItems.children[1].getElementsByClassName('d-flex')[0].children) { for (let item of TrendingItems.children[1].getElementsByClassName('d-flex')[0].children) {
const Price = item.getElementsByClassName('text-success')[0] const Price = item.getElementsByClassName('text-success')[0];
console.log(item, Price) if (Price !== undefined) {
const Result = await Utilities.CalculateIRL(Price.innerText, Settings.IRLPriceWithCurrencyCurrency) const IRLResult = await Utilities.CalculateIRL(Price.innerText, Settings.IRLPriceWithCurrency.Currency);
let Span = document.createElement('span') let Span = document.createElement('span');
Span.classList = 'text-muted polyplus-price-tag' Span.classList = 'text-muted polyplus-price-tag';
Span.style.fontSize = '0.7rem' Span.style = 'font-size: 0.7rem; font-weight: lighter;';
Span.innerText = "($" + Result.bricks + " " + Result.display + ")" Span.innerText = '($' + IRLResult.result + ' ' + IRLResult.display + ')';
Price.appendChild(Span) 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,47 +1,47 @@
ExpandMessages() ExpandMessages();
function ExpandMessages() { function ExpandMessages() {
const Messages = document.getElementById('messages') const Messages = document.getElementById('messages');
for (let message of Messages.children) { for (let message of Messages.children) {
let Expanded = false let Expanded = false;
let ContentDiv = null let ContentDiv = null;
const ViewButton = message.querySelector('a.btn[href^="/inbox/messages"]') const ViewButton = message.querySelector('a.btn[href^="/inbox/messages"]');
const MessageID = ViewButton.getAttribute('href').split('/')[3] const MessageID = ViewButton.getAttribute('href').split('/')[3];
const ExpandButton = document.createElement('button') const ExpandButton = document.createElement('button');
ExpandButton.classList = 'btn btn-outline-warning px-4 mt-1' ExpandButton.classList = 'btn btn-outline-warning px-4 mt-1';
ExpandButton.innerText = 'Expand' ExpandButton.innerText = 'Expand';
ViewButton.parentElement.appendChild(ExpandButton) ViewButton.parentElement.appendChild(ExpandButton);
ExpandButton.addEventListener('click', function () { ExpandButton.addEventListener('click', function () {
if (ContentDiv === null) { if (ContentDiv === null) {
fetch('https://polytoria.com/inbox/messages/' + MessageID) fetch('https://polytoria.com/inbox/messages/' + MessageID)
.then(response => { .then((response) => {
if (!response.ok) { if (!response.ok) {
throw new Error('Network not ok') throw new Error('Network not ok');
} }
return response.text() return response.text();
}) })
.then(data => { .then((data) => {
const Doc = new DOMParser().parseFromString(data, 'text/html') const Doc = new DOMParser().parseFromString(data, 'text/html');
const MessageContent = Doc.querySelector('p.mb-0').innerText const MessageContent = Doc.querySelector('p.mb-0').innerText;
ContentDiv = document.createElement('div') ContentDiv = document.createElement('div');
ContentDiv.classList = 'py-2' ContentDiv.classList = 'py-2';
ContentDiv.innerText = MessageContent ContentDiv.innerText = MessageContent;
message.appendChild(ContentDiv) message.appendChild(ContentDiv);
}) })
.catch(error => { .catch((error) => {
console.log(error) console.log(error);
}); });
} }
Expanded = !Expanded Expanded = !Expanded;
ExpandButton.innerText = (Expanded === false) ? 'Expand' : 'Minimize' ExpandButton.innerText = Expanded === false ? 'Expand' : 'Minimize';
ContentDiv.style.display = (Expanded === false) ? 'none' : 'block' ContentDiv.style.display = Expanded === false ? 'none' : 'block';
}); });
} }
} }

View file

@ -1,19 +1,102 @@
if (window.location.pathname.split('/')[3] === "inventory") { /*
const UserID = window.location.pathname.split('/')[2] Proper pagination will be added in v1.2.3
if (UserID === JSON.parse(window.localStorage.getItem('account_info')).ID) { */
let Nav = document.getElementsByClassName('nav-pills')[0]
let WishlistNav = document.createElement('li') Username = window.location.pathname.split('/')[2];
WishlistNav.classList.add('nav-item') let UserID;
let ItemGrid;
let _Utilities;
if (window.location.pathname.split('/')[3] === 'inventory') {
!(async () => {
UserID = (await (await fetch('https://api.polytoria.com/v1/users/find?username=' + Username )).json()).id;
ItemGrid = document.getElementsByClassName('itemgrid')[0];
/*
Rewritten item wishlist function will take in the data with a parameter instead
*/
chrome.storage.sync.get(['PolyPlus_Settings'], async function(result){
_Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
_Utilities = _Utilities.default
Settings = result.PolyPlus_Settings
const Nav = document.getElementsByClassName('nav-pills')[0];
if (Settings.ItemWishlistOn && Username === JSON.parse(window.localStorage.getItem('p+account_info')).Username) {
let WishlistNav = document.createElement('li');
WishlistNav.classList.add('nav-item');
WishlistNav.innerHTML = ` WishlistNav.innerHTML = `
<a href="/users/${UserID}/inventory/wishlist/" class="nav-link"> <a href="${ (window.location.pathname.split('/')[4] !== '') ? '../wishlist/' : 'wishlist/' }" class="nav-link">
<i class="fa-regular fa-sparkles me-1"></i> <i class="fa-regular fa-list me-1"></i>
<span class="pilltitle">Item Wishlist</span> <span class="pilltitle">Item Wishlist</span>
</a> </a>
` `;
Nav.appendChild(WishlistNav) Nav.appendChild(WishlistNav);
if (window.location.pathname.split('/')[4] === "wishlist") { if (window.location.pathname.split('/')[4] === 'wishlist') {
const ItemGrid = document.getElementsByClassName('itemgrid')[0] 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()
}
}
if (Settings.CollectibleInventoryCatOn) {
let CollectibleNav = document.createElement('li');
CollectibleNav.classList.add('nav-item');
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);
if (window.location.pathname.split('/')[4] === 'collectibles') {
Array.from(Nav.children).forEach((element) => {
element = element.children[0];
if (!(element === CollectibleNav)) {
if (element.classList.contains('active')) {
element.classList.remove('active');
}
}
});
CollectibleNav.children[0].classList.add('active');
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);
CollectibleCategory()
}
}
});
})();
}
/*
Item Wishlist will be rewritten in v1.2.3
*/
function ItemWishlist() {
const ItemGrid = document.getElementsByClassName('itemgrid')[0];
const ItemCardContents = ` const ItemCardContents = `
<a href="/store/:ItemID" class="text-reset"> <a href="/store/:ItemID" class="text-reset">
<div class="card mb-2"> <div class="card mb-2">
@ -30,22 +113,12 @@ if (window.location.pathname.split('/')[3] === "inventory") {
by <a href="/users/:CreatorID" class="text-muted">:CreatorName</a> by <a href="/users/:CreatorID" class="text-muted">:CreatorName</a>
</small> </small>
<button class="polyplus-itemwish-removebtn btn btn-danger btn-sm" style="width: 100%;">X</button> <button class="polyplus-itemwish-removebtn btn btn-danger btn-sm" style="width: 100%;">X</button>
` `;
Array.from(ItemGrid.children).forEach(element => { ItemGrid.innerHTML = ''
element.remove(); document.getElementsByClassName('pagination')[0].remove()
}); const Search = document.createElement('div');
Array.from(Nav.children).forEach(element => { Search.classList = 'row';
element = element.children[0]
if (!(element === WishlistNav)) {
if (element.classList.contains('active')) {
element.classList.remove('active')
}
}
});
WishlistNav.children[0].classList.add('active')
const Search = document.createElement('div')
Search.classList = 'row'
Search.innerHTML = ` Search.innerHTML = `
<div class="col-auto"> <div class="col-auto">
<select class="form-select" id="polyplus-itemwish-type" style="width: 150px;"> <select class="form-select" id="polyplus-itemwish-type" style="width: 150px;">
@ -64,8 +137,8 @@ if (window.location.pathname.split('/')[3] === "inventory") {
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="polyplus-itemwish-isLimited"> <input class="form-check-input" type="checkbox" value="" id="polyplus-itemwish-isLimited">
<label class="form-check-label" for="polyplus-itemwish-isLimited"> <label class="form-check-label" for="polyplus-itemwish-isLimited">
Is Limited? Is Collectible?
<span class="text-muted" style="font-size: 0.65rem; display: block;">Items that are limited</span> <span class="text-muted" style="font-size: 0.65rem; display: block;">Items that are collectible</span>
</label> </label>
</div> </div>
</div> </div>
@ -78,109 +151,202 @@ if (window.location.pathname.split('/')[3] === "inventory") {
</label> </label>
</div> </div>
</div> </div>
` `;
ItemGrid.parentElement.prepend(document.createElement('br'), ItemGrid.parentElement.children[0]) ItemGrid.parentElement.prepend(document.createElement('br'), ItemGrid.parentElement.children[0]);
ItemGrid.parentElement.prepend(Search, ItemGrid.parentElement.children[0]) ItemGrid.parentElement.prepend(Search, ItemGrid.parentElement.children[0]);
let Type = document.getElementById('polyplus-itemwish-type') let Type = document.getElementById('polyplus-itemwish-type');
let SearchBar = document.getElementById('polyplus-itemwish-searchbar') let SearchBar = document.getElementById('polyplus-itemwish-searchbar');
let IsLimited = document.getElementById('polyplus-itemwish-isLimited') let IsLimited = document.getElementById('polyplus-itemwish-isLimited');
let IsAvailable = document.getElementById('polyplus-itemwish-isAvailable') let IsAvailable = document.getElementById('polyplus-itemwish-isAvailable');
Type.addEventListener('change', function () { Type.addEventListener('change', function () {
Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked) Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked);
}); });
SearchBar.addEventListener('change', function () { SearchBar.addEventListener('change', function () {
Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked) Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked);
}); });
IsLimited.addEventListener('change', function () { IsLimited.addEventListener('change', function () {
Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked) Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked);
}); });
IsAvailable.addEventListener('change', function () { IsAvailable.addEventListener('change', function () {
Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked) Update(Type.options[Type.selectedIndex].value, SearchBar.value, IsLimited.checked, IsAvailable.checked);
}); });
chrome.storage.sync.get(['PolyPlus_ItemWishlist'], function (result) { chrome.storage.sync.get(['PolyPlus_ItemWishlist'], function (result) {
let Wishlist = result.PolyPlus_ItemWishlist || []; let Wishlist = result.PolyPlus_ItemWishlist || [];
console.log('wishlist: ', Wishlist) console.log('wishlist: ', Wishlist);
Wishlist.forEach(element => { Wishlist.forEach((element) => {
let NewItemCard = document.createElement('div') let NewItemCard = document.createElement('div');
NewItemCard.classList = 'px-0' NewItemCard.classList = 'px-0';
fetch('https://api.polytoria.com/v1/store/:id'.replace(':id', element)) fetch('https://api.polytoria.com/v1/store/' + element)
.then(response => response.json()) .then((response) => response.json())
.then(data => { .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) 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) { if (data.isLimited === true) {
NewItemCard.innerHTML = NewItemCard.innerHTML.replace(':LimitedTag', '<div class="ribbon ribbon-limited ribbon-top-right"><span>Limited</span></div>') NewItemCard.innerHTML = NewItemCard.innerHTML.replace(':LimitedTag', '<div class="ribbon ribbon-limited ribbon-top-right"><span>Limited</span></div>');
} else { } else {
NewItemCard.innerHTML = NewItemCard.innerHTML.replace(':LimitedTag', '') NewItemCard.innerHTML = NewItemCard.innerHTML.replace(':LimitedTag', '');
} }
NewItemCard.setAttribute('data-id', data.id) NewItemCard.setAttribute('data-id', data.id);
NewItemCard.setAttribute('data-name', data.name) NewItemCard.setAttribute('data-name', data.name);
NewItemCard.setAttribute('data-type', data.type) NewItemCard.setAttribute('data-type', data.type);
NewItemCard.setAttribute('data-creator', data.creator.name) NewItemCard.setAttribute('data-creator', data.creator.name);
NewItemCard.setAttribute('data-limited', data.isLimited) NewItemCard.setAttribute('data-limited', data.isLimited);
if (data.isLimited === false) { if (data.isLimited === false) {
NewItemCard.setAttribute('data-price', data.price) NewItemCard.setAttribute('data-price', data.price);
} }
ItemGrid.appendChild(NewItemCard) ItemGrid.appendChild(NewItemCard);
NewItemCard.getElementsByClassName('polyplus-itemwish-removebtn')[0].addEventListener('click', function () { NewItemCard.getElementsByClassName('polyplus-itemwish-removebtn')[0].addEventListener('click', function () {
let Index = Wishlist.indexOf(parseInt(NewItemCard.getAttribute('data-id'))) let Index = Wishlist.indexOf(parseInt(NewItemCard.getAttribute('data-id')));
if (Index === -1) { if (Index === -1) {
NewItemCard.remove(); NewItemCard.remove();
return return;
} else { } else {
Wishlist.splice(Index, 1) Wishlist.splice(Index, 1);
console.log(Wishlist) console.log(Wishlist);
NewItemCard.remove(); NewItemCard.remove();
} }
chrome.storage.sync.set({'PolyPlus_ItemWishlist': Wishlist, arrayOrder: true}, function() { chrome.storage.sync.set({PolyPlus_ItemWishlist: Wishlist, arrayOrder: true}, function () {
console.log('ItemWishlist successfully saved: ' + ItemWishlist) console.log('ItemWishlist successfully saved: ' + ItemWishlist);
}); });
}); });
}) })
.catch(error => { .catch((error) => {
console.error('Error:', error); console.error('Error:', error);
}); });
}); });
}); });
}
}
}
function Update(type, query, isLimited, isAvailable) { const Update = function(type, query, isLimited, isAvailable) {
let ItemGrid = document.getElementsByClassName('itemgrid')[0] let ItemGrid = document.getElementsByClassName('itemgrid')[0];
let BrickBalance = parseInt(JSON.parse(window.localStorage.getItem('account_info')).Bricks) let BrickBalance = parseInt(JSON.parse(window.localStorage.getItem('p+account_info')).Bricks);
query = query.toLowerCase(); query = query.toLowerCase();
let Results = Array.from(ItemGrid.children) let Results = Array.from(ItemGrid.children);
for (let i = 0; i < Results.length; i++) { for (let i = 0; i < Results.length; i++) {
let Show = true let Show = true;
console.log('type: ', type) console.log('type: ', type);
if (!(type === 'any')) { if (!(type === 'any')) {
console.log('isn\'t any') console.log("isn't any");
if (!(Results[i].getAttribute('data-type') === type)) {Show = false} if (!(Results[i].getAttribute('data-type') === type)) {
Show = false;
}
} }
if (!(Results[i].getAttribute('data-name').toLowerCase().startsWith(query))) {Show = false} if (!Results[i].getAttribute('data-name').toLowerCase().startsWith(query)) {
Show = false;
}
if (isLimited === true) { if (isLimited === true) {
if (!(Results[i].getAttribute('data-limited') === 'true')) {Show = false} if (!(Results[i].getAttribute('data-limited') === 'true')) {
Show = false;
}
} }
if (isAvailable === true) { if (isAvailable === true) {
if (!(parseInt(Results[i].getAttribute('data-price')) <= BrickBalance)) {Show = false} if (!(parseInt(Results[i].getAttribute('data-price')) <= BrickBalance)) {
Show = false;
}
} }
if (Show === true) { if (Show === true) {
Results[i].style.display = 'block' Results[i].style.display = 'block';
} else { } else {
Results[i].style.display = 'none' Results[i].style.display = 'none';
} }
} }
} }
}
async function CollectibleCategory() {
ItemGrid.innerHTML = ''
document.getElementsByClassName('pagination')[0].remove()
const CollectibleItemTypes = [
"hat",
"face",
"tool"
]
const Collectibles = []
for (let type of CollectibleItemTypes) {
const InitialInventory = (await (await fetch('https://api.polytoria.com/v1/users/' + UserID + '/inventory?type=' + type + '&limit=100')).json())
Collectibles.push(...InitialInventory.inventory.filter((x) => x.asset.isLimited === true))
if (InitialInventory.pages > 1) {
if (InitialInventory.pages > 12) {
InitialInventory.pages = 12
}
for (let page = 2; page < InitialInventory.pages; page++) {
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))
}
}
}
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,140 +1,258 @@
const UserID = window.location.pathname.split('/')[2]; let Username = window.location.pathname.split('/')[2];
const AvatarRow = document.getElementsByClassName('d-flex flex-row flex-nowrap overflow-x-scroll px-3 px-lg-0 mb-2 mb-lg-0')[0] 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 AvatarHeading = document.querySelector('.section-title:has(i.fa-user-crown)');
var Settings; var Settings;
var BestFriends; var BestFriends;
let FavoriteBtn; let FavoriteBtn;
let CalculateButton; let CalculateButton;
let AvatarIFrame;
if (UserID && !isNaN(UserID)) { let Utilities;
chrome.storage.sync.get(['PolyPlus_Settings'], function(result) {
Settings = result.PolyPlus_Settings || { if (Username) {
IRLPriceWithCurrencyOn: false, (async () => {
BestFriendsOn: false, UserID = (await (await fetch('https://api.'+window.location.hostname+'/v1/users/find?username=' + Username )).json());
OutfitCostOn: true Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default;
if (document.querySelector('.card .fa-ban')) {
Blocked = true;
} }
if (Settings.IRLPriceWithCurrencyOn === true) { chrome.storage.sync.get(['PolyPlus_Settings'], async function (result) {
IRLPrice() 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) {
IRLPrice();
} }
if (Settings.BestFriendsOn === true) { if (Settings.BestFriendsOn === true) {
BestFriends() BestFriends();
} }
if (Settings.OutfitCostOn === true) { if (Settings.OutfitCostOn === true) {
CalculateButton = document.createElement('button') CalculateButton = document.createElement('small');
CalculateButton.classList = 'btn btn-warning btn-sm' CalculateButton.classList = 'fw-normal text-success';
CalculateButton.innerText = 'Calculate Avatar Cost' CalculateButton.style.letterSpacing = '0px';
AvatarRow.parentElement.parentElement.prepend(CalculateButton) CalculateButton.setAttribute('data-bs-toggle', 'tooltip');
AvatarRow.parentElement.style.marginTop = '10px' CalculateButton.setAttribute('data-bs-title', "Calculate this avatar's cost!");
CalculateButton.setAttribute('data-bs-placement', 'right');
CalculateButton.innerHTML = `
<a class="text-decoration-underline text-success" style="text-decoration-color: rgb(15, 132, 79) !important;">$ calculate</a>
`;
AvatarHeading.appendChild(CalculateButton);
Utilities.InjectResource('registerTooltips');
let Calculating = false;
CalculateButton.addEventListener('click', function () { CalculateButton.addEventListener('click', function () {
OutfitCost() if (Calculating === false) {
Calculating = true;
CalculateButton.innerText = '$ Calculating...';
OutfitCost();
}
}); });
} }
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'
}
})
}
}); });
const AvatarIFrame = document.querySelector('[src^="/ptstatic"]') if (new URLSearchParams(window.location.search).get('anniversary') === '1') {
const DropdownMenu = document.getElementsByClassName('dropdown-menu dropdown-menu-right')[0] 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 CopyItem = document.createElement('a') const AnniversaryNumber = (new Date().getFullYear() - new Date(JoinDateRow.children[1].innerText).getFullYear())
CopyItem.classList = 'dropdown-item text-primary' BirthdayCard.innerHTML = `
CopyItem.href = '#' <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')
}
})();
if (AvatarIFrame === null) {
AvatarIFrame = document.getElementById('user-avatar-card').getElementsByTagName('iframe')[0]
}
const DropdownMenu = document.getElementsByClassName('dropdown-menu dropdown-menu-right')[0];
const CopyItem = document.createElement('a');
CopyItem.classList = 'dropdown-item text-primary';
CopyItem.href = '#';
CopyItem.innerHTML = ` CopyItem.innerHTML = `
<i class="fa-duotone fa-book"></i> <i class="fa-duotone fa-book"></i>
Copy 3D Avatar URL Copy 3D Avatar URL
` `;
DropdownMenu.appendChild(CopyItem) DropdownMenu.appendChild(CopyItem);
CopyItem.addEventListener('click', function () { CopyItem.addEventListener('click', function () {
navigator.clipboard.writeText(AvatarIFrame.src) navigator.clipboard
.writeText(AvatarIFrame.src)
.then(() => { .then(() => {
alert('Successfully copied 3D avatar URL!') alert('Successfully copied 3D avatar URL!');
}) })
.catch(() => { .catch(() => {
alert('Failure to copy 3D avatar URL.') alert('Failure to copy 3D avatar URL.');
}); });
}); });
const ShareItem = document.createElement('a') const ShareItem = document.createElement('a');
ShareItem.classList = 'dropdown-item text-warning' ShareItem.classList = 'dropdown-item text-warning';
ShareItem.href = '#' ShareItem.href = '#';
ShareItem.innerHTML = ` ShareItem.innerHTML = `
<i class="fa-duotone fa-book"></i> <i class="fa-duotone fa-book"></i>
Share your 3D Avatar URL! Share your 3D Avatar URL!
` `;
DropdownMenu.appendChild(ShareItem) DropdownMenu.appendChild(ShareItem);
ShareItem.addEventListener('click', function () { ShareItem.addEventListener('click', function () {
navigator.clipboard.writeText("Hey! Look at my Polytoria avatar in 3D [here](" + AvatarIFrame.src + ")!") navigator.clipboard
.writeText('Hey! Look at my Polytoria avatar in 3D [here](' + AvatarIFrame.src + ')!')
.then(() => { .then(() => {
alert('Successfully copied sharable 3D avatar URL!') alert('Successfully copied sharable 3D avatar URL!');
}) })
.catch(() => { .catch(() => {
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).get('ref') let Reference = new URLSearchParams(new URL(window.location.href).search);
if (Reference === null) { if (!Reference.has('ref')) {
Reference = "" Reference = '';
} }
fetch("https://api.polytoria.com/v1/users/find?username=" + Username) fetch('https://api.polytoria.com/v1/users/find?username=' + Username)
.then(response => { .then((response) => {
if (!response.ok) { if (!response.ok) {
window.location.href = window.location.origin + decodeURIComponent(Reference) window.location.href = window.location.origin + decodeURIComponent(Reference.get('ref'));
} else { } else {
return response.json() return response.json();
} }
}) })
.then(data => { .then((data) => {
window.location.href = "https://polytoria.com/users/" + data.id window.location.href = 'https://polytoria.com/users/' + data.id;
}) })
.catch(error => { .catch((error) => {
console.log("An error occurred:", error); console.log('An error occurred:', error);
}); });
} }
function IRLPrice() { async function IRLPrice() {
const NetWorthElement = document.getElementsByClassName('float-end text-success')[0]; const NetWorthElement = document.getElementsByClassName('float-end text-success')[0];
const NetWorth = parseInt(NetWorthElement.innerText.replace(/,/g, '')); const IRLResult = await Utilities.CalculateIRL(NetWorthElement.innerText, Settings.IRLPriceWithCurrency.Currency);
let IRL; NetWorthElement.innerHTML = NetWorthElement.innerHTML + '<div class="text-muted" style="font-size: 0.6rem;">(' + IRLResult.icon + IRLResult.result + ' ' + IRLResult.display + ')</div>';
let DISPLAY;
switch (Settings.IRLPriceWithCurrencyCurrency) {
case 0:
IRL = (NetWorth * 0.0099).toFixed(2)
DISPLAY = 'USD'
break
case 1:
IRL = (NetWorth * 0.009).toFixed(2)
DISPLAY = 'EUR'
break
case 2:
IRL = (NetWorth * 0.0131).toFixed(2)
DISPLAY = 'CAD'
break
case 3:
IRL = (NetWorth * 0.0077).toFixed(2)
DISPLAY = 'GBP'
break
case 4:
IRL = (NetWorth * 0.1691).toFixed(2)
DISPLAY = 'MXN'
break
case 5:
IRL = (NetWorth * 0.0144).toFixed(2)
DISPLAY = 'AUD'
break
case 6:
IRL = (NetWorth * 0.2338).toFixed(2)
DISPLAY = 'TRY'
break
}
NetWorthElement.innerText = NetWorthElement.innerText + " ($" + IRL + " " + DISPLAY + ")"
} }
function BestFriends() { function BestFriends() {
@ -143,49 +261,51 @@ 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 === 7)) { 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';
} }
} else { } else {
FavoriteBtn.innerText = 'Remove Best Friend Status (max 7/7)' FavoriteBtn.innerText = 'Remove Best Friend Status (max ' + Utilities.Limits.BestFriends + '/' + Utilities.Limits.BestFriends + ')';
} }
if (UserID !== JSON.parse(window.localStorage.getItem('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';
} }
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('account_info')).ID) { return } if (UserID.id === JSON.parse(window.localStorage.getItem('p+account_info')).ID) {
btn.setAttribute('disabled', 'true') return;
}
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 () {
console.log('BestFriends saved successfully!'); console.log('BestFriends saved successfully!');
setTimeout(function () { setTimeout(function () {
btn.removeAttribute('disabled') btn.removeAttribute('disabled');
}, 1500) }, 1500);
}); });
}); });
} }
@ -196,25 +316,25 @@ function BestFriends() {
chrome.storage.sync.get(['PolyPlus_BestFriends'], function (result) { chrome.storage.sync.get(['PolyPlus_BestFriends'], function (result) {
BestFriends = result.PolyPlus_BestFriends || []; BestFriends = result.PolyPlus_BestFriends || [];
if (!(BestFriends.length === 7)) { 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';
} }
} else { } else {
FavoriteBtn.innerText = 'Remove Best Friend Status (max 7/7)' FavoriteBtn.innerText = 'Remove Best Friend Status (max ' + Utilities.Limits.BestFriends + '/' + Utilities.Limits.BestFriends + ')';
} }
}); });
} }
}); });
function ClearBestFriends() { function ClearBestFriends() {
chrome.storage.sync.set({ 'PolyPlus_BestFriends': {"BestFriends": []}, arrayOrder: true }, function() { chrome.storage.sync.set({PolyPlus_BestFriends: {BestFriends: []}, arrayOrder: true}, function () {
console.log('BestFriends saved successfully!'); console.log('BestFriends saved successfully!');
setTimeout(function () { setTimeout(function () {
btn.removeAttribute('disabled') btn.removeAttribute('disabled');
}, 1500) }, 1500);
}); });
} }
} }
@ -222,37 +342,43 @@ function BestFriends() {
async function OutfitCost() { async function OutfitCost() {
const AvatarCost = { const AvatarCost = {
Total: 0, Total: 0,
Limiteds: 0, Collectibles: 0,
Exclusives: 0 Offsale: 0,
} HasProfileTheme: false
};
for (let item of AvatarRow.children) { for (let item of AvatarRow.children) {
const ItemID = item.getElementsByTagName('a')[0].href.split('/')[4] const ItemID = item.getElementsByTagName('a')[0].href.split('/')[4];
await fetch('https://api.polytoria.com/v1/store/' + ItemID) await fetch('https://api.polytoria.com/v1/store/' + ItemID)
.then(response => { .then((response) => {
if (!response.ok) { if (!response.ok) {
throw new Error('Network not ok') throw new Error('Network not ok');
} }
return response.json() return response.json();
}) })
.then(data => { .then((data) => {
let Price = data.price let Price = data.price;
if (data.isLimited === true) { if (data.isLimited === true) {
AvatarCost.Limiteds += 1 AvatarCost.Collectibles += 1;
Price = data.averagePrice Price = data.averagePrice;
} else if (data.sales === 0) { } else if (data.sales === 0) {
AvatarCost.Exclusives += 1 AvatarCost.Offsale += 1;
Price = 0 Price = 0;
} else if (data.type === 'profileTheme') {
AvatarCost.HasProfileTheme = true;
Price = 0;
} }
AvatarCost.Total += Price AvatarCost.Total += Price;
}) })
.catch(error => {console.log(error)}); .catch((error) => {
console.log(error);
});
} }
const ResultText = document.createElement('small');
ResultText.classList = 'fw-normal text-success';
ResultText.style.letterSpacing = '0px';
ResultText.innerHTML = `(<i class="pi pi-brick mx-1"></i> ${AvatarCost.Collectibles > 0 || AvatarCost.Offsale > 0 || AvatarCost.HasProfileTheme === true ? '~' : ''}${AvatarCost.Total.toLocaleString()} | ${AvatarCost.Collectibles > 0 ? AvatarCost.Collectibles + ' collectible' : ''}${AvatarCost.Offsale > 0 ? `, ${AvatarCost.Offsale} offsale` : ''}${AvatarCost.HasProfileTheme === true ? ', profile theme excluded' : ''})`;
const TotalCostText = document.createElement('small') CalculateButton.remove();
TotalCostText.classList = 'text-muted' AvatarHeading.appendChild(ResultText);
TotalCostText.style.padding = '20px'
TotalCostText.innerHTML = `${ (AvatarCost.Limiteds > 0 || AvatarCost.Exclusives > 0) ? '~' : '' }<i class="pi pi-brick me-2"></i> ${AvatarCost.Total.toLocaleString()}${ (AvatarCost.Limiteds > 0) ? ` (has ${AvatarCost.Limiteds} limiteds)` : '' }${ (AvatarCost.Exclusives > 0) ? ` (has ${AvatarCost.Exclusives} exclusives)` : '' }`
AvatarRow.parentElement.parentElement.prepend(TotalCostText)
AvatarRow.parentElement.style.marginTop = '10px'
} }

View file

@ -1,19 +1,16 @@
setTimeout(function () {}, 100)
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) {
let BlockedUsersCard = document.getElementsByClassName('card-body')[1] const BlockedUsersCard = document.querySelector('.card-body:has([action^="/api/users/"])');
let 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 = `
<input id="blocked-users-search" type="text" class="form-control bg-dark" placeholder="Search blocked users..."> <input id="blocked-users-search" type="text" class="form-control bg-dark" placeholder="Search blocked users...">
<button id="blocked-users-confirm" class="btn btn-secondary"><i class="fad fa-search"></i></button> <button id="blocked-users-confirm" class="btn btn-secondary"><i class="fad fa-search"></i></button>
` `;
BlockedUsersCard.insertBefore(InputGroup, BlockedUsersCard.children[0]) BlockedUsersCard.insertBefore(InputGroup, BlockedUsersCard.children[0]);
let SearchBar = document.getElementById('blocked-users-search') const SearchBar = document.getElementById('blocked-users-search');
let ConfirmBtn = document.getElementById('blocked-users-confirm')
ConfirmBtn.addEventListener('click', function(){ SearchBar.addEventListener('input', function () {
SearchBlockedUsers(SearchBar.value); SearchBlockedUsers(SearchBar.value);
}); });
@ -22,9 +19,9 @@ chrome.storage.sync.get(['PolyPlus_Settings'], function(result){
for (let i = 1; i < BlockedUsersCard.children.length; i++) { for (let i = 1; i < BlockedUsersCard.children.length; i++) {
let Username = BlockedUsersCard.children[i].getElementsByTagName('h5')[0].innerText.toLowerCase(); let Username = BlockedUsersCard.children[i].getElementsByTagName('h5')[0].innerText.toLowerCase();
if (Username.includes(query)) { if (Username.includes(query)) {
BlockedUsersCard.children[i].style.display = 'block' BlockedUsersCard.children[i].style.display = 'block';
} else { } else {
BlockedUsersCard.children[i].style.display = 'none' BlockedUsersCard.children[i].style.display = 'none';
} }
} }
} }

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

@ -1,110 +1,108 @@
setTimeout(function() {}, 100) var SelectedTrades = [];
var SelectedTrades = []
let Parent = document.getElementsByClassName('card mcard p-5 text-center text-muted')[0].parentElement let Parent = document.getElementsByClassName('card mcard p-5 text-center text-muted')[0].parentElement;
let Text = document.createElement('p') let Text = document.createElement('p');
Text.classList = 'mx-auto' Text.classList = 'mx-auto';
Text.style.textAlign = 'center' Text.style.textAlign = 'center';
Text.style.fontSize = '1.3rem' Text.style.fontSize = '1.3rem';
Text.innerHTML = ` Text.innerHTML = `
<span>0</span> trades selected! <span>0</span> trades selected!
<br> <br>
<button id="viewSelectionBtn" class="btn btn-primary">View Selection</button> <button id="viewSelectionBtn" class="btn btn-primary">View Selection</button>
<button id="clearSelectionBtn" class="btn btn-warning">Clear Selection</button> <button id="clearSelectionBtn" class="btn btn-warning">Clear Selection</button>
<button id="cancelSelectionBtn" class="btn btn-danger">Cancel Selected Trades</button> <button id="cancelSelectionBtn" class="btn btn-danger">Cancel Selected Trades</button>
` `;
Parent.insertBefore(Text, Parent.children[0]) Parent.insertBefore(Text, Parent.children[0]);
let Text_Span = Text.querySelector('span'); let Text_Span = Text.querySelector('span');
let Text_View = document.getElementById('viewSelectionBtn'); let Text_View = document.getElementById('viewSelectionBtn');
let Text_Clear = document.getElementById('clearSelectionBtn'); let Text_Clear = document.getElementById('clearSelectionBtn');
let Text_Cancel = document.getElementById('cancelSelectionBtn'); let Text_Cancel = document.getElementById('cancelSelectionBtn');
var ConfirmCancel = 0 var ConfirmCancel = 0;
Text_View.addEventListener('click', function () {}); Text_View.addEventListener('click', function () {});
Text_Clear.addEventListener('click', function () { Text_Clear.addEventListener('click', function () {
SelectedTrades = [] SelectedTrades = [];
UpdateCheckboxes(); UpdateCheckboxes();
Text_Span.innerText = SelectedTrades.length Text_Span.innerText = SelectedTrades.length;
}); });
Text_Cancel.addEventListener('click', function () { Text_Cancel.addEventListener('click', function () {
ConfirmCancel = ConfirmCancel + 1 ConfirmCancel = ConfirmCancel + 1;
switch (ConfirmCancel) { switch (ConfirmCancel) {
case 0: case 0:
Text_Cancel.innerText = 'Cancel Selected Trades' Text_Cancel.innerText = 'Cancel Selected Trades';
break break;
case 1: case 1:
Text_Cancel.innerText = 'Are you sure?' Text_Cancel.innerText = 'Are you sure?';
break break;
case 2: case 2:
let Success = true let Success = true;
for (let i = 0; i < SelectedTrades.length; i++) { for (let i = 0; i < SelectedTrades.length; i++) {
setTimeout(function () {}, 110) setTimeout(function () {}, 110);
console.log(SelectedTrades[i]) console.log(SelectedTrades[i]);
fetch('https://polytoria.com/api/trade/decline', { fetch('https://polytoria.com/api/trade/decline', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('input[name="_csrf"]').value 'X-CSRF-Token': document.querySelector('input[name="_csrf"]').value
}, },
body: JSON.stringify({ id: SelectedTrades[i] }), body: JSON.stringify({id: SelectedTrades[i]})
}) }).catch((error) => {
.catch(error => {
// Handle any errors // Handle any errors
console.error('Error:', error); console.error('Error:', error);
Success = false Success = false;
}); });
} }
SelectedTrades = [] SelectedTrades = [];
UpdateCheckboxes(); UpdateCheckboxes();
Text_Cancel.innerText = 'Cancel Selected Trades' Text_Cancel.innerText = 'Cancel Selected Trades';
ConfirmCancel = 0 ConfirmCancel = 0;
break break;
} }
}); });
LoadCheckBoxes(); LoadCheckBoxes();
function LoadCheckBoxes() { function LoadCheckBoxes() {
Array.from(document.getElementsByClassName('card-inbox')).forEach(element => { Array.from(document.getElementsByClassName('card-inbox')).forEach((element) => {
let ViewBtn = element.querySelector('a.btn.btn-primary') let ViewBtn = element.querySelector('a.btn.btn-primary');
let TradeID = parseInt(ViewBtn.getAttribute('href').split('/')[3]) let TradeID = parseInt(ViewBtn.getAttribute('href').split('/')[3]);
var NewCheckBox = document.createElement('button') var NewCheckBox = document.createElement('button');
NewCheckBox.classList = 'polyplus-multicanceltr-checkbox' NewCheckBox.classList = 'polyplus-multicanceltr-checkbox';
NewCheckBox.setAttribute('style', 'padding: 20px; background-color: #191919; border: 1px solid #393939; border-radius: 1rem; margin-left: 10px;') NewCheckBox.setAttribute('style', 'padding: 20px; background-color: #191919; border: 1px solid #393939; border-radius: 1rem; margin-left: 10px;');
var Index = SelectedTrades.indexOf(TradeID) var Index = SelectedTrades.indexOf(TradeID);
if (Index !== -1) { if (Index !== -1) {
NewCheckBox.style.borderColor = 'lime' NewCheckBox.style.borderColor = 'lime';
} }
ViewBtn.parentElement.appendChild(NewCheckBox) ViewBtn.parentElement.appendChild(NewCheckBox);
NewCheckBox.addEventListener('click', function () { NewCheckBox.addEventListener('click', function () {
var Index = SelectedTrades.indexOf(TradeID) var Index = SelectedTrades.indexOf(TradeID);
if (Index === -1) { if (Index === -1) {
SelectedTrades.push(TradeID) SelectedTrades.push(TradeID);
NewCheckBox.style.borderColor = 'lime' NewCheckBox.style.borderColor = 'lime';
} else { } else {
SelectedTrades.splice(Index, 1) SelectedTrades.splice(Index, 1);
NewCheckBox.style.borderColor = '#393939' NewCheckBox.style.borderColor = '#393939';
} }
Text_Span.innerText = SelectedTrades.length Text_Span.innerText = SelectedTrades.length;
UpdateCheckboxes(); UpdateCheckboxes();
}); });
}); });
} }
function UpdateCheckboxes() { function UpdateCheckboxes() {
document.querySelectorAll('.polyplus-multicanceltr-checkbox').forEach(element => { document.querySelectorAll('.polyplus-multicanceltr-checkbox').forEach((element) => {
let Parent = element.parentElement let Parent = element.parentElement;
let ViewBtn = Parent.querySelector('a.btn.btn-primary') let ViewBtn = Parent.querySelector('a.btn.btn-primary');
if (element.getAttribute('disabled')) { if (element.getAttribute('disabled')) {
element.removeAttribute('disabled') element.removeAttribute('disabled');
} }
if (SelectedTrades.IndexOf(ViewBtn.getAttribute('data-user-id')) === -1) { if (SelectedTrades.IndexOf(ViewBtn.getAttribute('data-user-id')) === -1) {
element.style.borderColor = '#393939' element.style.borderColor = '#393939';
} else { } else {
element.style.borderColor = 'lime' element.style.borderColor = 'lime';
if (SelectedTrades.length >= 10) { if (SelectedTrades.length >= 10) {
element.setAttribute('disabled', true) element.setAttribute('disabled', true);
} }
} }
}) });
} }

View file

@ -1,9 +1,7 @@
setTimeout(function () {}, 100)
/* /*
let Currencies; let Currencies;
LoadFile(chrome.runtime.getURL('js/resources/currencies.json'), function(text){ LoadFile(chrome.runtime.getURL('resources/currencies.json'), function(text){
Currencies = JSON.parse(text) Currencies = JSON.parse(text)
console.log(new Date(Currencies.Date).toLocaleDateString("en-US", {day:"numeric",month:"long",year:"numeric"}), Currencies) console.log(new Date(Currencies.Date).toLocaleDateString("en-US", {day:"numeric",month:"long",year:"numeric"}), Currencies)
}) })
@ -11,12 +9,12 @@ LoadFile(chrome.runtime.getURL('js/resources/currencies.json'), function(text){
let Utilities; let Utilities;
(async () => { (async () => {
Utilities = await import(chrome.runtime.getURL('/js/resources/utils.js')); Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default Utilities = Utilities.default;
})(); })();
let Nav = document.querySelector('.nav-pills') let Nav = document.getElementsByClassName('nav-pills')[0];
let DIV = document.createElement('div') let DIV = document.createElement('div');
DIV.innerHTML = ` DIV.innerHTML = `
<input id="polyplus-brickconverter-input" type="number" class="form-control bg-dark mb-2" placeholder="How many Bricks?"> <input id="polyplus-brickconverter-input" type="number" class="form-control bg-dark mb-2" placeholder="How many Bricks?">
<input id="polyplus-brickconverter-output" type="text" class="form-control bg-dark mb-2" placeholder="Result" disabled> <input id="polyplus-brickconverter-output" type="text" class="form-control bg-dark mb-2" placeholder="Result" disabled>
@ -39,24 +37,24 @@ DIV.innerHTML = `
<option value="5">d</option> <option value="5">d</option>
</select> </select>
--> -->
` `;
Nav.appendChild(document.createElement('hr')) Nav.appendChild(document.createElement('hr'));
Nav.appendChild(DIV) Nav.appendChild(DIV);
let Input = document.getElementById('polyplus-brickconverter-input') let Input = document.getElementById('polyplus-brickconverter-input');
let Output = document.getElementById('polyplus-brickconverter-output') let Output = document.getElementById('polyplus-brickconverter-output');
let Type = document.getElementById('polyplus-brickconverter-type') let Type = document.getElementById('polyplus-brickconverter-type');
chrome.storage.sync.get(['PolyPlus_Settings'], function (result) { chrome.storage.sync.get(['PolyPlus_Settings'], function (result) {
Type.selectedIndex = result.PolyPlus_Settings.IRLPriceWithCurrencyCurrency || 0 Type.selectedIndex = result.PolyPlus_Settings.IRLPriceWithCurrency.Currency || 0;
}); });
//let Package = document.getElementById('polyplus-brickconverter-package') //let Package = document.getElementById('polyplus-brickconverter-package')
Input.addEventListener('input', function () { Input.addEventListener('input', function () {
Update() Update();
}); });
Type.addEventListener('change', function () { Type.addEventListener('change', function () {
Update() Update();
}); });
/* /*
@ -66,16 +64,19 @@ Package.addEventListener('change', function(){
*/ */
async function Update() { async function Update() {
//let DISPLAY = Type.options[Type.selectedIndex].value if (Input.value === '') {
//let IRL = (parseInt(Input.value.replace(/,/g, '')) * Currencies.Data[Package.selectedIndex][DISPLAY]).toFixed(2) Output.value = '';
const Result = await Utilities.CalculateIRL(Input.value, Type.selectedIndex) return;
console.log(Input.value, Type.options[Type.selectedIndex].value, Result) }
Output.value = "$" + Result.bricks + " " + Result.display const IRLResult = await Utilities.CalculateIRL(Input.value, Type.selectedIndex);
Output.value = '$' + IRLResult.result + ' ' + IRLResult.display;
} }
function LoadFile(path, callback) { function LoadFile(path, callback) {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
xhr.onload = function () { return callback(this.responseText); } xhr.onload = function () {
xhr.open("GET", path, true); return callback(this.responseText);
};
xhr.open('GET', path, true);
xhr.send(); xhr.send();
} }

View file

@ -1,67 +1,505 @@
const Manifest = chrome.runtime.getManifest() const Manifest = chrome.runtime.getManifest();
const SettingsURL = chrome.runtime.getURL('settings.html');
const DefaultSettings = {
PinnedGamesOn: true,
ForumMentsOn: true,
BestFriendsOn: false,
ImprovedFrListsOn: false,
IRLPriceWithCurrency: {
Enabled: true,
Currency: 0,
Package: 0
},
IRLPriceWithCurrencyOn: true,
IRLPriceWithCurrencyCurrency: 0,
IRLPriceWithCurrencyPackage: 0,
HideNotifBadgesOn: false,
StoreOwnTagOn: true,
ThemeCreatorOn: false,
ThemeCreator: {
Enabled: false,
BGColor: null,
BGImage: null,
BGImageSize: 'fit',
PrimaryTextColor: null,
SecondaryTextColor: null,
LinkTextColor: null,
WebsiteLogo: null
},
ModifyNavOn: false,
ModifyNav: [
{
Label: 'Places',
Link: 'https://polytoria.com/places'
},
{
Label: 'Store',
Link: 'https://polytoria.com/store'
},
{
Label: 'Guilds',
Link: 'https://polytoria.com/guilds'
},
{
Label: 'People',
Link: 'https://polytoria.com/users'
},
{
Label: 'Forum',
Link: 'https://polytoria.com/forum'
}
],
MoreSearchFiltersOn: true,
ApplyMembershipTheme: {
Enabled: false,
Theme: 0
},
ApplyMembershipThemeOn: false,
ApplyMembershipThemeTheme: 0,
MultiCancelOutTradesOn: true,
ItemWishlistOn: true,
HideUpgradeBtnOn: false,
TryOnItemsOn: true,
OutfitCostOn: true,
ShowPlaceRevenueOn: true,
ReplaceItemSalesOn: false,
HoardersListOn: true,
HoardersList: {
Enabled: true,
AvatarsEnabled: false,
MinCopies: 2
},
LibraryDownloadsOn: true,
EventItemsCatOn: true,
HomeFriendCountOn: true,
HideUserAds: {
Enabled: false,
Banners: 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.storage.sync.get(['PolyPlus_Settings'], function(result){
const MergedSettings = MergeObjects((result.PolyPlus_Settings || DefaultSettings), DefaultSettings)
chrome.storage.sync.set({'PolyPlus_Settings': MergedSettings}, function(){
console.log('Successfully merged settings')
})
})
});
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: chrome.runtime.getURL('settings.html') }); 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') {
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 > Manifest.version) {
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 + ")",
requiresInteraction: true
}, 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 () {
// 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({ chrome.contextMenus.create({
title: 'Copy Asset ID', title: 'Copy ' + type + ' ID',
id: 'PolyPlus-CopyID', id: 'PolyPlus-Copy' + type + 'ID',
contexts: ['link'], contexts: ['link'],
documentUrlPatterns: ['https://polytoria.com/*'], documentUrlPatterns: ['https://polytoria.com/*', SettingsURL],
targetUrlPatterns: [ targetUrlPatterns: [
"https://polytoria.com/places/**", 'https://polytoria.com/places/**'
"https://polytoria.com/users/**", ]
"https://polytoria.com/store/**" });
})
*/
chrome.contextMenus.create({
title: 'Copy Place ID',
id: 'PolyPlus-CopyPlaceID',
contexts: ['link'],
documentUrlPatterns: ['https://polytoria.com/*', SettingsURL],
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/**'
] ]
}); });
@ -70,35 +508,53 @@ chrome.contextMenus.create({
title: 'Copy Avatar Hash', title: 'Copy Avatar Hash',
id: 'PolyPlus-CopyAvatarHash', id: 'PolyPlus-CopyAvatarHash',
contexts: ['image'], contexts: ['image'],
documentUrlPatterns: ['https://polytoria.com/*'], documentUrlPatterns: ['https://polytoria.com/*', SettingsURL],
targetUrlPatterns: [ targetUrlPatterns: [
"https://c0.ptacdn.com/thumbnails/avatars/**", 'https://c0.ptacdn.com/thumbnails/avatars/**'
"https://c0.ptacdn.com/thumbnails/avatars/**"
] ]
}); });
});
// HANDLE CONTEXT MENU ITEMS // HANDLE CONTEXT MENU ITEMS
chrome.contextMenus.onClicked.addListener(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) {
let ID = parseInt(info.linkUrl.split('/')[4]) let ID = info.linkUrl.split('/')[4];
if (info.linkUrl.split('/')[3] === 'u') {
ID = (await (await fetch('https://api.polytoria.com/v1/users/find?username=' + info.linkUrl.split('/')[4])).json()).id;
}
chrome.scripting chrome.scripting
.executeScript({ .executeScript({
target: {tabId: tab.id}, target: {tabId: tab.id},
func: CopyAssetID, func: CopyAssetID,
args: [ID] args: [ID]
}) })
.then(() => console.log("Copied ID!")); .then(() => console.log('Copied ID!'));
}
if (info.menuItemId === 'PolyPlus-CopyThreadID') {
let ID = info.linkUrl.split('/')[5];
chrome.scripting
.executeScript({
target: {tabId: tab.id},
func: CopyAssetID,
args: [ID]
})
.then(() => console.log('Copied ID!'));
} }
if (info.menuItemId === 'PolyPlus-CopyAvatarHash') { if (info.menuItemId === 'PolyPlus-CopyAvatarHash') {
let Hash = new URL(info.srcUrl).pathname.split('/')[3].replace('-icon', '').replace('.png', '') let Hash = new URL(info.srcUrl).pathname.split('/')[3].replace('-icon', '').replace('.png', '');
chrome.scripting chrome.scripting
.executeScript({ .executeScript({
target: {tabId: tab.id}, target: {tabId: tab.id},
func: CopyAvatarHash, func: CopyAvatarHash,
args: [Hash] args: [Hash]
}) })
.then(() => console.log("Copied ID!")); .then(() => console.log('Copied ID!'));
}
if (info.menuItemId === 'PolyPlus-RunUpdateNotifier') {
RunUpdateNotifier();
} }
}); });
@ -114,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) {
@ -138,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.')
}); });
} }
@ -150,9 +603,37 @@ function CopyAvatarHash(hash) {
navigator.clipboard navigator.clipboard
.writeText(hash) .writeText(hash)
.then(() => { .then(() => {
alert('Successfully copied avatar hash!') alert('Successfully copied avatar hash!');
}) })
.catch(() => { .catch(() => {
alert('Failure to copy avatar hash.') alert('Failure to copy avatar 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
function MergeObjects(obj1, obj2) {
var mergedObj = {};
// Copy the values from obj1 to the mergedObj
for (var key in obj1) {
mergedObj[key] = obj1[key];
}
// Merge the values from obj2 into the mergedObj, favoring obj2 for non-existing keys in obj1
for (var key in obj2) {
if (!obj1.hasOwnProperty(key)) {
mergedObj[key] = obj2[key];
}
}
return 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

@ -1,188 +0,0 @@
// DEBUG PAGE FOR BETA TESTING
const Version = chrome.runtime.getManifest().version
document.querySelector('#main-content .container').innerHTML = `
<style>
#main-content .container label {
font-size: 0.8rem;
color: darkgray;
}
#main-content .container label + p {
margin-bottom: 4px;
font-size: 0.9rem;
margin-top: -4px;
}
</style>
<div class="row">
<div class="col-md-2">
<div class="card">
<div class="card-body">
<p class="text-muted mb-1">Version: v${Version}</p>
<p class="text-muted mb-1">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="/users/2782" target="_blank">Index</a>
<br><br>
Beta Testers:
<ul>
<li><a href="/users/24435" target="_blank">datastore</a></li>
<li><a href="/users/17064" target="_blank">Emir</a></li>
<li><a href="/users/9219" target="_blank">InsertSoda</a></li>
<li><a href="/users/26895" target="_blank">qwp</a></li>
</ul>
</div>
<div class="col">
<label for="settingName">Edit Setting Value</label>
<p>Set a value of the extension's local settings data</p>
<div role="group" class="input-group mb-3">
<input type="text" name="settingName" id="edit-setting-name" class="form-control" placeholder="Setting Name..">
<input type="text" id="edit-setting-value" class="form-control" placeholder="New Value..">
<button class="btn btn-success" id="edit-setting">Submit</button>
</div>
<label>Load Example Data</label>
<p>Quickly clear specific parts of the extension's local data</p>
<div role="group" class="btn-group w-100 mb-3">
<button class="btn btn-secondary" id="example-pinnedgames">Load Example Pinned Games</button>
<button class="btn btn-secondary" id="example-bestfriends">Load Example Best Friends</button>
<button class="btn btn-secondary" id="example-itemwishlist">Load Example Item Wishlist</button>
</div>
<label>Clear Specific Local Data</label>
<p>Quickly clear specific parts of the extension's local data</p>
<div role="group" class="btn-group w-100 mb-3">
<button class="btn btn-secondary" id="reset-settings">Reset Settings to Defaults</button>
<button class="btn btn-secondary" id="clear-pinnedgames">Clear Pinned Games</button>
<button class="btn btn-secondary" id="clear-bestfriends">Clear Best Friends</button>
<button class="btn btn-secondary" id="clear-itemwishlist">Clear Item Wishlist</button>
</div>
<label style="color: red;">DANGER ZONE!</label>
<p>This will clear all local data associated with the extension</p>
<button class="btn btn-danger w-100" id="delete-all-data">Delete All Data</button>
</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 => {
/*
const Result = document.createElement('span')
if (data.version === Version || Math.floor((data.version - Version) * 10) === 0) {
Result.innerText = 'No updates available'
} else {
Result.innerText = Math.floor((data.version - Version) * 10) + ' updates available'
}
CheckForUpdatesButton.parentElement.insertBefore(Result, CheckForUpdatesButton)
CheckForUpdatesButton.remove()
*/
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)});
}
CheckForUpdatesButton.addEventListener('click', CheckForUpdates);
document.getElementById('edit-setting').addEventListener('click', function(){
const EditSettingName = document.getElementById('edit-setting-name')
const EditSettingValue = document.getElementById('edit-setting-value')
chrome.storage.sync.get(['PolyPlus_Settings'], function(result) {
result = result.PolyPlus_Settings
let NewValue = EditSettingValue.value
switch (NewValue) {
case 'true':
NewValue = true
break
case 'false':
NewValue = false
break
case 'null':
NewValue = null
break
case 'undefined':
NewValue = undefined
break
case parseInt(NewValue):
NewValue = parseInt(NewValue)
break
}
result[EditSettingName.value] = NewValue
chrome.storage.sync.set({ 'PolyPlus_Settings': result }, function() {
alert('Successfully set: "' + EditSettingName.value + '" to ' + NewValue)
});
});
});
document.getElementById('reset-settings').addEventListener('click', async function(){
let Utilities = await import(chrome.runtime.getURL('/js/resources/utils.js'))
Utilities = Utilities.default
chrome.storage.sync.set({ 'PolyPlus_Settings': Utilities.DefaultSettings }, function() {
alert('Successfully reset settings to their defaults!')
});
});
document.getElementById('example-pinnedgames').addEventListener('click', function(){
chrome.storage.sync.set({ 'PolyPlus_PinnedGames': [6012, 3857, 2537] }, function() {
alert('Successfully loaded example for Pinned Games!')
});
});
document.getElementById('example-bestfriends').addEventListener('click', function(){
chrome.storage.sync.set({ 'PolyPlus_BestFriends': [1, 2, 3] }, function() {
alert('Successfully loaded example for Best Friends!')
});
});
document.getElementById('example-itemwishlist').addEventListener('click', function(){
chrome.storage.sync.set({ 'PolyPlus_ItemWishlist': [31495, 31493, 31492] }, function() {
alert('Successfully loaded example for Item Wishlist!')
});
});
document.getElementById('clear-pinnedgames').addEventListener('click', function(){
chrome.storage.sync.set({ 'PolyPlus_PinnedGames': [] }, function() {
alert('Successfully cleared Pinned Games!')
});
});
document.getElementById('clear-bestfriends').addEventListener('click', function(){
chrome.storage.sync.set({ 'PolyPlus_BestFriends': [] }, function() {
alert('Successfully cleared Best Friends!')
});
});
document.getElementById('clear-itemwishlist').addEventListener('click', function(){
chrome.storage.sync.set({ 'PolyPlus_ItemWishlist': [] }, function() {
alert('Successfully cleared Item Wishlist!')
});
});
document.getElementById('delete-all-data').addEventListener('click', function(){
if (confirm("Are you sure you'd like to delete all local data associated with the extension?") === false) { return }
chrome.storage.sync.clear(function() {
alert('Successfully deleted all local data associated with the extension!')
});
});
chrome.storage.sync.getBytesInUse(["PolyPlus_Settings", "PolyPlus_PinnedGames", "PolyPlus_BestFriends", "PolyPlus_ItemWishlist"], function(bytes){
document.getElementById('data-size').innerText = bytes
});

View file

@ -1,194 +0,0 @@
var Settings;
let Theme = null;
(async () => {
let Utilities = await import(chrome.runtime.getURL('/js/resources/utils.js'));
Utilities = Utilities.default
chrome.storage.sync.get(["PolyPlus_Settings"], function(result) {
// Merge settings and expected settings to make sure all keys exist
const RawSettings = result.PolyPlus_Settings
Settings = MergeObjects(RawSettings || Utilities.DefaultSettings, Utilities.DefaultSettings);
// If theme exists, create a style element to represent it
if (Settings.ThemeCreatorOn && Settings.ThemeCreatorOn === true) {
Theme = document.createElement('style')
switch (Settings.ThemeCreator.BGImageSize) {
case 0:
Settings.ThemeCreator.BGImageSize = 'fit'
break
case 1:
Settings.ThemeCreator.BGImageSize = 'cover'
break
case 2:
Settings.ThemeCreator.BGImageSize = 'contain'
break
}
Theme.innerHTML = `
: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 {
background-color: var(--polyplus-navbgcolor) !important;
border-bottom: 1px solid var(--polyplus-navbordercolor) !important;
}
.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;
}
`
}
});
document.addEventListener('DOMContentLoaded', async function() {
if (document.getElementsByClassName('card-header')[0] && document.getElementsByClassName('card-header')[0].innerText === ' Page not found') {
return
}
// Check if Theme Exists, if so Load It
if (Settings.ThemeCreatorOn && Settings.ThemeCreatorOn === true) {
if (!(Settings.ThemeCreator.WebsiteLogo === null)) {
document.querySelector('.nav-sidebar img').setAttribute('src', Settings.ThemeCreator.WebsiteLogo)
}
}
if (Settings.ThemeCreatorOn && Settings.ThemeCreatorOn === true && Theme != null) {
document.body.prepend(Theme)
}
// Define Data
const UserData = {
Username: document.querySelector('a.text-reset.text-decoration-none[href^="/users"]').innerText.replace(/\s+/g,''),
ID: document.querySelector('.text-reset.text-decoration-none[href^="/users/"]').getAttribute('href').split('/')[2],
Bricks: document.querySelector('.brickBalanceCont').innerText.replace(/\s+/g,'')
}
window.localStorage.setItem('account_info', JSON.stringify(UserData))
document.body.setAttribute('data-URL', window.location.href)
// Add PolyPlus Settings link to Sidebar
const Parent = document.querySelector('ul.nav.nav-flush')
const Clone = Parent.querySelectorAll('li.nav-item')[0].cloneNode(true)
Clone.getElementsByTagName('a')[0].href = '/my/settings/polyplus'
Clone.getElementsByTagName('span')[0].innerText = "Poly+"
const Icon = Clone.querySelector('i')
Icon.classList = 'fa-regular fa-sparkles'
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.HideUpgradeBtnOn && Settings.HideUpgradeBtnOn === true) {
document.querySelector('.nav-sidebar a[href="/upgrade"].nav-link.py-1.nav-sidebar-link').remove()
}
if (Settings.IRLPriceWithCurrencyOn && Settings.IRLPriceWithCurrencyOn === true) {
const IRL = await Utilities.CalculateIRL(UserData.Bricks, Settings.IRLPriceWithCurrencyCurrency)
const BrickBalanceCount = [document.querySelector('.text-success .brickBalanceCount'), document.querySelector('.text-success .brickBalanceCont')]
BrickBalanceCount.forEach(element => {element.innerText = element.innerText + ` ($${IRL.bricks} ${IRL.display})`});
}
if (Settings.HideNotifBadgesOn && Settings.HideNotifBadgesOn === true) {
document.querySelectorAll('.notif-nav.notif-sidebar').forEach(element => {element.remove();});
}
});
})();
function MergeObjects(obj1, obj2) {
var mergedObj = {};
// Copy the values from obj1 to the mergedObj
for (var key in obj1) {
mergedObj[key] = obj1[key];
}
// Merge the values from obj2 into the mergedObj, favoring obj2 for non-existing keys in obj1
for (var key in obj2) {
if (!obj1.hasOwnProperty(key)) {
mergedObj[key] = obj2[key];
}
}
return mergedObj;
}

512
js/extra-pages.js Normal file
View file

@ -0,0 +1,512 @@
/*
Developer & Debug Page
Accessable at /my/settings/polyplus#dev and /my/settings/polyplus#debug
*/
document.title = 'Poly+ Debug - Polytoria';
const Version = chrome.runtime.getManifest().version;
if (window.location.pathname.split('/')[3] === 'polyplus' && window.location.hash === '#dev') {
document.addEventListener('DOMContentLoaded', function () {
document.querySelector('#main-content .container').innerHTML = `
<style>
#main-content .container label {
font-size: 0.8rem;
color: darkgray;
}
#main-content .container label + p {
margin-bottom: 4px;
font-size: 0.9rem;
margin-top: -4px;
}
</style>
<div class="text-center mb-3">
<h1 class="text-center" style="font-size: 4.6rem;">Poly+ Developer</h1>
<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>
</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">
<label for="settingName">Edit Setting Value</label>
<p>Set a value of the extension's local settings data</p>
<div role="group" class="input-group mb-3">
<input type="text" name="settingName" id="edit-setting-name" class="form-control" placeholder="Setting Name..">
<input type="text" id="edit-setting-value" class="form-control" placeholder="New Value..">
<button class="btn btn-success w-25" id="edit-setting">Submit</button>
</div>
<label>Load Example Data</label>
<p>Quickly clear specific parts of the extension's local data</p>
<div role="group" class="btn-group w-100 mb-3">
<button class="btn btn-secondary" id="example-pinnedgames">Load Example Pinned Games</button>
<button class="btn btn-secondary" id="example-bestfriends">Load Example Best Friends</button>
<button class="btn btn-secondary" id="example-itemwishlist">Load Example Item Wishlist</button>
</div>
<label>Clear Specific Local Data</label>
<p>Quickly clear specific parts of the extension's local data</p>
<div role="group" class="btn-group w-100 mb-3">
<button class="btn btn-secondary" id="reset-settings">Reset Settings to Defaults</button>
<button class="btn btn-secondary" id="clear-pinnedgames">Clear Pinned Games</button>
<button class="btn btn-secondary" id="clear-bestfriends">Clear Best Friends</button>
<button class="btn btn-secondary" id="clear-itemwishlist">Clear Item Wishlist</button>
</div>
<hr>
<div class="card">
<div class="card-header text-primary">Tools</div>
<div class="card-body">
<label>Generate <code style="color: orange;">JSON</code> for "Event Items" store category</label>
<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>
<hr>
<div class="card">
<div class="card-header" style="color: red;">Danger Zone!</div>
<div class="card-body">
<label>Clear Specific Data Locations</label>
<p>Quickly clear specific locations of the extension's local data</p>
<div role="group" class="btn-group w-100 mb-3">
<button class="btn btn-primary" id="delete-sync">Delete Sync Storage (primary, storage is backed up to Google account)</button>
<button class="btn btn-secondary" id="delete-local">Delete Local Storage (secondary, storage is only on local device)</button>
</div>
<label>Delete All Data</label>
<p>This will clear all local data associated with the extension</p>
<button class="btn btn-danger w-100 mb-3" id="delete-all-data">Delete All Data</button>
</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);
});
}
CheckForUpdatesButton.addEventListener('click', CheckForUpdates);
document.getElementById('edit-setting').addEventListener('click', function () {
const EditSettingName = document.getElementById('edit-setting-name');
const EditSettingValue = document.getElementById('edit-setting-value');
chrome.storage.sync.get(['PolyPlus_Settings'], function (result) {
result = result.PolyPlus_Settings;
let NewValue = EditSettingValue.value;
switch (NewValue) {
case 'true':
NewValue = true;
break;
case 'false':
NewValue = false;
break;
case 'null':
NewValue = null;
break;
case 'undefined':
NewValue = undefined;
break;
case parseInt(NewValue):
NewValue = parseInt(NewValue);
break;
}
result[EditSettingName.value] = NewValue;
chrome.storage.sync.set({PolyPlus_Settings: result}, function () {
alert('Successfully set: "' + EditSettingName.value + '" to ' + NewValue);
});
});
});
document.getElementById('reset-settings').addEventListener('click', async function () {
let Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default;
chrome.storage.sync.set({PolyPlus_Settings: Utilities.DefaultSettings}, function () {
alert('Successfully reset settings to their defaults!');
});
});
document.getElementById('example-pinnedgames').addEventListener('click', function () {
chrome.storage.sync.set({PolyPlus_PinnedGames: [6012, 3857, 2537]}, function () {
alert('Successfully loaded example for Pinned Games!');
});
});
document.getElementById('example-bestfriends').addEventListener('click', function () {
chrome.storage.sync.set({PolyPlus_BestFriends: [1, 2, 3]}, function () {
alert('Successfully loaded example for Best Friends!');
});
});
document.getElementById('example-itemwishlist').addEventListener('click', function () {
chrome.storage.sync.set({PolyPlus_ItemWishlist: [31495, 31493, 31492]}, function () {
alert('Successfully loaded example for Item Wishlist!');
});
});
document.getElementById('clear-pinnedgames').addEventListener('click', function () {
chrome.storage.sync.set({PolyPlus_PinnedGames: []}, function () {
alert('Successfully cleared Pinned Games!');
});
});
document.getElementById('clear-bestfriends').addEventListener('click', function () {
chrome.storage.sync.set({PolyPlus_BestFriends: []}, function () {
alert('Successfully cleared Best Friends!');
});
});
document.getElementById('clear-itemwishlist').addEventListener('click', function () {
chrome.storage.sync.set({PolyPlus_ItemWishlist: []}, function () {
alert('Successfully cleared Item Wishlist!');
});
});
document.getElementById('delete-sync').addEventListener('click', function () {
if (confirm("Are you sure you'd like to delete all sync data associated with the extension?") === false) {
return;
}
chrome.storage.sync.clear(function () {
alert('Successfully deleted all sync data associated with the extension!');
});
});
document.getElementById('delete-local').addEventListener('click', function () {
if (confirm("Are you sure you'd like to delete all local data associated with the extension?") === false) {
return;
}
chrome.storage.local.clear(function () {
alert('Successfully deleted all local data associated with the extension!');
});
});
document.getElementById('delete-all-data').addEventListener('click', function () {
if (confirm("Are you sure you'd like to delete all sync and local data associated with the extension?") === false) {
return;
}
chrome.storage.sync.clear(function () {
alert('Successfully deleted all sync data associated with the extension!');
});
chrome.storage.local.clear(function () {
alert('Successfully deleted all local data associated with the extension!');
});
});
const GenerateEventItems = document.getElementById('generate-event-items')
GenerateEventItems.addEventListener('click', async function(){
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') {
document.addEventListener('DOMContentLoaded', function () {
chrome.storage.sync.get(['PolyPlus_Settings', 'PolyPlus_PinnedGames', 'PolyPlus_BestFriends', 'PolyPlus_ItemWishlist', 'PolyPlus_AvatarSandboxOutfits', 'PolyPlus_TimePlayed'], function(sync) {
chrome.storage.local.get(['PolyPlus_InventoryCache', 'PolyPlus_GreatDivideStats', 'PolyPlus_FriendCount', 'PolyPlus_AssetDesigners'], function(local){
document.querySelector('#main-content .container').innerHTML = `
<style>
#main-content .container label {
font-size: 0.8rem;
color: darkgray;
}
#main-content .container label + p {
margin-bottom: 4px;
font-size: 0.9rem;
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);
});
}
CheckForUpdatesButton.addEventListener('click', CheckForUpdates);
chrome.storage.sync.getBytesInUse(['PolyPlus_Settings', 'PolyPlus_PinnedGames', 'PolyPlus_ItemWishlist', 'PolyPlus_TimePlayed', 'PolyPlus_AvatarSandboxOutfits'], function (sync) {
chrome.storage.local.getBytesInUse(['PolyPlus_InventoryCache'], function(local){
document.getElementById('data-size').innerText = (sync + local).toLocaleString();
})
});
})
})
});
}

View file

@ -1,34 +1,34 @@
setTimeout(function () {}, 100) setTimeout(function () {}, 100);
var Settings; var Settings;
chrome.storage.sync.get(['PolyPlus_Settings'], function (result) { chrome.storage.sync.get(['PolyPlus_Settings'], function (result) {
Settings = result.PolyPlus_Settings; Settings = result.PolyPlus_Settings;
if (!(Settings.MoreSearchFiltersOn === true)) { if (!(Settings.MoreSearchFiltersOn === true)) {
return return;
} }
let Form = document.querySelector('form[action="/forum/search"]') let Form = document.querySelector('form[action="/forum/search"]');
let SearchBtn = document.querySelector('button[type="submit"]') let SearchBtn = document.querySelector('button[type="submit"]');
let CreatedByFilter = document.createElement('div') let CreatedByFilter = document.createElement('div');
CreatedByFilter.classList = 'input-group mt-2' CreatedByFilter.classList = 'input-group mt-2';
CreatedByFilter.innerHTML = ` CreatedByFilter.innerHTML = `
<div class="form-check"> <div class="form-check">
<input class="form-check-input" type="checkbox"> <input class="form-check-input" type="checkbox">
<label class="form-check-label" for="createdBy">Created by <input type="text" class="form-control" placeholder="willemsteller" id="createdBy" name="createdBy"></label> <label class="form-check-label" for="createdBy">Created by <input type="text" class="form-control" placeholder="willemsteller" id="createdBy" name="createdBy"></label>
</div> </div>
` `;
console.log(SearchBtn) console.log(SearchBtn);
Form.insertBefore(CreatedByFilter, SearchBtn.parentElement) Form.insertBefore(CreatedByFilter, SearchBtn.parentElement);
let CreatedByFilter_Checkbox = CreatedByFilter.querySelector('input[type="checkbox"]') let CreatedByFilter_Checkbox = CreatedByFilter.querySelector('input[type="checkbox"]');
let CreatedByFilter_Input = CreatedByFilter.querySelector('input[type="text"]') let CreatedByFilter_Input = CreatedByFilter.querySelector('input[type="text"]');
let CreatedByValue = GetURLParameter("createdBy") let CreatedByValue = GetURLParameter('createdBy');
console.log(CreatedByValue) console.log(CreatedByValue);
if (CreatedByValue) { if (CreatedByValue) {
CreatedByFilter_Checkbox.setAttribute('checked', true) CreatedByFilter_Checkbox.setAttribute('checked', true);
CreatedByFilter_Input.setAttribute('value', CreatedByValue) CreatedByFilter_Input.setAttribute('value', CreatedByValue);
CreatedByFilter_Input.removeAttribute('disabled') CreatedByFilter_Input.removeAttribute('disabled');
document.querySelectorAll('.forum-entry').forEach(element => { document.querySelectorAll('.forum-entry').forEach((element) => {
console.log(element.querySelectorAll('a[href^="/users/"]')[1].innerText) console.log(element.querySelectorAll('a[href^="/users/"]')[1].innerText);
if (!(element.querySelectorAll('a[href^="/users/"]')[1].innerText === CreatedByValue)) { if (!(element.querySelectorAll('a[href^="/users/"]')[1].innerText === CreatedByValue)) {
element.remove(); element.remove();
} }

View file

@ -1,51 +1,58 @@
const ForumText = document.querySelectorAll('p:not(.text-muted):not(.mb-0)') const ForumText = document.querySelectorAll('p:not(.text-muted):not(.mb-0)');
var Settings = [] var Settings = [];
chrome.storage.sync.get(['PolyPlus_Settings'], function (result) { chrome.storage.sync.get(['PolyPlus_Settings'], function (result) {
Settings = result.PolyPlus_Settings || { Settings = result.PolyPlus_Settings || {
ForumMentsOn: false, ForumMentsOn: false,
ForumUnixStampsOn: false ForumUnixStampsOn: false
} };
if (Settings.ForumMentsOn === true) { if (Settings.ForumMentsOn === true) {
ForumMentions() ForumMentions();
} }
if (Settings.ForumUnixStampsOn === true) { if (Settings.ForumUnixStampsOn === true) {
ForumUnixTimestamps() ForumUnixTimestamps();
} }
}); });
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="/profile/${Username}?ref=${encodeURIComponent(window.location.pathname)}" class="polyplus-mention">${match[0]}</a>`) let Match;
let Replacement = Node.nodeValue;
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;
} }
text.innerHTML = FormattedText
} }
} }
function ForumUnixTimestamps() { function ForumUnixTimestamps() {
const Regex = /&lt;t:[A-Za-z0-9]+&gt;/i const Regex = /&lt;t:[A-Za-z0-9]+&gt;/i;
for (let text of ForumText) { for (let text of ForumText) {
let FormattedText = text.innerHTML let FormattedText = text.innerHTML;
let match; let match;
while ((match = Regex.exec(FormattedText)) !== null) { while ((match = Regex.exec(FormattedText)) !== null) {
const Timestamp = new Date(match[0].substring(6, match[0].length - 4) * 1000) const Timestamp = new Date(match[0].substring(6, match[0].length - 4) * 1000);
const Months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] const Months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
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;
} }
} }

52
js/guilds.js Normal file
View file

@ -0,0 +1,52 @@
const StoreItems = document.getElementById('store-items');
var Settings;
let Utilities;
chrome.storage.sync.get(['PolyPlus_Settings'], function (result) {
Settings = result.PolyPlus_Settings;
if (Settings.IRLPriceWithCurrency && Settings.IRLPriceWithCurrency.Enabled === true) {
(async () => {
Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default;
for (let item of Array.from(StoreItems.children)) {
IRLPrice(item);
}
const PreviousPage = document.querySelector('#store-prev a');
const NextPage = document.querySelector('#store-next a');
//PreviousPage.addEventListener('click', IRLPrice)
//NextPage.addEventListener('click', IRLPrice)
})();
}
});
async function IRLPrice(item) {
const Price = item.getElementsByClassName('text-success')[0];
if (Price !== undefined && Price.innerText !== 'Free') {
const IRLResult = await Utilities.CalculateIRL(Price.innerText, Settings.IRLPriceWithCurrency.Currency);
let Span = document.createElement('span');
Span.classList = 'text-muted polyplus-price-tag';
Span.style.fontSize = '0.7rem';
Span.innerText = ' ($' + IRLResult.result + ' ' + IRLResult.display + ')';
Price.appendChild(Span);
}
}
const observer = new MutationObserver(async function (list) {
for (const record of list) {
for (const element of record.addedNodes) {
if (element.tagName === 'DIV' && element.classList.value === 'col-auto mb-3') {
if (Settings.IRLPriceWithCurrency && Settings.IRLPriceWithCurrency.Enabled === true) {
IRLPrice(element);
}
}
}
observer.observe(StoreItems, {attributes: false, childList: true, subtree: false});
}
});
observer.observe(StoreItems, {attributes: false, childList: true, subtree: false});

137
js/library-download.js Normal file
View file

@ -0,0 +1,137 @@
const AssetID = window.location.pathname.split('/')[2];
const LibraryType = document.querySelectorAll('ol a')[1].innerText.toLowerCase();
const LibraryTypes = ['model', 'audio', 'decal', 'mesh', 'shirt', 'pant'];
if (LibraryTypes.some(element => LibraryType.startsWith(element))) {
chrome.storage.sync.get(['PolyPlus_Settings'], async function (result) {
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) {
return;
}
const Dropdown = document.querySelector('#app div[style] .dropdown-menu li');
const DownloadLink = document.createElement('a');
DownloadLink.classList = 'dropdown-item text-warning';
DownloadLink.href = '#';
DownloadLink.innerHTML = `<i class="fa-duotone fa-download"></i> Download`;
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) {
case LibraryType.startsWith('model'):
DownloadLink.href = 'https://api.polytoria.com/v1/models/get-model?id=' + AssetID;
break;
case LibraryType.startsWith('audio'):
const AudioBlob = new Blob([document.getElementsByTagName('audio')[0]], {type: 'octet-steam'});
DownloadLink.href = URL.createObjectURL(AudioBlob);
DownloadLink.download = document.getElementsByTagName('h1')[0].innerText + '.mp3';
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'});
DownloadLink.href = URL.createObjectURL(DecalBlob);
DownloadLink.download = document.getElementsByTagName('h1')[0].innerText + '.png';
break;
*/
}
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;
DownloadLink.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) {
DownloadLink.href = MeshURL.url;
DownloadLink.download = document.getElementsByTagName('h1')[0].innerText + '.glb';
DownloadLink.click();
} else {
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,19 +1,25 @@
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) {return} if (Settings.ApplyMembershipTheme.Enabled !== true) {
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') {
return return;
} }
if (document.getElementsByTagName('NAV')[0].classList.contains('navbar-plus') === true || document.getElementsByTagName('NAV')[0].classList.contains('navbar-plusdx') === true) { if (document.getElementsByTagName('NAV')[0].classList.contains('navbar-plus') === true || document.getElementsByTagName('NAV')[0].classList.contains('navbar-plusdx') === true) {
return return;
} }
const Navbar = document.querySelector('.navbar.navbar-expand-lg.navbar-light.bg-navbar.nav-topbar'); const Navbar = document.querySelector('.navbar.navbar-expand-lg.navbar-light.bg-navbar.nav-topbar');
@ -22,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,55 +1,61 @@
let Comments = document.getElementById('comments') /*
const Type = window.location.pathname.split('/')[1] this script will need to be updated when the new profile URLs are fully rolled out
*/
!(() => {
let Comments = document.getElementById('comments');
const Type = window.location.pathname.split('/')[1];
let CreatorID; let CreatorID;
switch (Type) { if (Type === 'store') {
case 'store':
if (document.querySelector('h5 .text-reset[href^="/users/"]')) { if (document.querySelector('h5 .text-reset[href^="/users/"]')) {
CreatorID = document.querySelector('h5 .text-reset[href^="/users/"]').getAttribute('href').split('/')[2] CreatorID = document.querySelector('h5 .text-reset[href^="/users/"]').getAttribute('href').split('/')[2];
} else {CreatorID = 1} } else {
break CreatorID = 1;
case 'places': }
CreatorID = document.querySelector('.mcard .col .text-muted [href^="/users/"]').getAttribute('href').split('/')[2] } else if (Type === 'places') {
break CreatorID = document.querySelector('.mcard .col .text-muted [href^="/users/"]').getAttribute('href').split('/')[2];
/* } else if (Type === 'feed') {
case 'feed': CreatorID = document.querySelector('p a[href^="/users/"].text-reset').getAttribute('href').split('/')[2];
CreatorID = document.querySelector('p a[href^="/users/"].text-reset').getAttribute('href').split('/')[2] } else if (Type === 'guilds') {
break CreatorID = document.querySelector('[class^="userlink-"][href^="/users/"]').getAttribute('href').split('/')[2];
*/ Comments = document.getElementById('wall-posts');
case 'guilds':
CreatorID = document.querySelector('[class^="userlink-"][href^="/users/"]').getAttribute('href').split('/')[2]
Comments = document.getElementById('wall-posts')
break
} }
Array.from(Comments.children).forEach(element => {
LoadCreatorTag(element)
});
const Observer = new MutationObserver(function (list) { const Observer = new MutationObserver(function (list) {
for (let record of list) { for (let record of list) {
for (let element of record.addedNodes) { for (let element of record.addedNodes) {
LoadCreatorTag(element) if (element.classList.contains('card')) {
LoadCreatorTag(element);
}
} }
} }
}); });
Observer.observe(Comments, {attributes: false, childList: true, subtree: false}) Observer.observe(Comments, {attributes: false, childList: true, subtree: false});
function LoadCreatorTag(element) { const LoadCreatorTag = function (element) {
let NameElement; let NameElement = element.querySelector('.text-reset[href^="/users/"]');
if (!(Type === 'guilds')) { if (Type === 'guilds') {
NameElement = element.querySelector('.text-reset[href^="/users/"]') NameElement = element.querySelector('[class^="userlink-"][href^="/users/"]');
} else {
NameElement = element.querySelector('[class^="userlink-"][href^="/users/"]')
} }
let UserID = NameElement.getAttribute('href').split('/')[2]
let UserID = NameElement.getAttribute('href').split('/')[2];
if (UserID === CreatorID) { if (UserID === CreatorID) {
let Tag = document.createElement('span') let Tag = document.createElement('span');
Tag.classList = 'badge bg-primary' Tag.classList = 'badge bg-primary';
Tag.style.marginLeft = '5px' Tag.style.marginLeft = '5px';
Tag.style.verticalAlign = 'text-top' Tag.style.verticalAlign = 'text-top';
Tag.innerText = 'CREATOR' Tag.innerText = 'CREATOR';
NameElement.appendChild(Tag) if (Type === 'guilds') {
//console.log(window.bootstrap) Tag.innerText = 'LEADER';
}
NameElement.appendChild(Tag);
//new window.bootstrap.Tooltip(Tag, {toggle:"tooltip",title:"This user is the creator of this asset!"}) //new window.bootstrap.Tooltip(Tag, {toggle:"tooltip",title:"This user is the creator of this asset!"})
} }
} };
Array.from(Comments.children).forEach((element) => {
LoadCreatorTag(element);
});
})();

View file

@ -1,89 +0,0 @@
const ID = window.location.pathname.split('/')[3]
const Form = document.querySelector('form[action="/create/place/update"]')
var Settings;
!(async () => {
ActivityToggle()
//RequestGameProfile()
})()
async function ActivityToggle() {
const Response = await fetch('https://api.polytoria.com/v1/places/'+ID)
let Status = await Response.json()
Status = Status.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/${ID}/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=' + ID + '&profile=' + encodeURIComponent(btoa(JSON.stringify(Result)))
});
}

View file

@ -3,19 +3,19 @@
*/ */
!(() => { !(() => {
return return;
const PlaceID = parseInt(window.location.pathname.split('/')[2]) const PlaceID = parseInt(window.location.pathname.split('/')[2]);
fetch('https://polytoria.com/home') fetch('https://polytoria.com/home')
.then(response => { .then((response) => {
if (!response.ok) { if (!response.ok) {
throw new Error('Network not ok') throw new Error('Network not ok');
} }
return response.text() return response.text();
}) })
.then(data => { .then((data) => {
const Parser = new DOMParser() const Parser = new DOMParser();
const Doc = Parser.parseFromString(data, 'text/html') const Doc = Parser.parseFromString(data, 'text/html');
fetch('https://polytoria.com/api/places/join', { fetch('https://polytoria.com/api/places/join', {
method: 'POST', method: 'POST',
@ -24,22 +24,26 @@
'X-Csrf-Token': Doc.querySelector('[name="_csrf"]').value 'X-Csrf-Token': Doc.querySelector('[name="_csrf"]').value
}, },
body: JSON.stringify({ body: JSON.stringify({
'placeID': PlaceID placeID: PlaceID
}) })
}) })
.then(response => { .then((response) => {
if (!response.ok) { if (!response.ok) {
throw new Error('Network not ok') throw new Error('Network not ok');
} }
return response.json() return response.json();
}) })
.then(data => { .then((data) => {
if (data.success !== true) {throw new Error(data.message)} if (data.success !== true) {
throw new Error(data.message);
}
setTimeout(function () { setTimeout(function () {
window.location.href = 'polytoria://client/' + data.token window.location.href = 'polytoria://client/' + data.token;
window.location.href = 'https://polytoria.com/places/' + PlaceID window.location.href = 'https://polytoria.com/places/' + PlaceID;
}, 5000) }, 5000);
})
.catch(error => {console.log(error)})
}) })
.catch((error) => {
console.log(error);
});
});
})(); })();

View file

@ -1,364 +1,387 @@
const GameID = window.location.pathname.split('/')[2] const PlaceID = parseInt(window.location.pathname.split('/')[2]);
const UserID = JSON.parse(window.localStorage.getItem('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];
let Utilities; let Utilities;
let PlaceDetails = null;
var Settings; var Settings;
var PinnedGamesData = [] let TimePlayed;
var PinnedGamesData = [];
let GamePinned; let GamePinned;
!(() => { let InfoColumns = document.querySelectorAll('#main-content .col:has(#likes-data-container) .card:has(.fas.fa-chart-bar) ul');
if (GameID === undefined) { return } let CalculateRevenueButton;
const DataContainer = document.getElementById('likes-data-container') const AchievementsTab = document.getElementById('achievements-tabpane');
const GamepassesTab = document.getElementById('gamepasses-tabpane');
const Achievements = Array.from(AchievementsTab.getElementsByClassName('card')) || [];
const Gamepasses = Array.from(GamepassesTab.getElementsByClassName('card')) || [];
!(() => {
if (PlaceID === undefined) {
return;
}
const DataContainer = document.getElementById('likes-data-container');
const RatingsData = { const RatingsData = {
Likes: parseInt(DataContainer.getAttribute('data-like-count')), Likes: parseInt(DataContainer.getAttribute('data-like-count')),
Dislikes: parseInt(DataContainer.getAttribute('data-dislike-count')), Dislikes: parseInt(DataContainer.getAttribute('data-dislike-count')),
Percentage: null Percentage: null
} };
RatingsData.Percentage = Math.floor((RatingsData.Likes / (RatingsData.Likes + RatingsData.Dislikes)) * 100) RatingsData.Percentage = Math.floor((RatingsData.Likes / (RatingsData.Likes + RatingsData.Dislikes)) * 100);
const RatingsContainer = document.getElementById('thumbup-btn').parentElement.parentElement const RatingsContainer = document.getElementById('thumbup-btn').parentElement.parentElement;
const PercentageLabel = document.createElement('small') const PercentageLabel = document.createElement('small');
PercentageLabel.classList = 'text-muted' PercentageLabel.classList = 'text-muted';
PercentageLabel.style.fontSize = '0.8rem' PercentageLabel.style.fontSize = '0.8rem';
PercentageLabel.style.marginLeft = '10px' PercentageLabel.style.marginLeft = '10px';
PercentageLabel.style.marginRight = '10px' PercentageLabel.style.marginRight = '10px';
if (!isNaN(RatingsData.Percentage)) { if (!isNaN(RatingsData.Percentage)) {
PercentageLabel.innerText = RatingsData.Percentage + '%' PercentageLabel.innerText = RatingsData.Percentage + '%';
} else { } else {
PercentageLabel.innerText = 'N/A' PercentageLabel.innerText = 'N/A';
} }
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 = Utilities.default;
if (Settings.PinnedGamesOn === true) { if (Settings.PinnedGamesOn === true) {
PinnedGames() PinnedGames(result.PolyPlus_PinnedGames);
} }
// Work in Progress if (Settings.InlineEditingOn === true && GameCreator === UserID) {
if (Settings.InlineEditingOn === true) { InlineEditing();
InlineEditing()
} }
// Work in Progress const Description = document.querySelector('.col:has(#likes-data-container) .card.mcard.mb-2 .card-body.p-3.small');
/* if (Settings.GameProfilesOn === true && Description !== null) {
if (Settings.GameProfilesOn === true) { const GameProfileRegex = /p\+gp;(#(?:[A-Fa-f0-9]{3}){1,2}\b(;#(?:[A-Fa-f0-9]{3}){1,2}\b)+)/gm;
GameProfiles() if (GameProfileRegex.test(Description.innerText)) {
const Info = GameProfileRegex.exec(Description.innerText)[1].split(';');
GameProfile(Info);
}
} }
*/
if (Settings.IRLPriceWithCurrencyOn === true) { if (Settings.IRLPriceWithCurrency && Settings.IRLPriceWithCurrency.Enabled === true) {
(async () => { IRLPrice();
Utilities = await import(chrome.runtime.getURL('/js/resources/utils.js')); }
Utilities = Utilities.default
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) {
const NameRow = document.createElement('li');
NameRow.innerText = 'Revenue:';
CalculateRevenueButton = document.createElement('li');
CalculateRevenueButton.classList = 'fw-normal text-success';
CalculateRevenueButton.style.letterSpacing = '0px';
CalculateRevenueButton.innerHTML = `
<a class="text-decoration-underline text-success" style="text-decoration-color: rgb(15, 132, 79) !important;">$ Calculate</a>
`;
InfoColumns[0].appendChild(NameRow);
InfoColumns[1].appendChild(CalculateRevenueButton);
let Calculating = false;
CalculateRevenueButton.addEventListener('click', function () {
if (Calculating === false) {
Calculating = true;
CalculateRevenueButton.innerText = '$ Calculating...';
PlaceRevenue();
}
});
}
if (Settings.ImprovedAchievements && Settings.ImprovedAchievements.Enabled === true && AchievementsTab.getElementsByClassName('display-3')[0] === undefined) {
if (Settings.ImprovedAchievements.ProgressBarOn && Settings.ImprovedAchievements.ProgressBarOn === true) {
AchievementProgressBar();
}
if (Settings.ImprovedAchievements.PercentageOn && Settings.ImprovedAchievements.PercentageOn === true) {
AchievementEarnedPercentage();
}
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>
`;
if (Settings.StoreOwnTagsOn === true) { const UpdatePinButtonState = function() {
OwnedTags() PinButton.classList = 'btn btn-primary btn-sm';
PinButton.disabled = false; // Ensure button is enabled initially
if (placeIDs.indexOf(PlaceID) === -1) {
// Not Pinned
if (placeIDs.length >= Utilities.Limits.PinnedGames) {
PinButton.disabled = true;
}
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');
} }
}); });
})()
async function PinnedGames() { PinButton.addEventListener('mouseleave', function() {
chrome.storage.sync.get(['PolyPlus_PinnedGames'], function(result){ if (placeIDs.indexOf(PlaceID) !== -1) {
PinnedGamesData = result.PolyPlus_PinnedGames || []; PinButton.classList.add('btn-primary');
/* PinButton.classList.remove('btn-danger');
const PinBtn = document.createElement('button'); PinButton.children[0].classList.add('fa-star');
PinBtn.classList = 'btn btn-warning btn-sm'; PinButton.children[0].classList.remove('fa-star-half-stroke');
PinBtn.style = 'position: absolute; right: 0; margin-right: 7px;'
if (PinnedGamesData[GameID]) {
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Un-pin';
} else {
if (PinnedGames.length !== 5) {
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)'
} }
}
*/
const PinBtn = document.createElement('button');
PinBtn.classList = 'btn btn-warning btn-sm';
PinBtn.style = 'position: absolute; right: 0; margin-right: 7px;'
if (PinnedGamesData.includes(parseInt(GameID))) {
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Un-pin';
} else {
if (PinnedGamesData.length !== 5) {
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)'
}
}
PinBtn.addEventListener('click', function() {
PinBtn.setAttribute('disabled', 'true')
chrome.storage.sync.get(['PolyPlus_PinnedGames'], function(result) {
PinnedGamesData = result.PolyPlus_PinnedGames || [];
/*
const Index = PinnedGames.indexOf(parseInt(GameID))
if (Index !== -1) {
//delete PinnedGames[GameID]
PinnedGames.splice(Index, 1)
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Pin'
} else {
//PinnedGames[GameID] = {lastVisited: new Date()}
PinnedGames.push(parseInt(GameID))
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Un-pin'
}
*/
const Index = PinnedGamesData.indexOf(parseInt(GameID));
if (Index !== -1) {
PinnedGamesData.splice(Index, 1);
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Pin'
} else {
PinnedGamesData.push(parseInt(GameID));
PinBtn.innerHTML = '<i class="fa-duotone fa-star"></i> Un-pin'
}
chrome.storage.sync.set({ 'PolyPlus_PinnedGames': PinnedGamesData, arrayOrder: true }, function() {
setTimeout(function() {
PinBtn.removeAttribute('disabled')
console.log(PinnedGamesData)
}, 1250)
});
});
}); });
document.getElementsByClassName('card-header')[2].appendChild(PinBtn); UpdatePinButtonState();
document.querySelector('h1.my-0').parentElement.appendChild(PinButton);
chrome.storage.onChanged.addListener(function(changes, namespace) { PinButton.addEventListener('click', function() {
if (placeIDs.indexOf(PlaceID) === -1) {
placeIDs.push(PlaceID);
} else {
placeIDs.splice(placeIDs.indexOf(PlaceID), 1);
}
// clear cache
chrome.storage.local.set({'PolyPlus_PinnedGamesData':{
data: undefined,
requested: 0
}}, function(){});
UpdatePinButtonState();
chrome.storage.sync.set({'PolyPlus_PinnedGames': placeIDs}, function() {
PinButton.disabled = true;
setTimeout(() => {
PinButton.disabled = false;
UpdatePinButtonState(); // Ensure state is updated after re-enabling
}, 750);
});
});
chrome.storage.onChanged.addListener(function(changes) {
if ('PolyPlus_PinnedGames' in changes) { if ('PolyPlus_PinnedGames' in changes) {
chrome.storage.sync.get(['PolyPlus_PinnedGames'], function(result) { placeIDs = changes.PolyPlus_PinnedGames.newValue;
PinnedGamesData = result.PolyPlus_PinnedGames || []; UpdatePinButtonState();
/*
if (PinnedGamesData[GameID]) {
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(GameID))) {
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)'
}
}
});
}
});
}); });
} }
async function InlineEditing() { async function InlineEditing() {
// Fix description editing /*
// Make it possible to edit description even if the game doesn't initially have a description INLINE EDITING TO-DO:
// Add the ability to edit the game's genre - Make it possible to edit the description even if there is no description initially
// Improve editing visuals overall - Make it possible to edit the place's genre
*/
const GameCreator = document.querySelector('#main-content .card-body .col div.text-muted a[href^="/users/"]').getAttribute('href').split('/')[2] let Editing = false;
if (GameCreator !== UserID) {
return
}
let Editing = false const Style = document.createElement('style');
const Style = document.createElement('style')
Style.innerHTML = ` Style.innerHTML = `
body[data-polyplus-inlineEditing="true"] .polyplus-inlineEditing-visible {display: block !important;} body[data-polyplus-inlineEditing="true"] .polyplus-inlineEditing-visible {display: block !important;}
.polyplus-inlineEditing-visible {display: none;} .polyplus-inlineEditing-visible {display: none;}
body[data-polyplus-inlineEditing="true"] .polyplus-inlineEditing-hidden {display: none !important;} body[data-polyplus-inlineEditing="true"] .polyplus-inlineEditing-hidden {display: none !important;}
.polyplus-inlineEditing-hidden {display: block;} .polyplus-inlineEditing-hidden {display: block;}
` `;
document.body.prepend(Style) document.body.prepend(Style);
const Inputs = [ const Inputs = [
{ {
name: "name", name: 'name',
element: null, element: null,
reference: '.card-header h1[style="font-weight:800;font-size:1.6em"]', reference: '.card-header h1[style="font-weight:800;font-size:1.6em"]',
placeholder: "Place Title..", placeholder: 'Place Title..',
required: true, required: true,
isTextarea: false, isTextarea: false,
styles: 'font-weight:800;font-size:1.6em' styles: 'font-weight:800;font-size:1.6em'
}, },
{ {
name: "description", name: 'description',
element: null, element: null,
reference: '.col:has(#likes-data-container) .card.mcard.mb-2 .card-body.p-3.small', reference: '.col:has(#likes-data-container) .card.mcard.mb-2 .card-body.p-3.small',
placeholder: "Place Description..", placeholder: 'Place Description..',
required: false, required: false,
isTextarea: true, isTextarea: true,
styles: 'height:300px; overflow-y:auto;' styles: 'height:300px; overflow-y:auto;'
} }
] ];
console.log(Inputs) console.log(Inputs);
for (let input of Inputs) { for (let input of Inputs) {
let Input = (input.isTextarea === true) ? document.createElement('textarea') : document.createElement('input') let Input = input.isTextarea === true ? document.createElement('textarea') : document.createElement('input');
input.element = Input input.element = Input;
const Reference = document.querySelector(input.reference) const Reference = document.querySelector(input.reference);
Input.classList = 'polyplus-inlineEditing-visible form-control' Input.classList = 'polyplus-inlineEditing-visible form-control';
Input.placeholder = input.placeholder Input.placeholder = input.placeholder;
Input.value = Reference.innerText Input.value = Reference.innerText;
Input.style = input.styles Input.style = input.styles;
Reference.classList.add('polyplus-inlineEditing-hidden') Reference.classList.add('polyplus-inlineEditing-hidden');
Reference.parentElement.appendChild(Input) Reference.parentElement.appendChild(Input);
} }
const PlaceGenre = document.getElementsByClassName('list-unstyled m-0 col')[0].children[3] const PlaceGenre = document.getElementsByClassName('list-unstyled m-0 col')[0].children[3];
const Genres = [ const Genres = [
"other", 'other',
"adventure", 'adventure',
"building", 'building',
"competitive", 'competitive',
"creative", 'creative',
"fighting", 'fighting',
"funny", 'funny',
"hangout", 'hangout',
"medieval", 'medieval',
"parkour", 'parkour',
"puzzle", 'puzzle',
"racing", 'racing',
"roleplay", 'roleplay',
"sandbox", 'sandbox',
"showcase", 'showcase',
"simulator", 'simulator',
"sports", 'sports',
"strategy", 'strategy',
"survival", 'survival',
"techdemo", 'techdemo',
"trading", 'trading',
"tycoon", 'tycoon',
"western" 'western'
] ];
const EditBtn = document.createElement('button'); const EditBtn = document.createElement('button');
EditBtn.classList = 'btn btn-primary btn-sm'; EditBtn.classList = 'btn btn-primary btn-sm';
EditBtn.style = 'position: absolute; right: 0; margin-right: 7px;' EditBtn.style = 'position: absolute; right: 0; margin-right: 7px;';
EditBtn.innerHTML = '<i class="fa-duotone fa-hammer"></i> <span>Edit Details</span>' EditBtn.innerHTML = '<i class="fa-duotone fa-hammer"></i> <span>Edit Details</span>';
document.getElementsByClassName('card-header')[3].appendChild(EditBtn); document.getElementsByClassName('card-header')[3].appendChild(EditBtn);
EditBtn.addEventListener('click', function () { EditBtn.addEventListener('click', function () {
Editing = !Editing Editing = !Editing;
EditBtn.children[0].classList.toggle('fa-hammer') EditBtn.children[0].classList.toggle('fa-hammer');
EditBtn.children[0].classList.toggle('fa-check-double') EditBtn.children[0].classList.toggle('fa-check-double');
EditBtn.children[0].classList.toggle('fa-fade') EditBtn.children[0].classList.toggle('fa-fade');
document.body.setAttribute('data-polyplus-inlineEditing', Editing) document.body.setAttribute('data-polyplus-inlineEditing', Editing);
if (Editing === false) { if (Editing === false) {
const Send = new FormData() const Send = new FormData();
Send.append("_csrf", document.querySelector('input[name="_csrf"]').value) Send.append('_csrf', document.querySelector('input[name="_csrf"]').value);
Send.append("id", GameID) Send.append('id', PlaceID);
for (let input of Inputs) { for (let input of Inputs) {
console.log('start of loop') console.log('start of loop');
Send.append(input.name, input.element.value) Send.append(input.name, input.element.value);
} }
console.log('after') console.log('after');
fetch('/create/place/update', {method:"POST",body:Send}) fetch('/create/place/update', {method: 'POST', body: Send})
.then(response => { .then((response) => {
if (!response.ok) { if (!response.ok) {
throw new Error('Network not ok') throw new Error('Network not ok');
} }
return response.text() return response.text();
}) })
.then(data => { .then((data) => {
console.log('Successfully edited game') console.log('Successfully edited game');
for (let input of Inputs) { for (let input of Inputs) {
const Reference = document.querySelector(input.reference) const Reference = document.querySelector(input.reference);
Reference.innerText = input.element.value Reference.innerText = input.element.value;
} }
}) })
.catch(error => { .catch((error) => {
alert('Error while saving changes') alert('Error while saving changes');
console.log('Error while editing game') console.log('Error while editing game');
});
}
}); });
} }
/* async function GameProfile(data) {
PlaceTitleSpan.setAttribute('contenteditable', Editing.toString()) document.querySelector('h1.my-0').setAttribute('game-key', 'true');
if (PlaceDesc !== null) { document.querySelector('div[style="min-height: 60vh;"]').id = 'gameprofile';
console.log('Description exists')
PlaceDesc.setAttribute('contenteditable', Editing.toString())
}
if (Editing === false) {
const Send = new FormData()
Send.append("_csrf", document.querySelector('input[name="_csrf"]').value)
Send.append("id", GameID)
Send.append("name", PlaceTitle.innerText || '')
fetch('/create/place/update', {method:"POST",body:Send}) const Style = document.createElement('style');
.then(response => {
if (!response.ok) {
throw new Error('Network not ok')
}
return response.text()
})
.then(data => {
console.log('Successfully edited game')
})
.catch(error => {
console.log('Error while editing game')
});
}
*/
});
}
//const Data = JSON.parse('{"gameTitle": "Hyper[Fart]","bg": "#000","accent": "#007bff","secondary": "#","cardBg": "#313131","font": "","text": "#fff"}')
const Data = JSON.parse('{"gameTitle":"Isolation: Brix High School","bg":"#0148af","accent":"#986c6a","secondary":"#b7d3f2","cardBg":"#313131","text":"#fff"}')
async function GameProfiles(data) {
return
data = Data
document.querySelector('h1.my-0')
.setAttribute('game-key', 'true');
document.querySelector('div[style="min-height: 60vh;"]')
.id = 'gameprofile';
const Style = document.createElement('style')
Style.innerHTML = ` Style.innerHTML = `
div#app { div#app {
background: ${Data.bg} !important; background: ${data[0]} !important;
} }
#gameprofile { #gameprofile {
/*font-family: ${Data.font} !important;*/ /*font-family: no !important;*/
color: ${Data.text} !important; color: ${data[4]} !important;
} }
#gameprofile .card { #gameprofile .card {
--bs-card-bg: ${Data.cardBg}; --bs-card-bg: ${data[3]};
} }
/* /*
@ -369,44 +392,208 @@ async function GameProfiles(data) {
*/ */
#gameprofile .card.mcard [game-key] { #gameprofile .card.mcard [game-key] {
background: linear-gradient(to bottom, ${Data.accent}, ${Data.secondary}); background: linear-gradient(to bottom, ${data[1]}, ${data[2]});
background-clip: text; background-clip: text;
-webkit-background-clip: text; -webkit-background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
} }
` `;
document.body.appendChild(Style) document.body.appendChild(Style);
} }
async function IRLPrice() { async function IRLPrice() {
const Gamepasses = document.querySelector('#gamepasses-tabpane .row.flex-row').children const Gamepasses = document.querySelector('#gamepasses-tabpane .row.flex-row').children;
for (let gamepass of Gamepasses) { for (let gamepass of Gamepasses) {
const Price = gamepass.getElementsByClassName('text-success')[0] const Price = gamepass.getElementsByClassName('text-success')[0];
const Result = await Utilities.CalculateIRL(Price.innerText, Settings.IRLPriceWithCurrencyCurrency) const IRLResult = await Utilities.CalculateIRL(Price.innerText, Settings.IRLPriceWithCurrency.Currency);
let Span = document.createElement('span') let Span = document.createElement('span');
Span.classList = 'text-muted polyplus-price-tag' Span.classList = 'text-muted polyplus-price-tag';
Span.style.fontSize = '0.7rem' Span.style.fontSize = '0.7rem';
Span.innerText = "($" + Result.bricks + " " + Result.display + ")" Span.innerText = '($' + IRLResult.result + ' ' + IRLResult.display + ')';
Price.appendChild(Span) Price.appendChild(Span);
} }
} }
async function OwnedTags() { async function PlaceRevenue() {
const BricksPerView = 5;
if (PlaceDetails === null) {
PlaceDetails = (await (await fetch('https://api.polytoria.com/v1/places/' + PlaceID)).json());
}
let CreatorDetails = await fetch('https://api.polytoria.com/v1/users/' + GameCreator);
CreatorDetails = await CreatorDetails.json();
let Total = round5(PlaceDetails.uniqueVisits) / 5;
let Revenue = round5(PlaceDetails.uniqueVisits) / 5;
let CreatorTax = 0.35;
switch (CreatorDetails.membershipType) {
case 'plus':
CreatorTax = 0.25;
break;
case 'plusDeluxe':
CreatorTax = 0.15;
break;
}
let Achievements = await fetch('https://api.polytoria.com/v1/places/' + PlaceID + '/achievements');
Achievements = await Achievements.json();
let Gamepasses = await fetch('https://api.polytoria.com/v1/places/' + PlaceID + '/gamepasses');
Gamepasses = await Gamepasses.json();
for (let gamepass of Gamepasses.gamepasses) {
const PriceAfterTax = Math.floor(gamepass.asset.price - gamepass.asset.price * CreatorTax);
Revenue += PriceAfterTax * gamepass.asset.sales;
}
/* /*
This feature is disabled due to the the public API for user inventories is broken when specifying an asset type let AchievementCost = 0
let FreeAchievements = null;
for (let achievement of Achievements.achievements) {
// decrease total by price of achievement creation based on when the achievement was created
if ()
}
*/ */
return
const Response = await fetch('https://api.polytoria.com/v1/users/' + UserID + '/inventory/') const ResultText = document.createElement('li');
const Gamepasses = document.querySelector('#gamepasses-tabpane .row.flex-row').children ResultText.classList = 'fw-normal text-success';
for (let gamepass of Gamepasses) { ResultText.style.letterSpacing = '0px';
const GamePassID = gamepass.getElementsByTagName('a')[0].getAttribute('href').split('/') ResultText.innerHTML = `<i class="pi pi-brick mx-1"></i> ~` + Revenue.toLocaleString();
console.log(GamePassID)
CalculateRevenueButton.remove();
InfoColumns[1].appendChild(ResultText);
} }
const Achievements = document.querySelector('#achievements-tabpane .row.flex-row').children function round5(number) {
for (let gamepass of Achievements) { const remainder = number % 5;
const GamePassID = gamepass.getElementsByTagName('a')[0].getAttribute('href').split('/') if (remainder < 2.5) {
console.log(GamePassID) return number - remainder;
} else {
return number + (5 - remainder);
}
}
function AchievementProgressBar() {
const AchievementCount = Achievements.length;
let AchievementsEarned = 0;
for (let achievement of Achievements) {
Achieved = achievement.getElementsByClassName('fad fa-check-circle')[0] !== undefined;
if (Achieved === true) {
AchievementsEarned++;
}
}
const PercentageEarned = ((AchievementsEarned * 100) / AchievementCount).toFixed(0);
const ProgressBar = document.createElement('div');
ProgressBar.role = 'progressbar';
ProgressBar.classList = 'progress';
ProgressBar.style.background = '#000';
ProgressBar.ariaValueNow = PercentageEarned;
ProgressBar.ariaValueMin = '0';
ProgressBar.ariaValueMax = '100';
ProgressBar.innerHTML = `<div class="progress-bar progress-bar-striped text-bg-warning" style="width: ${PercentageEarned}%">${PercentageEarned}%</div>`;
AchievementsTab.prepend(document.createElement('hr'));
AchievementsTab.prepend(ProgressBar);
}
async function AchievementEarnedPercentage() {
if (PlaceDetails === null) {
PlaceDetails = (await (await fetch('https://api.polytoria.com/v1/places/' + PlaceID)).json());
}
const GetAchievementDifficulty = function(percent) {
if (percent >= 90 && percent <= 100) {
return 'Freebie';
} else if (percent >= 80 && percent <= 89.9) {
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';
}
}
const UserCount = PlaceDetails.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) + ')';
}
}
async function ReaddCopyable() {
if (PlaceDetails === null) {
PlaceDetails = (await (await fetch('https://api.polytoria.com/v1/places/' + PlaceID)).json());
}
if (PlaceDetails.isCopyable) {
const TitleCardButtons = document.querySelector('.card:has(h1.my-0) .col-auto[style^="m"]')
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,23 +0,0 @@
const InExtensionSettings = (window.location.pathname.split('/')[3] === "polyplus")
if (InExtensionSettings === true) { window.location.href = chrome.runtime.getURL('settings.html') }
document.addEventListener('DOMContentLoaded', function(){
const Nav = document.getElementsByClassName('nav nav-pills')[0]
if (InExtensionSettings === true) {
Array.from(Nav.children).forEach(item => {
if (item.classList.contains('active')) {
item.classList.remove('active')
}
})
}
const PolyPlusItem = document.createElement('a')
PolyPlusItem.classList = 'nav-link'
PolyPlusItem.href = chrome.runtime.getURL('settings.html')
PolyPlusItem.innerHTML = `
<i class="fa-regular fa-sparkles me-1"></i> <span class="pilltitle">Poly+</span>
`
Nav.insertBefore(PolyPlusItem, Nav.getElementsByTagName('hr')[0])
});

View file

@ -1,129 +0,0 @@
<style>
#options {
position: absolute;
bottom: 0;
margin: 20px;
margin-bottom: 40px;
font-size: 1.25rem;
}
#options *:not(input) {
background: transparent;
border: none;
color: #fff;
font-size: 1.25rem;
}
#options *:not(input):not(:nth-child(2)) {
margin-bottom: 3.5px;
}
</style>
<div class="row">
<div class="col-12 col-lg-3">
<div class="card mcard mb-3">
<h6 class="card-header">
<i class="fad fa-user-crown"></i>
Avatar
</h6>
<div class="card-body">
<iframe id="viewFrame" style="width: 100%; height: 314px; border-radius: 0.65rem;"></iframe>
<div id="options">
<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;">
<button aria-label="Upload JSON!" onclick="this.parentElement.previousElementSibling.click()">
<i class="fa-duotone fa-download"></i>
</button>
</label>
<button id="jsonSave" aria-label="Save as JSON!" style="display: block;">
<i class="fa-duotone fa-upload"></i>
</button>
<button id="openNewTab" aria-label="View in a new tab!" style="display: block;">
<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>
</button>
</div>
</div>
</div>
<div class="row">
<div class="col">
<button id="myself" class="btn btn-outline-primary w-100 mb-3">
<i class="fa-duotone fa-shirt"></i>
Myself
</button>
</div>
<div class="col">
<button id="clear" class="btn btn-outline-warning w-100 mb-3">
<i class="fa-duotone fa-trash"></i>
Clear
</button>
</div>
</div>
<div class="card mcard">
<h6 class="card-header">
<i class="fad fa-palette"></i>
Body Colors
</h6>
<div class="card-body">
<div class="card-body text-center" id="body-parts">
<div style="margin-bottom: 5px">
<button id="head" class="avatarAction bodypart bp1x1" style="background-color: #e0e0e0;"></button>
</div>
<div style="margin-bottom: 5px">
<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 class="avatarAction bodypart bp1x2" id="leftArm" style="background-color: #e0e0e0; margin-left: 5px;"></button>
</div>
<div>
<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>
</div>
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-9">
<ul class="nav nav-pills nav-justified mb-3" id="tabs">
<li class="nav-item">
<a class="nav-link active" data-tab="hat">
<i class="fas fa-hat-cowboy me-1"></i>
Hats
</a>
</li>
<li class="nav-item">
<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">
<i class="fas fa-tshirt me-1"></i>
Shirts
</a>
</li>
<li class="nav-item">
<a class="nav-link" data-tab="pants">
<i class="fas fa-socks me-1"></i>
Pants
</a>
</li>
</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;">
<input id="item-search" type="text" class="form-control mb-2" placeholder="Search for an item...">
<div class="row alignleft itemgrid" id="inventory"></div>
</div>
<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="row alignleft itemgrid" id="wearing"></div>
</div>
</div>
</div>

View file

@ -1,59 +0,0 @@
{
"Date": "2023-7-17",
"Data": [
{
"USD": 0.009899999999999999,
"EUR": 0.011200000000000002,
"CAD": 0.0123,
"GBP": 0.008100000000000001,
"MXN": 0.1928,
"AUD": 0.014199999999999999,
"TRY": 0.1583
},
{
"USD": 0.009072727272727274,
"EUR": 0.010163636363636363,
"CAD": 0.011290909090909091,
"GBP": 0.007363636363636363,
"MXN": 0.17534545454545455,
"AUD": 0.013418181818181819,
"TRY": 0.14616363636363636
},
{
"USD": 0.00868695652173913,
"EUR": 0.009721739130434783,
"CAD": 0.010730434782608695,
"GBP": 0.007043478260869565,
"MXN": 0.1676521739130435,
"AUD": 0.012834782608695652,
"TRY": 0.13767826086956522
},
{
"USD": 0.009087272727272727,
"EUR": 0.00996,
"CAD": 0.010625454545454546,
"GBP": 0.006981818181818182,
"MXN": 0.17294545454545454,
"AUD": 0.012698181818181819,
"TRY": 0.14424
},
{
"USD": 0.008331666666666668,
"EUR": 0.009323333333333333,
"CAD": 0.00974,
"GBP": 0.006066666666666666,
"MXN": 0.15853333333333333,
"AUD": 0.012306666666666667,
"TRY": 0.13222
},
{
"USD": 0.0079992,
"EUR": 0.008950399999999999,
"CAD": 0.0093504,
"GBP": 0.005824,
"MXN": 0.152192,
"AUD": 0.011814400000000001,
"TRY": 0.12693120000000002
}
]
}

View file

@ -1,109 +0,0 @@
/*
HOW TO USE IN CONTENT SCRIPTS:
(async () => {
let Utilities = await import(chrome.runtime.getURL('/js/resources/utils.js'));
Utilities = Utilities.default
})();
*/
export default {
DefaultSettings: {
PinnedGamesOn: true,
ForumMentsOn: true,
BestFriendsOn: false,
ImprovedFrListsOn: false,
IRLPriceWithCurrencyOn: true,
IRLPriceWithCurrencyCurrency: 0,
IRLPriceWithCurrencyPackage: 0,
HideNotifBadgesOn: false,
StoreOwnTagOn: true,
ThemeCreatorOn: false,
ThemeCreator: {
BGColor: null,
BGImage: null,
BGImageSize: 'fit',
PrimaryTextColor: null,
SecondaryTextColor: null,
LinkTextColor: null,
WebsiteLogo: null
},
ModifyNavOn: false,
ModifyNav: [
{
Label: "Play",
Link: "https://polytoria.com/places"
},
{
Label: "Store",
Link: "https://polytoria.com/store"
},
{
Label: "Guilds",
Link: "https://polytoria.com/guilds"
},
{
Label: "People",
Link: "https://polytoria.com/users"
},
{
Label: "Forum",
Link: "https://polytoria.com/forum"
}
],
MoreSearchFiltersOn: true,
ApplyMembershipThemeOn: false,
ApplyMembershipThemeTheme: 0,
MultiCancelOutTradesOn: true,
ItemWishlistOn: true,
HideUpgradeBtnOn: false
},
CalculateIRL: async function(bricks, to, brickPackage) {
/*
const response = await fetch(chrome.runtime.getURL('/js/resources/currencies.json'))
if (!response.ok) {
throw new Error('Getting currency data failure')
}
const data = await response.json()
*/
let IRL;
let DISPLAY;
//const UnitPrice = data.Data[brickPackage][to]
switch (to) {
case 0:
IRL = (bricks.replace(/,/g, '') * 0.0099).toFixed(2)
DISPLAY = 'USD'
break
case 1:
IRL = (bricks.replace(/,/g, '') * 0.009).toFixed(2)
DISPLAY = 'EUR'
break
case 2:
IRL = (bricks.replace(/,/g, '') * 0.0131).toFixed(2)
DISPLAY = 'CAD'
break
case 3:
IRL = (bricks.replace(/,/g, '') * 0.0077).toFixed(2)
DISPLAY = 'GBP'
break
case 4:
IRL = (bricks.replace(/,/g, '') * 0.1691).toFixed(2)
DISPLAY = 'MXN'
break
case 5:
IRL = (bricks.replace(/,/g, '') * 0.0144).toFixed(2)
DISPLAY = 'AUD'
break
case 6:
IRL = (bricks.replace(/,/g, '') * 0.2338).toFixed(2)
DISPLAY = 'TRY'
break
case 7:
IRL = (bricks.replace(/,/g, '') * 0.49).toFixed(2)
DISPLAY = 'BRL'
break
}
return {bricks: IRL, display: DISPLAY}
}
}

19
js/settings.js Executable file
View file

@ -0,0 +1,19 @@
const SettingsURL = chrome.runtime.getURL('settings.html');
const InExtensionSettings = window.location.pathname.split('/')[3] === 'polyplus' && window.location.hash !== '#dev' && window.location.hash !== '#debug';
if (InExtensionSettings === true) {
window.location.href = SettingsURL + window.location.hash;
}
document.addEventListener('DOMContentLoaded', function () {
const Nav = document.getElementsByClassName('nav nav-pills')[0];
const PolyPlusItem = document.createElement('a');
PolyPlusItem.classList = 'nav-link';
PolyPlusItem.href = SettingsURL;
PolyPlusItem.innerHTML = `
<i class="fa-regular fa-sparkles me-1"></i> <span class="pilltitle">Poly+</span>
`;
Nav.insertBefore(PolyPlusItem, Nav.getElementsByTagName('hr')[0]);
});

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)
}

289
js/sitewide.js Executable file
View file

@ -0,0 +1,289 @@
var Settings;
let Theme = ``;
(async () => {
let Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default;
chrome.storage.sync.get(['PolyPlus_Settings'], function (result) {
// Merge settings and expected settings to make sure all keys exist
const RawSettings = result.PolyPlus_Settings;
Settings = Utilities.MergeObjects(RawSettings || Utilities.DefaultSettings, Utilities.DefaultSettings);
const PageLoad = 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);
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) {
Needed[i].children[1].innerText = Settings.ModifyNav[i].Label;
Needed[i].href = Settings.ModifyNav[i].Link;
}
}
}
if (Settings.HideUserAds && Settings.HideUserAds.Enabled === true) {
if (Settings.HideUserAds.Banners && Settings.HideUserAds.Banners === true) {
Theme += `
div[style^="max-width: 728px;"]:has(a[href^="/ads"]) {
display: none;
}
`;
}
if (Settings.HideUserAds.Rectangles && Settings.HideUserAds.Rectangles === true) {
Theme += `
div[style^="max-width: 300px;"]:has(a[href^="/ads"]) {
display: none;
}
`;
}
}
if (Settings.HideNotifBadgesOn === true) {
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;
}
`;
}
// Credit to @SK-Fast (also known as DevPixels) for the improved loading code (taken from his original Poly+, and reformatted to Index Poly+)
const ThemeBlob = new Blob([Theme], { type: 'text/css' });
const ThemeURL = URL.createObjectURL(ThemeBlob);
document.head.innerHTML += `<link href="${ThemeURL}" rel="stylesheet" type="text/css">`;
/*
chrome.storage.local.get(['PolyPlus_AdCache'], async function(result){
const AdCache = result.PolyPlus_AdCache || {};
const NewCacheAdditions = Array.from(document.querySelectorAll('a[href^="/ads"]')).map(async (ad) => {
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
}
})
await Promise.all(NewCacheAdditions)
console.log(AdCache)
})
*/
if (/\/inbox\/messages\/[0-9]+\/compose/.test(window.location.pathname) && new URLSearchParams(window.location.search).has('anniversaryPreset')) {
const AnniversaryNumber = parseInt(new URLSearchParams(window.location.search).get('anniversaryPreset'))
const MessageSubject = document.querySelector('[action^="/inbox"] #subject')
const MessageBody = document.querySelector('[action^="/inbox"] #body')
const RandomAnniversaryMessage = [
{
subject: "Happy :number+ Polytorian Anniversary!",
body: `Congratulations on your :number full year on Polytoria, :recipient! 🎉🎂🎈
Best wishes,
- :username
`
},
{
subject: `:number Year${ (AnniversaryNumber > 1) ? 's' : '' } on Polytoria!`,
body: `Happy :number+ Polytorian Anniversary, :recipient! 🎈🎈🎉🎉
Yours truly,
- :username
`
},
{
subject: `:number Year${ (AnniversaryNumber > 1) ? 's' : '' } of Polytorian Fun!`,
body: `I am so proud of you for reaching :number years on Polytoria, :recipient! 🎉🎂🎈
Warm regards,
- :username
`
},
{
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);
}
}
if (document.readyState === 'complete' || document.readyState === 'interactive') {
PageLoad()
} else {
document.addEventListener('DOMContentLoaded', PageLoad);
}
// Apply Theme
LoadTheme();
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 LoadTheme() {
if (!Settings.ThemeCreator || Settings.ThemeCreator.Enabled !== true) { return }
switch (Settings.ThemeCreator.BGImageSize) {
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 {
background-color: var(--polyplus-navbgcolor) !important;
border-bottom: 1px solid var(--polyplus-navbordercolor) !important;
}
.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;
}
`;
}

File diff suppressed because it is too large Load diff

View file

@ -1,63 +1,81 @@
const UserID = JSON.parse(window.localStorage.getItem('account_info')).ID const ItemID = window.location.pathname.split('/')[2];
const UserID = JSON.parse(window.localStorage.getItem('p+account_info')).ID;
const ItemGrid = document.getElementById('assets');
const Categories = document.getElementById('store-categories').children[0];
var Settings; var Settings;
var Inventory = null;
var Utilities; var Utilities;
(async () => {
Utilities = await import(chrome.runtime.getURL('/js/resources/utils.js'));
Utilities = Utilities.default
Update() let EventItemsShown = false;
})();
const ItemGrid = document.getElementById('assets') chrome.storage.sync.get(['PolyPlus_Settings'], async function (result) {
var Inventory = []; Settings = result.PolyPlus_Settings || {};
chrome.storage.sync.get(['PolyPlus_Settings'], function(result){ Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Settings = result.PolyPlus_Settings || Utilities.DefaultSettings; Utilities = Utilities.default;
console.log(Settings)
if (Settings.IRLPriceWithCurrency && Settings.IRLPriceWithCurrency.Enabled === true) {
Array.from(ItemGrid.children).forEach((element) => {
LoadIRLPrices(element);
}); });
}
function Update() { if (Settings.EventItemsCatOn === true) {
if (Settings.IRLPriceWithCurrencyOn === true) { EventItems();
Array.from(ItemGrid.children).forEach(element => {
LoadIRLPrices(element)
});
} }
if (Settings.StoreOwnTagOn === true) { if (Settings.StoreOwnTagOn === true) {
Array.from(ItemGrid.children).forEach(element => { chrome.storage.local.get('PolyPlus_InventoryCache', async function(result){
LoadOwnedTags(element) 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);
const observer = new MutationObserver(async function (list){
for (const record of list) {
for (const element of record.addedNodes) {
if (element.tagName === "DIV" && element.classList.value === 'mb-3 itemCardCont') {
if (Settings.IRLPriceWithCurrencyOn === true) {LoadIRLPrices(element)}
if (Settings.StoreOwnTagOn === true) {
if (Inventory.length === 0) {
await fetch("https://api.polytoria.com/v1/users/:id/inventory".replace(':id', UserID))
.then(response => {
if (!response.ok) {
throw new Error('Network not ok')
}
return response.json()
})
.then(data => {
Inventory = data.data;
LoadOwnedTags(element)
return
})
.catch(error => {
console.log(error)
}); });
} else { } else {
LoadOwnedTags(element) 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) {
if (Settings.EventItemsCatOn === true) {
if (document.getElementById('event-items-pagination') === null) {
ItemGrid.classList.add('itemgrid');
ItemGrid.parentElement.classList.remove('col-lg-9');
document.getElementById('pagination').style.display = 'block';
if (document.getElementById('storecat-eventitems') !== null) {
document.getElementById('storecat-eventitems').checked = false;
}
} else {
ItemGrid.classList.remove('itemgrid');
ItemGrid.parentElement.classList.add('col-lg-9');
document.getElementById('pagination').style.display = 'none';
}
}
for (const record of list) {
for (const element of record.addedNodes) {
if (element.tagName === 'DIV' && element.classList.value === 'mb-3 itemCardCont') {
if (Settings.IRLPriceWithCurrency && Settings.IRLPriceWithCurrency.Enabled === true) {
LoadIRLPrices(element);
}
if (Settings.StoreOwnTagOn === true && Inventory !== null) {
LoadOwnedTags(element);
}
} }
} }
observer.observe(ItemGrid, {attributes: false, childList: true, subtree: false}); observer.observe(ItemGrid, {attributes: false, childList: true, subtree: false});
@ -67,40 +85,285 @@ const observer = new MutationObserver(async function (list){
observer.observe(ItemGrid, {attributes: false, childList: true, subtree: false}); observer.observe(ItemGrid, {attributes: false, childList: true, subtree: false});
async function LoadIRLPrices(element) { async function LoadIRLPrices(element) {
if (element.tagName != "DIV") {return} if (element.tagName != 'DIV' || element.querySelector('small.text-primary')) {
if (element.querySelector('small.text-primary')) {return} return;
const Parent = element.getElementsByTagName('small')[1] }
if (Parent.innerText === "") { return } const Parent = element.getElementsByTagName('small')[1];
const Span = document.createElement('span') if (Parent.innerText !== '') {
Span.classList = 'text-muted polyplus-price-tag' const Span = document.createElement('span');
Span.style.fontSize = '0.7rem' Span.classList = 'text-muted polyplus-price-tag';
const Price = Parent.innerText.split(' ')[1] Span.style = 'font-size: 0.7rem; font-weight: lighter;';
const Result = await Utilities.CalculateIRL(Price, Settings.IRLPriceWithCurrencyCurrency) const Price = Parent.innerText.split(' ')[1];
Span.innerText = "($" + Result.bricks + " " + Result.display + ")" const IRLResult = await Utilities.CalculateIRL(Price, Settings.IRLPriceWithCurrency.Currency);
Parent.appendChild(Span) Span.innerText = '($' + IRLResult.result + ' ' + IRLResult.display + ')';
Parent.appendChild(Span);
}
} }
function LoadOwnedTags(element) { function LoadOwnedTags(element, id) {
let Item = CheckInventory(parseInt(element.querySelector('[href^="/store/"]').getAttribute('href').split('/')[2])) let Item;
if (Item.id) { if (id !== undefined) {
var Tag = document.createElement('span') Item = CheckInventory(parseInt(id))
Tag.classList = 'badge bg-primary polyplus-own-tag'
Tag.setAttribute('style', 'position: absolute;font-size: 0.7rem;top: 0px;left: 0px;padding: 5.5px;border-top-left-radius: var(--bs-border-radius-lg)!important;border-top-right-radius: 0px;border-bottom-left-radius: 0px;font-size: 0.65rem;')
if (Item.asset.isLimited === false) {
Tag.innerText = "owned"
} else { } else {
Tag.innerHTML = 'owned<br><span class="text-muted" style="font-size: 0.65rem;">#' + Item.serial Item = CheckInventory(parseInt(element.querySelector('[href^="/store/"]').getAttribute('href').split('/')[2]));
}
if (Item !== null) {
const Tag = document.createElement('span');
Tag.classList = `badge ${Item.asset.isLimited === false ? 'bg-primary' : 'bg-warning'} polyplus-own-tag`;
Tag.style =
'position: absolute;font-size: 0.9rem;top: 0px;left: 0px;padding: 5.5px;border-top-left-radius: var(--bs-border-radius-lg)!important;border-top-right-radius: 0px;border-bottom-left-radius: 0px;font-size: 0.65rem;';
Tag.innerHTML = "<i class='fas fa-star'></i>";
element.getElementsByTagName('img')[0].parentElement.appendChild(Tag);
if (Item.asset.isLimited === true) {
Tag.setAttribute('data-bs-toggle', 'tooltip');
Tag.setAttribute('data-bs-title', '#' + Item.serial);
Utilities.InjectResource('registerTooltips');
} }
element.getElementsByTagName('img')[0].parentElement.appendChild(Tag)
} }
} }
function CheckInventory(id) { function CheckInventory(id) {
let Item = {} let Item = null;
Inventory.forEach(element => { Inventory.forEach((element) => {
if (element.asset.id === id) { if (element.asset.id === id) {
Item = element Item = element;
} }
}) });
return Item return Item;
}
function EventItems() {
const Selector = document.createElement('div');
Selector.classList = 'form-check store-category-check fw-bold';
Selector.style.borderColor = '#B008B0';
Selector.innerHTML = `
<input class="form-check-input" type="radio" name="storecat" id="storecat-eventitems">
<label class="form-check-label" for="storecat-eventitems">
<i class="fad fa-party-horn"></i> Event Items
</label>
`;
Categories.appendChild(Selector);
let EventData = null;
let Events = [];
let Groups = [];
let Page = 0;
Selector.children[0].addEventListener('click', async function () {
Array.from(Categories.children).forEach((selector) => {
selector.classList.remove('active');
});
Selector.classList.add('active');
if (EventData === null) {
EventData = await (await fetch('https://polyplus.vercel.app/data/eventItems.json')).json();
Object.values(EventData.eventDetails).forEach((x, index) => {
Events.push({
...x,
items: EventData.items.filter((x) => x.event === Object.keys(EventData.eventDetails)[index]).sort((a, b) => a.id - b.id)
});
});
while (Events.length > 0) {
Groups.push(Events.splice(0, 5));
}
}
ItemGrid.classList.remove('itemgrid');
ItemGrid.innerHTML = `
<div id="p+ei">
${Groups[Page].map((x, index) => `
<div class="row px-2 px-lg-0" style="animation-delay: 0.24s;">
<div class="col">
<h6 class="dash-ctitle2">${x.date}</h6>
<h5 class="dash-ctitle">${x.name}</h5>
</div>
${x.link !== undefined ? typeof(x.link) !== "object" ? `
<div class="col-auto d-flex align-items-center">
<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>
<i class="fas fa-angle-right ms-2"></i>
</a>
</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 class="card card-dash mcard mb-3" style="animation-delay: 0.27s;">
<div class="card-body p-0 m-1 scrollFadeContainer">
<div class="d-flex">
${x.items.map((x) => `
<a href="/store/${x.id}">
<div class="scrollFade card me-2 place-card force-desktop text-center mb-2" style="opacity: 1;">
<div class="card-body">
<img src="${x.thumbnail}" class="place-card-image">
<div>
<div class="mt-2 mb-1 place-card-title">
${x.name}
</div>
</div>
</div>
</div>
</a>
`).join('')}
</div>
</div>
</div>
`).join('')}
</div>
<div class="d-flex justify-content-center mt-3">
<nav id="event-items-pagination">
<ul class="pagination">
<li class="page-item disabled">
<a class="page-link" href="#!" id="p+ei-pagination-first">«</a>
</li>
<li class="page-item disabled">
<a class="page-link" href="#!" id="p+ei-pagination-prev"></a>
</li>
<li class="page-item active">
<a class="page-link">
<span class="visually-hidden">Page</span>
<span id="p+ei-pagination-current">1</span>
</a>
</li>
<li class="page-item">
<a class="page-link" href="#!" id="p+ei-pagination-next"></a>
</li>
<li class="page-item">
<a class="page-link" href="#!" id="p+ei-pagination-last">»</a>
</li>
</ul>
</nav>
</div>
`;
const Container = document.getElementById('p+ei');
const Pagination = document.getElementById('event-items-pagination');
const First = document.getElementById('p+ei-pagination-first');
const Prev = document.getElementById('p+ei-pagination-prev');
const Current = document.getElementById('p+ei-pagination-current');
const Next = document.getElementById('p+ei-pagination-next');
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) {
Prev.parentElement.classList.remove('disabled');
First.parentElement.classList.remove('disabled');
} else {
Prev.parentElement.classList.add('disabled');
First.parentElement.classList.add('disabled');
}
if (Page < Groups.length - 1) {
Next.parentElement.classList.remove('disabled');
Last.parentElement.classList.remove('disabled');
} else {
Next.parentElement.classList.add('disabled');
Last.parentElement.classList.add('disabled');
}
First.addEventListener('click', function () {
if (Page > 0) {
Page = 0;
UpdateEventItems();
}
});
Prev.addEventListener('click', function () {
if (Page > 0) {
Page--;
UpdateEventItems();
}
});
Next.addEventListener('click', function () {
if (Page < Groups.length - 1) {
Page++;
UpdateEventItems();
}
});
Last.addEventListener('click', function () {
if (Page < Groups.length - 1) {
Page = Groups.length - 1;
UpdateEventItems();
}
});
const UpdateEventItems = function () {
Current.innerText = Page + 1;
Container.innerHTML = Groups[Page].map((x, index) => `
<div class="row px-2 px-lg-0" style="animation-delay: 0.24s;">
<div class="col">
<h6 class="dash-ctitle2">${x.date}</h6>
<h5 class="dash-ctitle">${x.name}</h5>
</div>
${x.link !== undefined ? `
<div class="col-auto d-flex align-items-center">
<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>
<i class="fas fa-angle-right ms-2"></i>
</a>
</div>
`
: ''
}
</div>
<div class="card card-dash mcard mb-3" style="animation-delay: 0.27s;">
<div class="card-body p-0 m-1 scrollFadeContainer">
<div class="d-flex">
${x.items.map((x) => `
<a href="/store/${x.id}">
<div class="scrollFade card me-2 place-card force-desktop text-center mb-2" style="opacity: 1;">
<div class="card-body">
<img src="${x.thumbnail}" class="place-card-image">
<div>
<div class="mt-2 mb-1 place-card-title">
${x.name}
</div>
</div>
</div>
</div>
</a>
`).join('')}
</div>
</div>
</div>
</div>
`
).join('');
if (Page > 0) {
Prev.parentElement.classList.remove('disabled');
First.parentElement.classList.remove('disabled');
} else {
Prev.parentElement.classList.add('disabled');
First.parentElement.classList.add('disabled');
}
if (Page < Groups.length - 1) {
Next.parentElement.classList.remove('disabled');
Last.parentElement.classList.remove('disabled');
} else {
Next.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]);
});
}
};
});
} }

View file

@ -1,148 +0,0 @@
let Avatar = {
"useCharacter": true,
"items": [],
"shirt": "https://c0.ptacdn.com/assets/uWrrnFGwgNN5W171vqYTWY7E639rKiXK.png",
"pants": "https://c0.ptacdn.com/assets/HD6TFdXD8CaflRNmd84VCNyNsmTB0SH3.png",
"headColor": "#e0e0e0",
"torsoColor": "#e0e0e0",
"leftArmColor": "#e0e0e0",
"rightArmColor": "#e0e0e0",
"leftLegColor": "#e0e0e0",
"rightLegColor": "#e0e0e0"
}
const Style = document.createElement('style')
Style.innerHTML = `
html:has(.polyplus-modal[open]), body:has(.polyplus-modal[open]) {
overflow: hidden;
}
.polyplus-modal::backdrop {
background: rgba(0, 0, 0, 0.73);
}
`
document.body.prepend(Style)
const ItemType = document.getElementsByClassName('px-4 px-lg-0 text-muted text-uppercase mb-3')[0].innerText.toLowerCase().split(' ')[1]
const ItemThumbnail = document.getElementsByClassName('store-thumbnail')[0]
const IFrame = document.getElementsByClassName('store-thumbnail-3d')[0]
const TryIFrame = document.createElement('iframe')
TryIFrame.setAttribute('style', 'width: 100%; height: auto; aspect-ratio: 1; border-radius: 20px;')
const TryOnBtn = document.createElement('button')
TryOnBtn.classList = 'btn btn-outline-warning'
TryOnBtn.setAttribute('style', 'position: absolute; bottom: 15px;')
TryOnBtn.innerHTML = '<i class="fa-duotone fa-vial"></i>'
TryOnBtn.addEventListener('click', function (){
TryOnModal.showModal()
});
if (typeof(document.getElementsByClassName('3dviewtoggler')[0]) === 'object') {
TryOnBtn.style.right = '60px'
} else {
TryOnBtn.style.right = '10px'
}
let TryOnModal = document.createElement('dialog')
TryOnModal.classList = 'polyplus-modal'
TryOnModal.setAttribute('style', 'width: 450px; border: 1px solid #484848; background-color: #181818; border-radius: 20px; overflow: hidden;')
TryOnModal.innerHTML = `
<div class="text-muted mb-2" style="font-size: 0.8rem;">
<h5 class="mb-0" style="color: #fff;">Preview</h5>
Try on this item!
</div>
<div class="modal-body">
<button class="btn btn-primary w-100 mx-auto" onclick="this.parentElement.parentElement.close();">Close</button>
</div>
`
document.body.prepend(TryOnModal)
ItemThumbnail.parentElement.appendChild(TryOnBtn)
TryOnModal.children[1].prepend(TryIFrame)
fetch("https://api.polytoria.com/v1/users/:id/avatar".replace(':id', JSON.parse(window.localStorage.getItem('account_info')).ID))
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
data.assets.forEach(item => {
switch (item.type) {
case 'hat':
Avatar.items[Avatar.items.length] = item.path || ''
break
case 'face':
Avatar.face = item.path || ''
break
case 'tool':
Avatar.tool = item.path || ''
break
case 'shirt':
Avatar.shirt = item.path || ''
break
case 'pants':
Avatar.pants = item.path || ''
break
}
});
Avatar.headColor = "#" + data.colors.head
Avatar.torsoColor = "#" + data.colors.torso
Avatar.leftArmColor = "#" + data.colors.leftArm
Avatar.rightArmColor = "#" + data.colors.rightArm
Avatar.leftLegColor = "#" + data.colors.leftLeg
Avatar.rightLegColor = "#" + data.colors.rightLeg
if (ItemType === 'hat' || ItemType === 'tool') {
fetch("https://api.polytoria.com/v1/assets/serve-mesh/:id".replace(':id', window.location.pathname.split('/')[2]))
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
if (ItemType === 'hat') {
Avatar.items[Avatar.items.length] = data.url
} else if (ItemType === 'tool') {
Avatar.tool = data.url
}
console.log(Avatar)
TryIFrame.src = 'https://polytoria.com/ptstatic/itemview/#' + btoa(encodeURIComponent(JSON.stringify(Avatar)))
})
.catch(error => {
console.error('Fetch error:', error);
});
} else {
fetch("https://api.polytoria.com/v1/assets/serve/:id/Asset".replace(':id', window.location.pathname.split('/')[2]))
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
switch (ItemType) {
case 'shirt':
Avatar.shirt = data.url
break
case 'pants':
Avatar.pants = data.url
break
case 'face':
Avatar.face = data.url
break
}
TryIFrame.src = 'https://polytoria.com/ptstatic/itemview/#' + btoa(encodeURIComponent(JSON.stringify(Avatar)))
})
.catch(error => {
console.error('Fetch error:', error);
});
}
})
.catch(error => {
console.error('Fetch error:', error);
});

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]
});
}
})();

95
manifest.json Executable file → Normal file
View file

@ -1,20 +1,23 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"author": "Index",
"name": "Poly+", "name": "Poly+",
"version": "1.11", "version": "1.3",
"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.",
"permissions": ["storage", "contextMenus", "tabs", "scripting", "alarms", "notifications"], "homepage_url": "https://polyplus.vercel.app/",
"permissions": ["storage", "contextMenus", "scripting", "alarms", "notifications"],
"content_scripts": [ "content_scripts": [
{ {
"matches": ["https://polytoria.com/*"], "matches": ["https://polytoria.com/*"],
"js": ["/js/everywhere.js","/js/membership-themes.js"], "js": ["/js/sitewide.js", "/js/membership-themes.js"],
"css": ["/css/specific.css"], "css": ["/css/specific.css"],
"run_at": "document_start" "run_at": "document_start"
}, },
{ {
"matches": ["https://polytoria.com/my/settings/*"], "matches": ["https://polytoria.com/my/settings/*"],
"js": ["/js/polyplus-settings.js"], "js": ["/js/settings.js", "/js/extra-pages.js"],
"run_at": "document_start" "run_at": "document_start"
}, },
@ -24,24 +27,19 @@
"run_at": "document_idle" "run_at": "document_idle"
}, },
{
"matches": ["https://polytoria.com/my/settings/polyplus-debug/*"],
"js": ["/js/debug.js"]
},
{ {
"matches": ["https://polytoria.com/places/**"], "matches": ["https://polytoria.com/places/**"],
"js": ["/js/places/place-view.js"] "js": ["/js/places/place-view.js"]
}, },
{ {
"matches": ["https://polytoria.com/join-place/**"], "matches": ["https://polytoria.com/create/place/**/access"],
"js": ["/js/places/place-join.js"] "js": ["/js/create/place-access.js"]
}, },
{ {
"matches": ["https://polytoria.com/create/place/**"], "matches": ["https://polytoria.com/create/image", "https://polytoria.com/create/image/"],
"js": ["/js/places/place-edit.js"] "js": ["/js/create/image.js"]
}, },
{ {
@ -50,8 +48,8 @@
}, },
{ {
"matches": ["https://polytoria.com/users/**"], "matches": ["https://polytoria.com/u/**", "https://polytoria.com/users/**"],
"js": ["/js/account/profile.js"] "js": ["/js/account/profile.js", "/js/account/inventory.js"]
}, },
{ {
@ -65,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"]
}, },
@ -80,7 +78,7 @@
}, },
{ {
"matches": ["https://polytoria.com/users/*/inventory/*"], "matches": ["https://polytoria.com/u/*/inventory/"],
"js": ["/js/account/inventory.js"], "js": ["/js/account/inventory.js"],
"run_at": "document_idle" "run_at": "document_idle"
}, },
@ -91,7 +89,7 @@
}, },
{ {
"matches": ["https://polytoria.com/my/settings/transactions"], "matches": ["https://polytoria.com/my/settings/transactions*"],
"js": ["/js/account/transactions.js"] "js": ["/js/account/transactions.js"]
}, },
@ -101,25 +99,53 @@
}, },
{ {
"matches": ["https://polytoria.com/my/avatar?sandbox=true"], "matches": ["https://polytoria.com/my/avatar*"],
"js": ["/js/account/avatar-sandbox2.js"] "js": ["/js/account/avatar-sandbox.js"]
}, },
{ {
"matches": ["https://polytoria.com/inbox/*", "https://polytoria.com/inbox?*"], "matches": ["https://polytoria.com/inbox*"],
"js": ["/js/account/inbox.js"] "js": ["/js/account/inbox.js"]
},
{
"matches": ["https://polytoria.com/guilds/**"],
"js": ["/js/guilds.js"]
},
{
"matches": ["https://polytoria.com/store/**", "https://polytoria.com/models/**"],
"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": {
"service_worker": "/js/background.js", "service_worker": "/js/background.js",
"type": "module" "type": "module"
}, },
"host_permissions": [ "host_permissions": ["https://*.polytoria.com/*"],
"https://*.polytoria.com/*"
],
"web_accessible_resources": [ "web_accessible_resources": [
{ {
"resources": ["js/resources/*"], "resources": ["resources/*"],
"matches": ["https://polytoria.com/*"] "matches": ["https://polytoria.com/*"]
}, },
@ -132,16 +158,17 @@
"action": { "action": {
"default_title": "Poly+", "default_title": "Poly+",
"default_icon": { "default_icon": {
"16": "/icon.png", "16": "/images/icon.png",
"32": "/icon.png", "32": "/images/icon.png",
"48": "/icon.png", "48": "/images/icon.png",
"128": "/icon.png" "128": "/images/icon.png"
} },
"default_popup": "/popup.html"
}, },
"icons": { "icons": {
"16": "/icon.png", "16": "/images/icon.png",
"32": "/icon.png", "32": "/images/icon.png",
"48": "/icon.png", "48": "/images/icon.png",
"128": "/icon.png" "128": "/images/icon.png"
} }
} }

97
old.js
View file

@ -1,97 +0,0 @@
let CurrencyNames = [
"USD",
"EUR",
"CAD",
"GBP",
"MXN",
"AUD",
"TRY"
]
let Divides = [
100,
550,
1150,
2750,
6000,
12500
]
let Currencies = [
[
0.99, // USD
1.12, // EUR
1.23, // CAD
0.81, // GBP
19.28, // MXN
1.42, // AUD
15.83, // TRY
],
[
4.99, // USD
5.59, // EUR
6.21, // CAD
4.05, // GBP
96.44, // MXN
7.38, // AUD
80.39, // TRY
],
[
9.99, // USD
11.18, // EUR
12.34, // CAD
8.10, // GBP
192.80, // MXN
14.76, // AUD
158.33, // TRY
],
[
24.99, // USD
27.39, // EUR
29.22, // CAD
19.20, // GBP
475.60, // MXN
34.92, // AUD
396.66, // TRY
],
[
49.99, // USD
55.94, // EUR
58.44, // CAD
36.40, // GBP
951.20, // MXN
73.84, // AUD
793.32, // TRY
],
[
99.99, // USD
111.88, // EUR
116.88, // CAD
72.80, // GBP
1902.40, // MXN
147.68, // AUD
1586.64, // TRY
]
]
let UnitPrices = [
{},
{},
{},
{},
{},
{}
]
Currencies.forEach((_value, _index) => {
Currencies[_index].forEach((value, index) => {
UnitPrices[_index][CurrencyNames[index]] = (value / Divides[_index])
})
});
console.log(JSON.stringify(UnitPrices))

35
popup.html Executable file → Normal file
View file

@ -1,11 +1,36 @@
<!doctype html> <!doctype html>
<html style="border-radius: 20px;"> <html>
<head> <head>
<title>Poly+</title> <!-- 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> </head>
<body> <body style="width: 245px;">
<div style="text-align: center; width: 300px; padding: 10px;"> <div class="p-2 pt-0">
<a class="btn" href="https://polytoria.com/my/settings/polyplus">Settings</a> <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> </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> </body>
</html> </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);
}

383
resources/avatar-sandbox.html Executable file
View file

@ -0,0 +1,383 @@
<style>
#options {
position: absolute;
bottom: 0;
margin-bottom: 15px;
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) {
background: transparent;
border: none;
color: #fff;
font-size: 1.25rem;
}
#options *:not(input):not(:nth-child(2)) {
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>
<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="col-12 col-lg-3">
<div class="card mcard mb-3">
<div class="card-body">
<iframe id="viewFrame" style="width: 100%; height: 314px; border-radius: 0.65rem;"></iframe>
<div id="options">
<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;">
<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-upload"></i>
</button>
</label>
<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-download"></i>
</button>
<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-minimize"></i>
</button>
</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="col">
<button id="clear" class="btn btn-outline-warning w-100">
<i class="fa-duotone fa-trash"></i>
Clear
</button>
</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>
<hr class="mt-2 mb-3">
<div class="card mcard mb-3">
<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>
Body Colors
</h6>
<div class="card-body">
<div class="card-body text-center" id="body-parts">
<div style="margin-bottom: 5px">
<button id="head" class="avatarAction bodypart bp1x1" style="background-color: #e0e0e0;"></button>
</div>
<div style="margin-bottom: 5px">
<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 class="avatarAction bodypart bp1x2" id="leftArm" style="background-color: #e0e0e0; margin-left: 5px;"></button>
</div>
<div>
<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>
</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>
<!--
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 class="col-12 col-lg-9">
<ul class="nav nav-pills no-shrink avatar-nav-pills nav-justified row" id="tabs">
<li class="nav-item col-6 col-md-4 col-xl-3" style="flex: unset">
<a class="nav-link active" data-tab="hat">
<i class="fas fa-hat-wizard me-1"></i>
<span class="pilltitle">Accessories</span>
</a>
</li>
<li class="nav-item col-6 col-md-4 col-xl-3" style="flex: unset">
<a class="nav-link" data-tab="shirt">
<i class="fas fa-tshirt me-1"></i>
<span class="pilltitle">Shirts</span>
</a>
</li>
<li class="nav-item col-6 col-md-4 col-xl-3" style="flex: unset">
<a class="nav-link" data-tab="pants">
<i class="fas fa-socks me-1"></i>
<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>
</li>
</ul>
<div role="alert" class="alert bg-warning text-center mb-1 mt-2" id="p+retro_items_warning">
Items in this category are from past iterations of Polytoria. They can only be worn in the Avatar Sandbox.
</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>
<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: transparent; border-color: transparent; border-top-left-radius: 20px; border-top-right-radius: 20px;">
<div class="row alignleft itemgrid" id="wearing"></div>
</div>
</div>
</div>

View file

@ -0,0 +1,8 @@
window.localStorage.setItem(
'p+account_info',
JSON.stringify({
ID: userID,
Username: document.querySelector('a[href^="/u"]:has(.dropdown-item):first-child').innerText.replaceAll('\n', '').replaceAll('\t', '').trim(),
Bricks: document.querySelector('.brickBalanceCont').innerText.replace(/\s+/g, '').split('(')[0]
})
);

View file

@ -0,0 +1,5 @@
var tooltips = document.querySelectorAll('[data-bs-toggle="tooltip"]');
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));

317
resources/utils.js Normal file
View file

@ -0,0 +1,317 @@
/*
HOW TO USE IN CONTENT SCRIPTS:
(async () => {
let Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default
})();
*/
function ParseFullNumber(ab) {
if (typeof ab === 'number') {
return ab;
}
const Suffixes = {k: 1000, m: 1000000, b: 1000000000};
const Suffix = ab.slice(-1).toLowerCase();
if (Suffixes[Suffix]) {
return parseFloat(ab) * Suffixes[Suffix];
} else {
return parseFloat(ab);
}
}
export default {
DefaultSettings: {
PinnedGamesOn: true,
ForumMentsOn: true,
BestFriendsOn: false,
ImprovedFrListsOn: false,
IRLPriceWithCurrency: {
Enabled: true,
Currency: 0,
Package: 0
},
IRLPriceWithCurrencyOn: true,
IRLPriceWithCurrencyCurrency: 0,
IRLPriceWithCurrencyPackage: 0,
HideNotifBadgesOn: false,
StoreOwnTagOn: true,
ThemeCreatorOn: false,
ThemeCreator: {
Enabled: false,
BGColor: null,
BGImage: null,
BGImageSize: 'fit',
PrimaryTextColor: null,
SecondaryTextColor: null,
LinkTextColor: null,
WebsiteLogo: null
},
ModifyNavOn: false,
ModifyNav: [
{
Label: 'Places',
Link: 'https://polytoria.com/places'
},
{
Label: 'Store',
Link: 'https://polytoria.com/store'
},
{
Label: 'Guilds',
Link: 'https://polytoria.com/guilds'
},
{
Label: 'People',
Link: 'https://polytoria.com/users'
},
{
Label: 'Forum',
Link: 'https://polytoria.com/forum'
}
],
MoreSearchFiltersOn: true,
ApplyMembershipTheme: {
Enabled: false,
Theme: 0
},
ApplyMembershipThemeOn: false,
ApplyMembershipThemeTheme: 0,
MultiCancelOutTradesOn: true,
ItemWishlistOn: true,
HideUpgradeBtnOn: false,
TryOnItemsOn: true,
OutfitCostOn: true,
ShowPlaceRevenueOn: true,
ReplaceItemSalesOn: false,
HoardersListOn: true,
HoardersList: {
Enabled: true,
AvatarsEnabled: false,
MinCopies: 2
},
LibraryDownloadsOn: true,
EventItemsCatOn: true,
HomeFriendCountOn: true,
HideUserAds: {
Enabled: false,
Banners: 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: {
PinnedGames: 10,
BestFriends: 15,
// Item Wishlist and ImprovedFrLists limit here is not implemented in the code
ImprovedFrLists: 20,
ItemWishlist: 20,
HoardersListPages: 4
},
MeshTypes: ['hat', 'hair', 'head attachment', 'face accessory', 'neck accessory', 'head cover', 'back accessory', 'shoulder accessory', 'tool'],
TextureTypes: ['shirt', 'pants', 'face'],
CalculateIRL: async function (bricks, to, brickPackage) {
/*
Disabled for now: currency retrieval from currencies.json
const response = await fetch(chrome.runtime.getURL('resources/currencies.json'))
if (!response.ok) {
throw new Error('Getting currency data failure')
}
const data = await response.json()
const UnitPrice = data.Data[brickPackage][to]
*/
let Icon = '$';
let Result = 'N/A';
let Display = 'Currency Not Found';
// is the icon abbreviated text, or an entity
let IsIconAbbr = false;
bricks = ParseFullNumber(bricks.replace(/,/g, ''));
switch (to) {
// U.S. Dollar
case 0:
Result = (bricks * 0.0099).toFixed(2);
Display = 'USD';
break;
// Euro
case 1:
Icon = '&#8364;';
Result = (bricks * 0.009).toFixed(2);
Display = 'EUR';
break;
// Canadian Dollar
case 2:
Icon = 'CAD$';
IsIconAbbr = true;
Result = (bricks * 0.0131).toFixed(2);
Display = 'CAD';
break;
// Great British Pound
case 3:
Icon = '&#163;';
Result = (bricks * 0.0077).toFixed(2);
Display = 'GBP';
break;
// Mexican Peso
case 4:
Icon = 'MXN$';
IsIconAbbr = true;
Result = (bricks * 0.1691).toFixed(2);
Display = 'MXN';
break;
// Australia Dollar
case 5:
Icon = 'AU$';
IsIconAbbr = true;
Result = (bricks * 0.0144).toFixed(2);
Display = 'AUD';
break;
// Turkish Lira
case 6:
Icon = '&#8378;';
Result = (bricks * 0.2338).toFixed(2);
Display = 'TRY';
break;
// Brazillian Real
case 7:
Icon = 'R$';
IsIconAbbr = true;
Result = (bricks * 0.49).toFixed(2);
Display = 'BRL';
break;
// Zimbabwean Dollar
case 8:
Icon = 'Z$'
IsIconAbbr = true
Result = (bricks * 0.13739106).toFixed(2)
Display = 'ZWL'
break;
}
if (typeof Result === 'number') {
Result = Result.toFixed(2);
}
if (IsIconAbbr) {
Icon = "$"
}
return {
result: Result,
display: Display,
icon: Icon,
isIconAbbr: IsIconAbbr
};
},
InjectResource: function (path, element) {
/*
Function by devjin0617 on GitHub
Gist: https://gist.github.com/devjin0617/3e8d72d94c1b9e69690717a219644c7a
Slightly modified to use constants and fit the rest of the code style more
Function only used for registering bootstrap tooltips and getting the signed-in user's username, user ID, and brick count currently
*/
/*
Potentially make this use chrome.runtime.sendMessage in the background.js script soon
*/
if (element === undefined) {
element = 'body';
}
const Node = document.getElementsByTagName(element)[0];
const Script = document.createElement('script');
Script.setAttribute('type', 'text/javascript');
Script.setAttribute('src', chrome.runtime.getURL('resources/' + path + '.js'));
Script.addEventListener('load', function () {
Script.remove();
});
Node.appendChild(Script);
},
// MergeObjects function was written by ChatGPT cause I was lazy and it was a while ago
MergeObjects: function(obj1, obj2) {
var mergedObj = {};
// Copy the values from obj1 to the mergedObj
for (var key in obj1) {
mergedObj[key] = obj1[key];
}
// Merge the values from obj2 into the mergedObj, favoring obj2 for non-existing keys in obj1
for (var key in obj2) {
if (!obj1.hasOwnProperty(key)) {
mergedObj[key] = obj2[key];
}
}
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;
}
};

File diff suppressed because it is too large Load diff

662
settings.js Executable file → Normal file
View file

@ -1,399 +1,469 @@
const SaveBtn = document.getElementById('Save') const SaveBtn = document.getElementById('Save');
const Elements = Array.from(document.getElementsByClassName('setting-container')) const Elements = Array.from(document.getElementsByClassName('setting-container'));
var RecentSave;
var Settings; var Settings;
/*
var ExpectedSettings = {
PinnedGamesOn: true,
ForumMentsOn: false,
BestFriendsOn: true,
ImprovedFrListsOn: true,
IRLPriceWithCurrencyOn: true,
IRLPriceWithCurrencyCurrency: 0,
IRLPriceWithCurrencyPackage: 0,
HideNotifBadgesOn: false,
StoreOwnTagOn: true,
ThemeCreatorOn: false,
ThemeCreator: {
BGColor: null,
BGImage: null,
BGImageSize: 'fit',
PrimaryTextColor: null,
SecondaryTextColor: null,
LinkTextColor: null,
WebsiteLogo: null
},
ModifyNavOn: false,
ModifyNav: [
{
Label: "Places",
Link: "https://polytoria.com/places"
},
{
Label: "Store",
Link: "https://polytoria.com/store"
},
{
Label: "Guilds",
Link: "https://polytoria.com/guilds"
},
{
Label: "People",
Link: "https://polytoria.com/users"
},
{
Label: "Forum",
Link: "https://polytoria.com/forum"
}
],
MoreSearchFiltersOn: true,
ApplyMembershipThemeOn: false,
ApplyMembershipThemeTheme: 0,
MultiCancelOutTradesOn: true,
ItemWishlistOn: true,
HideUpgradeBtnOn: false
}
*/
var ExpectedSettings;
var Utilities; var Utilities;
(async () => { (async () => {
Utilities = await import(chrome.runtime.getURL('/js/resources/utils.js')); Utilities = await import(chrome.runtime.getURL('resources/utils.js'));
Utilities = Utilities.default Utilities = Utilities.default;
LoadCurrent();
ExpectedSettings = Utilities.DefaultSettings document.getElementById('PinnedGames-limit').innerText = Utilities.Limits.PinnedGames;
//document.getElementById('ImprovedFrLists-limit').innerText = Utilities.Limits.ImprovedFrLists;
LoadCurrent() //document.getElementById('ItemWishlist-limit').innerText = Utilities.Limits.ItemWishlist;
document.getElementById('HoardersList-pageLimit').innerText = Utilities.Limits.HoardersListPages;
})(); })();
const ResetDefaultsModal = document.getElementById('ResetDefaults-Modal') // Handle buttons at the bottom of the page
var ThemeCreatorModal = { document.getElementById('ResetDefaults').addEventListener('click', function () {
Modal: document.getElementById('ThemeCreator-Modal'), document.getElementById('ResetDefaults-Modal').showModal();
Save: document.getElementById('ThemeCreator-Modal-Save'),
BGColor: document.getElementById('ThemeCreator-Modal-BGColor'),
BGImage: document.getElementById('ThemeCreator-Modal-BGImage'),
BGImageSize: document.getElementById('ThemeCreator-Modal-BGImageSize'),
PrimaryTextColor: document.getElementById('ThemeCreator-Modal-PrimaryTextColor'),
SecondaryTextColor: document.getElementById('ThemeCreator-Modal-SecondaryTextColor'),
LinkTextColor: document.getElementById('ThemeCreator-Modal-LinkTextColor'),
WebsiteLogo: document.getElementById('ThemeCreator-Modal-WebsiteLogo')
}
var ModifyNavModal = {
Modal: document.getElementById('ModifyNav-Modal'),
Save: document.getElementById('ModifyNav-Modal-Save'),
"1Label": document.getElementById('ModifyNav-Modal-1Label'),
"1Link": document.getElementById('ModifyNav-Modal-1Link'),
"2Label": document.getElementById('ModifyNav-Modal-2Label'),
"2Link": document.getElementById('ModifyNav-Modal-2Link'),
"3Label": document.getElementById('ModifyNav-Modal-3Label'),
"3Link": document.getElementById('ModifyNav-Modal-3Link'),
"4Label": document.getElementById('ModifyNav-Modal-4Label'),
"4Link": document.getElementById('ModifyNav-Modal-4Link'),
"5Label": document.getElementById('ModifyNav-Modal-5Label'),
"5Link": document.getElementById('ModifyNav-Modal-5Link'),
}
SaveBtn.addEventListener("click", function() {
Save();
}); });
Elements.forEach(element => { SaveBtn.addEventListener('click', Save);
let Button = element.getElementsByTagName('button')[0]
let Options = element.getElementsByTagName('button')[1] // Handle modal buttons for Reset Defaults modal
let Select = element.getElementsByTagName('select') || [] document.getElementById('ResetDefaults-Modal-Yes').addEventListener('click', function () {
console.log(element, Select) Settings = Utilities.DefaultSettings;
Save();
setTimeout(function () {
LoadCurrent();
document.getElementById('ResetDefaults-Modal').close();
}, 400);
});
document.getElementById('ResetDefaults-Modal-No').addEventListener('click', function () {
document.getElementById('ResetDefaults-Modal').close();
});
// Loop thru each setting container and handle toggling, selecting, opening modal, etc
Elements.forEach((element) => {
let Button = element.getElementsByClassName('toggle-btn')[0];
let Options = element.getElementsByClassName('options-btn')[0];
let Select = element.getElementsByTagName('select')[0];
let Checkbox = element.getElementsByTagName('input');
if (Button) { if (Button) {
Button.addEventListener('click', function () { Button.addEventListener('click', function () {
ToggleSetting(Button.getAttribute('data-setting'), element) SetSetting(Button, 'bool');
}); });
} }
if (Select) {
Select.addEventListener('change', function () {
if (Select.getAttribute('data-useValue') !== null) {
let Value = Select.options[Select.selectedIndex].value;
if (!isNaN(Value)) {
Value = parseInt(Value);
}
SetSetting(Select, Value, false);
} else {
SetSetting(Select, Select.selectedIndex, false);
}
});
}
if (Checkbox) {
Array.from(Checkbox).forEach(check => {
check.addEventListener('change', function () {
SetSetting(check, check.checked, false);
});
})
}
if (Options) { if (Options) {
Options.addEventListener('click', function() { const Modal = document.getElementById(Options.getAttribute('data-modal') + '-Modal');
let Modal = document.getElementById(Options.getAttribute('data-modal') + '-Modal') const ModalButtons = Modal.getElementsByTagName('button');
let ModalButtons = Modal.getElementsByTagName('button') const ModalInputs = Modal.getElementsByTagName('input');
let ModalInputs = Modal.getElementsByTagName('input') const ModalSelect = Modal.getElementsByTagName('select');
let ModalSelect = Modal.getElementsByTagName('select')
Options.addEventListener('click', function () {
Array.from(ModalButtons)
.filter((x) => !x.classList.contains('ignore'))
.forEach((button) => {
button.addEventListener('click', function () {
const Setting = button.getAttribute('data-setting');
Array.from(ModalButtons).forEach(btn => {
if (!(btn.getAttribute('data-ignore') === 'true')) {
btn.addEventListener('click', function(){
let Setting = btn.getAttribute('data-setting')
if (Setting === '[save]') { if (Setting === '[save]') {
Array.from(ModalInputs).forEach(input => { // Save Modal Button
if (!(input.getAttribute('data-ignore') === 'true')) {
if (!(input.getAttribute('data-parent'))) { // Save Modal Inputs
Settings[Modal.getAttribute('data-setting')][input.getAttribute('data-setting')] = input.value || null Array.from(ModalInputs)
} else { .filter((x) => !x.classList.contains('ignore'))
let Parent = input.getAttribute('data-parent') .forEach((input) => {
if (!isNaN(parseInt(Parent))) {Parent = parseInt(Parent)} SetSetting(input, input.value, false, Modal.getAttribute('data-setting'));
Settings[Modal.getAttribute('data-setting')][Parent][input.getAttribute('data-setting')] = input.value || null
}
}
}); });
Array.from(ModalSelect).forEach(select => {
if (!(select.getAttribute('data-ignore') === 'true')) { // Save Modal Select Menus
if (!(select.getAttribute('data-parent'))) { Array.from(ModalSelect)
Settings[Modal.getAttribute('data-setting')][select.getAttribute('data-setting')] = select.selectedIndex .filter((x) => !x.classList.contains('ignore'))
} else { .forEach((select) => {
let Parent = input.getAttribute('data-parent') SetSetting(select, select.selectedIndex, false, Modal.getAttribute('data-setting'));
if (!isNaN(parseInt(Parent))) {Parent = parseInt(Parent)}
Settings[Modal.getAttribute('data-setting')][Parent][select.getAttribute('data-setting')] = select.selectedIndex
}
}
}); });
Save(); Save();
setTimeout(function () { setTimeout(function () {
LoadCurrent(); LoadCurrent();
Modal.close(); Modal.close();
}, 400) }, 400);
} else if (Setting === '[reset-default]') {
// Reset to Defaults Modal Button
if (confirm("Are you sure you'd like to reset these options to their defaults?") === true) {
Settings[Modal.getAttribute('data-setting')] = Utilities.DefaultSettings[Modal.getAttribute('data-setting')];
Save();
Modal.close();
}
} else if (Setting === '[cancel]') { } else if (Setting === '[cancel]') {
// Cancel Changes Button
Modal.close(); Modal.close();
} else { } else {
if (!(btn.getAttribute('data-parent'))) { // Default Toggle Button
ToggleSetting(Modal.getAttribute('data-setting')[btn.getAttribute('data-setting')], null)
} else { SetSetting(button, 'bool', false, Modal.getAttribute('data-setting'));
let Parent = input.getAttribute('data-parent')
if (!isNaN(parseInt(Parent))) {Parent = parseInt(Parent)}
ToggleSetting(Modal.getAttribute('data-setting')[Parent][btn.getAttribute('data-setting')], null)
}
} }
}); });
});
Array.from(ModalInputs)
.filter((x) => !x.classList.contains('ignore'))
.forEach((input) => {
const Status = GetSettingValue(input, Modal.getAttribute('data-setting'));
if (Status !== 'undefined' && Status !== undefined) {
input.value = Status;
} else {
input.value = '';
} }
}); });
Array.from(ModalInputs).forEach(input => { Array.from(ModalSelect)
if (!(input.getAttribute('data-ignore') === 'true')) { .filter((x) => !x.classList.contains('ignore'))
if (!(input.getAttribute('data-parent'))) { .forEach((select) => {
if (Settings[Modal.getAttribute('data-setting')][input.getAttribute('data-setting')] !== "undefined" && Settings[Modal.getAttribute('data-setting')][input.getAttribute('data-setting')] !== undefined) { const Status = GetSettingValue(select, Modal.getAttribute('data-setting'));
input.value = Settings[Modal.getAttribute('data-setting')][input.getAttribute('data-setting')] if (Status !== 'undefined' && Status !== undefined) {
} else { select.selectedIndex = Status;
input.value = ''
}
} else {
let Parent = input.getAttribute('data-parent')
if (Settings[Modal.getAttribute('data-setting')][Parent][input.getAttribute('data-setting')] !== "undefined" && Settings[Modal.getAttribute('data-setting')][Parent][input.getAttribute('data-setting')] !== undefined) {
if (!isNaN(parseInt(Parent))) {Parent = parseInt(Parent)}
input.value = Settings[Modal.getAttribute('data-setting')][Parent][input.getAttribute('data-setting')]
} else {
input.value = ''
}
}
} }
}); });
Array.from(ModalSelect).forEach(select => { Modal.showModal();
if (!(select.getAttribute('data-ignore') === 'true')) {
if (!(select.getAttribute('data-parent'))) {
if (Settings[Modal.getAttribute('data-setting')][select.getAttribute('data-setting')] !== "undefined") {
select.selectedIndex = Settings[Modal.getAttribute('data-setting')][select.getAttribute('data-setting')]
}
} else {
let Parent = input.getAttribute('data-parent')
if (Settings[Modal.getAttribute('data-setting')][Parent][select.getAttribute('data-setting')] !== "undefined") {
if (!isNaN(parseInt(Parent))) {Parent = parseInt(Parent)}
select.selectedIndex = Settings[Modal.getAttribute('data-setting')][Parent][select.getAttribute('data-setting')]
}
}
}
});
Modal.showModal()
}); });
} }
if (Select.length > 0) {
Array.from(Select).forEach(element => {
element.addEventListener('change', function() {
SetSetting(element.getAttribute('data-setting'), element, element.selectedIndex)
});
});
}
});
document.getElementById('ResetDefaults').addEventListener('click', function() {
ResetDefaultsModal.showModal();
});
document.getElementById('ResetDefaults-Modal-Yes').addEventListener('click', function() {
Settings = ExpectedSettings
Save()
setTimeout(function () {
LoadCurrent();
ResetDefaultsModal.close();
}, 400)
});
document.getElementById('ResetDefaults-Modal-No').addEventListener('click', function() {
ResetDefaultsModal.close();
}); });
function LoadCurrent() { function LoadCurrent() {
chrome.storage.sync.get(["PolyPlus_Settings"], function(result) { chrome.storage.sync.get(['PolyPlus_Settings'], function (result) {
Settings = MergeObjects(result.PolyPlus_Settings || ExpectedSettings, ExpectedSettings) Settings = Utilities.MergeObjects(result.PolyPlus_Settings || Utilities.DefaultSettings, Utilities.DefaultSettings);
RecentSave = structuredClone(Settings)
console.log(Settings) console.log('Current Settings: ', Settings);
Elements.forEach(element => { Elements.forEach((element) => {
let Status = element.getElementsByClassName('status')[0] UpdateElementState(element);
if (Status !== undefined) {
Status.innerText = FormatBool(Settings[element.getElementsByTagName('button')[0].getAttribute('data-setting')])
}
let SelectInput = element.getElementsByTagName('select')[0]
if (SelectInput) {
SelectInput.selectedIndex = Settings[SelectInput.getAttribute('data-setting')]
}
}); });
}); });
} }
function ToggleSetting(Name, Element) { function SetSetting(element, value, update, modalParent) {
if (Settings[Name] === true) { document.title = '*unsaved | Poly+ Settings'
Settings[Name] = false; const name = element.getAttribute('data-setting');
let parent = element.getAttribute('data-parent');
if (modalParent !== undefined) {
console.log(modalParent);
parent = modalParent;
}
if (value === 'bool') {
value = !GetSettingValue(element, modalParent);
}
if (parent !== null) {
let Parent = Object.values(Settings)[Object.keys(Settings).indexOf(parent)];
if (!isNaN(element.getAttribute('data-parent')) && element.getAttribute('data-parent') !== null) {
Parent = Parent[parseInt(element.getAttribute('data-parent'))];
}
Parent[name] = value;
} else { } else {
Settings[Name] = true; Settings[name] = value;
} }
if (update !== false) {
if (Element != null) { UpdateElementState(document.querySelector(`.setting-container:has([data-setting="${name}"])${parent !== null ? `:has([data-parent="${parent}"])` : ''}`), value);
Element.getElementsByClassName('status')[0].innerText = FormatBool(Settings[Name])
} }
if (SaveBtn.getAttribute('disabled')) { if (SaveBtn.getAttribute('disabled')) {
console.log('is disabled button - toggle') SaveBtn.removeAttribute('disabled');
SaveBtn.removeAttribute('disabled')
// Handle leaving the settings page before saving
window.onbeforeunload = function (e) {
return "Are you sure you'd like to leave? Your Poly+ settings haven't been saved."
};
} }
/*
if (AreIdentical(Settings, RecentSave) === true) {
document.title = 'Poly+ Settings'
SaveBtn.disabled = true
}
*/
} }
function SetSetting(Name, Element, Value) { function GetSettingValue(element, modalParent) {
console.log(Settings) const name = element.getAttribute('data-setting');
Settings[Name] = Value let parent = element.getAttribute('data-parent');
if (SaveBtn.getAttribute('disabled')) { if (modalParent !== undefined) {
console.log('is disabled button') parent = modalParent;
SaveBtn.removeAttribute('disabled') }
let Status = name;
if (parent !== null) {
let Parent = Object.values(Settings)[Object.keys(Settings).indexOf(parent)];
if (!isNaN(element.getAttribute('data-parent')) && element.getAttribute('data-parent') !== null) {
Parent = Parent[parseInt(element.getAttribute('data-parent'))];
Status = Parent[name];
} else {
Status = Object.values(Parent)[Object.keys(Parent).indexOf(name)];
}
} else {
Status = Settings[Status];
}
if (element.tagName === 'SELECT' && element.getAttribute('data-useValue') === 'true') {
Status = Array.from(element.children).indexOf(element.querySelector('option[value="' + Status + '"]'))
}
return Status;
}
function UpdateElementState(element, status) {
const Button = element.getElementsByClassName('toggle-btn')[0];
if (status === undefined) {
status = GetSettingValue(Button);
}
if (status === true) {
element.classList.add('enabled');
element.classList.remove('disabled');
Button.innerText = 'Disable';
Button.classList.add('btn-danger');
Button.classList.remove('btn-success');
} else {
element.classList.add('disabled');
element.classList.remove('enabled');
Button.innerText = 'Enable';
Button.classList.add('btn-success');
Button.classList.remove('btn-danger');
}
let SelectInput = element.getElementsByTagName('select')[0];
if (SelectInput) {
SelectInput.selectedIndex = GetSettingValue(SelectInput);
}
let Checkbox = Array.from(element.getElementsByTagName('input'));
if (Checkbox.length > 0) {
Checkbox.forEach((check) => {
check.checked = GetSettingValue(check);
});
} }
} }
function Save() { function Save() {
SaveBtn.setAttribute('disabled', 'true') document.title = 'Poly+ Settings';
chrome.storage.sync.set({ 'PolyPlus_Settings': Settings, arrayOrder: true }, function() { SaveBtn.setAttribute('disabled', 'true');
chrome.storage.sync.set({PolyPlus_Settings: Settings}, function () {
console.log('Saved successfully!'); console.log('Saved successfully!');
RecentSave = Settings
}); });
console.log(Settings); // Handle leaving the settings page after saving
window.onbeforeunload = null
console.log('Save:', Settings);
} }
let LoadThemeFromJSONBtn = document.getElementById('LoadThemeFromJSONBtn') let LoadThemeFromJSONBtn = document.getElementById('LoadThemeFromJSONBtn');
let SaveThemeToJSONInput = document.getElementById('SaveThemeToJSONInput') let SaveThemeToJSONInput = document.getElementById('SaveThemeToJSONInput');
let CopyThemeJSONBtn = document.getElementById('CopyThemeJSONBtn') let CopyThemeJSONBtn = document.getElementById('CopyThemeJSONBtn');
LoadThemeFromJSONBtn.addEventListener('click', function () { LoadThemeFromJSONBtn.addEventListener('click', function () {
LoadThemeJSON(LoadThemeFromJSONBtn.previousElementSibling.value) LoadThemeJSON(LoadThemeFromJSONBtn.previousElementSibling.value);
}); });
document.getElementById('ThemeCreator').getElementsByTagName('button')[1].addEventListener('click', function(){ document
SaveThemeToJSONInput.value = JSON.stringify(Settings.ThemeCreator) .getElementById('theme-creator')
.getElementsByTagName('button')[1]
.addEventListener('click', function () {
SaveThemeToJSONInput.value = JSON.stringify(Settings.ThemeCreator);
}); });
CopyThemeJSONBtn.addEventListener('click', function () { CopyThemeJSONBtn.addEventListener('click', function () {
if (SaveThemeToJSONInput.value.length > 0) { if (SaveThemeToJSONInput.value.length > 0) {
navigator.clipboard.writeText(SaveThemeToJSONInput.value) navigator.clipboard
.writeText(SaveThemeToJSONInput.value)
.then(() => { .then(() => {
alert('Successfully copied theme data to clipboard!') alert('Successfully copied theme data to clipboard!');
}) })
.catch(() => { .catch(() => {
alert('Failure to copy theme data to clipboard.') alert('Failure to copy theme data to clipboard.');
}); });
} }
}); });
let CurrencyDate =
LoadFile(chrome.runtime.getURL('js/resources/currencies.json'), function(text){
CurrencyDate = new Date(JSON.parse(text).Date).toLocaleDateString("en-US", {day:"numeric",month:"long",year:"numeric"})
document.getElementById('IRLPriceWithCurrencyCurrency').previousElementSibling.children[1].innerText = document.getElementById('IRLPriceWithCurrencyCurrency').previousElementSibling.children[1].innerText.replace('[DATE]', CurrencyDate)
})
function LoadThemeJSON(string) { function LoadThemeJSON(string) {
alert('This feature has been disabled for now.')
return
try { try {
let JSONTable = JSON.parse(string) let JSONTable = JSON.parse(string);
if (JSONTable.length === ExpectedSettings.ThemeCreator.length) { if (JSONTable.length === Utilities.DefaultSettings.ThemeCreator.length) {
if (confirm('Are you sure you\'d like to replace this theme with the theme specified in the JSON?') === true) { if (confirm("Are you sure you'd like to replace this theme with the theme specified in the JSON?") === true) {
LoadThemeFromJSONBtn.previousElementSibling.value = '' LoadThemeFromJSONBtn.previousElementSibling.value = '';
document.getElementById('ThemeCreator-Modal').close() document.getElementById('ThemeCreator-Modal').close();
/*
for (let i = 0; i < JSONTable.length; i++) { for (let i = 0; i < JSONTable.length; i++) {
JSONTable[i] = new Sanitzer(JSONTable[i]) if (JSONTable[i][0] !== '#') {
JSONTable[i] = '';
} }
*/ }
Settings.ThemeCreator = MergeObjects(JSONTable, ExpectedSettings.ThemeCreator) Settings.ThemeCreator = Utilities.MergeObjects(JSONTable, Utilities.DefaultSettings.ThemeCreator);
Save(); Save();
console.log(JSONTable.length, JSONTable, 'applied') console.log(JSONTable.length, JSONTable, 'applied');
document.getElementById('ThemeCreator').getElementsByTagName('button')[1].click(); document.getElementById('ThemeCreator').getElementsByTagName('button')[1].click();
} }
} else { } else {
alert('JSON is not a theme!') alert('JSON is not a theme!');
//LoadThemeFromJSONBtn.innerText = 'JSON is too short or too long!'
//setTimeout(function () {LoadThemeFromJSONBtn.innerText = 'Load'}, 1250)
} }
} catch (error) { } catch (error) {
alert('JSON is invalid!') alert('JSON is invalid!');
//LoadThemeFromJSONBtn.innerText = 'JSON is invalid!'
//setTimeout(function () {LoadThemeFromJSONBtn.innerText = 'Load'}, 1250)
} }
} }
/* chrome.storage.sync.get(['PolyPlus_AutoAds'], function(result){
function MergeObjects(obj1, obj2) { let AutoAds = result.PolyPlus_AutoAds || [];
var mergedObj = {};
// Copy the values from obj1 to the mergedObj const Modal = document.getElementById("AutoAdBidding-Modal")
for (var key in obj1) { const AddButton = document.getElementById('auto-ad-bidding-add')
mergedObj[key] = obj1[key];
}
// Merge the values from obj2 into the mergedObj, favoring obj2 for non-existing keys in obj1 AddButton.addEventListener('click', async function() {
for (var key in obj2) { const Page = new DOMParser().parseFromString((await (await fetch('https://polytoria.com/create/ad/' + AddButton.previousElementSibling.value)).text()), 'text/html')
if (!obj1.hasOwnProperty(key)) {
mergedObj[key] = obj2[key];
} else if (obj1[key] !== obj2[key]) {
mergedObj[key] = obj2[key];
}
}
// Remove keys from mergedObj if they are not present in obj2
for (var key in mergedObj) {
if (!obj2.hasOwnProperty(key)) {
delete mergedObj[key];
}
}
return mergedObj; })
}
*/
function MergeObjects(obj1, obj2) { const AddRow = function(index, info) {
var mergedObj = {}; const Row = document.createElement('tr')
Row.innerHTML = `
// Copy the values from obj1 to the mergedObj <th scope="row">${index+1}</th>
for (var key in obj1) { <td><a href="https://polytoria.com/create/ad/${info.id}">"${info.name}"</a></td>
mergedObj[key] = obj1[key]; <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>
`
} }
})
// Merge the values from obj2 into the mergedObj, favoring obj2 for non-existing keys in obj1 function AreIdentical(obj1, obj2) {
for (var key in obj2) { if (obj1.length !== obj2.length) { return false }
if (!obj1.hasOwnProperty(key)) { return JSON.stringify(obj1) === JSON.stringify(obj2)
mergedObj[key] = obj2[key];
}
}
return mergedObj;
} }
function FormatBool(bool) { function FormatBool(bool) {
if (bool === true) { if (bool === true) {
return 'enabled' return 'enabled';
} else { } else {
return 'disabled' return 'disabled';
} }
} }
function LoadFile(path, callback) { const Manifest = chrome.runtime.getManifest();
var xhr = new XMLHttpRequest(); let BuildType = 'Stable';
xhr.onload = function () { return callback(this.responseText); } if (Manifest.version_name !== undefined) {
xhr.open("GET", path, true); BuildType = 'Pre-Release';
xhr.send();
} }
const FooterText = document.getElementById('footer-text');
FooterText.children[0].innerHTML = `Version: v${Manifest.version} | Build Type: ${BuildType}`;
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 === Manifest.version || Math.floor((data.version - Manifest.version) * 10) === 0) {
CheckForUpdatesButton.innerHTML = '<b>No updates available</b>';
alert('No updates available');
} else {
const NumberOfUpdatesAvailable = Math.floor((data.version - Version) * 10);
CheckForUpdatesButton.innerHTML = '<b>' + NumberOfUpdatesAvailable + ' update(s) available</b>';
alert(NumberOfUpdatesAvailable + ' updates available');
}
})
.catch((error) => {
console.log(error);
});
}
CheckForUpdatesButton.addEventListener('click', CheckForUpdates);
/*
fetch(chrome.runtime.getURL('resources/currencies.json'))
.then((response) => {
if (!response.ok) {
throw new Error('Network not ok');
}
return response.json();
})
.then((data) => {
const DateText = new Date(data.Date).toLocaleDateString('en-US', {day: 'numeric', month: 'long', year: 'numeric'});
document.getElementById('IRLPriceWithCurrency-Date').innerText = DateText;
})
.catch((error) => {
console.log(error);
});
*/
chrome.storage.local.get(['PolyPlus_OutOfDate', 'PolyPlus_LiveVersion', 'PolyPlus_ReleaseNotes', '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) {
const Banner = document.createElement('div');
Banner.classList = 'alert position-sticky p-3';
Banner.style = 'top: 30px; box-shadow: 0 0 20px 2px #000; z-index: 2000; background: rgb(163 39 39);';
Banner.innerHTML = `
<b>New Update Available!</b>
<br>
Your Poly+ installation is out of date! If you would like to get the latest and greatest features, improvements, and bug fixes click on one of the links below to dismiss this banner!
<br>
<div role="group" class="btn-group w-100 mt-2">
<a href="${result.PolyPlus_ReleaseNotes}" class="btn btn-primary btn-sm w-25" target="_blank">Go to Release Notes</a>
<button id="skip-this-update" class="btn btn-warning btn-sm w-25">(Not Recommended) Skip this Update</button>
</div>
`;
document.getElementById('page').insertBefore(Banner, document.getElementById('page').children[1]);
const SkipButton = document.getElementById('skip-this-update');
SkipButton.addEventListener('click', function () {
Banner.remove();
chrome.storage.local.set({PolyPlus_SkipUpdate: result.PolyPlus_LiveVersion}, function () {
console.log('set skip update to live version: ', result.PolyPlus_LiveVersion);
});
});
}
});