Greasy Fork

🐭️ MouseHunt - Item Links

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

目前为 2022-08-18 提交的版本。查看 最新版本

// ==UserScript==
// @name         🐭️ MouseHunt - Item Links
// @version      1.2.10
// @description  Add links to the MouseHunt wiki, MHCT looter, MHDB, and Markethunt for items.
// @license      MIT
// @author       bradp
// @namespace    bradp
// @match        https://www.mousehuntgame.com/*
// @icon         https://brrad.com/mouse.png
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function () {
	'use strict';

	/**
	 * Add styles to the page.
	 *
	 * @param {string} styles The styles to add.
	 */
	const addStyles = (styles) => {
		const existingStyles = document.getElementById('mh-mouseplace-custom-styles');

		if (existingStyles) {
			existingStyles.innerHTML += styles;
		} else {
			const style = document.createElement('style');
			style.id = 'mh-mouseplace-custom-styles';

			style.innerHTML = styles;
			document.head.appendChild(style);
		}
	};

	/**
	 * Do something when ajax requests are completed.
	 *
	 * @param {Function} callback    The callback to call when an ajax request is completed.
	 * @param {string}   url         The url to match. If not provided, all ajax requests will be matched.
	 * @param {boolean}  skipSuccess Skip the success check.
	 */
	const onAjaxRequest = (callback, url = null, skipSuccess = false) => {
		const req = XMLHttpRequest.prototype.open;
		XMLHttpRequest.prototype.open = function () {
			this.addEventListener('load', function () {
				if (this.responseText) {
					let response = {};
					try {
						response = JSON.parse(this.responseText);
					} catch (e) {
						return;
					}

					if (response.success || skipSuccess) {
						if (! url) {
							callback(this);
							return;
						}

						if (this.responseURL.indexOf(url) !== -1) {
							callback(this);
						}
					}
				}
			});
			req.apply(this, arguments);
		};
	};

	/**
	 * Return an anchor element with the given text and href.
	 *
	 * @param {string} text Text to use for link.
	 * @param {string} href URL to link to.
	 *
	 * @return {string} HTML for link.
	 */
	const makeLink = (text, href) => {
		href = href.replace(/\s/g, '_');
		return `<a href="${ href }" class="mousehuntActionButton tiny mhItemLinks"><span>${ text }</span></a>`;
	};

	/**
	 * Return a node with links after grabbing the item ID and name from the page.
	 *
	 * @param {Object} args       Arguments to use for the links.
	 * @param {string} args.id    CSS selector for the item ID.
	 * @param {string} args.name  CSS selector for the item name.
	 * @param {string} args.class CSS class to add to the node.
	 *
	 * @return {false|string} False if no item ID or name found, otherwise HTML for links.
	 */
	const getLinksNode = (args) => {
		const itemInfo = document.querySelector(args.id);
		if (! itemInfo) {
			return false;
		}

		const itemID = itemInfo.getAttribute('data-item-id');
		const itemName = document.querySelector(args.name).textContent;
		if (! itemID || ! itemName) {
			return false;
		}

		const existingText = document.querySelector('.mh-item-info-text');
		if (existingText) {
			existingText.remove();
		}

		const newText = document.createElement('span');
		newText.classList.add('mh-item-info-text');

		if (args.class) {
			newText.classList.add(args.class);
		}

		// Add link to the wiki.
		newText.innerHTML = makeLink('Wiki', `https://mhwiki.hitgrab.com/wiki/index.php/${ itemName }`);

		// Link to MHCT, either converter or looter.
		const isConvertible = document.querySelector(args.id + ' .itemView-action.convertible');
		newText.innerHTML += (isConvertible && isConvertible.innerText)
			? makeLink('MHCT Converter', `https://www.mhct.win/converter.php?item=${ itemID }&timefilter=all_time`)
			: makeLink('MHCT Looter', `https://www.mhct.win/loot.php?item=${ itemID }&timefilter=all_time`);

		// Link to mhdb.
		const mhdbName = itemName.replace(/'/g, '');
		newText.innerHTML += makeLink('mhdb', `https://dbgames.info/mousehunt/items/${ mhdbName }`);

		// Link to markethunt.
		const isTradable = document.querySelectorAll('.itemView-sidebar-checklistItem.checked');
		if (args.forceType === 'marketplace' || (isTradable && isTradable.length === 2)) {
			newText.innerHTML += makeLink('Markethunt', `https://markethunt.vsong.ca/index.php?item_id=${ itemID }`);
		}

		return newText;
	};

	/**
	 * Append text to a node, either before or after another node.
	 *
	 * @param {Object} args         Arguments to use for the text.
	 * @param {string} args.parent  CSS selector for the parent node.
	 * @param {string} args.child   CSS selector for the child node.
	 * @param {string} args.content Text to append.
	 *
	 * @return {Node} The node that was appended to.
	 */
	const appendText = (args) => {
		const append = document.querySelector(args.parent);
		if (! append) {
			return false;
		}

		if (args.child) {
			const child = document.querySelector(args.child);
			if (child) {
				return append.insertBefore(args.content, child);
			}
		} else {
			return append.appendChild(args.content);
		}

		return false;
	};

	/**
	 * Add links to the marketplace page for an item.
	 */
	const addMarketplaceLinks = () => {
		appendText({
			parent: '.marketplaceView-item-titleActions',
			child: '.marketplaceView-userGold',

			content: getLinksNode({
				id: '.marketplaceView-item.view',
				name: '.marketplaceView-item-titleName',
				forceType: 'marketplace',
			})
		});
	};

	/**
	 * Add links to the item popup for an item.
	 */
	const addItemPopupLinks = () => {
		appendText({
			parent: '.itemView-header-name',
			content: getLinksNode({
				id: '.itemViewContainer',
				name: '.itemView-header-name span',
				class: 'mh-item-info-text-item-popup'
			}),
		});
	};

	/**
	 * Fix item qty bug - see https://greasyfork.org/en/scripts/445926-mousehunt-item-quantity-fix
	 */
	const fixItemQtyBug = () => {
		// Make sure we have the ID parameter.
		if (window.location.href.indexOf('i.php?id=') === -1) {
			return;
		}

		// Grab the item ID.
		const itemID = window.location.href.split('i.php?id=')[ 1 ];
		if (! itemID) {
			return;
		}

		// Make sure the quantity shown is 0.
		const qty = document.querySelector('.itemView-sidebar-quantity');
		if (! (qty && qty.textContent.indexOf('You Own:') !== -1)) {
			return;
		}

		// Grab the item slug.
		const itemName = document.querySelector('.itemViewContainer').getAttribute('data-item-type');
		if (! itemName) {
			return;
		}

		// redirect to item.php?item_type=itemName
		const newLocation = window.location.href.replaceAll(`i.php?id=${ itemID }`, `item.php?item_type=${ itemName }`);
		if (newLocation !== window.location.href) {
			window.location.href = newLocation;
		}
	};

	fixItemQtyBug();

	addStyles(`
	.mh-item-info-text {
		margin-left: 10px;
		margin-right: 10px;
		font-size: 12px !important;
		font-weight: 300 !important;
	}
	.mh-item-info-text-item-popup {
		padding-top: 2px;
		text-align: right;
	}
	.mhItemLinks {
		margin-left: 5px;
	}
	.mhItemLinks span {
		font-weight: normal;
		font-size: 11px;
	}`);

	onAjaxRequest((request) => {
		if (request.responseURL.indexOf('managers/ajax/users/marketplace.php') !== -1) {
			addMarketplaceLinks();
		} else if (request.responseURL.indexOf('managers/ajax/users/userInventory.php') !== -1) {
			addItemPopupLinks();
		}
	}, null, true);

	// if we're on an item page, add the links.
	if (window.location.href.indexOf('item.php') !== -1) {
		addItemPopupLinks();
	}
}());