hwmAdvancedMenu

Расширенное меню

当前为 2023-12-26 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name hwmAdvancedMenu
  3. // @namespace Tamozhnya1
  4. // @author Tamozhnya1
  5. // @description Расширенное меню
  6. // @version 1.7
  7. // @include *heroeswm.ru/*
  8. // @include *lordswm.com/*
  9. // @exclude */rightcol.php*
  10. // @exclude */ch_box.php*
  11. // @exclude */chat*
  12. // @exclude */ticker.html*
  13. // @exclude */frames*
  14. // @exclude */brd.php*
  15. // @license MIT
  16. // ==/UserScript==
  17.  
  18. if(!this.GM_getValue) {
  19. this.GM_getValue = function(key, def) { return localStorage[key] || def; };
  20. this.GM_setValue = function(key, value) { localStorage[key] = value; };
  21. this.GM_deleteValue = function(key) { return delete localStorage[key]; };
  22. }
  23. const StoredForumTreadsAmount = 10;
  24. const playerIdMatch = document.cookie.match(/pl_id=(\d+)/);
  25. if(!playerIdMatch) {
  26. return;
  27. }
  28. const PlayerId = playerIdMatch[1];
  29. const isEn = document.documentElement.lang == "en";
  30. const isNewInterface = document.querySelector("div#hwm_header") ? true : false;
  31.  
  32. main();
  33. function main() {
  34. processHouses();
  35. processQuickLinks();
  36. processForum();
  37. checkMilitaryClan();
  38. // После рынка вставим ссылки на ресурсы, с задержкой в пол секунды
  39. const auctionRef = isNewInterface ? document.querySelector("div.sh_dd_container a[href='auction.php']") : getParent(document.querySelector("li > a[href='auction.php']"), "li");
  40. if(auctionRef) {
  41. let resourcesShowTimer;
  42. let resourcesShown = false;
  43. const resources = [ { type: "1", name: isEn ? 'Wood' : 'Древесина' }, { type: "2", name: isEn ? 'Ore' : 'Руда' }, { type: "3", name: isEn ? 'Mercury' : 'Ртуть' }, { type: "4", name: isEn ? 'Sulfur' : 'Сера' }, { type: "5", name: isEn ? 'Crystals' : 'Кристаллы' }, { type: "6", name: isEn ? 'Gems' : 'Самоцветы' } ];
  44. auctionRef.addEventListener("mouseover", function() { if(!resourcesShown) resourcesShowTimer = setTimeout(function() {
  45. const html = resources.reduce((t, x) => t + getMenuItemTemplate(`auction.php?cat=res&sort=0&type=${x.type}`, `  ${x.name}`), "");
  46. auctionRef.insertAdjacentHTML('afterend', html);
  47. resourcesShown = true;
  48. }, 500) } );
  49. auctionRef.addEventListener("mouseout", function() { clearTimeout(resourcesShowTimer); });
  50. }
  51. // После передачи ресурсов вставим основные ссылки персонажа
  52. const transferRef = isNewInterface ? document.querySelector("div.sh_dd_container a[href='transfer.php']") : getParent(document.querySelector("li > a[href='transfer.php']"), "li");
  53. if(transferRef) {
  54. const personalReferences = [
  55. { href: `el_transfer.php`, text: isEn ? 'Transfer elements' : 'Передача элементов' },
  56. { href: 'javascript:void(0);', text: "" },
  57. { href: `pl_info.php?id=${PlayerId}`, text: isEn ? 'Character' : 'Персонаж' },
  58. { href: `pl_transfers.php?id=${PlayerId}`, text: isEn ? 'Transfer log' : 'Протокол передач' },
  59. { href: `pl_warlog.php?id=${PlayerId}`, text: isEn ? 'Combat log' : 'Протокол боев' },
  60. { href: `pl_cardlog.php?id=${PlayerId}`, text: isEn ? 'Game log' : 'Протокол игр' },
  61. { href: `friends.php`, text: isEn ? 'Your friends' : 'Ваши друзья' },
  62. { href: `ephoto_albums.php`, text: isEn ? 'Your photos' : 'Ваш фотоальбом' },
  63. { href: 'javascript:void(0);', text: "" },
  64. { href: `logout.php?${Math.round( Math.random()* 100000 )}`, text: isEn ? 'Logout' : 'Выход' }
  65. ];
  66. const html = personalReferences.reduce((t, x) => t + getMenuItemTemplate(x.href, x.text), "");
  67. transferRef.insertAdjacentHTML('afterend', html);
  68. }
  69. // Расширение карты
  70. const mapMenuContainer = isNewInterface ? document.querySelector("div.sh_dd_container a[href='map.php?st=hs']") : getParent(document.querySelector("li > a[href='map.php?st=hs']"), "li");
  71. if(mapMenuContainer) {
  72. const housesInfo = JSON.parse(GM_getValue(`PlayerHouses${PlayerId}`, "{}"));
  73. let mapExtenders = Object.keys(housesInfo).map(x => ({ href: `house_info.php?id=${x}`, text: housesInfo[x].replace(" ", " ") }));
  74. // Арендованные дома. Берутся из скрипта Transporter
  75. for(let locationNumber = 1; locationNumber <= 27; locationNumber++) {
  76. const guestInfo = JSON.parse(GM_getValue(`GuestInfo${locationNumber}`, "{}"));
  77. for(const key in guestInfo) {
  78. mapExtenders = [...mapExtenders, { href: `house_info.php?id=${key}`, text: guestInfo[key].HostInfo, title: `до ${(new Date(guestInfo[key].ExpireDate)).toLocaleString()}` }];
  79. }
  80. }
  81. //
  82. mapExtenders = [...mapExtenders, { href: 'javascript:void(0);', text: "" }, { href: 'ecostat.php', text: isEn ? 'Economic statistics' : 'Эконом. статистика' }];
  83. // Ссылки по боевому клану. Появляются после захода на страницу кланов
  84. if(GM_getValue(`MilitaryClanId${PlayerId}`)) {
  85. mapExtenders = [...mapExtenders, { href: 'javascript:void(0);', text: "" },
  86. { href: `/clan_info.php?id=${GM_getValue(`MilitaryClanId${PlayerId}`)}`, text: isEn ? 'Military clan' : 'Боевой клан' },
  87. { href: `/sklad_info.php?clan_id=${GM_getValue(`MilitaryClanId${PlayerId}`)}`, text: isEn ? 'Clan depository' : 'Клан-склад' },
  88. { href: `/sms_clans.php?clan_id=${GM_getValue(`MilitaryClanId${PlayerId}`)}`, text: isEn ? 'Clan post' : 'Клановая рассылка' }
  89. ];
  90. }
  91. const html = mapExtenders.reduce((t, x) => t + getMenuItemTemplate(x.href, x.text, x.title), "");
  92. //console.log(mapExtenders)
  93. mapMenuContainer.insertAdjacentHTML('afterend', html);
  94. }
  95. // Добавим форумов и дейли
  96. const forumsContainer = isNewInterface ? document.querySelector("div.sh_dd_container a[href='forum.php#t1']") : getParent(document.querySelector("li > a[href='forum.php#t1']"), "li");
  97. if(forumsContainer) {
  98. const forumExtenders = [
  99. { href: `forum_thread.php?id=${isEn ? '103' : '3'}`, text: isEn ? 'Ideas and suggestions' : 'Идеи и предложения' },
  100. { href: `forum_thread.php?id=${isEn ? '121' : '22'}`, text: isEn ? 'Smiths and Ench. services' : 'Услуги кузнецов и оруж.' },
  101. { href: 'javascript:void(0);', text: "" },
  102. { href: `${isEn ? 'http://daily.heroeswm.ru/newscom.php' : 'http://daily.heroeswm.ru/'}`, text: isEn ? 'HWM Daily ENG' : 'Геройская лента' }
  103. ];
  104. let html = forumExtenders.reduce((t, x) => t + getMenuItemTemplate(x.href, x.text), "");
  105. const lastForumTreads = JSON.parse(GM_getValue("LastForumTreads", "[]"));
  106. if(lastForumTreads.length > 0) {
  107. html += getMenuItemTemplate('javascript:void(0);', "");
  108. }
  109. html += lastForumTreads.reduce((t, x) => t + getMenuItemTemplate(`/forum_messages.php?tid=${x.threadId}${x.pageIndex ? `&page=${x.pageIndex}` : ""}`, x.threadName, "", `forumReference${x.threadId}`), "");
  110. forumsContainer.insertAdjacentHTML('afterend', html);
  111. Array.from(document.querySelectorAll(`span[id^='forumReference']`)).forEach(x => x.addEventListener("click", deleteForumReferenceMenuItem));
  112. }
  113. // После чатов добавим быстрые ссылки
  114. const framesContainer = isNewInterface ? document.querySelector("div.sh_dd_container a[href='frames.php?room=4']") : getParent(document.querySelector("li > a[href='frames.php?room=4']"), "li");
  115. if(framesContainer) {
  116. //GM_deleteValue(`QuickLinks${PlayerId}`);
  117. let quickLinks = JSON.parse(GM_getValue(`QuickLinks${PlayerId}`, "[]")).filter(x => x.Name != "" && x.Refernce != "");
  118. if(quickLinks.length > 0) {
  119. quickLinks = [{ Refernce: "javascript:void(0);", Name: "" }, ...quickLinks];
  120. const html = quickLinks.reduce((t, x) => t + getMenuItemTemplate(x.Refernce, x.Name), "");
  121. framesContainer.insertAdjacentHTML('afterend', html);
  122. }
  123. }
  124. }
  125. function deleteForumReferenceMenuItem(e) {
  126. //e.stopPropagation();
  127. e.preventDefault();
  128. const threadId = e.target.id.replace("forumReference", "");
  129. //console.log(`threadId: ${threadId}`)
  130. const lastForumTreads = JSON.parse(GM_getValue("LastForumTreads", "[]")).filter(x => x.threadId != threadId);
  131. GM_setValue("LastForumTreads", JSON.stringify(lastForumTreads));
  132. const menuItem = isNewInterface ? getParent(e.target, "a") : getParent(e.target, "li");
  133. //console.log(menuItem)
  134. menuItem.remove();
  135. }
  136. function processForum() {
  137. if(location.pathname == '/forum_messages.php') {
  138. if(GM_getValue("LastForumTreads")) {
  139. const saved = JSON.parse(GM_getValue("LastForumTreads"));
  140. if(!Array.isArray(saved)) {
  141. GM_deleteValue("LastForumTreads");
  142. }
  143. }
  144. //https://www.heroeswm.ru/forum_messages.php?tid=2964583&page=5
  145. const threadId = getUrlParamValue(location.href, "tid");
  146. const pageIndex = getUrlParamValue(location.href, "page");
  147. const threadName = document.querySelector(`a[href='forum_messages.php?tid=${threadId}'`).innerText;
  148. const newThread = { threadId: threadId, pageIndex: pageIndex || 0, threadName: threadName, viewTime: Date.now() };
  149. let lastForumTreads = JSON.parse(GM_getValue("LastForumTreads", "[]"));
  150. const thisThread = lastForumTreads.find(x => x.threadId == newThread.threadId && x.pageIndex == newThread.pageIndex);
  151. if(thisThread) {
  152. window.scrollTo(0, thisThread.scrollPosition);
  153. }
  154. newThread.scrollPosition = window.scrollY;
  155. lastForumTreads = lastForumTreads.filter(x => x.threadId != newThread.threadId);
  156. lastForumTreads.unshift(newThread);
  157. lastForumTreads = lastForumTreads.slice(0, StoredForumTreadsAmount);
  158. GM_setValue("LastForumTreads", JSON.stringify(lastForumTreads));
  159. document.addEventListener("scroll", (event) => {
  160. const lastForumTreads = JSON.parse(GM_getValue("LastForumTreads", "[]"))
  161. const scrolledTread = lastForumTreads.find(x => x.threadId == newThread.threadId);
  162. if(scrolledTread) {
  163. scrolledTread.scrollPosition = window.scrollY;
  164. GM_setValue("LastForumTreads", JSON.stringify(lastForumTreads));
  165. }
  166. });
  167. }
  168. }
  169. function getMenuItemTemplate(href, text, title, deleteId) {
  170. if(text.length > 30) {
  171. if(!title) {
  172. title = text;
  173. }
  174. text = text.substring(0, 30) + "...";
  175. }
  176. if(!isNewInterface) {
  177. if(text == "") {
  178. return "<hr>";
  179. }
  180. let deleteElementText = "";
  181. if(deleteId) {
  182. deleteElementText = `<span id="${deleteId}" title='${isEn ? "Delete" : "Удалить"}' style="cursor: pointer; display: inline; color: yellow;">[x]</span>`;
  183. }
  184. return `<li><a href='${href}' title='${title || ""}' style="">${text}${deleteElementText}</a></li>`;
  185. } else {
  186. let deleteElementText = "";
  187. if(deleteId) {
  188. deleteElementText = `<span id="${deleteId}" title='${isEn ? "Delete" : "Удалить"}' style="cursor: pointer; display: inline; float: right;">[x]</span>`;
  189. }
  190. return `<a href='${href}' title='${title || ""}' style='text-decoration: none;'><div style='${text == "" ? "padding: 0; height: 2px;" : ""}'>${text}${deleteElementText}</div></a>`;
  191. }
  192. }
  193. function processHouses() {
  194. if(location.pathname == "/pl_info_realty.php" && getUrlParamValue(location.href, "id") == PlayerId) {
  195. const housesInfo = Array.from(document.querySelectorAll("a[href^='house_info.php']")).reduce((t, x) => ({...t, [getUrlParamValue(x.href, "id")]: getParent(x, "tr").cells[4].innerText }), {});
  196. //console.log(housesInfo);
  197. GM_setValue(`PlayerHouses${PlayerId}`, JSON.stringify(housesInfo));
  198. }
  199. }
  200. function processQuickLinks() {
  201. if(location.pathname == "/pers_navlinks.php") {
  202. const tbody = getParent(document.querySelector("form[action='pers_navlinks.php']"), "tbody");
  203. const tr = addElement("tr", tbody);
  204. const td = addElement("td", tr);
  205. const table = addElement("table", td);
  206. const quickLinkAmount = 10;
  207. let quickLinks = JSON.parse(GM_getValue(`QuickLinks${PlayerId}`, `[${Array(quickLinkAmount).fill('{"Name":"","Refernce":""}').join()}]`));
  208. if(quickLinks.length != quickLinkAmount) {
  209. GM_deleteValue(`QuickLinks${PlayerId}`);
  210. quickLinks = JSON.parse(`[${Array(quickLinkAmount).fill('{"Name":"","Refernce":""}').join()}]`);
  211. }
  212. //console.log(quickLinks)
  213. let i = 0;
  214. for(const quickLink of quickLinks) {
  215. const html = `
  216. <tr>
  217. <td>
  218. <input id="linkName${i}" name="linkName" type="text" style="width: 200px;" />
  219. <input id="linkValue${i}" name="linkValue" type="text" style="width: 300px;" />
  220. </td>
  221. </tr>`;
  222. table.insertAdjacentHTML('beforeend', html);
  223. table.querySelector(`#linkName${i}`).value = quickLink.Name;
  224. table.querySelector(`#linkValue${i}`).value = quickLink.Refernce;
  225. table.querySelector(`#linkName${i}`).addEventListener("change", saveQuickLinks);
  226. table.querySelector(`#linkValue${i}`).addEventListener("change", saveQuickLinks);
  227. i++;
  228. }
  229. }
  230. }
  231. function saveQuickLinks() {
  232. const quickLinks = Array.from(document.querySelectorAll("input[name=linkName]")).map(x => ({ Name: x.value, Refernce: document.getElementById(x.id.replace("linkName", "linkValue")).value }));
  233. //console.log(quickLinks)
  234. GM_setValue(`QuickLinks${PlayerId}`, JSON.stringify(quickLinks));
  235. }
  236. async function checkMilitaryClan() {
  237. if(location.pathname == '/pl_clans.php') {
  238. const doc = location.pathname == '/pl_clans.php' ? document : await getRequest(`/pl_clans.php`);
  239. if(location.pathname == '/pl_clans.php') {
  240. GM_deleteValue(`MilitaryClanId${PlayerId}`);
  241. }
  242. const clanInfos = Array.from(doc.querySelectorAll("td > li > a[href^='clan_info.php']")).map(x => { return { Id: getUrlParamValue(x.href, "id"), Name: x.firstChild.innerText, Ref: x.href }; });
  243. for(const clanInfo of clanInfos) {
  244. const clanInfoDoc = await getRequest(clanInfo.Ref);
  245. if(clanInfoDoc.body.innerHTML.includes(isEn ? "[Military clan]" : "[боевой клан]")) {
  246. GM_setValue(`MilitaryClanId${PlayerId}`, clanInfo.Id);
  247. break;
  248. }
  249. }
  250. }
  251. if(!GM_getValue(`MilitaryClanId${PlayerId}`)) {
  252. console.log("Вы не состоите в боевом клане");
  253. return false;
  254. }
  255. return true;
  256. }
  257. function addElement(type, parent, data, insertFirst = false) {
  258. let el = createElement(type, data);
  259. if(parent) {
  260. if(insertFirst) {
  261. parent.insertBefore(el, parent.firstChild);
  262. } else {
  263. parent.appendChild(el);
  264. }
  265. }
  266. return el;
  267. }
  268. function createElement(type, data) {
  269. let el = document.createElement(type);
  270. if(data) {
  271. for(let key in data) {
  272. if(key == "innerText" || key == "innerHTML") {
  273. el[key] = data[key];
  274. } else {
  275. el.setAttribute(key, data[key]);
  276. }
  277. }
  278. }
  279. return el;
  280. }
  281. function getParent(element, parentType, number = 1) {
  282. if(!element) {
  283. return;
  284. }
  285. let result = element;
  286. let foundNumber = 0;
  287. while(result = result.parentNode) {
  288. if(result.nodeName.toLowerCase() == parentType.toLowerCase()) {
  289. foundNumber++;
  290. if(foundNumber == number) {
  291. return result;
  292. }
  293. }
  294. }
  295. }
  296. function getUrlParamValue(url, paramName) { return (new URLSearchParams(url.split("?")[1])).get(paramName); }