hwmAdvancedMenu

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

当前为 2024-03-14 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name hwmAdvancedMenu
  3. // @namespace Tamozhnya1
  4. // @author Tamozhnya1
  5. // @description Расширенное меню
  6. // @version 2.5
  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. // @grant GM_deleteValue
  16. // @grant GM_getValue
  17. // @grant GM_setValue
  18. // @grant GM.xmlHttpRequest
  19. // @license MIT
  20. // ==/UserScript==
  21.  
  22. const StoredForumTreadsAmount = 10;
  23. const playerIdMatch = document.cookie.match(/pl_id=(\d+)/);
  24. if(!playerIdMatch) {
  25. return;
  26. }
  27. const PlayerId = playerIdMatch[1];
  28. const isEn = document.documentElement.lang == "en";
  29. const isNewInterface = document.querySelector("div#hwm_header") ? true : false;
  30.  
  31. main();
  32. async function main() {
  33. initUserName();
  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: getPlayerValue("UserName") || (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: `/pl_clans.php`, text: isEn ? 'Your clans' : 'Ваши кланы' },
  63. { href: `ephoto_albums.php`, text: isEn ? 'Your photos' : 'Ваш фотоальбом' },
  64. { href: 'javascript:void(0);', text: "" },
  65. { href: `logout.php?${Math.round( Math.random()* 100000 )}`, text: isEn ? 'Logout' : 'Выход' }
  66. ];
  67. const html = personalReferences.reduce((t, x) => t + getMenuItemTemplate(x.href, x.text), "");
  68. transferRef.insertAdjacentHTML('afterend', html);
  69. }
  70. // Расширение карты
  71. 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");
  72. if(mapMenuContainer) {
  73. const housesInfo = JSON.parse(getPlayerValue("PlayerHouses", "{}"));
  74. let mapExtenders = Object.keys(housesInfo).map(x => ({ href: `house_info.php?id=${x}`, text: housesInfo[x].replace(" ", " ") }));
  75. // Арендованные дома. Берутся из скрипта Transporter
  76. for(let locationNumber = 1; locationNumber <= 27; locationNumber++) {
  77. const guestInfo = JSON.parse(getValue(`GuestInfo${locationNumber}`, "{}"));
  78. for(const key in guestInfo) {
  79. mapExtenders = [...mapExtenders, { href: `house_info.php?id=${key}`, text: guestInfo[key].HostInfo, title: `до ${(new Date(guestInfo[key].ExpireDate)).toLocaleString()}` }];
  80. }
  81. }
  82. //
  83. mapExtenders = [...mapExtenders, { href: 'javascript:void(0);', text: "" }, { href: 'ecostat.php', text: isEn ? 'Economic statistics' : 'Эконом. статистика' }];
  84. // Ссылки по боевому клану. Появляются после захода на страницу кланов
  85. const clanId = getPlayerValue("MilitaryClanId");
  86. if(clanId) {
  87. const clanName = getPlayerValue("MilitaryClanName");
  88. mapExtenders = [...mapExtenders, { href: 'javascript:void(0);', text: "" },
  89. { href: `/clan_info.php?id=${clanId}`, text: clanName || (isEn ? 'Military clan' : 'Боевой клан') },
  90. { href: `/sklad_info.php?clan_id=${clanId}`, text: isEn ? 'Clan depository' : 'Клан-склад' },
  91. { href: `/sms_clans.php?clan_id=${clanId}`, text: isEn ? 'Clan post' : 'Клановая рассылка' }
  92. ];
  93. }
  94. const html = mapExtenders.reduce((t, x) => t + getMenuItemTemplate(x.href, x.text, x.title), "");
  95. //console.log(mapExtenders)
  96. mapMenuContainer.insertAdjacentHTML('afterend', html);
  97. }
  98. const leaderGuildRef = isNewInterface ? document.querySelector("div.sh_dd_container a[href='leader_guild.php']") : getParent(document.querySelector("li > a[href='leader_guild.php']"), "li");
  99. if(leaderGuildRef) {
  100. const leaderGuildReferences = [
  101. { href: `leader_army.php`, text: isEn ? 'Recruiting' : 'Набор армии' },
  102. { href: `leader_army_exchange.php`, text: isEn ? 'Creature Exchange' : 'Обмен существ' }
  103. ];
  104. const html = leaderGuildReferences.reduce((t, x) => t + getMenuItemTemplate(x.href, x.text), "");
  105. leaderGuildRef.insertAdjacentHTML('afterend', html);
  106. }
  107. // Добавим для ГЛ набор армии и объмены
  108. // Добавим форумов и дейли
  109. const forumsContainer = isNewInterface ? document.querySelector("div.sh_dd_container a[href='forum.php#t1']") : getParent(document.querySelector("li > a[href='forum.php#t1']"), "li");
  110. if(forumsContainer) {
  111. const forumExtenders = [
  112. { href: `forum_thread.php?id=${isEn ? '103' : '3'}`, text: isEn ? 'Ideas and suggestions' : 'Идеи и предложения' },
  113. { href: `forum_thread.php?id=${isEn ? '121' : '22'}`, text: isEn ? 'Smiths and Ench. services' : 'Услуги кузнецов и оруж.' },
  114. { href: 'javascript:void(0);', text: "" },
  115. { href: `${isEn ? 'http://daily.heroeswm.ru/newscom.php' : 'http://daily.heroeswm.ru/'}`, text: isEn ? 'HWM Daily ENG' : 'Геройская лента' }
  116. ];
  117. let html = forumExtenders.reduce((t, x) => t + getMenuItemTemplate(x.href, x.text), "");
  118. const lastForumTreads = JSON.parse(getValue("LastForumTreads", "[]"));
  119. if(lastForumTreads.length > 0) {
  120. html += getMenuItemTemplate('javascript:void(0);', "");
  121. }
  122. html += lastForumTreads.reduce((t, x) => t + getMenuItemTemplate(`/forum_messages.php?tid=${x.threadId}${x.pageIndex ? `&page=${x.pageIndex}` : ""}`, x.threadName, "", `forumReference${x.threadId}`), "");
  123. forumsContainer.insertAdjacentHTML('afterend', html);
  124. Array.from(document.querySelectorAll(`span[id^='forumReference']`)).forEach(x => x.addEventListener("click", deleteForumReferenceMenuItem));
  125. }
  126. // После чатов добавим быстрые ссылки
  127. 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");
  128. if(framesContainer) {
  129. //GM_deleteValue(`QuickLinks${PlayerId}`);
  130. let quickLinks = JSON.parse(getPlayerValue("QuickLinks", "[]")).filter(x => x.Name != "" && x.Reference != "");
  131. quickLinks.push({ Name: isEn ? "Settings" : "Настройки", Reference: "javascript:void(0);", id: `${GM_info.script.name}Settings` });
  132. if(quickLinks.length > 0) {
  133. quickLinks = [{ Reference: "javascript:void(0);", Name: "" }, ...quickLinks];
  134. const html = quickLinks.reduce((t, x) => t + getMenuItemTemplate(x.Reference, x.Name, undefined, undefined, x.id), "");
  135. framesContainer.insertAdjacentHTML('afterend', html);
  136. }
  137. document.getElementById(`${GM_info.script.name}Settings`).addEventListener("click", showSettings);
  138. }
  139. if(getPlayerBool("hideTavern")) {
  140. if(isNewInterface) {
  141. document.querySelector("div.mm_item.mm_item_blue > a[href='tavern.php']").closest("div").remove();
  142. document.querySelector("a[href='frames.php?room=4']").remove();
  143. } else {
  144. const menuItem = getParent(document.querySelector("li a[href='tavern.php']"), "td", 3);
  145. menuItem.previousElementSibling.remove();
  146. menuItem.remove();
  147. document.querySelector("a[href='frames.php?room=4']").closest("li").remove();
  148. }
  149. if(location.pathname == "/tavern.php") {
  150. location.href = "/home.php";
  151. }
  152. }
  153. if(getPlayerBool("hideRoulette")) {
  154. if(isNewInterface) {
  155. document.querySelector("div.mm_item.mm_item_blue > a[href='roulette.php']").closest("div").remove();
  156. document.querySelector("a[href='frames.php?room=3']").remove();
  157. } else {
  158. const menuItem = getParent(document.querySelector("li a[href='roulette.php']"), "td", 3);
  159. menuItem.previousElementSibling.remove();
  160. menuItem.remove();
  161. document.querySelector("a[href='frames.php?room=3']").closest("li").remove();
  162. }
  163. if(location.pathname == "/roulette.php") {
  164. location.href = "/home.php";
  165. }
  166. }
  167. }
  168. function deleteForumReferenceMenuItem(e) {
  169. //e.stopPropagation();
  170. e.preventDefault();
  171. const threadId = e.target.id.replace("forumReference", "");
  172. //console.log(`threadId: ${threadId}`)
  173. const lastForumTreads = JSON.parse(getValue("LastForumTreads", "[]")).filter(x => x.threadId != threadId);
  174. setValue("LastForumTreads", JSON.stringify(lastForumTreads));
  175. const menuItem = isNewInterface ? getParent(e.target, "a") : getParent(e.target, "li");
  176. //console.log(menuItem)
  177. menuItem.remove();
  178. }
  179. function processForum() {
  180. if(location.pathname == '/forum_messages.php') {
  181. if(getValue("LastForumTreads")) {
  182. const saved = JSON.parse(getValue("LastForumTreads"));
  183. if(!Array.isArray(saved)) {
  184. GM_deleteValue("LastForumTreads");
  185. }
  186. }
  187. //https://www.heroeswm.ru/forum_messages.php?tid=2964583&page=5
  188. const threadId = getUrlParamValue(location.href, "tid");
  189. const pageIndex = getUrlParamValue(location.href, "page");
  190. const threadName = document.querySelector(`a[href='forum_messages.php?tid=${threadId}'`).innerText;
  191. const newThread = { threadId: threadId, pageIndex: pageIndex || 0, threadName: threadName, viewTime: Date.now() };
  192. let lastForumTreads = JSON.parse(getValue("LastForumTreads", "[]"));
  193. const thisThread = lastForumTreads.find(x => x.threadId == newThread.threadId && x.pageIndex == newThread.pageIndex);
  194. if(thisThread) {
  195. window.scrollTo(0, thisThread.scrollPosition);
  196. }
  197. newThread.scrollPosition = window.scrollY;
  198. lastForumTreads = lastForumTreads.filter(x => x.threadId != newThread.threadId);
  199. lastForumTreads.unshift(newThread);
  200. lastForumTreads = lastForumTreads.slice(0, StoredForumTreadsAmount);
  201. setValue("LastForumTreads", JSON.stringify(lastForumTreads));
  202. document.addEventListener("scroll", (event) => {
  203. const lastForumTreads = JSON.parse(getValue("LastForumTreads", "[]"))
  204. const scrolledTread = lastForumTreads.find(x => x.threadId == newThread.threadId);
  205. if(scrolledTread) {
  206. scrolledTread.scrollPosition = window.scrollY;
  207. setValue("LastForumTreads", JSON.stringify(lastForumTreads));
  208. }
  209. });
  210. }
  211. }
  212. function getMenuItemTemplate(href, text, title, deleteId, id) {
  213. if(text.length > 30) {
  214. if(!title) {
  215. title = text;
  216. }
  217. text = text.substring(0, 30) + "...";
  218. }
  219. if(!isNewInterface) {
  220. if(text == "") {
  221. return "<hr>";
  222. }
  223. let deleteElementText = "";
  224. if(deleteId) {
  225. deleteElementText = `<span id="${deleteId}" title='${isEn ? "Delete" : "Удалить"}' style="cursor: pointer; display: inline; color: yellow;">[x]</span>`;
  226. }
  227. return `<li><a${id ? ` id="${id}"` : ""} href='${href}' title='${title || ""}' style="">${text}${deleteElementText}</a></li>`;
  228. } else {
  229. let deleteElementText = "";
  230. if(deleteId) {
  231. deleteElementText = `<span id="${deleteId}" title='${isEn ? "Delete" : "Удалить"}' style="cursor: pointer; display: inline; float: right;">[x]</span>`;
  232. }
  233. return `<a${id ? ` id="${id}"` : ""} href='${href}' title='${title || ""}' style='text-decoration: none;'><div style='${text == "" ? "padding: 0; height: 2px;" : ""}'>${text}${deleteElementText}</div></a>`;
  234. }
  235. }
  236. function processHouses() {
  237. if(location.pathname == "/pl_info_realty.php" && getUrlParamValue(location.href, "id") == PlayerId) {
  238. 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 }), {});
  239. //console.log(housesInfo);
  240. setPlayerValue("PlayerHouses", JSON.stringify(housesInfo));
  241. }
  242. }
  243. function processQuickLinks() {
  244. if(location.pathname == "/pers_navlinks.php") {
  245. const tbody = getParent(document.querySelector("form[action='pers_navlinks.php']"), "tbody");
  246. const tr = addElement("tr", undefined, tbody);
  247. const td = addElement("td", undefined, tr);
  248. const table = addElement("table", undefined, td);
  249. const quickLinkAmount = 10;
  250. let quickLinks = JSON.parse(getPlayerValue("QuickLinks", `[${Array(quickLinkAmount).fill('{"Name":"","Reference":""}').join()}]`));
  251. if(quickLinks.length != quickLinkAmount) {
  252. deletePlayerValue("QuickLinks");
  253. quickLinks = JSON.parse(`[${Array(quickLinkAmount).fill('{"Name":"","Reference":""}').join()}]`);
  254. }
  255. //console.log(quickLinks)
  256. let i = 0;
  257. for(const quickLink of quickLinks) {
  258. const html = `
  259. <tr>
  260. <td>
  261. <input id="linkName${i}" name="linkName" type="text" style="width: 200px;" />
  262. <input id="linkValue${i}" name="linkValue" type="text" style="width: 300px;" />
  263. </td>
  264. </tr>`;
  265. table.insertAdjacentHTML('beforeend', html);
  266. table.querySelector(`#linkName${i}`).value = quickLink.Name;
  267. table.querySelector(`#linkValue${i}`).value = quickLink.Reference;
  268. table.querySelector(`#linkName${i}`).addEventListener("change", saveQuickLinks);
  269. table.querySelector(`#linkValue${i}`).addEventListener("change", saveQuickLinks);
  270. i++;
  271. }
  272. }
  273. }
  274. function saveQuickLinks() {
  275. const quickLinks = Array.from(document.querySelectorAll("input[name=linkName]")).map(x => ({ Name: x.value, Reference: document.getElementById(x.id.replace("linkName", "linkValue")).value }));
  276. //console.log(quickLinks)
  277. setPlayerValue("QuickLinks", JSON.stringify(quickLinks));
  278. }
  279. async function checkMilitaryClan() {
  280. if(location.pathname == '/pl_clans.php') {
  281. const doc = location.pathname == '/pl_clans.php' ? document : await getRequest(`/pl_clans.php`);
  282. 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 }; });
  283. for(const clanInfo of clanInfos) {
  284. const clanInfoDoc = await getRequest(clanInfo.Ref);
  285. if(clanInfoDoc.body.innerHTML.includes(isEn ? "[Military clan]" : "[боевой клан]")) {
  286. var militaryClanId = clanInfo.Id;
  287. var clanName = clanInfo.Name;
  288. break;
  289. }
  290. }
  291. if(militaryClanId) {
  292. setPlayerValue("MilitaryClanId", militaryClanId);
  293. setPlayerValue("MilitaryClanName", clanName);
  294. } else {
  295. deletePlayerValue("MilitaryClanId");
  296. deletePlayerValue("MilitaryClanName");
  297. }
  298. }
  299. if(!getPlayerValue("MilitaryClanId")) {
  300. console.log("Вы не состоите в боевом клане");
  301. return false;
  302. }
  303. return true;
  304. }
  305. async function initUserName() {
  306. if(location.pathname == "/pl_info.php" && getUrlParamValue(location.href, "id") == PlayerId) {
  307. //console.log(document.querySelector("h1").innerText)
  308. setPlayerValue("UserName", document.querySelector("h1").innerText);
  309. }
  310. if(location.pathname == "/home.php") {
  311. //console.log(document.querySelector(`a[href='pl_info.php?id=${PlayerId}'] > b`).innerText)
  312. setPlayerValue("UserName", document.querySelector(`a[href='pl_info.php?id=${PlayerId}'] > b`).innerText);
  313. }
  314. if(!getPlayerValue("UserName")) {
  315. const doc = await getRequest(`/pl_info.php?id=${PlayerId}`);
  316. setPlayerValue("UserName", doc.querySelector("h1").innerText);
  317. }
  318. }
  319. function showSettings() {
  320. if(showPupupPanel(GM_info.script.name)) {
  321. return;
  322. }
  323. const fieldsMap = [];
  324.  
  325. const hideTavernLable = addElement("label", { for: "hideTavernCheckbox", innerText: isEn ? "Hide tavern" : "Скрыть таверну" });
  326. const hideTavernCheckbox = addElement("input", { id: "hideTavernCheckbox", type: "checkbox" });
  327. hideTavernCheckbox.checked = getPlayerBool("hideTavern");
  328. hideTavernCheckbox.addEventListener("change", function() { setPlayerValue("hideTavern", this.checked); }, false);
  329. fieldsMap.push([hideTavernLable, hideTavernCheckbox]);
  330.  
  331. const hideRouletteLable = addElement("label", { for: "hideRouletteCheckbox", innerText: isEn ? "Hide roulette" : "Скрыть рулетку" });
  332. const hideRouletteCheckbox = addElement("input", { id: "hideRouletteCheckbox", type: "checkbox" });
  333. hideRouletteCheckbox.checked = getPlayerBool("hideRoulette");
  334. hideRouletteCheckbox.addEventListener("change", function() { setPlayerValue("hideRoulette", this.checked); }, false);
  335. fieldsMap.push([hideRouletteLable, hideRouletteCheckbox]);
  336.  
  337. createPupupPanel(GM_info.script.name, getScriptReferenceHtml() + " " + getSendErrorMailReferenceHtml(), fieldsMap);
  338. }
  339. // API
  340. function addElement(type, data = {}, parent = undefined, insertPosition = "beforeend") {
  341. const el = document.createElement(type);
  342. for(const key in data) {
  343. if(key == "innerText" || key == "innerHTML") {
  344. el[key] = data[key];
  345. } else {
  346. el.setAttribute(key, data[key]);
  347. }
  348. }
  349. if(parent) {
  350. parent.insertAdjacentElement(insertPosition, el);
  351. }
  352. return el;
  353. }
  354. function getParent(element, parentType, number = 1) {
  355. if(!element) {
  356. return;
  357. }
  358. let result = element;
  359. let foundNumber = 0;
  360. while(result = result.parentNode) {
  361. if(result.nodeName.toLowerCase() == parentType.toLowerCase()) {
  362. foundNumber++;
  363. if(foundNumber == number) {
  364. return result;
  365. }
  366. }
  367. }
  368. }
  369. function getUrlParamValue(url, paramName) { return (new URLSearchParams(url.split("?")[1])).get(paramName); }
  370. function getRequest(url) {
  371. return new Promise((resolve, reject) => {
  372. GM.xmlHttpRequest({ method: "GET", url: url, overrideMimeType: "text/html; charset=windows-1251",
  373. onload: function(response) { resolve((new DOMParser).parseFromString(response.responseText, "text/html")); },
  374. onerror: function(error) { reject(error); }
  375. });
  376. });
  377. }
  378. function getValue(key, defaultValue) { return GM_getValue(key, defaultValue); };
  379. function setValue(key, value) { GM_setValue(key, value); };
  380. function deleteValue(key) { return GM_deleteValue(key); };
  381. function getPlayerValue(key, defaultValue) { return GM_getValue(`${key}${PlayerId}`, defaultValue); };
  382. function setPlayerValue(key, value) { GM_setValue(`${key}${PlayerId}`, value); };
  383. function deletePlayerValue(key) { return GM_deleteValue(`${key}${PlayerId}`); };
  384. function getValue(key, defaultValue) { return GM_getValue(key, defaultValue); };
  385. function setValue(key, value) { GM_setValue(key, value); };
  386. function deleteValue(key) { return GM_deleteValue(key); };
  387. function getPlayerValue(key, defaultValue) { return GM_getValue(`${key}${PlayerId}`, defaultValue); };
  388. function setPlayerValue(key, value) { GM_setValue(`${key}${PlayerId}`, value); };
  389. function deletePlayerValue(key) { return GM_deleteValue(`${key}${PlayerId}`); };
  390. function listValues() { return GM_listValues(); }
  391. function getPlayerBool(valueName, defaultValue = false) { return getBool(valueName + PlayerId, defaultValue); }
  392. function getBool(valueName, defaultValue = false) {
  393. const value = getValue(valueName);
  394. //console.log(`valueName: ${valueName}, value: ${value}, ${typeof(value)}`)
  395. if(value != undefined) {
  396. if(typeof(value) == "string") {
  397. return value == "true";
  398. }
  399. if(typeof(value) == "boolean") {
  400. return value;
  401. }
  402. }
  403. return defaultValue;
  404. }
  405. function getScriptLastAuthor() {
  406. let authors = GM_info.script.author;
  407. if(!authors) {
  408. const authorsMatch = GM_info.scriptMetaStr.match(/@author(.+)\n/);
  409. authors = authorsMatch ? authorsMatch[1] : "";
  410. }
  411. const authorsArr = authors.split(",").map(x => x.trim()).filter(x => x);
  412. return authorsArr[authorsArr.length - 1];
  413. }
  414. function getDownloadUrl() {
  415. let result = GM_info.script.downloadURL;
  416. if(!result) {
  417. const downloadURLMatch = GM_info.scriptMetaStr.match(/@downloadURL(.+)\n/);
  418. result = downloadURLMatch ? downloadURLMatch[1] : "";
  419. result = result.trim();
  420. }
  421. return result;
  422. }
  423. function createPupupPanel(panelName, panelTitle, fieldsMap, panelToggleHandler) {
  424. const backgroundPopupPanel = addElement("div", { id: panelName + "1", style: "position: fixed; left: 0pt; width: 100%; background: none repeat scroll 0% 0% gray; opacity: 0.5; top: 0px; height: 100%; display: block; z-index: 200;" }, document.body);
  425. backgroundPopupPanel.addEventListener("click", function() { hidePupupPanel(panelName, panelToggleHandler); });
  426.  
  427. const popupPanel = addElement("div", { id: panelName + "2", style: `position: fixed; width: 650px; background: none repeat scroll 0% 0%; background-image: linear-gradient(to right, #eea2a2 0%, #bbc1bf 19%, #57c6e1 42%, #b49fda 79%, #7ac5d8 100%); left: ${((document.body.offsetWidth - 650) / 2)}px; top: 150px; display: block; z-index: 200; border: 4mm ridge rgba(211, 220, 50, .6);` }, document.body);
  428. const contentDiv = addElement("div", { id: panelName + "3", style: "border: 1px solid #abc; padding: 5px; margin: 2px; display: flex; flex-wrap: wrap;" }, popupPanel);
  429.  
  430. if(panelTitle) {
  431. addElement("b", { innerHTML: panelTitle, style: "text-align: center; margin: auto; width: 90%; display: block;" }, contentDiv);
  432. }
  433. const divClose = addElement("div", { id: panelName + "close", title: "Close", innerText: "x", style: "border: 1px solid #abc; width: 15px; height: 15px; text-align: center; cursor: pointer;" }, contentDiv);
  434. divClose.addEventListener("click", function() { hidePupupPanel(panelName, panelToggleHandler); });
  435.  
  436. addElement("div", { style: "flex-basis: 100%; height: 0;"}, contentDiv);
  437.  
  438. if(fieldsMap) {
  439. let contentTable = addElement("table", undefined, contentDiv);
  440. for(const rowData of fieldsMap) {
  441. if(rowData.length == 0) { // Спомощью передачи пустой стороки-массива, указываем, что надо начать новую таблицу после брейка
  442. addElement("div", { style: "flex-basis: 100%; height: 0;"}, contentDiv);
  443. contentTable = addElement("table", undefined, contentDiv);
  444. continue;
  445. }
  446. const row = addElement("tr", undefined, contentTable);
  447. for(const cellData of rowData) {
  448. const cell = addElement("td", undefined, row);
  449. if(cellData) {
  450. if(typeof(cellData) == "string") {
  451. cell.innerText = cellData;
  452. } else {
  453. cell.appendChild(cellData);
  454. }
  455. }
  456. }
  457. }
  458. }
  459. if(panelToggleHandler) {
  460. panelToggleHandler(true);
  461. }
  462. return contentDiv;
  463. }
  464. function showPupupPanel(panelName, panelToggleHandler) {
  465. let backgroundPopupPanel = document.getElementById(panelName + "1");
  466. let popupPanel = document.getElementById(panelName + "2");
  467. if(backgroundPopupPanel) {
  468. backgroundPopupPanel.style.display = popupPanel.style.display = 'block';
  469. if(panelToggleHandler) {
  470. panelToggleHandler(true);
  471. }
  472. return true;
  473. }
  474. return false;
  475. }
  476. function hidePupupPanel(panelName, panelToggleHandler) {
  477. let backgroundPopupPanel = document.getElementById(panelName + "1");
  478. let popupPanel = document.getElementById(panelName + "2");
  479. backgroundPopupPanel.style.display = popupPanel.style.display = 'none';
  480. if(panelToggleHandler) {
  481. panelToggleHandler(false);
  482. }
  483. }
  484. function getScriptReferenceHtml() { return `<a href="${getDownloadUrl()}" title="${isEn ? "Check for update" : "Проверить обновление скрипта"}" target=_blanc>${GM_info.script.name} ${GM_info.script.version}</a>`; }
  485. function getSendErrorMailReferenceHtml() { return `<a href="sms-create.php?mailto=${getScriptLastAuthor()}&subject=${isEn ? "Error in" : "Ошибка в"} ${GM_info.script.name} ${GM_info.script.version} (${GM_info.scriptHandler} ${GM_info.version})" target=_blanc>${isEn ? "Bug report" : "Сообщить об ошибке"}</a>`; }
  486. function getParent(element, parentType, number = 1) {
  487. if(!element) {
  488. return;
  489. }
  490. let result = element;
  491. let foundNumber = 0;
  492. while(result = result.parentNode) {
  493. if(result.nodeName.toLowerCase() == parentType.toLowerCase()) {
  494. foundNumber++;
  495. if(foundNumber == number) {
  496. return result;
  497. }
  498. }
  499. }
  500. }