🐭️ MouseHunt - Item Links

Add links to the MouseHunt wiki, MHCT looter, MHDB, and Markethunt for items.

当前为 2022-08-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name 🐭️ MouseHunt - Item Links
  3. // @version 1.2.10
  4. // @description Add links to the MouseHunt wiki, MHCT looter, MHDB, and Markethunt for items.
  5. // @license MIT
  6. // @author bradp
  7. // @namespace bradp
  8. // @match https://www.mousehuntgame.com/*
  9. // @icon https://brrad.com/mouse.png
  10. // @grant none
  11. // @run-at document-end
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. 'use strict';
  16.  
  17. /**
  18. * Add styles to the page.
  19. *
  20. * @param {string} styles The styles to add.
  21. */
  22. const addStyles = (styles) => {
  23. const existingStyles = document.getElementById('mh-mouseplace-custom-styles');
  24.  
  25. if (existingStyles) {
  26. existingStyles.innerHTML += styles;
  27. } else {
  28. const style = document.createElement('style');
  29. style.id = 'mh-mouseplace-custom-styles';
  30.  
  31. style.innerHTML = styles;
  32. document.head.appendChild(style);
  33. }
  34. };
  35.  
  36. /**
  37. * Do something when ajax requests are completed.
  38. *
  39. * @param {Function} callback The callback to call when an ajax request is completed.
  40. * @param {string} url The url to match. If not provided, all ajax requests will be matched.
  41. * @param {boolean} skipSuccess Skip the success check.
  42. */
  43. const onAjaxRequest = (callback, url = null, skipSuccess = false) => {
  44. const req = XMLHttpRequest.prototype.open;
  45. XMLHttpRequest.prototype.open = function () {
  46. this.addEventListener('load', function () {
  47. if (this.responseText) {
  48. let response = {};
  49. try {
  50. response = JSON.parse(this.responseText);
  51. } catch (e) {
  52. return;
  53. }
  54.  
  55. if (response.success || skipSuccess) {
  56. if (! url) {
  57. callback(this);
  58. return;
  59. }
  60.  
  61. if (this.responseURL.indexOf(url) !== -1) {
  62. callback(this);
  63. }
  64. }
  65. }
  66. });
  67. req.apply(this, arguments);
  68. };
  69. };
  70.  
  71. /**
  72. * Return an anchor element with the given text and href.
  73. *
  74. * @param {string} text Text to use for link.
  75. * @param {string} href URL to link to.
  76. *
  77. * @return {string} HTML for link.
  78. */
  79. const makeLink = (text, href) => {
  80. href = href.replace(/\s/g, '_');
  81. return `<a href="${ href }" class="mousehuntActionButton tiny mhItemLinks"><span>${ text }</span></a>`;
  82. };
  83.  
  84. /**
  85. * Return a node with links after grabbing the item ID and name from the page.
  86. *
  87. * @param {Object} args Arguments to use for the links.
  88. * @param {string} args.id CSS selector for the item ID.
  89. * @param {string} args.name CSS selector for the item name.
  90. * @param {string} args.class CSS class to add to the node.
  91. *
  92. * @return {false|string} False if no item ID or name found, otherwise HTML for links.
  93. */
  94. const getLinksNode = (args) => {
  95. const itemInfo = document.querySelector(args.id);
  96. if (! itemInfo) {
  97. return false;
  98. }
  99.  
  100. const itemID = itemInfo.getAttribute('data-item-id');
  101. const itemName = document.querySelector(args.name).textContent;
  102. if (! itemID || ! itemName) {
  103. return false;
  104. }
  105.  
  106. const existingText = document.querySelector('.mh-item-info-text');
  107. if (existingText) {
  108. existingText.remove();
  109. }
  110.  
  111. const newText = document.createElement('span');
  112. newText.classList.add('mh-item-info-text');
  113.  
  114. if (args.class) {
  115. newText.classList.add(args.class);
  116. }
  117.  
  118. // Add link to the wiki.
  119. newText.innerHTML = makeLink('Wiki', `https://mhwiki.hitgrab.com/wiki/index.php/${ itemName }`);
  120.  
  121. // Link to MHCT, either converter or looter.
  122. const isConvertible = document.querySelector(args.id + ' .itemView-action.convertible');
  123. newText.innerHTML += (isConvertible && isConvertible.innerText)
  124. ? makeLink('MHCT Converter', `https://www.mhct.win/converter.php?item=${ itemID }&timefilter=all_time`)
  125. : makeLink('MHCT Looter', `https://www.mhct.win/loot.php?item=${ itemID }&timefilter=all_time`);
  126.  
  127. // Link to mhdb.
  128. const mhdbName = itemName.replace(/'/g, '');
  129. newText.innerHTML += makeLink('mhdb', `https://dbgames.info/mousehunt/items/${ mhdbName }`);
  130.  
  131. // Link to markethunt.
  132. const isTradable = document.querySelectorAll('.itemView-sidebar-checklistItem.checked');
  133. if (args.forceType === 'marketplace' || (isTradable && isTradable.length === 2)) {
  134. newText.innerHTML += makeLink('Markethunt', `https://markethunt.vsong.ca/index.php?item_id=${ itemID }`);
  135. }
  136.  
  137. return newText;
  138. };
  139.  
  140. /**
  141. * Append text to a node, either before or after another node.
  142. *
  143. * @param {Object} args Arguments to use for the text.
  144. * @param {string} args.parent CSS selector for the parent node.
  145. * @param {string} args.child CSS selector for the child node.
  146. * @param {string} args.content Text to append.
  147. *
  148. * @return {Node} The node that was appended to.
  149. */
  150. const appendText = (args) => {
  151. const append = document.querySelector(args.parent);
  152. if (! append) {
  153. return false;
  154. }
  155.  
  156. if (args.child) {
  157. const child = document.querySelector(args.child);
  158. if (child) {
  159. return append.insertBefore(args.content, child);
  160. }
  161. } else {
  162. return append.appendChild(args.content);
  163. }
  164.  
  165. return false;
  166. };
  167.  
  168. /**
  169. * Add links to the marketplace page for an item.
  170. */
  171. const addMarketplaceLinks = () => {
  172. appendText({
  173. parent: '.marketplaceView-item-titleActions',
  174. child: '.marketplaceView-userGold',
  175.  
  176. content: getLinksNode({
  177. id: '.marketplaceView-item.view',
  178. name: '.marketplaceView-item-titleName',
  179. forceType: 'marketplace',
  180. })
  181. });
  182. };
  183.  
  184. /**
  185. * Add links to the item popup for an item.
  186. */
  187. const addItemPopupLinks = () => {
  188. appendText({
  189. parent: '.itemView-header-name',
  190. content: getLinksNode({
  191. id: '.itemViewContainer',
  192. name: '.itemView-header-name span',
  193. class: 'mh-item-info-text-item-popup'
  194. }),
  195. });
  196. };
  197.  
  198. /**
  199. * Fix item qty bug - see https://greasyfork.org/en/scripts/445926-mousehunt-item-quantity-fix
  200. */
  201. const fixItemQtyBug = () => {
  202. // Make sure we have the ID parameter.
  203. if (window.location.href.indexOf('i.php?id=') === -1) {
  204. return;
  205. }
  206.  
  207. // Grab the item ID.
  208. const itemID = window.location.href.split('i.php?id=')[ 1 ];
  209. if (! itemID) {
  210. return;
  211. }
  212.  
  213. // Make sure the quantity shown is 0.
  214. const qty = document.querySelector('.itemView-sidebar-quantity');
  215. if (! (qty && qty.textContent.indexOf('You Own:') !== -1)) {
  216. return;
  217. }
  218.  
  219. // Grab the item slug.
  220. const itemName = document.querySelector('.itemViewContainer').getAttribute('data-item-type');
  221. if (! itemName) {
  222. return;
  223. }
  224.  
  225. // redirect to item.php?item_type=itemName
  226. const newLocation = window.location.href.replaceAll(`i.php?id=${ itemID }`, `item.php?item_type=${ itemName }`);
  227. if (newLocation !== window.location.href) {
  228. window.location.href = newLocation;
  229. }
  230. };
  231.  
  232. fixItemQtyBug();
  233.  
  234. addStyles(`
  235. .mh-item-info-text {
  236. margin-left: 10px;
  237. margin-right: 10px;
  238. font-size: 12px !important;
  239. font-weight: 300 !important;
  240. }
  241. .mh-item-info-text-item-popup {
  242. padding-top: 2px;
  243. text-align: right;
  244. }
  245. .mhItemLinks {
  246. margin-left: 5px;
  247. }
  248. .mhItemLinks span {
  249. font-weight: normal;
  250. font-size: 11px;
  251. }`);
  252.  
  253. onAjaxRequest((request) => {
  254. if (request.responseURL.indexOf('managers/ajax/users/marketplace.php') !== -1) {
  255. addMarketplaceLinks();
  256. } else if (request.responseURL.indexOf('managers/ajax/users/userInventory.php') !== -1) {
  257. addItemPopupLinks();
  258. }
  259. }, null, true);
  260.  
  261. // if we're on an item page, add the links.
  262. if (window.location.href.indexOf('item.php') !== -1) {
  263. addItemPopupLinks();
  264. }
  265. }());