├── .eslintrc.js ├── CHANGELOG.md ├── LICENSE ├── README.md ├── backpack.tf extended sorting.meta.js ├── backpack.tf extended sorting.user.js ├── preview1.png ├── preview2.png ├── preview3.png ├── preview4.png └── preview5.png /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "jquery": true, 6 | "greasemonkey": true 7 | }, 8 | "extends": "eslint:recommended", 9 | "globals": { 10 | "Atomics": "readonly", 11 | "SharedArrayBuffer": "readonly", 12 | }, 13 | "parserOptions": { 14 | "ecmaVersion": 2018, 15 | "sourceType": "module" 16 | }, 17 | "rules": { 18 | "indent": [ 19 | "error", 20 | "tab" 21 | ], 22 | "linebreak-style": [ 23 | "error", 24 | "unix" 25 | ], 26 | "quotes": [ 27 | "error", 28 | "double" 29 | ], 30 | "semi": [ 31 | "error", 32 | "always" 33 | ], 34 | "no-prototype-builtins": "off", 35 | "no-inner-declarations": "off" 36 | } 37 | }; -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.1.27 4 | 5 | Added: 6 | 7 | * Grouping by killstreak 8 | * Grouping by original id 9 | 10 | Fixed: 11 | 12 | * Detect "Footsteps Spell" instead of "Footprints Spell" 13 | 14 | ## 0.1.26 15 | 16 | Added: 17 | 18 | * Utilities for Killstreak Kits 19 | * Shortcuts to different Killstreak tiers of one weapon, including shortcuts to either the fabricators or kits 20 | 21 | Fixed: 22 | 23 | * Incorrect classifieds queries on paintkit items 24 | 25 | ## 0.1.25 26 | 27 | Improved: 28 | 29 | * All skin / war paint names for the auto complete which were added to the game since the Winter 2019 Collection (up to Scream Fortress XIII Collection) 30 | * Tradeoffer links are (when available) now correctly displayed on all user pages (Profile and Friends), instead of only the Backpack page. 31 | 32 | ## 0.1.24 33 | 34 | Added: 35 | 36 | * Grouping by craft number 37 | 38 | ## 0.1.23 39 | 40 | Fixed: 41 | 42 | * Spell sorting 43 | 44 | ## 0.1.22 45 | 46 | Added: 47 | 48 | * Double spell grouping 49 | * Choosing group by spells groups by colors then by footprints. Group by spell again to inverse. 50 | 51 | Fixed: 52 | 53 | * Voices from Below sorting 54 | 55 | ## 0.1.21 56 | 57 | Added: 58 | 59 | * Added particle search in classifieds search 60 | 61 | ## 0.1.20 62 | 63 | Improved: 64 | 65 | * Generation of Marketplace.tf Links 66 | * Now using an ESLint config 67 | 68 | ## 0.1.19 69 | 70 | Added: 71 | 72 | * Added trade offer link to every profile (where the trade offer link is known) 73 | 74 | Improved: 75 | 76 | * Cleared up the code a tiny bit 77 | 78 | ## 0.1.18 79 | 80 | Added: 81 | 82 | * Added a premium search button on the item hover popup for decorated weapons. This will display correct results when trying to search for skins with premium 83 | 84 | ## 0.1.17 85 | 86 | Added: 87 | 88 | * Added sorting by scm price in inventory 89 | 90 | ## 0.1.16 91 | 92 | Improved: 93 | 94 | * Highlighting spells works now in more places (added: classifieds and compare links) 95 | 96 | ## 0.1.15 97 | 98 | Added: 99 | 100 | * Group by Classifieds 101 | 102 | Improved: 103 | 104 | * Formatting (and changed to tab indentation) 105 | 106 | ## 0.1.14 107 | 108 | Added: 109 | 110 | * Added Halloween war paints to the Autocomplete 111 | 112 | Improved: 113 | 114 | * Now when using the refresh button spells will still be marked 115 | * After a backpack update changed the name from "Sort by" to "Group by" it was also changed in this userscript 116 | * Replaced the default Group by item index (because that grouped only per page). By default this will now add 1 group for every defindex, if you want all items in a single page ordered instead of per defindex there is a variable singlepage in the code which you can set to true 117 | 118 | ## 0.1.13 119 | 120 | Added: 121 | 122 | * When entering a Texture Name in Texture & Wear you can now autocomplete with tab (Press tab again to cycle between alternative suggestions) 123 | 124 | ## 0.1.12 125 | 126 | Fixed: 127 | 128 | * Wrong URL syntax for skins when generating a marketplace.tf link 129 | 130 | ## 0.1.11 131 | 132 | Fixed: 133 | 134 | * Function to access Popover now works on Items which are loaded after page load 135 | 136 | ## 0.1.10 137 | 138 | Fixed: 139 | 140 | * Spells not being display with the inventory load now after page loaded 141 | * When searching in the effect or unusuals the unusuals didn't reappear but stayed hidden (in some cases) when removing parts of the search term 142 | 143 | ## 0.1.9 (After the new Inventory Update (Inventories load after the page loaded)) 144 | 145 | Fixed: 146 | 147 | * fixed custom sorts being on a wrong drop down 148 | * custom sorts break when using them before the inventory loaded 149 | 150 | Removed: 151 | 152 | * Price Module (wasn't used anyways, and should it be needed again I will make an own implementation) 153 | 154 | ## 0.1.8 155 | 156 | Added: 157 | 158 | * In the class menus on the effect / unusual pricing pages add Miscs as class (meaning unusuals which you can equip over other unusuals) (Should I miss some there feel free to send me a message) 159 | 160 | ## 0.1.7 161 | 162 | Fixed: 163 | 164 | * Marketplace link appearing multiple times when hovering multiple times 165 | * Marketplace link now only appears if no price is known 166 | 167 | ## 0.1.6 168 | 169 | Added: 170 | 171 | * In the popover of an item, show a link to the marketplace item (So you have a link even when there are no sales) 172 | 173 | ## 0.1.5 174 | 175 | Added: 176 | 177 | * Like with my custom sorting methods you can now reverse the groups of the already implemented sorting methods (so you can switch f.e. between old to new and new to old when sorting by time) 178 | 179 | ## 0.1.4 180 | 181 | Added: 182 | 183 | * On the pricelist page of unusual effects you now can filter them by class like the unusual by item page 184 | 185 | ## 0.1.3 186 | 187 | Fixed: 188 | 189 | * added missing spell (Halloween Fire) 190 | * reset last sort when non custom sort was used 191 | * fixed filtering for items with custom sorts (meaning added complete new filtering which additionally searches for f.e. strange parts, spells, custom name) 192 | 193 | Added: 194 | 195 | * Sort by level 196 | * Possibility to write code to change the order of the backpack pages 197 | 198 | ## 0.1.2 199 | 200 | Added: 201 | 202 | * generic sort can now ignore empty pages 203 | * spells are visually marked (green for 1 spell, red for multi-spell) 204 | * added sorting using spells 205 | 206 | ## 0.1.1 207 | 208 | Fixed: 209 | 210 | * generalized Sorting functions 211 | * removed empty item containers which were used for spacing the normal inventory pages 212 | 213 | Added: 214 | 215 | * sorting the single pages (f.e. sorting Paints by price) 216 | * meta data to automatically update the script 217 | 218 | ### 0.1 219 | 220 | * Released with additional sort by paint 221 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2017 NetroScript 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # backpack.tf * Miscellaneous Extensions 2 | 3 | This is an userscript to add miscellaneous functionality to backpack.tf (which I would have liked for myself). 4 | Currently included is: 5 | 6 | * In inventories add a Group by paint 7 | * In inventories add a Group by spell (including grouping and displaying double spelled items) 8 | * In inventories add a Group by level 9 | * In inventories add a Group by scm price 10 | * In inventories add a Group by Classifieds (shows you all the listed items) 11 | * In inventories add a Group by Craft numbers (cases and crates have their own category) 12 | * In inventories add a Group by Killstreak Kits (group by effect, click again to group by sheen) 13 | * Allow the reversion of the groups from a sort 14 | * Filter by class on backpack.tf/effect/ 15 | * For unusuals you can not only filter for class but now also filter for miscs (manually curated list, if you want more open an issue) 16 | * Improve classifieds / premium search 17 | * Autocomplete skin names 18 | * Filter unusual effects 19 | * Marketplace link for any item 20 | * Premium search for decorated weapons 21 | * Spells are highlighted in inventories (green dotted lower border for 1 spell, red for multiple spells) 22 | * If possible a trade offer link will be added to the profile links, so you can offer a trade even when the user has no listed item 23 | * Utilities for Killstreak Kits 24 | * Shortcuts to different Killstreak tiers of one weapon, including shortcuts to either the fabricators or kits 25 | 26 | ## Installation 27 | 28 | _____________________________________________ 29 | 30 | Use an extension which can execute userscripts (F.e. [Tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) for [Chrome](https://www.google.com/chrome/) or [Greasemonkey](https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/) for [Firefox](https://www.mozilla.org/firefox)) 31 | and then install using this link: [backpack.tf extended sorting.user.js](https://github.com/NetroScript/backpack.tf-miscellaneous-extensions/raw/master/backpack.tf%20extended%20sorting.user.js). 32 | 33 | (Or paste / install it manually for your plugin) 34 | 35 | ## Changelog 36 | 37 | _____________________________________________ 38 | 39 | You can find the changelog [here](CHANGELOG.md). 40 | 41 | ## Preview 42 | 43 | _____________________________________________ 44 | 45 | Paint sorting: 46 | ![Preview](https://raw.githubusercontent.com/NetroScript/backpack.tf-miscellaneous-extensions/master/preview1.png) 47 | 48 | Spell sorting (green dots mean a single spell, red dots mean multi spell): 49 | ![Preview](https://raw.githubusercontent.com/NetroScript/backpack.tf-miscellaneous-extensions/master/preview2.png) 50 | 51 | Level sorting: 52 | ![Preview](https://raw.githubusercontent.com/NetroScript/backpack.tf-miscellaneous-extensions/master/preview3.png) 53 | 54 | SCM sorting: 55 | ![Preview](https://raw.githubusercontent.com/NetroScript/backpack.tf-miscellaneous-extensions/master/preview5.png) 56 | 57 | Filter the unusual hats by class on the unusual effects pricelist page : 58 | ![Preview](https://raw.githubusercontent.com/NetroScript/backpack.tf-miscellaneous-extensions/master/preview4.png) 59 | -------------------------------------------------------------------------------- /backpack.tf extended sorting.meta.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name backpack.tf - Miscellaneous Extensions 3 | // @description Adds more options for sorting items in backpacks (currently Sorting for paints, spells, levels, scm price, classified listings) and other stuff which I would have liked (including highlighting spells, autocompleting spell names, autocompleting particle names or sorting unusuals by class) 4 | // @version 0.1.29 5 | // @author Netroscript 6 | // @namespace https://github.com/NetroScript 7 | // @include /^https?:\/\/(.*\.)?backpack\.tf\/.* 8 | // @downloadURL https://github.com/NetroScript/backpack.tf-miscellaneous-extensions/raw/master/backpack.tf%20extended%20sorting.user.js 9 | // @updateURL https://github.com/NetroScript/backpack.tf-miscellaneous-extensions/raw/master/backpack.tf%20extended%20sorting.meta.js 10 | // @grant none 11 | // @run-at document-end 12 | // ==/UserScript== 13 | -------------------------------------------------------------------------------- /backpack.tf extended sorting.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name backpack.tf - Miscellaneous Extensions 3 | // @description Adds more options for sorting items in backpacks (currently Sorting for paints, spells, levels, scm price, classified listings) and other stuff which I would have liked (including highlighting spells, autocompleting spell names, autocompleting particle names or sorting unusuals by class) 4 | // @version 0.1.29 5 | // @author Netroscript 6 | // @namespace https://github.com/NetroScript 7 | // @include /^https?:\/\/(.*\.)?backpack\.tf\/.* 8 | // @downloadURL https://github.com/NetroScript/backpack.tf-miscellaneous-extensions/raw/master/backpack.tf%20extended%20sorting.user.js 9 | // @updateURL https://github.com/NetroScript/backpack.tf-miscellaneous-extensions/raw/master/backpack.tf%20extended%20sorting.meta.js 10 | // @grant none 11 | // @run-at document-end 12 | // ==/UserScript== 13 | 14 | (function () { 15 | "use strict"; 16 | 17 | const Voices = "Voices from Below"; 18 | const DieJob = "Die Job"; 19 | const Corruption = "Chromatic Corruption"; 20 | const Pigmentation = "Putrescent Pigmentation"; 21 | const Spectrum = "Spectral Spectrum"; 22 | const Sinister = "Sinister Staining"; 23 | const TeamSpirit = "Team Spirit Footprints"; 24 | const Headless = "Headless Horseshoes"; 25 | const CorpseGray = "Corpse Gray Footprints"; 26 | const Violet = "Violent Violet Footprints"; 27 | const Purple = "Bruised Purple Footprints"; 28 | const Gangreen = "Gangreen Footprints"; 29 | const Orange = "Rotten Orange Footprints"; 30 | const Exorcism = "Exorcism"; 31 | 32 | function isColor(spell) { 33 | return [DieJob, Corruption, Pigmentation, Spectrum, Sinister].includes(spell); 34 | } 35 | 36 | function isFootprint(spell) { 37 | return [TeamSpirit, Headless, CorpseGray, Violet, Purple, Gangreen, Orange].includes(spell); 38 | } 39 | 40 | function isExorcism(spell) { 41 | return Exorcism === spell; 42 | } 43 | 44 | function isVoices(spell) { 45 | return Voices === spell; 46 | } 47 | 48 | function normalize(spell1, spell2) { 49 | const normal = [spell1, spell2]; 50 | const flip = [spell2, spell1]; 51 | const voices = isVoices(spell1) || isVoices(spell2); 52 | const colored = isColor(spell1) || isColor(spell2); 53 | const prints = isFootprint(spell1) || isFootprint(spell2); 54 | if (colored && voices) { 55 | return isColor(spell1) ? normal : flip; 56 | } else if (prints && voices) { 57 | return isFootprint(spell1) ? normal : flip; 58 | } else if (prints && colored) { 59 | return isColor(spell1) ? normal : flip; 60 | } else { 61 | return isExorcism(spell1) ? flip : normal; 62 | } 63 | } 64 | 65 | //###############Source: https://stackoverflow.com/a/15605648 66 | String.prototype.fmt = function (hash) { 67 | var string = this, 68 | key; 69 | for (key in hash) 70 | string = string.replace(new RegExp("\\{" + key + "\\}", "gm"), hash[key]); 71 | return string; 72 | }; 73 | //############################################################## 74 | 75 | //###############Source: https://stackoverflow.com/a/4726403 76 | function idealTextColor(bgColor) { 77 | 78 | var nThreshold = 105; 79 | var components = getRGBComponents(bgColor); 80 | var bgDelta = (components.R * 0.299) + (components.G * 0.587) + (components.B * 0.114); 81 | 82 | return ((255 - bgDelta) < nThreshold) 83 | ? "#000000" 84 | : "#ffffff"; 85 | } 86 | 87 | function getRGBComponents(color) { 88 | 89 | var r = color.substring(1, 3); 90 | var g = color.substring(3, 5); 91 | var b = color.substring(5, 7); 92 | 93 | return { 94 | R: parseInt(r, 16), 95 | G: parseInt(g, 16), 96 | B: parseInt(b, 16) 97 | }; 98 | } 99 | //############################################################## 100 | 101 | var colors = { 102 | "A Color Similar to Slate": { 103 | "cc": ["#2F4F4F"] 104 | }, 105 | "A Deep Commitment to Purple": { 106 | "cc": ["#7D4071"] 107 | }, 108 | "A Distinctive Lack of Hue": { 109 | "cc": ["#141414"] 110 | }, 111 | "A Mann's Mint": { 112 | "cc": ["#BCDDB3"] 113 | }, 114 | "After Eight": { 115 | "cc": ["#2D2D24"] 116 | }, 117 | "Aged Moustache Grey": { 118 | "cc": ["#7E7E7E"] 119 | }, 120 | "An Extraordinary Abundance of Tinge": { 121 | "cc": ["#E6E6E6"] 122 | }, 123 | "Australium Gold": { 124 | "cc": ["#E7B53B"] 125 | }, 126 | "Color No. 216-190-216": { 127 | "cc": ["#D8BED8"] 128 | }, 129 | "Dark Salmon Injustice": { 130 | "cc": ["#E9967A"] 131 | }, 132 | "Drably Olive": { 133 | "cc": ["#808000"] 134 | }, 135 | "Indubitably Green": { 136 | "cc": ["#729E42"] 137 | }, 138 | "Mann Co. Orange": { 139 | "cc": ["#CF7336"] 140 | }, 141 | "Muskelmannbraun": { 142 | "cc": ["#A57545"] 143 | }, 144 | "Noble Hatter's Violet": { 145 | "cc": ["#51384A"] 146 | }, 147 | "Peculiarly Drab Tincture": { 148 | "cc": ["#C5AF91"] 149 | }, 150 | "Pink as Hell": { 151 | "cc": ["#FF69B4"] 152 | }, 153 | "Radigan Conagher Brown": { 154 | "cc": ["#694D3A"] 155 | }, 156 | "The Bitter Taste of Defeat and Lime": { 157 | "cc": ["#32CD32"] 158 | }, 159 | "The Color of a Gentlemann's Business Pants": { 160 | "cc": ["#F0E68C"] 161 | }, 162 | "Ye Olde Rustic Colour": { 163 | "cc": ["#7C6C57"] 164 | }, 165 | "Zepheniah's Greed": { 166 | "cc": ["#424F3B"] 167 | }, 168 | "An Air of Debonair": { 169 | "cc": [ 170 | "#654740", "#28394D" 171 | ] 172 | }, 173 | "Balaclavas Are Forever": { 174 | "cc": [ 175 | "#3B1F23", "#18233D" 176 | ] 177 | }, 178 | "Cream Spirit": { 179 | "cc": [ 180 | "#C36C2D", "#B88035" 181 | ] 182 | }, 183 | "Operator's Overalls": { 184 | "cc": [ 185 | "#483838", "#384248" 186 | ] 187 | }, 188 | "Team Spirit": { 189 | "cc": [ 190 | "#B8383B", "#5885A2" 191 | ] 192 | }, 193 | "The Value of Teamwork": { 194 | "cc": [ 195 | "#803020", "#256D8D" 196 | ] 197 | }, 198 | "Waterlogged Lab Coat": { 199 | "cc": [ 200 | "#A89A8C", "#839FA3" 201 | ] 202 | } 203 | }; 204 | 205 | var spells = { //Footprints 206 | "Footsteps Spell: Team Spirit Footprints": { 207 | "cc": [ 208 | "#B8383B", "#5885A2" 209 | ] 210 | }, 211 | "Footsteps Spell: Gangreen Footprints": { 212 | "cc": ["#79c46b"] 213 | }, 214 | "Footsteps Spell: Corpse Gray Footprints": { 215 | "cc": ["#8e9f9d"] 216 | }, 217 | "Footsteps Spell: Violent Violet Footprints": { 218 | "cc": ["#f7b4fe"] 219 | }, 220 | "Footsteps Spell: Rotten Orange Footprints": { 221 | "cc": ["#CF7336"] 222 | }, 223 | "Footsteps Spell: Bruised Purple Footprints": { 224 | "cc": ["#7D4071"] 225 | }, 226 | "Footsteps Spell: Headless Horseshoes": { 227 | "cc": ["#ba76ff"] 228 | }, //Paint changing 229 | "Paint Spell: Die Job": { 230 | "cc": [ 231 | "#E7B53B", "#8586ff" 232 | ] 233 | }, 234 | "Paint Spell: Spectral Spectrum": { 235 | "cc": [ 236 | "#B8383B", "#5885A2" 237 | ] 238 | }, 239 | "Paint Spell: Putrescent Pigmentation": { 240 | "cc": [ 241 | "#79c46b", "#67B037" 242 | ] 243 | }, 244 | "Paint Spell: Sinister Staining": { 245 | "cc": [ 246 | "#F2EF46", "#808000" 247 | ] 248 | }, 249 | "Paint Spell: Chromatic Corruption": { 250 | "cc": [ 251 | "#DB42BD", "#7D4071" 252 | ] 253 | }, //Voice Changing 254 | "Standard Spell: Voices from Below": { //this is not the official spell name but it seems the single spell names get merged into it 255 | "cc": ["#D11959"] 256 | }, 257 | "Standard Spell: Scout's Spectral Snarl": { 258 | "cc": ["#acfd9e"] 259 | }, 260 | "Standard Spell: Soldier's Booming Bark": { 261 | "cc": ["#f48280"] 262 | }, 263 | "Standard Spell: Pyro's Muffled Moan": { 264 | "cc": ["#f8bbfe"] 265 | }, 266 | "Standard Spell: Demoman's Cadaverous Croak": { 267 | "cc": ["#90fdfd"] 268 | }, 269 | "Standard Spell: Heavy's Bottomless Bass": { 270 | "cc": ["#f57eff"] 271 | }, 272 | "Standard Spell: Engineers's Gravelly Growl": { 273 | "cc": ["#fcfd95"] 274 | }, 275 | "Standard Spell: Medic's Blood-curdling Bellow": { 276 | "cc": ["#f7b786"] 277 | }, 278 | "Standard Spell: Sniper's Deep Downunder Drawl": { 279 | "cc": ["#7c7cff"] 280 | }, 281 | "Standard Spell: Spy's Creepy Croon": { 282 | "cc": ["#e5e5e5"] 283 | }, // Weapon changing 284 | "Weapon Spell: Pumpkin Bombs": { //this is not the official spell name but it seems the single spell names get merged into it 285 | "cc": ["#68167d"] 286 | }, 287 | "Weapon Spell: Exorcism": { 288 | "cc": [ 289 | "#B8383B", "#5885A2" 290 | ] 291 | }, 292 | "Weapon Spell: Squash Rockets": { 293 | "cc": ["#dc93ff"] 294 | }, 295 | "Weapon Spell: Spectral Flame": { 296 | "cc": ["#5faf53"] 297 | }, 298 | "Weapon Spell: Sentry Quad-Pumpkins": { 299 | "cc": ["#8b8ce9"] 300 | }, 301 | "Weapon Spell: Gourd Grenades": { 302 | "cc": ["#f8bb8c"] 303 | }, 304 | "Weapon Spell: Halloween Fire": { 305 | "cc": ["#4ef442"] 306 | } 307 | }; 308 | 309 | let filteri, 310 | sIc, 311 | filtersearchval, 312 | filterValue, 313 | filtertimeout, 314 | sIc2; 315 | 316 | if ((window.location.pathname.split("/effect/").length > 1 || window.location.pathname.startsWith("/unusuals")) && $("table.unusuallist-view").length == 0) { 317 | let misc_ids = ["938_", "361_", "30140_", "993_", "30329_", "783_", "393_", "30646_", "451_", "339_", "54_", "315_", "590_", "316_", "605_", "30095_", "110_", "337_", "380_", "48_", "30004_"]; 318 | 319 | let items = $("li.item"); 320 | let filtervar = "All"; 321 | 322 | filteri = function () { 323 | 324 | $(items).filter(function () { 325 | $(items).show(); 326 | var i = $(this).attr("data-class"); 327 | if (filtervar == "misci") { 328 | return (misc_ids.indexOf($(this).attr("data-defindex") + "_") == -1); 329 | } 330 | if (typeof i == "undefined" && filtervar == "Multi") 331 | return false; 332 | if (typeof i == "undefined") 333 | return true; 334 | return (i.indexOf(filtervar) == -1); 335 | }).hide(); 336 | 337 | }; 338 | 339 | sIc = setInterval(function () { 340 | let e = $(".form-control[placeholder='Filter items...']"); 341 | 342 | if (e.length == 1) { 343 | e = e[0]; 344 | //Remove the old events 345 | let c = e.cloneNode(); 346 | while (e.firstChild) { 347 | c.appendChild(e.lastChild); 348 | } 349 | e.parentNode.replaceChild(c, e); 350 | let classbuttonstemplate = `
351 | 355 | 377 |
`; 378 | 379 | if (window.location.pathname.startsWith("/unusuals")) { 380 | $($("#unusual-pricelist-input-group").children()[0]).remove(); 381 | } 382 | $("#unusual-pricelist-input-group").prepend(classbuttonstemplate); 383 | 384 | $("#classmenu a").click(function (e) { 385 | 386 | $("#className").text($(e.target).text()); 387 | filtervar = $(e.target).attr("data-class"); 388 | filterValue = $("#filterlist").val(); 389 | $(items).show(); 390 | if (filtervar != "All") 391 | filteri(); 392 | $(items).filter(function () { 393 | if (filterValue.length == 0) return false; 394 | return ($(this).attr("data-name").toLowerCase().indexOf(filterValue.toLowerCase()) == -1); 395 | }).hide(); 396 | 397 | }); 398 | 399 | //Bind new events 400 | $(".form-control[placeholder='Filter items...']").on("keyup paste", function () { 401 | clearTimeout(filtertimeout); 402 | filtertimeout = setTimeout(function () { 403 | filterValue = $("#filterlist").val(); 404 | $(items).show(); 405 | if (filtervar != "All") 406 | filteri(); 407 | 408 | $(items).filter(function () { 409 | if (filterValue.length == 0) return false; 410 | return ($(this).attr("data-name").toLowerCase().indexOf(filterValue.toLowerCase()) == -1); 411 | }).hide(); 412 | 413 | }, 80); 414 | 415 | }); 416 | 417 | clearInterval(sIc); 418 | } 419 | 420 | }, 150); 421 | 422 | } 423 | 424 | // Add a trade offer link to every profile possible 425 | 426 | let trade_link = $(".user-link").attr("data-offers-params"); 427 | 428 | if(trade_link != undefined && $("span:contains('Profile Information')").length == 1){ 429 | $(".profile .information .buttons").append(` 430 | Open trade offer`); 431 | } 432 | 433 | if (window.location.pathname.startsWith("/profiles/") || window.location.pathname.startsWith("/id/")) { 434 | 435 | 436 | //################################## Changes to Filtering ###################### 437 | filteri = function () { }; 438 | //Disable custom filtering if you liked the old method more, use at your own risk, I won't test anything without my filter version 439 | let ToggleCustomFilter = true; 440 | 441 | //Declaring Variables so they are accessible outside the scope 442 | { 443 | //Wait until the input is generated 444 | if (ToggleCustomFilter) 445 | sIc = setInterval(function () { 446 | let e = $(".form-control[placeholder='Search...']"); 447 | 448 | if (e.length == 1) { 449 | e = e[0]; 450 | //Remove the old events 451 | let c = e.cloneNode(); 452 | while (e.firstChild) { 453 | c.appendChild(e.lastChild); 454 | } 455 | e.parentNode.replaceChild(c, e); 456 | 457 | //Add an attribute with information for filtering to every item 458 | let aitems = $("#backpack .item:not('.spacer')"); 459 | let aitemsl = aitems.length; 460 | for (let i = 0; i < aitemsl; i++) { 461 | let d = $(aitems[i]); 462 | let info = (d.children().last().text() + " " + d.attr("data-spell_1") + " " + d.attr("data-spell_2") + " " + d.attr("data-custom_name") + " " + d.attr("data-part_name_3") + " " + d.attr("data-part_name_2") + " " + d.attr("data-part_name_1")).toLowerCase().replace(/ undefined/g, ""); 463 | d.attr("data-filterinfo", info); 464 | } 465 | 466 | //Bind new events 467 | $(".form-control[placeholder='Search...']").on("keyup paste", function () { 468 | clearTimeout(filtertimeout); 469 | filtertimeout = setTimeout(function () { 470 | filteri(); 471 | }, 200); 472 | }); 473 | 474 | //New Filter function 475 | filteri = function () { 476 | if (ToggleCustomFilter) { 477 | filtersearchval = $(".form-control[placeholder='Search...']").val().toLowerCase(); 478 | 479 | $(".backpack-page").show(); 480 | let sitems = $("#backpack .item"); 481 | sitems.parent().find(".filterhidden").show(); 482 | 483 | if (filtersearchval === "") 484 | return; 485 | 486 | sitems.filter(function () { 487 | if (!this.dataset.hasOwnProperty("filterinfo")) 488 | return true; 489 | if (!(this.dataset.filterinfo.indexOf(filtersearchval) !== -1)) 490 | return true; 491 | return false; 492 | }).hide().addClass("filterhidden"); 493 | } 494 | 495 | //Hide empty backpack pages 496 | $(".backpack-page:has(li:visible)").addClass("dhide"); 497 | $(".backpack-page").hide(); 498 | $(".dhide").show().removeClass("dhide"); 499 | 500 | }; 501 | 502 | markSpells(); 503 | 504 | //Execute the needed functions after the inventory is reloaded 505 | $("#refresh-inventory").click(function () { 506 | sIc2 = setInterval(function () { 507 | let e = $(".inventory .item"); 508 | 509 | if (e.length > 0) { 510 | //Add an attribute with information for filtering to every item 511 | let aitems = $("#backpack .item:not('.spacer')"); 512 | let aitemsl = aitems.length; 513 | for (let i = 0; i < aitemsl; i++) { 514 | let d = $(aitems[i]); 515 | let info = (d.children().last().text() + " " + d.attr("data-spell_1") + " " + d.attr("data-spell_2") + " " + d.attr("data-custom_name") + " " + d.attr("data-part_name_3") + " " + d.attr("data-part_name_2") + " " + d.attr("data-part_name_1")).toLowerCase().replace(/ undefined/g, ""); 516 | d.attr("data-filterinfo", info); 517 | } 518 | 519 | //Hide empty backpack pages 520 | $(".backpack-page:has(li:visible)").addClass("dhide"); 521 | $(".backpack-page").hide(); 522 | $(".dhide").show().removeClass("dhide"); 523 | 524 | 525 | markSpells(); 526 | filteri(); 527 | clearInterval(sIc2); 528 | } 529 | }, 150); 530 | }); 531 | clearInterval(sIc); 532 | } 533 | }, 150); 534 | 535 | 536 | } 537 | 538 | //############################################################################## 539 | 540 | // Get the keyprice for later use 541 | let matched_keyprice = $("meta[name=description]").attr("content").match(/value of ([0-9,.]+) ref, ([0-9,.]+) keys/); 542 | let keyprice = parseFloat(matched_keyprice[1].replace(/,/g, "")) / parseFloat(matched_keyprice[2].replace(/,/g, "")); 543 | 544 | let pagetemplate = `
545 |
546 |
547 |
548 | {pagename} 549 | Select Page 550 |
551 | 553 |
`; 554 | 555 | var newSorts = [ 556 | [ 557 | "Group by paint", "paint", sortByPaint 558 | ], 559 | [ 560 | "Group by spell", "spell", sortBySpell 561 | ], 562 | [ 563 | "Group by level", "level", sortByLevel 564 | ], 565 | [ 566 | "Group by scm", "scm", sortBySCM 567 | ], 568 | [ 569 | "Group by classifieds", "classifieds", sortByClassifiedListing 570 | ], 571 | [ 572 | "Group by craft number", "craftnumber", sortByCraftNumber 573 | ], 574 | [ 575 | "Group by killstreak", "killstreak", sortByKillstreak 576 | ], 577 | [ 578 | "Group by original id", "original", sortByOriginalId 579 | ], 580 | ]; 581 | 582 | function sortByOriginalId() { 583 | let d = {}; 584 | d["No Original Id Detected"] = { 585 | "cc": ["#676780"], 586 | "items": [] 587 | }; 588 | d["Items"] = { 589 | "cc": ["#676780"], 590 | "items": [] 591 | }; 592 | d["Hidden Items"] = { 593 | "cc": ["#676780"] 594 | }; 595 | 596 | let z = $(".backpack-page .item:not(.spacer)"); 597 | 598 | 599 | //If you want all defindexes not on a single page and as a group for each original id change the singlepage value 600 | let singlepage = true; 601 | 602 | d["Hidden Items"]["items"] = $(".temp-page .item:not(.spacer)"); 603 | 604 | for (let p = 0; p < z.length; p++) { 605 | if ($(z[p]).attr("data-original_id") !== undefined && 606 | $(z[p]).attr("data-original_id") !== "0") { 607 | let def = $(z[p]).attr("data-original_id"); 608 | if (!singlepage) { 609 | if (!d.hasOwnProperty(def)) { 610 | d[def] = { 611 | "cc": ["#676780"], 612 | "items": [] 613 | }; 614 | } 615 | d[def]["items"].push($(z[p])[0]); 616 | } else { 617 | d["Items"]["items"].push($(z[p])[0]); 618 | } 619 | } else { 620 | d["No Original Id Detected"]["items"].push($(z[p])[0]); 621 | } 622 | } 623 | 624 | if (!singlepage) 625 | for (let k in d) { 626 | d[k]["items"] = genericItemSort("data-original_id", d[k]["items"]); 627 | } 628 | else 629 | for (let k in d) { 630 | d[k]["items"] = genericItemSort("data-original_id", d[k]["items"]); 631 | } 632 | 633 | genericSort(d, "d", true, { 634 | "use": true, 635 | "funct": function (a, b) { 636 | return parseInt(a[0].split(" ")[1]) - parseInt(b[0].split(" ")[1]); 637 | } 638 | }); 639 | } 640 | 641 | function sortByPaint() { 642 | let paints = colors; 643 | paints["Not Painted"] = { 644 | "cc": ["#676780"] 645 | }; 646 | paints["Hidden Items"] = { 647 | "cc": ["#676780"] 648 | }; 649 | 650 | for (let k in paints) { 651 | paints[k]["items"] = []; 652 | 653 | } 654 | 655 | let z = $(".backpack-page .item:not(.spacer)"); 656 | 657 | paints["Hidden Items"]["items"] = $(".temp-page .item:not(.spacer)"); 658 | 659 | for (let p = 0; p < z.length; p++) { 660 | if (paints.hasOwnProperty($(z[p]).attr("data-paint_name"))) { 661 | paints[$(z[p]).attr("data-paint_name")]["items"].push($(z[p])[0]); 662 | } else { 663 | paints["Not Painted"]["items"].push($(z[p])[0]); 664 | } 665 | } 666 | 667 | for (let k in paints) { 668 | paints[k]["items"] = genericItemSort("data-price", paints[k]["items"]); 669 | } 670 | 671 | genericSort(paints, "p"); 672 | 673 | } 674 | 675 | var spellNormalize = [ 676 | { 677 | "n": function (a, b) { 678 | return normalize(a, b); 679 | }, 680 | "id": "s" 681 | }, 682 | { 683 | "n": function (a, b) { 684 | let [s1, s2] = normalize(a, b); 685 | return [s2, s1]; 686 | }, 687 | "id": "s2" 688 | } 689 | ]; 690 | 691 | var activeSort = 0; 692 | 693 | function sortBySpell() { 694 | let n = spellNormalize[activeSort]["n"]; 695 | let id = spellNormalize[activeSort]["id"]; 696 | let s = spells; 697 | s["No Spell"] = { 698 | "cc": ["#676780"] 699 | }; 700 | s["Hidden Items"] = { 701 | "cc": ["#676780"] 702 | }; 703 | 704 | for (let k in s) { 705 | s[k]["items"] = []; 706 | } 707 | 708 | let z = $(".backpack-page .item:not(.spacer)"); 709 | 710 | s["Hidden Items"]["items"] = $(".temp-page .item:not(.spacer)"); 711 | 712 | for (let p = 0; p < z.length; p++) { 713 | let sk = Object.keys(s); 714 | if ((s.hasOwnProperty($(z[p]).attr("data-spell_1"))) && (s.hasOwnProperty($(z[p]).attr("data-spell_2")))) { 715 | let [s1, s2] = n($(z[p]).attr("data-spell_1"), $(z[p]).attr("data-spell_2")); 716 | let spellName = `${s1} and ${s2.slice(s2.indexOf(":") + 2)}`; 717 | if(sk.includes(spellName)) { 718 | s[spellName]["items"].push($(z[p])[0]); 719 | } else { 720 | s[spellName] = {"items": [$(z[p])[0]], "cc": [s[s1]["cc"][0], s[s2]["cc"][0]]}; 721 | } 722 | } else if (s.hasOwnProperty($(z[p]).attr("data-spell_1"))) { 723 | s[$(z[p]).attr("data-spell_1")]["items"].push($(z[p])[0]); 724 | } else { 725 | s["No Spell"]["items"].push($(z[p])[0]); 726 | } 727 | } 728 | 729 | for (let k in s) { 730 | s[k]["items"] = genericItemSort("data-price", s[k]["items"]); 731 | } 732 | let ns = s["No Spell"]; 733 | delete s["No Spell"]; 734 | let hi = s["Hidden Items"]; 735 | delete s["Hidden Items"]; 736 | let reducer = function (obj, key) { 737 | obj[key] = s[key]; 738 | return obj; 739 | }; 740 | const ordered = Object.keys(s).sort().reduce(reducer, {}); 741 | ordered["No Spell"] = ns; 742 | ordered["Hidden Items"] = hi; 743 | 744 | genericSort(ordered, id, true); 745 | if (activeSort + 1 >= spellNormalize.length) { 746 | activeSort = 0; 747 | } else { 748 | activeSort = activeSort + 1; 749 | } 750 | } 751 | 752 | var sheens = { 753 | "Team Shine": { 754 | "cc": ["#FF1D15", "#005CFF"] 755 | }, 756 | "Hot Rod": { 757 | "cc": ["#FF79FF"] 758 | }, 759 | "Manndarin": { 760 | "cc": ["#FF6F01"] 761 | }, 762 | "Deadly Daffodil": { 763 | "cc": ["#FFD63F"] 764 | }, 765 | "Agonizing Emerald": { 766 | "cc": ["#67FF7A"] 767 | }, 768 | "Mean Green": { 769 | "cc": ["#C2FF3B"] 770 | }, 771 | "Villainous Violet": { 772 | "cc": ["#690CFF"] 773 | } 774 | }; 775 | 776 | var effects = { 777 | "Cerebral Discharge": { 778 | "cc": ["#633310"] 779 | }, 780 | "Fire Horns": { 781 | "cc": ["#633310"] 782 | }, 783 | "Flames": { 784 | "cc": ["#633310"] 785 | }, 786 | "Hypno-Beam": { 787 | "cc": ["#633310"] 788 | }, 789 | "Incinerator": { 790 | "cc": ["#633310"] 791 | }, 792 | "Singularity": { 793 | "cc": ["#633310"] 794 | }, 795 | "Tornado": { 796 | "cc": ["#633310"] 797 | } 798 | } 799 | 800 | var ksNormalize = [ 801 | { 802 | "n": function (a, b) { 803 | return [a, b]; 804 | }, 805 | "id": "ks" 806 | }, 807 | { 808 | "n": function (a, b) { 809 | return [b, a]; 810 | }, 811 | "id": "ks2" 812 | } 813 | ]; 814 | 815 | function sortByKillstreak() { 816 | let id = ksNormalize[activeSort]["id"]; 817 | let sortedKits = [effects, sheens][activeSort]; 818 | sortedKits["No Killstreak"] = { 819 | "cc": ["#676780"] 820 | }; 821 | sortedKits["Hidden Items"] = { 822 | "cc": ["#676780"] 823 | }; 824 | sortedKits["Killstreak"] = { 825 | "cc": ["#676780"] 826 | }; 827 | 828 | for (let key in sortedKits) { 829 | sortedKits[key]["items"] = []; 830 | } 831 | 832 | let z = $(".backpack-page .item:not(.spacer)"); 833 | 834 | sortedKits["Hidden Items"]["items"] = $(".temp-page .item:not(.spacer)"); 835 | 836 | for (let p = 0; p < z.length; p++) { 837 | let sk = Object.keys(sortedKits); 838 | if (sortedKits.hasOwnProperty($(z[p]).attr("data-killstreaker"))) { 839 | if(sk.includes($(z[p]).attr("data-killstreaker"))) { 840 | sortedKits[$(z[p]).attr("data-killstreaker")]["items"].push($(z[p])[0]); 841 | } 842 | } else if (sortedKits.hasOwnProperty($(z[p]).attr("data-sheen"))) { 843 | if(sk.includes($(z[p]).attr("data-sheen"))) { 844 | sortedKits[$(z[p]).attr("data-sheen")]["items"].push($(z[p])[0]); 845 | } 846 | } else if ("1" === $(z[p]).attr("data-ks_tier")) { 847 | sortedKits["Killstreak"]["items"].push($(z[p])[0]); 848 | } else { 849 | sortedKits["No Killstreak"]["items"].push($(z[p])[0]); 850 | } 851 | } 852 | 853 | for (let key in sortedKits) { 854 | sortedKits[key]["items"] = genericItemSort("data-price", sortedKits[key]["items"]); 855 | } 856 | let ns = sortedKits["No Killstreak"]; 857 | delete sortedKits["No Killstreak"]; 858 | let hi = sortedKits["Hidden Items"]; 859 | delete sortedKits["Hidden Items"]; 860 | let nks = sortedKits["Killstreak"]; 861 | delete sortedKits["Killstreak"]; 862 | let reducer = function (obj, key) { 863 | obj[key] = sortedKits[key]; 864 | return obj; 865 | }; 866 | const ordered = Object.keys(sortedKits).sort().reduce(reducer, {}); 867 | ordered["Killstreak"] = nks; 868 | ordered["No Killstreak"] = ns; 869 | ordered["Hidden Items"] = hi; 870 | 871 | genericSort(ordered, id, true); 872 | if (activeSort + 1 >= ksNormalize.length) { 873 | activeSort = 0; 874 | } else { 875 | activeSort = activeSort + 1; 876 | } 877 | } 878 | 879 | function sortByLevel() { 880 | let l = {}; 881 | l["No Level"] = { 882 | "cc": ["#676780"], 883 | "items": [] 884 | }; 885 | //Add Special Colors to specific levels like this 886 | l["Level 0"] = { 887 | "cc": ["#DD5522"], 888 | "items": [] 889 | }; 890 | l["Hidden Items"] = { 891 | "cc": ["#676780"] 892 | }; 893 | 894 | let z = $(".backpack-page .item:not(.spacer)"); 895 | 896 | l["Hidden Items"]["items"] = $(".temp-page .item:not(.spacer)"); 897 | 898 | for (let p = 0; p < z.length; p++) { 899 | if ($(z[p]).attr("data-level") !== undefined) { 900 | let level = "Level " + $(z[p]).attr("data-level"); 901 | if (!l.hasOwnProperty(level)) { 902 | l[level] = { 903 | "cc": ["#676780"], 904 | "items": [] 905 | }; 906 | } 907 | l[level]["items"].push($(z[p])[0]); 908 | } else { 909 | l["No Level"]["items"].push($(z[p])[0]); 910 | } 911 | } 912 | 913 | for (let k in l) { 914 | l[k]["items"] = genericItemSort("data-price", l[k]["items"]); 915 | } 916 | 917 | genericSort(l, "l", true, { 918 | "use": true, 919 | "funct": function (a, b) { 920 | return parseInt(a[0].split(" ")[1]) - parseInt(b[0].split(" ")[1]); 921 | } 922 | }); 923 | 924 | } 925 | 926 | 927 | function sortByDefindex() { 928 | let d = {}; 929 | d["No Defindex"] = { 930 | "cc": ["#676780"], 931 | "items": [] 932 | }; 933 | d["Items"] = { 934 | "cc": ["#676780"], 935 | "items": [] 936 | }; 937 | d["Hidden Items"] = { 938 | "cc": ["#676780"] 939 | }; 940 | 941 | let z = $(".backpack-page .item:not(.spacer)"); 942 | 943 | 944 | //If you want all defindexes on a single page and not a group for each defindex change the singlepage value 945 | let singlepage = false; 946 | 947 | d["Hidden Items"]["items"] = $(".temp-page .item:not(.spacer)"); 948 | 949 | for (let p = 0; p < z.length; p++) { 950 | if ($(z[p]).attr("data-defindex") !== undefined) { 951 | let def = "Defindex: " + $(z[p]).attr("data-defindex"); 952 | if (!singlepage) { 953 | if (!d.hasOwnProperty(def)) { 954 | d[def] = { 955 | "cc": ["#676780"], 956 | "items": [] 957 | }; 958 | } 959 | d[def]["items"].push($(z[p])[0]); 960 | } else { 961 | d["Items"]["items"].push($(z[p])[0]); 962 | } 963 | } else { 964 | d["No Defindex"]["items"].push($(z[p])[0]); 965 | } 966 | } 967 | 968 | if (!singlepage) 969 | for (let k in d) { 970 | d[k]["items"] = genericItemSort("data-price", d[k]["items"]); 971 | } 972 | else 973 | for (let k in d) { 974 | d[k]["items"] = genericItemSort("data-defindex", d[k]["items"]); 975 | } 976 | 977 | genericSort(d, "d", true, { 978 | "use": true, 979 | "funct": function (a, b) { 980 | return parseInt(a[0].split(" ")[1]) - parseInt(b[0].split(" ")[1]); 981 | } 982 | }); 983 | 984 | 985 | } 986 | 987 | function sortBySCM() { 988 | let scm = {}; 989 | scm["SCM"] = { 990 | "cc": ["#DD5522"], 991 | "items": [] 992 | }; 993 | 994 | scm["Not SCM"] = { 995 | "cc": ["#676780"], 996 | "items": [] 997 | }; 998 | 999 | scm["Hidden Items"] = { 1000 | "cc": ["#676780"] 1001 | }; 1002 | 1003 | let z = $(".backpack-page .item:not(.spacer)"); 1004 | 1005 | scm["Hidden Items"]["items"] = $(".temp-page .item:not(.spacer)"); 1006 | 1007 | for (let p = 0; p < z.length; p++) { 1008 | if ($(z[p]).attr("data-p_scm")) { 1009 | scm["SCM"]["items"].push($(z[p])[0]); 1010 | } else { 1011 | scm["Not SCM"]["items"].push($(z[p])[0]); 1012 | } 1013 | 1014 | } 1015 | 1016 | for (let k in scm) { 1017 | scm[k]["items"] = genericItemSort("data-price", scm[k]["items"]); 1018 | } 1019 | 1020 | genericSort(scm, "scm", true, { 1021 | "use": true, 1022 | "funct": function(a, b) { 1023 | return parseInt(a[0].split(" ")[1]) - parseInt(b[0].split(" ")[1]); 1024 | } 1025 | }); 1026 | 1027 | } 1028 | 1029 | function sortByClassifiedListing() { 1030 | let c = { 1031 | "Classified Listings": {}, 1032 | "Unlisted Items": {}, 1033 | "Hidden Items": {} 1034 | }; 1035 | 1036 | 1037 | for (let k in c) { 1038 | c[k]["items"] = []; 1039 | c[k]["cc"] = ["#676780"]; 1040 | } 1041 | 1042 | let z = $(".backpack-page .item:not(.spacer)"); 1043 | 1044 | c["Hidden Items"]["items"] = $(".temp-page .item:not(.spacer)"); 1045 | 1046 | for (let p = 0; p < z.length; p++) { 1047 | if ($(z[p]).attr("data-listing_price") != undefined) { 1048 | c["Classified Listings"]["items"].push($(z[p])[0]); 1049 | } else { 1050 | c["Unlisted Items"]["items"].push($(z[p])[0]); 1051 | } 1052 | } 1053 | 1054 | for (let k in c) { 1055 | if (k == "Classified Listings") { 1056 | c[k]["items"] = c[k]["items"].sort((a, b) => { 1057 | return listing_price_to_scrap($(b).attr("data-listing_price")) - listing_price_to_scrap($(a).attr("data-listing_price")); 1058 | }); 1059 | } else { 1060 | c[k]["items"] = genericItemSort("data-price", c[k]["items"]); 1061 | } 1062 | } 1063 | 1064 | genericSort(c, "c", true); 1065 | 1066 | } 1067 | 1068 | function sortByCraftNumber() { 1069 | let cn = {}; 1070 | cn["Craft Number"] = { 1071 | "cc": ["#DD5522"], 1072 | "items": [] 1073 | }; 1074 | 1075 | cn["Crates"] = { 1076 | "cc": ["#676780"], 1077 | "items": [] 1078 | }; 1079 | 1080 | cn["No Craft Number"] = { 1081 | "cc": ["#676780"], 1082 | "items": [] 1083 | }; 1084 | 1085 | cn["Hidden Items"] = { 1086 | "cc": ["#676780"] 1087 | }; 1088 | 1089 | let z = $(".backpack-page .item:not(.spacer)"); 1090 | 1091 | cn["Hidden Items"]["items"] = $(".temp-page .item:not(.spacer)"); 1092 | for (let p = 0; p < z.length; p++) { 1093 | if ($(z[p])[0].outerText.includes("#")) { 1094 | if($(z[p]).data("crate") == undefined) { 1095 | cn["Craft Number"]["items"].push($(z[p])[0]); 1096 | } else { 1097 | cn["Crates"]["items"].push($(z[p])[0]); 1098 | } 1099 | } else { 1100 | cn["No Craft Number"]["items"].push($(z[p])[0]); 1101 | } 1102 | 1103 | } 1104 | 1105 | for (let k in cn) { 1106 | if (k == "Craft Number") { 1107 | cn[k]["items"] = cn[k]["items"].sort((a, b) => Number($(a)[0].outerText.split("#")[1]) - Number($(b)[0].outerText.split("#")[1])); 1108 | } else { 1109 | cn[k]["items"] = genericItemSort("data-price", cn[k]["items"]); 1110 | } 1111 | } 1112 | 1113 | genericSort(cn, "craftnumber", true); 1114 | } 1115 | 1116 | var lasttype = ""; 1117 | var sortType = ""; 1118 | var text = false; 1119 | 1120 | function genericItemSort(sortTyp, array, t = false) { 1121 | sortType = sortTyp; 1122 | text = t; 1123 | 1124 | array.sort(function (a, b) { 1125 | 1126 | // To reverse the sort, but currently no where needed so commented out 1127 | // if(toggle && sortType == osortType){ 1128 | // var tmp = a; 1129 | // a = b; 1130 | // b = tmp; 1131 | // } 1132 | 1133 | if (text) { 1134 | if ($(a).attr(sortType).toLowerCase() < $(b).attr(sortType).toLowerCase()) 1135 | return -1; 1136 | if ($(a).attr(sortType).toLowerCase() > $(b).attr(sortType).toLowerCase()) 1137 | return 1; 1138 | return 0; 1139 | } 1140 | return parseFloat($(b).attr(sortType)) - parseFloat($(a).attr(sortType)); 1141 | 1142 | }); 1143 | return array; 1144 | } 1145 | 1146 | function genericSort(o, type, hide = false, csort = { 1147 | "use": false 1148 | }) { 1149 | $(".item:not(.spacer)").detach(); 1150 | $("#backpack").empty(); 1151 | let a = []; 1152 | for (let k in o) { 1153 | a.push([ 1154 | k, o[k]["cc"], 1155 | o[k]["items"] 1156 | ]); 1157 | } 1158 | 1159 | if (csort.use !== false) { 1160 | 1161 | for (let i = 0; i < a.length; i++) { 1162 | if (a[i][2].length == 0) { 1163 | a.splice(i, 1); 1164 | i--; 1165 | } 1166 | } 1167 | a.sort(csort.funct); 1168 | } 1169 | 1170 | if (lasttype == type) { 1171 | a.reverse(); 1172 | lasttype = type + "1"; 1173 | } else 1174 | lasttype = type; 1175 | 1176 | for (let i = 0; i < a.length; i++) { 1177 | if (!hide || a[i][2].length != 0) { 1178 | let textcolor = "color: " + idealTextColor(a[i][1][0]) + ";"; 1179 | let bgcolor = (a[i][1].length == 1) 1180 | ? "background-color: " + a[i][1][0] + ";" 1181 | : "background: linear-gradient( " + a[i][1][0] + " 0%, " + a[i][1][0] + " 50%, " + a[i][1][1] + " 50%," + a[i][1][1] + " 100% );"; 1182 | 1183 | $("#backpack").append(pagetemplate.fmt({ 1184 | pagenum: i, 1185 | pagestyle: textcolor + bgcolor, 1186 | pagename: a[i][0] 1187 | })); 1188 | 1189 | for (let r = 0; r < a[i][2].length; r++) { 1190 | $("#page" + i).parent().parent().find(".item-list").append(a[i][2][r]); 1191 | } 1192 | } 1193 | } 1194 | 1195 | filteri(); 1196 | $(".backpack-page a:contains('Hidden Items')").parent().parent().hide().addClass("temp-page"); 1197 | } 1198 | 1199 | 1200 | //Adding new Sorting Methods 1201 | for (let i = 0; i < newSorts.length; i++) { 1202 | 1203 | $(".panel-extras #inventory-sort-menu .dropdown-menu.dropdown-menu-right.pull-right").append(` 1204 |
  • " + newSorts[i][0] + `
  • 1205 | `); 1206 | $("#customsort" + i).click(function (e) { 1207 | let i = parseInt($(e.target).parent().attr("id").replace(/^\D+/g, "")); 1208 | $("#inventory-sort-menu").removeClass("open"); 1209 | $(".current-sort").text(newSorts[i][0]); 1210 | newSorts[i][2](); 1211 | e.preventDefault(); 1212 | e.stopPropagation(); 1213 | }); 1214 | 1215 | } 1216 | 1217 | 1218 | //Replace Sorting 1219 | var replaceSorts = { 1220 | "defindex": sortByDefindex 1221 | }; 1222 | 1223 | 1224 | //Stop reverse sorting when another sort was clicked on before 1225 | $(".dropdown-menu.dropdown-menu-right.pull-right li:not([id^='custom'])").click(function (e) { 1226 | if (lasttype == $(this).attr("data-value")) { 1227 | e.preventDefault(); 1228 | e.stopPropagation(); 1229 | let b = $("#backpack"); 1230 | b.children().each(function (e, p) { 1231 | b.prepend(p); 1232 | }); 1233 | } else if (replaceSorts.hasOwnProperty($(this).attr("data-value"))) { 1234 | $("#inventory-sort-menu").removeClass("open"); 1235 | $(".current-sort").text($(this).text()); 1236 | replaceSorts[$(this).attr("data-value")](); 1237 | e.preventDefault(); 1238 | e.stopPropagation(); 1239 | } 1240 | lasttype = $(this).attr("data-value"); 1241 | filteri(); 1242 | }); 1243 | 1244 | function listing_price_to_scrap(price) { 1245 | if (price == undefined) return undefined; 1246 | let out = 0; 1247 | let price_parts = price.split(","); 1248 | price_parts.forEach(part => { 1249 | part = part.trim(); 1250 | if (part.indexOf("key") != -1) { 1251 | out += parseFloat(part.split(" ")[0]) * keyprice; 1252 | } else if (part.indexOf("ref") != -1) { 1253 | out += parseFloat(part.split(" ")[0]); 1254 | } 1255 | }); 1256 | return out; 1257 | } 1258 | 1259 | // Code to mark spells in the compare link too 1260 | let compare_observer = undefined; 1261 | let modal_observer = new MutationObserver(()=>{ 1262 | if($("#active-modal").find(".modal-title").text() == "Compare inventories"){ 1263 | compare_observer = new MutationObserver(()=>{ 1264 | markSpells(); 1265 | }); 1266 | compare_observer.observe($("#inventory-cmp-bins")[0], {"childList": true}); 1267 | } else { 1268 | if(compare_observer != undefined){ 1269 | compare_observer.disconnect(); 1270 | compare_observer = undefined; 1271 | } 1272 | } 1273 | }); 1274 | modal_observer.observe($("#page-content")[0], {"childList": true}); 1275 | } 1276 | 1277 | if (window.location.pathname.startsWith("/classifieds")) { 1278 | markSpells(); 1279 | } 1280 | 1281 | function markSpells() { 1282 | $("[data-spell_1]").attr("style", "border-bottom: 6px dotted #10ff00!important"); 1283 | $("[data-spell_2]").attr("style", "border-bottom: 6px dotted #ff2121!important"); 1284 | } 1285 | 1286 | function genMP(element) { 1287 | let query = "https://marketplace.tf/items/"; 1288 | let item = $(element); 1289 | let defindex = item.attr("data-defindex"); 1290 | query += defindex + ";" + item.attr("data-quality"); 1291 | 1292 | if (item.attr("data-craftable") !== "1") 1293 | query += ";uncraftable"; 1294 | 1295 | let effectid = item.attr("data-effect_id"); 1296 | if (effectid !== null && effectid !== undefined) { 1297 | query += ";u" + effectid; 1298 | } 1299 | 1300 | let skininfo = item.find(".item-icon"); 1301 | if (skininfo.length > 0) { 1302 | skininfo = skininfo.css("background-image").match(/warpaint\/[(?!_)\S]+_[0-9]+_[0-9]+_[0-9]+\.png/g); 1303 | if (skininfo !== null) { 1304 | let skin = skininfo[0].split("_")[1]; 1305 | let wear = skininfo[0].split("_")[2]; 1306 | query += ";w" + wear + ";pk" + skin; 1307 | } 1308 | } 1309 | 1310 | 1311 | 1312 | if (item.attr("data-quality_elevated") == "11") 1313 | query += ";strange"; 1314 | 1315 | let kstier = item.attr("data-ks_tier"); 1316 | if (kstier !== null && kstier !== undefined) { 1317 | query += ";kt-" + kstier; 1318 | } 1319 | 1320 | if (item.attr("data-original-title").toLowerCase().indexOf("festivized") != -1) 1321 | query += ";festive"; 1322 | 1323 | return query; 1324 | } 1325 | 1326 | function genWeaponSearch(element, query) { 1327 | let item = $(element); 1328 | let name = item.attr("data-base_name"); 1329 | let quality = item.attr("data-quality"); 1330 | let elQuality = item.attr("data-quality_elevated") ? item.attr("data-quality_elevated") : ""; 1331 | let killstreak = item.attr("data-ks_tier") ? item.attr("data-ks_tier") : 0; 1332 | let effect = item.attr("data-effect_id"); 1333 | let skin = item.attr("data-paint_kit"); 1334 | 1335 | let skininfo = item.find(".item-icon").css("background-image").match(/warpaint\/[(?!_)\S]+_[0-9]+_[0-9]+_[0-9]+\.png/g); 1336 | let wear = skininfo[0].split("_")[2]; 1337 | query += "item=" + name + "&quality=" + quality + "&texture_name=" + skin + "&wear_tier=" + wear + "&killstreak_tier=" + killstreak; 1338 | if (effect) { 1339 | query += "&particle=" + effect; 1340 | } 1341 | if (elQuality) { 1342 | query += "&elevated=" + elQuality; 1343 | } 1344 | return query; 1345 | } 1346 | 1347 | let skinnames = ["Bovine Blazemaker", "War Room", "Treadplate Tormenter", "Bogtrotter", "Earth, Sky and Fire", "Team Sprayer", "Spruce Deuce", "Hickory Hole-Puncher", "Rooftop Wrangler", "Civic Duty", "Civil Servant", "Local Hero", "Mayor", "Smalltown Bringdown", "Citizen Pain", "Tartan Torpedo", "Lumber From Down Under", "Rustic Ruiner", "Barn Burner", "Homemade Heater", "Plaid Potshotter", "Country Crusher", "Iron Wood", "Shot in the Dark", "Blasted Bombardier", "Backcountry Blaster", "Antique Annihilator", "Old Country", "American Pastoral", "Reclaimed Reanimator", "Red Rock Roscoe", "Sand Cannon", "Sudden Flurry", "Psychedelic Slugger", "Purple Range", "Night Terror", "Carpet Bomber", "Woodland Warrior", "Wrapped Reviver", "Forest Fire", "Night Owl", "Woodsy Widowmaker", "Backwoods Boomstick", "King of the Jungle", "Masked Mender", "Thunderbolt", "Liquid Asset", "Shell Shocker", "Current Event", "Pink Elephant", "Flash Fryer", "Spark of Life", "Dead Reckoner", "Black Dahlia", "Sandstone Special", "Lightning Rod", "Brick House", "Aqua Marine", "Low Profile", "Turbine Torcher", "Boneyard", "Pumpkin Patch", "Macabre Web", "Autumn", "Nutcracker", "Wildwood", "Top Shelf", "High Roller's", "Coffin Nail", "Dressed to Kill", "Rainbow", "Balloonicorn", "Sweet Dreams", "Mister Cuddles", "Blue Mew", "Shot to Hell", "Torqued to Hell", "Stabbed to Hell", "Brain Candy", "Flower Power", "Killer Bee", "Warhawk", "Red Bear", "Butcher Bird", "Airwolf", "Blitzkrieg", "Corsair", "Anodized Aloha", "Bamboo Brushed", "Croc Dusted", "Leopard Printed", "Macaw Masked", "Mannana Peeled", "Park Pigmented", "Piña Polished", "Sax Waxed", "Tiger Buffed", "Yeti Coated", "Bank Rolled", "Bloom Buffed", "Bonk Varnished", "Cardboard Boxed", "Clover Camo'd", "Dream Piped", "Fire Glazed", "Freedom Wrapped", "Kill Covered", "Merc Stained", "Pizza Polished", "Quack Canvassed", "Star Crossed", "Carpet Bomber Mk.II", "Woodland Warrior Mk.II", "Wrapped Reviver Mk.II", "Forest Fire Mk.II", "Night Owl Mk.II", "Woodsy Widowmaker Mk.II", "Autumn Mk.II", "Plaid Potshotter Mk.II", "Civic Duty Mk.II", "Civil Servant Mk.II", "Dead Reckoner Mk.II", "Bovine Blazemaker Mk.II", "Backwoods Boomstick Mk.II", "Masked Mender Mk.II", "Macabre Web Mk.II", "Iron Wood Mk.II", "Nutcracker Mk.II", "Smalltown Bringdown Mk.II", "Dragon Slayer", "Smissmas Sweater", "Miami Element", "Jazzy", "Mosaic", "Cosmic Calamity", "Hana", "Uranium", "Neo Tokyo", "Hazard Warning", "Damascus & Mahogany", "Dovetailed", "Alien Tech", "Cabin Fevered", "Polar Surprise", "Bomber Soul", "Geometrical Teams", "Horror Holiday", "Spectral Shimmered", "Haunted Ghosts", "Totally Boned", "Spirit of Halloween", "Calavera Canvas", "Skull Study", "Ghost Town", "Tumor Toasted", "Electroshocked", "Seriously Snowed", "Igloo", "Gift Wrapped", "Alpine", "Snow Covered", "Sleighin' Style", "Frost Ornamented", "Smissmas Village", "Smissmas Camo", "Winterland Wrapped", "Organ-ically Hellraised", "Spider Season", "Gourdy Green", "Eyestalker", "Death Deluxe", "Portal Plastered", "Candy Coated", "Spider's Cluster", "Raving Dead", "Crawlspace Critters", "Sweet Toothed", "Helldriver", "Mummified Mimic", "Pumpkin Pied", "Spectrum Splattered", "Snowflake Swirled", "Snow Globalization", "Glacial Glazed", "Gifting Mann's Wrapping Paper", "Peppermint Swirl", "Gingerbread Winner", "Smissmas Spycrabs", "Elfin Enamel", "Frozen Aurora", "Cookie Fortress", "Frosty Delivery", "Saccharine Striped", "Starlight Serenity", "Skull Cracked", "Simple Spirits", "Searing Souls", "Sarsparilla Sprayed", "Potent Poison", "Kiln and Conquer", "Swashbuckled", "Polter-Guised", "Neon-ween", "Necromanced", "Party Phantoms", "Broken Bones", "Misfortunate"]; 1348 | let svariants = []; 1349 | 1350 | //Skin Name Autocomplete 1351 | 1352 | //Prevent Tab changing the focus 1353 | $("#page-content").on("keydown", "input[placeholder='An exact texture name is required (Warhawk, Coffin Nail, Dressed to Kill, etc.)']", function (e) { 1354 | if (e.which == 9 || e.keyCode == 9) { 1355 | e.preventDefault(); 1356 | } 1357 | }); 1358 | $("#page-content").on("keyup", "input[placeholder='An exact texture name is required (Warhawk, Coffin Nail, Dressed to Kill, etc.)']", function (e) { 1359 | 1360 | if (e.which == 9 || e.keyCode == 9) { 1361 | e.preventDefault(); 1362 | $("input[placeholder='An exact texture name is required (Warhawk, Coffin Nail, Dressed to Kill, etc.)").val(svariants[0]); 1363 | svariants.push(svariants.shift()); 1364 | $("#autocompskin").text(""); 1365 | } else { 1366 | let template = "
    "; 1367 | svariants = []; 1368 | let int = $("input[placeholder='An exact texture name is required (Warhawk, Coffin Nail, Dressed to Kill, etc.)"); 1369 | if ($("#autocompskin").length == 0) $(template).insertAfter(int); 1370 | let sl = skinnames.length; 1371 | for (let i = 0; i < sl; i++) { 1372 | if (skinnames[i].toLowerCase().startsWith(int.val().toLowerCase())) svariants.push(skinnames[i]); 1373 | } 1374 | 1375 | if (svariants.length == sl || svariants.length == 0) 1376 | $("#autocompskin").text(""); 1377 | else 1378 | $("#autocompskin").text(svariants[0] + " - Press Tab to autocomplete, press again for alternative suggestions"); 1379 | 1380 | } 1381 | }); 1382 | 1383 | //Modify Popover 1384 | $("body").on("mouseover", ".item", function () { 1385 | let self = this; 1386 | let id = setInterval(function () { 1387 | 1388 | if ($(self).next().hasClass("popover")) { 1389 | 1390 | let popover = $(self).next().find("#popover-price-links"); 1391 | 1392 | let id2 = setInterval(function () { 1393 | 1394 | let checkingmp = popover.find(".fa-spinner").length; 1395 | if (checkingmp <= 0) { 1396 | 1397 | if (popover.find("a[href^='https://marketplace.tf']").length !== 1 && popover.find(".cmpb").length < 1) { 1398 | popover.append(" Marketplace"); 1399 | } 1400 | if($(self).attr("data-paint_kit")){ 1401 | if (popover.find("a[href^='/premium/search']").length !== 1) { 1402 | popover.append("Skin Search"); 1403 | } 1404 | setTimeout(()=>popover.parent().find("#popover-search-links > a").first()[0].href = genWeaponSearch($(self)[0], "/classifieds?"), 100); 1405 | } 1406 | 1407 | 1408 | clearInterval(id2); 1409 | } 1410 | }, 50); 1411 | 1412 | setTimeout(function () { 1413 | clearInterval(id2); 1414 | }, 1000); 1415 | 1416 | clearInterval(id); 1417 | } 1418 | }, 50); 1419 | setTimeout(function () { 1420 | clearInterval(id); 1421 | }, 750); 1422 | }); 1423 | 1424 | // Modify Search Modal 1425 | new MutationObserver(() => { 1426 | 1427 | let searchModal = $(".modal-title:contains(Advanced Search), .modal-title:contains(Build Query)").parents("#active-modal"); 1428 | 1429 | // The Modal which opened is the search modal 1430 | if(searchModal.length > 0) { 1431 | 1432 | // Filters are loaded later, so we need to listen to the onload of the filters 1433 | new MutationObserver((mutations) => { 1434 | 1435 | // Check if Loading filters text gets removed 1436 | if(mutations.some(mutation => Array.from(mutation.removedNodes.entries()).some(node => $(node).text().includes("Loading filters...")))){ 1437 | let particleEffectContainer = $("#panel-particle > div"); 1438 | 1439 | // Add search box 1440 | particleEffectContainer.prepend(""); 1441 | 1442 | // On search hide / unhide all the elements 1443 | $("#particle-filter").keyup(() => { 1444 | let filterWord = $("#particle-filter").val().toLowerCase(); 1445 | let effects = particleEffectContainer.find(".btn-multi-filter"); 1446 | effects.each(function() { 1447 | let el = $(this); 1448 | el.show(); 1449 | if(!el.text().toLowerCase().includes(filterWord)){ 1450 | el.hide(); 1451 | } 1452 | }); 1453 | }); 1454 | } 1455 | }).observe($(searchModal).find(".modal-body")[0], { 1456 | childList: true, 1457 | }); 1458 | } 1459 | }).observe($("main")[0], { 1460 | childList: true 1461 | }); 1462 | 1463 | // ################# Killstreak Kit Additions for Stats pages ############## 1464 | 1465 | function appendKSToPath(tier,path,currentTier){ 1466 | const ksTierDict = { 1467 | 1 : "Killstreak%20", 1468 | 2 : "Specialized%20Killstreak%20", 1469 | 3 : "Professional%20Killstreak%20", 1470 | }; 1471 | let splitPath = path.split("/"); 1472 | splitPath[3] = splitPath[3].replace(ksTierDict[currentTier],""); 1473 | if(tier > 0){ 1474 | splitPath[3] = ksTierDict[tier].concat("",splitPath[3]); 1475 | } 1476 | return splitPath.join("/"); 1477 | } 1478 | 1479 | class itemStatPageInfo { 1480 | constructor(headerItem){ 1481 | this.name = headerItem.dataset.base_name; 1482 | this.defindex = headerItem.dataset.defindex; 1483 | this.killstreak_tier = headerItem.dataset.ks_tier || 0; 1484 | this.aussie = headerItem.dataset.australium || 0; 1485 | this.slot = headerItem.dataset.slot || "none"; 1486 | this.idreplacedict = { 1487 | 10 : 199, // Shotgun 1488 | 18 : 205, // Rocket Launcher 1489 | 23 : 209, // Pistol 1490 | 13 : 200, // Scattergun 1491 | 294 : 160, // Lugermorph 1492 | 833 : 812, // Flying Guillotine 1493 | 1071 : 264, // Golden Frying Pan 1494 | 21 : 208, // Flame Thrower 1495 | 30474 : 208, // Nostromo Napalmer 1496 | 834 : 813, // Neon Annihilator 1497 | 20 : 207, // Stickybomb Launcher 1498 | 1 : 191, // Bottle 1499 | 266 : 132, // Headtaker 1500 | 15 : 202, // Minigun 1501 | 832 : 811, // Huo-Long Heater 1502 | 5 : 195, // Fists 1503 | 169 : 197, // Golden Wrench 1504 | 17 : 204, // Syringe Gun 1505 | 29 : 211, // Medi Gun 1506 | 8 : 198, // Bonesaw 1507 | 14 : 201, // Sniper Rifle 1508 | 16 : 203, // SMG 1509 | 24 : 210, // Revolver 1510 | 4 : 194, // Knife 1511 | }; 1512 | 1513 | this.nameiddict = { 1514 | "Shotgun" : 199, 1515 | "Rocket Launcher" : 205, 1516 | "Pistol": 209, 1517 | "Scattergun" : 200, 1518 | "Force-A-Nature" : 45, 1519 | "Holy Mackerel" : 221, 1520 | "Bat" : 190, 1521 | "Rocket Launcher" : 205, 1522 | "Black Box" : 228, 1523 | "Shotgun" : 199, 1524 | "Flame Thrower" : 208, 1525 | "Backburner" : 40, 1526 | "Flare Gun" : 39, 1527 | "Axtinguisher" : 38, 1528 | "Grenade Launcher" : 206, 1529 | "Stickybomb Launcher" : 207, 1530 | "Chargin' Targe" : 131, 1531 | "Eyelander" : 132, 1532 | "Minigun" : 202, 1533 | "Gloves of Running Urgently" : 239, 1534 | "Frontier Justice" : 141, 1535 | "Crusader's Crossbow" : 305, 1536 | "Medi Gun" : 211, 1537 | "Bonesaw" : 198, 1538 | "Ubersaw" : 37, 1539 | "Sniper Rifle" : 201, 1540 | "Huntsman" : 56, 1541 | "SMG" : 203, 1542 | "Revolver" : 210, 1543 | "Ambassador" : 61, 1544 | "Knife" : 194, 1545 | }; 1546 | } 1547 | 1548 | getItemName(){ 1549 | return (this.aussie == 1) ? "Australium " + this.name : this.name; 1550 | } 1551 | 1552 | getItemId(){ 1553 | let itemName = this.name.match(/((?<=((Blood)|(Gold)|(Silver)|(Rust)|(Carbonado)|(Diamond)) Botkiller ).+(?= Mk.II?))|((?<=Festive ).+)/g); // Regex courtesy of Moder112 1554 | if(itemName){ 1555 | this.defindex = this.nameiddict[itemName[0]]; 1556 | return this.defindex; 1557 | } 1558 | let defindexInt = parseInt(this.defindex); 1559 | if(this.idreplacedict.hasOwnProperty(defindexInt)) 1560 | return this.idreplacedict[defindexInt].toString(); 1561 | return this.defindex; 1562 | } 1563 | 1564 | getItemSlot(){ 1565 | switch(this.slot){ 1566 | case "primary": case "secondary": case "melee": case "pda": 1567 | return "weapon"; 1568 | case "misc": 1569 | return "misc"; 1570 | case "none": 1571 | if(this.name == "Kit") 1572 | return "kit"; 1573 | if (this.name == "Fabricator") 1574 | return "fabricator"; 1575 | return "misc"; 1576 | } 1577 | } 1578 | 1579 | getKillstreakTier(){ 1580 | return this.killstreak_tier; 1581 | } 1582 | } 1583 | 1584 | if (window.location.pathname.startsWith("/stats")) { 1585 | const currentItem = new itemStatPageInfo(document.querySelector(".item")); 1586 | let itemId = currentItem.getItemId(); 1587 | let killstreakTier = currentItem.getKillstreakTier(); 1588 | let currentPath = window.location.pathname; 1589 | 1590 | markSpells(); 1591 | 1592 | $(".stats-header-controls").append(` 1593 |
    1594 |
    1595 | `); 1596 | 1597 | switch(currentItem.getItemSlot()){ 1598 | 1599 | case "misc": 1600 | break; 1601 | 1602 | case "kit": { 1603 | 1604 | let weaponName = encodeURIComponent($(".stats-breadcrumb").last().text().trim()); 1605 | let weaponDefIndex = $(".stats-header-item .item").attr("data-priceindex").split("-").pop(); 1606 | 1607 | $(".stats-killstreak-list").append(` 1608 | Basic 1609 | Specialized 1610 | Professional 1611 | `); 1612 | document.querySelector(".stats-killstreak-list").children[killstreakTier-1].classList.add("active"); 1613 | if(killstreakTier > 1){ 1614 | $(".stats-header-controls").append(` 1615 | 1616 | 1617 | Fabricator Stats 1618 | 1619 | `); 1620 | } 1621 | 1622 | $(".stats-header-controls").append(` 1623 |
    1624 |
    Loading applicable weapons...
    1625 | `); 1626 | $(".temp-weaponlist-div").load(`https://backpack.tf/overview/${weaponName} .overview-quality-list`,function(){ 1627 | for(let button of this.firstChild.children){ 1628 | button.href = appendKSToPath(killstreakTier, button.getAttribute("href"), 0); 1629 | } 1630 | $(this.firstChild).removeClass("overview-quality-list expanded") 1631 | .append(""); 1632 | 1633 | $("#btn-expand-list").click(function () { 1634 | $(this).parent().toggleClass("expanded"); 1635 | }); 1636 | }); 1637 | break; 1638 | } 1639 | 1640 | case "fabricator": { 1641 | let weaponDefIndex = window.location.pathname.match(/\d+$/g); 1642 | 1643 | $(".stats-killstreak-list").append(` 1644 | Specialized 1645 | Professional 1646 | `); 1647 | document.querySelector(".stats-killstreak-list").children[killstreakTier-2].classList.add("active"); 1648 | $(".stats-header-controls").append(` 1649 | 1650 | 1651 | Kit Stats 1652 | 1653 | `); 1654 | break; 1655 | } 1656 | 1657 | case "weapon": { 1658 | 1659 | $(".stats-killstreak-list").append(` 1660 | No Kit 1661 | Basic 1662 | Specialized 1663 | Professional 1664 | `); 1665 | document.querySelector(".stats-killstreak-list").children[killstreakTier].classList.add("active"); 1666 | if(killstreakTier > 0){ 1667 | $(".stats-header-controls").append(` 1668 | 1669 | 1670 | Kit Stats 1671 | 1672 | `); 1673 | } 1674 | } 1675 | } 1676 | } 1677 | })(); 1678 | 1679 | -------------------------------------------------------------------------------- /preview1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetroScript/backpack.tf-miscellaneous-extensions/f540ff974e9f2908b593d13f041e7fd52111b66c/preview1.png -------------------------------------------------------------------------------- /preview2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetroScript/backpack.tf-miscellaneous-extensions/f540ff974e9f2908b593d13f041e7fd52111b66c/preview2.png -------------------------------------------------------------------------------- /preview3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetroScript/backpack.tf-miscellaneous-extensions/f540ff974e9f2908b593d13f041e7fd52111b66c/preview3.png -------------------------------------------------------------------------------- /preview4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetroScript/backpack.tf-miscellaneous-extensions/f540ff974e9f2908b593d13f041e7fd52111b66c/preview4.png -------------------------------------------------------------------------------- /preview5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NetroScript/backpack.tf-miscellaneous-extensions/f540ff974e9f2908b593d13f041e7fd52111b66c/preview5.png --------------------------------------------------------------------------------