Anime Website (Custom) Buttons Plus

A script that adds buttons on Anime Planet, MAL, Kitsu, Anilist and aniDB for searching various sites.

  1. // ==UserScript==
  2. // @author Deathwing
  3. // @name Anime Website (Custom) Buttons Plus
  4. // @include https://www.anime-planet.com/anime/*
  5. // @include http://myanimelist.net/anime/*
  6. // @include https://myanimelist.net/anime/*
  7. // @include http://myanimelist.net/manga/*
  8. // @include https://myanimelist.net/manga/*
  9. // @include https://anilist.co/*
  10. // @include https://kitsu.io/*
  11. // @include https://anidb.net/anime/*
  12. // @exclude https://www.anime-planet.com/anime/
  13. // @exclude https://www.anime-planet.com/anime/all?name=*
  14. // @exclude https://www.anime-planet.com/anime/recommendations/*
  15. // @exclude https://myanimelist.net/anime/producer*
  16. // @description A script that adds buttons on Anime Planet, MAL, Kitsu, Anilist and aniDB for searching various sites.
  17. // @version 2.820
  18. // @grant GM_setValue
  19. // @grant GM_getValue
  20. // @grant GM_listValues
  21. // @grant GM_deleteValue
  22. // @grant GM_addStyle
  23. // @grant window.onurlchange
  24. // @namespace https://gf.qytechs.cn/users/18375
  25. // ==/UserScript==
  26.  
  27.  
  28. var header;
  29. var headerQueryString = '';
  30. var outerButtonsDiv;
  31. var host = document.location.host;
  32. var malHost = 'myanimelist.net';
  33. var apHost = 'www.anime-planet.com';
  34. var alHost = 'anilist.co';
  35. var kHost = 'kitsu.io';
  36. var adHost = 'anidb.net';
  37.  
  38. var hideList = [];
  39.  
  40. var buttDivLeft = 0;
  41. var autoHide = GM_getValue('setting:autoHide', false);
  42.  
  43. if (host === apHost) {
  44. headerQueryString = '#siteContainer h1';
  45. header = getElement(headerQueryString);
  46. main();
  47. }
  48. else if (host === malHost) {
  49. if (location.pathname.includes('/anime/')) {
  50. headerQueryString = 'h1.title-name strong';
  51. }
  52. else {
  53. headerQueryString = '.h1-title span';
  54. }
  55.  
  56. header = getElement(headerQueryString);
  57. main();
  58. }
  59. else if (host === alHost) {
  60. headerQueryString = 'div.content h1';
  61. getSPAHeader(headerQueryString);
  62. }
  63. else if (host === kHost) {
  64. headerQueryString = '.media--title';
  65. getSPAHeader(headerQueryString);
  66. }
  67. else if (host === adHost) {
  68. headerQueryString = 'h1.anime';
  69. header = getElement(headerQueryString);
  70. main();
  71. }
  72.  
  73. function getSPAHeader(queryString) {
  74. header = getElement(queryString);
  75.  
  76. if (header) {
  77. main();
  78. }
  79. else {
  80. setTimeout(getSPAHeader, 300, queryString);
  81. }
  82. }
  83.  
  84. function getElement(atr) {
  85. return document.querySelector(atr);
  86. }
  87.  
  88. function addHeaderEventListeners() {
  89. header.addEventListener('mouseover', showEditButton);
  90. header.addEventListener('mouseout', hideEditButton);
  91. }
  92.  
  93.  
  94. function main() {
  95. var animeName;
  96. var reff;
  97.  
  98. if (host === malHost) {
  99. if (location.pathname.includes('/anime/')) {
  100. animeName = getAnimeName();
  101. }
  102. else {
  103. animeName = getAnimeName(header.firstChild);
  104. }
  105. }
  106. else if (host === apHost) {
  107. animeName = getAnimeName();
  108. }
  109. else if (host === adHost) {
  110. animeName = getAnimeName().replace('Anime: ', '');
  111. }
  112. else if (host === kHost) {
  113. animeName = getAnimeName(header.firstElementChild);
  114. reff = header.firstElementChild;
  115. extractNameOnChange();
  116. }
  117. else if (host === alHost) {
  118. animeName = getAnimeName(header.firstChild);
  119. extractNameOnChange();
  120. }
  121.  
  122. function extractNameOnChange() {
  123. window.addEventListener('urlchange', (el) => {
  124. var animeButtonsDiv = document.querySelector('.animeButtons');
  125. if (el.url.includes('/anime/')) {
  126. if (animeButtonsDiv == null) {
  127. header = document.querySelector(headerQueryString);
  128. header.appendChild(outerButtonsDiv);
  129. addHeaderEventListeners();
  130. }
  131. else {
  132. animeButtonsDiv.style.display = 'block';
  133. }
  134. if (host === kHost) {
  135. reff = header.firstElementChild;
  136. }
  137. if (getAnimeName(reff) !== animeName) {
  138. animeName = getAnimeName(reff);
  139. var urlsObjs = setSearchURLs();
  140. var animeButtons = document.querySelectorAll('.animeButton');
  141. var customButtonsObjs = getAnimeButtonsFromStorage();
  142.  
  143. animeButtons.forEach(b => {
  144. if (b.className.includes('stockButton')) {
  145. b.href = urlsObjs.find(o => o.name === b.title).url;
  146. }
  147. else {
  148. b.href = customButtonsObjs.find(o => o.title === b.title).url
  149. .replace('ANIMENAME', animeName);
  150. }
  151. });
  152. }
  153. }
  154. else {
  155. if (animeButtonsDiv != null) {
  156. animeButtonsDiv.style.display = 'none';
  157. }
  158. }
  159. });
  160. }
  161.  
  162. function getAnimeName(ref = header) {
  163. return ref.textContent.replace('(TV)', '').trim();
  164. }
  165.  
  166. function creteButton(icon, searchUrl, title, isStock) {
  167. var buttImg = createHTMLElement("img", null, null,
  168. [{ n: 'style', v: 'width:16px;height:16px;margin-right:2px;' }]);
  169.  
  170. if (icon) {
  171. buttImg.src = icon;
  172. }
  173. else {
  174. buttImg.src = getIconUrl(searchUrl);
  175. }
  176.  
  177. var button = createHTMLElement("a", null, 'animeButton', [{ n: 'id', v: `animeButton${makeButtonId(title)}` },
  178. { n: 'href', v: searchUrl }, { n: 'target', v: "_blank" }, { n: 'title', v: title }]);
  179.  
  180. if (isStock) {
  181. button.className += ' stockButton';
  182. }
  183.  
  184. button.appendChild(buttImg);
  185. return button;
  186. }
  187.  
  188. //Set buttons with information
  189. var malSearchUrl;
  190. var alSearchUrl;
  191. var apSearchUrl;
  192. var kSearchUrl;
  193. var adSearchUrl;
  194. var ytSearchUrl;
  195. var gSearchUrl;
  196. var nySearchUrl;
  197. var ampSearchUrl;
  198.  
  199. function setSearchURLs() {
  200. malSearchUrl = `http://myanimelist.net/anime.php?q=${animeName}`;
  201. alSearchUrl = `https://anilist.co/search/anime?search=${animeName}&sort=SEARCH_MATCH`;
  202. apSearchUrl = `https://www.anime-planet.com/anime/all?name=${animeName}`;
  203. kSearchUrl = `https://kitsu.io/anime?text=${animeName}`;
  204. adSearchUrl = `https://anidb.net/anime/?adb.search=${animeName}&do.search=1`;
  205. ytSearchUrl = `https://www.youtube.com/results?search_query=${animeName} trailer`;
  206. gSearchUrl = `https://google.com/search?tbm=isch&biw=&bih=&gbv=2&q=${animeName}`;
  207. nySearchUrl = `https://nyaa.si/?f=0&c=1_2&q=${animeName}`;
  208. if (host === malHost && location.pathname.includes('/anime/')) {
  209. ampSearchUrl = `https://animixplay.to${location.pathname}`;
  210. }
  211. else {
  212. ampSearchUrl = `https://animixplay.to/?q=${animeName}&sengine=mal`;
  213. }
  214.  
  215. return [
  216. { name: malTitle, url: malSearchUrl },
  217. { name: alTitle, url: alSearchUrl },
  218. { name: apTitle, url: apSearchUrl },
  219. { name: kTitle, url: kSearchUrl },
  220. { name: adTitle, url: adSearchUrl },
  221. { name: ytTitle, url: ytSearchUrl },
  222. { name: gTitle, url: gSearchUrl },
  223. { name: nyTitle, url: nySearchUrl },
  224. { name: ampTitle, url: ampSearchUrl }
  225. ];
  226. }
  227.  
  228. setSearchURLs();
  229.  
  230. //MAL Button
  231. var icon = null;
  232. var malTitle = "Search MyAnimeList";
  233.  
  234. var malButton = creteButton(icon, malSearchUrl, malTitle, true);
  235.  
  236.  
  237. //Anilist Button
  238. var alTitle = "Search Anilist";
  239.  
  240. var alButton = creteButton(icon, alSearchUrl, alTitle, true);
  241.  
  242.  
  243. //Anime-Planet Button
  244. var apTitle = "Search Anime-Planet";
  245.  
  246. var apButton = creteButton(icon, apSearchUrl, apTitle, true);
  247.  
  248. //Kitsu Button
  249. var kTitle = "Search Kitsu";
  250.  
  251. var kButton = creteButton(icon, kSearchUrl, kTitle, true);
  252.  
  253. //aniDB Button
  254. var adTitle = "Search aniDB";
  255.  
  256. var adButton = creteButton(icon, adSearchUrl, adTitle, true);
  257.  
  258.  
  259. //YouTube Button
  260. var ytTitle = 'YouTube Trailer';
  261.  
  262. var ytButton = creteButton(icon, ytSearchUrl, ytTitle, true);
  263.  
  264.  
  265. //Google Images button
  266. var gTitle = "Search with Google Images";
  267.  
  268. var giButton = creteButton(icon, gSearchUrl, gTitle, true);
  269.  
  270.  
  271. //Nyaa button
  272. var nyTitle = "Search Nyaa";
  273.  
  274. var nyButton = creteButton(icon, nySearchUrl, nyTitle, true);
  275.  
  276.  
  277. //AniMixPlay button
  278. var ampTitle;
  279. if (host === malHost && location.pathname.includes('/anime/')) {
  280. ampTitle = "Go to AniMixPlay";
  281. }
  282. else {
  283. ampTitle = "Search AniMixPlay";
  284. }
  285.  
  286. var ampButton = creteButton(icon, ampSearchUrl, ampTitle, true);
  287.  
  288.  
  289. //Edit button
  290. var ebTitle = "Edit Custom Buttons";
  291.  
  292. var arrowButtonIcon = createHTMLElement('i', null, 'arrowButton fa fa-angle-right',
  293. [{ n: 'title', v: ebTitle },
  294. { n: 'style', v: 'font-size:16px;vertical-align: text-top;transition: all 0.3s linear 0s;left:-18px;position: relative;' }]);
  295.  
  296. var editButtonIcon = createHTMLElement('i', null, 'editButton fa fa-edit',
  297. [{ n: 'title', v: ebTitle },
  298. { n: 'style', v: 'font-size:16px;vertical-align: text-top;transition: all 0.3s linear 0s;opacity:0;' }]);
  299.  
  300. var editButton = createHTMLElement('div', null, null,
  301. [{ n: 'style', v: 'width:16px;height:16px;margin-right:2px;display:inline;' }]);
  302.  
  303. if (!autoHide) {
  304. editButtonIcon.style.opacity = '1';
  305. arrowButtonIcon.style.opacity = '0';
  306. }
  307.  
  308. addHeaderEventListeners();
  309.  
  310. editButton.append(editButtonIcon, arrowButtonIcon);
  311. editButton.addEventListener('click', () => { togglePopup(true); });
  312.  
  313. var customButtons = [];
  314. var customButtonsObj = [];
  315.  
  316. if (!(GM_listValues()).includes('setting:buttonsNames')) {
  317. var values = GM_listValues();
  318.  
  319. for (var i = 0; i < values.length; i++) {
  320. if (!values[i].includes('setting:')) {
  321. customButtonsObj.push(JSON.parse(GM_getValue(values[i], '{}')));
  322. GM_deleteValue(values[i]);
  323. }
  324. }
  325.  
  326. setAnimeButtonsToStorage(customButtonsObj);
  327. }
  328. else {
  329. customButtonsObj = getAnimeButtonsFromStorage();
  330. }
  331.  
  332. customButtonsObj.forEach((b) => {
  333. customButtons.push(creteButton(b.icon, b.url.replace('ANIMENAME', animeName), b.title));
  334. });
  335.  
  336. //Add Website Buttons
  337. if (host === apHost) {
  338. appendButtons([malButton, alButton, kButton, adButton]);
  339. outerButtonsDiv.style.top = '6px';
  340. arrowButtonIcon.style.verticalAlign = '';
  341. editButtonIcon.style.verticalAlign = '';
  342. }
  343. else if (host === alHost) {
  344. appendButtons([malButton, apButton, kButton, adButton]);
  345. outerButtonsDiv.style.top = '8px';
  346. }
  347. else if (host === malHost) {
  348. appendButtons([apButton, alButton, kButton, adButton]);
  349. outerButtonsDiv.style.top = '2px';
  350. }
  351. else if (host === kHost) {
  352. appendButtons([malButton, apButton, alButton, adButton]);
  353. outerButtonsDiv.style.top = '2px';
  354. arrowButtonIcon.style.verticalAlign = '';
  355. editButtonIcon.style.verticalAlign = '';
  356. }
  357. else if (host === adHost) {
  358. appendButtons([malButton, apButton, alButton, kButton]);
  359. outerButtonsDiv.style.top = '9px';
  360. }
  361.  
  362. function appendButtons(mainButtonsArray) {
  363. header.appendChild(document.createTextNode(" "));
  364.  
  365. var allButtonsArray = mainButtonsArray.concat([ytButton, giButton, nyButton, ampButton], customButtons, editButton);
  366. var buttonsDiv = createHTMLElement('div', null, 'animeButtons',
  367. [{ n: 'style', v: 'position:relative;transition: all 0.4s cubic-bezier(0.79, 0.88, 0.16, 0.98) 0s;' }]);
  368. outerButtonsDiv = createHTMLElement('div', null, null,
  369. [{ n: 'style', v: 'display:inline-block;position:relative;overflow:hidden;' }]);
  370.  
  371. // buttonsDiv.appendChild(allButtonsArray);
  372. outerButtonsDiv.appendChild(buttonsDiv);
  373.  
  374. allButtonsArray.forEach((b) => {
  375. buttonsDiv.appendChild(b);
  376. if (b.id !== '') {
  377. hideList.push({
  378. bId: b.id,
  379. h: 'show'
  380. });
  381. }
  382. });
  383.  
  384. header.appendChild(outerButtonsDiv);
  385.  
  386. getHideList();
  387. hideButtons();
  388. addButtonPopup();
  389. hideEditButton();
  390. }
  391.  
  392. var fontAwesome = '@import url("https://use.fontawesome.com/releases/v5.15.2/css/all.css");';
  393. GM_addStyle(fontAwesome);
  394. }
  395.  
  396.  
  397. function getHideList() {
  398. var hideListNew;
  399.  
  400. if ((GM_listValues()).includes('hideList')) {
  401. hideListNew = GM_getValue('hideList', '[]');
  402. GM_deleteValue('hideList');
  403. }
  404. else {
  405. hideListNew = GM_getValue('setting:hideList', '[]');
  406. }
  407.  
  408. if (!hideListNew || hideListNew === undefined || hideListNew === 'undefined') {
  409. hideListNew = '[]';
  410. }
  411.  
  412. concatHideList(JSON.parse(hideListNew));
  413. }
  414.  
  415. function concatHideList(v) {
  416. v.forEach(b => {
  417. var item = hideList.find(n => n.bId === b.bId);
  418.  
  419. if (item) {
  420. return Object.assign(item, b);
  421. }
  422.  
  423. hideList.push(b);
  424. });
  425. }
  426.  
  427. function hideButtons() {
  428. buttDivLeft = 0;
  429.  
  430. hideList.forEach((o) => {
  431. var button = getElement(`#${o.bId}`);
  432.  
  433. if (button) {
  434. if (o.h === 'show') {
  435. button.style.display = '';
  436. buttDivLeft++;
  437. }
  438. else if (o.h === 'hide') {
  439. button.style.display = 'none';
  440. }
  441. }
  442. });
  443. }
  444.  
  445. function makeButtonId(buttonName) {
  446. var result = 0;
  447.  
  448. for (var i = 0; i < buttonName.length; i++) {
  449. result += buttonName.charCodeAt(i);
  450. }
  451.  
  452. return result * buttonName.charCodeAt(buttonName.length - 1);
  453. }
  454.  
  455.  
  456. function getPopup() {
  457. return getElement('.buttonPopup');
  458. }
  459.  
  460. function showEditButton() {
  461. var editButton = getElement('.editButton');
  462.  
  463. if (autoHide) {
  464. var arrowButton = getElement('.arrowButton');
  465. var buttonsDiv = getElement('.animeButtons');
  466. buttonsDiv.style.left = '0%';
  467. arrowButton.style.opacity = '0';
  468. }
  469.  
  470. editButton.style.opacity = '1';
  471. }
  472.  
  473. function hideEditButton() {
  474. var editButton = getElement('.editButton');
  475.  
  476. if (autoHide) {
  477. var buttonsDiv = getElement('.animeButtons');
  478. var arrowButton = getElement('.arrowButton');
  479. hideButtons();
  480. buttonsDiv.style.left = `-${buttDivLeft * 18}px`;
  481. arrowButton.style.opacity = '1';
  482. }
  483.  
  484. editButton.style.opacity = '0';
  485. }
  486.  
  487. function addAndCancelButtonsHandler(e) {
  488. var targetEl = e.target;
  489.  
  490. if (targetEl.className === 'addButton') {
  491. addButtonLogic(GM_listValues());
  492. }
  493. else if (targetEl.className === 'cancelButton') {
  494. togglePopup(false);
  495. }
  496. }
  497.  
  498. function addAndEditTabButtonsHandler(e) {
  499. var target = e.target;
  500.  
  501. if (target.className.includes('Text')) {
  502. target = e.target.parentElement;
  503. }
  504.  
  505. if (target.className === 'addTab' && target.style.color === 'white') {
  506. var editTab = target.parentElement.children[1];
  507. hideTabSection(editTab, target);
  508. }
  509. else if (target.className === 'editTab' && target.style.color === 'white') {
  510. var addTab = target.parentElement.firstElementChild;
  511. hideTabSection(addTab, target);
  512. }
  513. }
  514.  
  515. function hideTabSection(toHide, toShow) {
  516. toHide.style.color = 'white';
  517. toHide.style.backgroundColor = '#d8d8d8';
  518. toShow.style.color = 'black';
  519. toShow.style.backgroundColor = 'white';
  520.  
  521. var sectionToHide;
  522. var sectionToShow;
  523.  
  524. if (toHide.className === 'addTab') {
  525. sectionToHide = getElement('.addSection');
  526. sectionToShow = getElement('.editSection');
  527. }
  528. else {
  529. sectionToHide = getElement('.editSection');
  530. sectionToShow = getElement('.addSection');
  531. }
  532.  
  533. sectionToHide.style.opacity = '0';
  534.  
  535. setTimeout(() => {
  536. sectionToHide.style.display = 'none';
  537. sectionToShow.style.display = 'flex';
  538. setTimeout(() => sectionToShow.style.opacity = '1', 50);
  539. }, 200);
  540. }
  541.  
  542. function togglePopup(show) {
  543. var popUp = getPopup();
  544.  
  545. if (show) {
  546. header.removeEventListener('mouseout', hideEditButton);
  547. popUp.style.opacity = '1';
  548. popUp.style.top = '50%';
  549. }
  550. else {
  551. header.addEventListener('mouseout', hideEditButton);
  552. hideEditButton();
  553. popUp.style.opacity = '0';
  554. popUp.style.top = '-100%';
  555. }
  556. }
  557.  
  558. function getAnimeButtonsFromStorage() {
  559. return JSON.parse(GM_getValue('setting:buttonsNames', '[]'));
  560. }
  561.  
  562. function setAnimeButtonsToStorage(buttonsNames) {
  563. GM_setValue('setting:buttonsNames', JSON.stringify(buttonsNames));
  564. }
  565.  
  566. function addButtonLogic() {
  567. var titleField = getElement('.titleInput');
  568. var searchField = getElement('.URLInput');
  569. var iconField = getElement('.iconInput');
  570.  
  571. var buttons = getAnimeButtonsFromStorage();
  572.  
  573. if (titleField.value === '') {
  574. toggleMsgBox(true, 'Title cannot be empty!');
  575. }
  576. else if (searchField.value === '') {
  577. toggleMsgBox(true, 'Search URL cannot be empty!');
  578. }
  579. else if (!searchField.value.includes('ANIMENAME')) {
  580. toggleMsgBox(true, 'Search URL must contain ANIMENAME!');
  581. }
  582. else if (buttons.find((o) => o.title === titleField.value)) {
  583. toggleMsgBox(true, 'Button with the same name already exists!');
  584. }
  585. else {
  586. if (iconField.value === '') {
  587. iconField.value = getIconUrl(searchField.value);
  588. }
  589.  
  590. var newButton = {
  591. title: titleField.value,
  592. url: searchField.value,
  593. icon: iconField.value
  594. };
  595.  
  596. buttons.push(newButton);
  597.  
  598. setAnimeButtonsToStorage(buttons);
  599.  
  600. hideList.push({ bId: `animeButton${makeButtonId(titleField.value)}`, h: 'show' });
  601. GM_setValue('setting:hideList', JSON.stringify(hideList));
  602.  
  603. toggleMsgBox(true, `Button ${titleField.value} added succsessfully! Reload to see it!`, true);
  604.  
  605. titleField.value = '';
  606. searchField.value = '';
  607. iconField.value = '';
  608. }
  609. }
  610.  
  611. function getIconUrl(fromUrl) {
  612. var regex = /(?:https?:\/\/)(w{0,3}\.?[\s\S]+?\.\w+)\//;
  613. var result = '';
  614.  
  615. if (regex.test(fromUrl)) {
  616. result = `https://www.google.com/s2/favicons?domain=${fromUrl.match(regex)[1]}`;
  617. }
  618.  
  619. return result;
  620. }
  621.  
  622. function toggleMsgBox(toggle, msg, showReload) {
  623. var msgBox = getElement('.addMsgBox');
  624.  
  625. if (msg) {
  626. msgBox.firstElementChild.textContent = msg;
  627. }
  628.  
  629. if (showReload) {
  630. msgBox.children[1].style.display = 'inline';
  631. }
  632. else {
  633. msgBox.children[1].style.display = 'none';
  634. }
  635.  
  636. if (toggle) {
  637. msgBox.style.opacity = '1';
  638. msgBox.style.bottom = '15%';
  639. }
  640. else {
  641. msgBox.style.opacity = '0';
  642. setTimeout(() => { msgBox.style.bottom = '150%'; }, 250);
  643. }
  644. }
  645.  
  646. function hideAndDeleteHandler(e) {
  647. var target = e.target;
  648. var buttParent = target.parentElement;
  649. var button = getElement(`#${buttParent.className}`);
  650.  
  651. if (target.classList.contains('removeButton') && !target.classList.contains('disabled')) {
  652. button.remove();
  653. target.parentElement.remove();
  654. var buttonsObjs = getAnimeButtonsFromStorage();
  655. buttonsObjs = buttonsObjs.filter((o) => buttParent.textContent !== o.title);
  656. setAnimeButtonsToStorage(buttonsObjs);
  657. hideList = hideList.filter(obj => obj.bId !== button.id);
  658.  
  659. GM_setValue('setting:hideList', JSON.stringify(hideList));
  660. }
  661. else if (target.classList.contains('hideButton')) {
  662. if (button.style.display === 'none') {
  663. button.style.display = '';
  664. concatHideList([{ bId: button.id, h: 'show' }]);
  665. target.classList.replace('fa-eye-slash', 'fa-eye');
  666. // target.setAttribute('src', iconVisible);
  667. }
  668. else {
  669. button.style.display = 'none';
  670. concatHideList([{ bId: button.id, h: 'hide' }]);
  671. target.classList.replace('fa-eye', 'fa-eye-slash');
  672. // target.setAttribute('src', iconInvisible);
  673. }
  674.  
  675. GM_setValue('setting:hideList', JSON.stringify(hideList));
  676.  
  677. hideButtons();
  678. }
  679. }
  680.  
  681. function msgButtonsHandler(e) {
  682. var target = e.target;
  683.  
  684. if (target.className === 'reloadButton') {
  685. location.reload();
  686. }
  687. else if (target.className === 'closeButton') {
  688. toggleMsgBox(false);
  689. }
  690. }
  691.  
  692. function settingsHandler(e) {
  693. var target = e.target;
  694.  
  695. if (target.className === 'editCheckbox') {
  696. autoHide = target.checked;
  697. GM_setValue('setting:autoHide', autoHide);
  698. }
  699. }
  700.  
  701. function popupClickHandler(e) {
  702. if (!e.target.className.includes('infoBox')) {
  703. var infoBoxes = document.querySelectorAll('.infoBox');
  704. infoBoxes.forEach(b => {
  705. if (b.style.opacity === '1') {
  706. hideInfoBox(b);
  707. }
  708. });
  709. }
  710. }
  711.  
  712. function URLQuestionmarkHandler(e) {
  713. showInfoBox(e.target.parentElement.lastElementChild);
  714. var iconInfoBox = getElement('.iconInfoBox');
  715. hideInfoBox(iconInfoBox);
  716. }
  717.  
  718. function iconQuestionmarkHandler(e) {
  719. showInfoBox(e.target.parentElement.lastElementChild);
  720. var URLInfoBox = getElement('.URLInfoBox');
  721. hideInfoBox(URLInfoBox);
  722. }
  723.  
  724. function hideInfoBox(infoBox) {
  725. infoBox.style.opacity = '0';
  726. setTimeout(() => infoBox.style.display = 'none', 300);
  727. }
  728.  
  729. function showInfoBox(infoBox) {
  730. infoBox.style.display = 'inline-block';
  731. setTimeout(() => infoBox.style.opacity = '1', 10);
  732. }
  733.  
  734. function addButtonPopup() {
  735. var style = 'margin:auto;text-align: center;display:block;margin-bottom: 5px;';
  736. var popUp = createHTMLElement('div', null, 'buttonPopup',
  737. [{ n: 'style', v: 'position:fixed;top:-100%;left:50%;transform: translate(-50%, -50%);background-color:white;width:400px;height:560px;box-shadow: 0 0 15px rgba(0, 0, 0, 0.4);border-radius: 8px;font-size:medium;z-index:9999;opacity:0;transition: all 0.7s cubic-bezier(0.45, -0.24, 0.43, 1.14) 0s;' }]);
  738.  
  739. var tabs = createHTMLElement('div', null, 'popupTabs',
  740. [{ n: 'style', v: 'width: 100%;height: 40px;cursor: default;' }]);
  741. var addTab = createHTMLElement('div', null, 'addTab',
  742. [{ n: 'style', v: 'height: 100%;width: 50%;background-color: white;left: 50%;border-top-left-radius: 8px;text-align: center;transition: all 0.2s linear 0s;' }]);
  743. var textTabsStyle = 'position: relative;top: 11px;font-weight: bold;';
  744. var addTabText = createHTMLElement('div', 'ADD', 'addTabText',
  745. [{ n: 'style', v: textTabsStyle }]);
  746. addTab.appendChild(addTabText);
  747.  
  748. var editTab = createHTMLElement('div', null, 'editTab',
  749. [{ n: 'style', v: 'top: -40px;height: 100%;width: 50%;background-color: #d8d8d8;left: 50%;position: relative;border-top-right-radius: 8px;text-align: center;color: white;transition: all 0.2s linear 0s;' }]);
  750. var editTabText = createHTMLElement('div', 'EDIT', 'editTabText',
  751. [{ n: 'style', v: textTabsStyle }]);
  752. editTab.appendChild(editTabText);
  753.  
  754. tabs.append(addTab, editTab);
  755.  
  756. var addSection = createHTMLElement('div', null, 'addSection',
  757. [{ n: 'style', v: 'height: calc(100% - 40px);width:100%;transition: all 0.2s linear 0s;display:flex; flex-direction: column;' }]);
  758. var addSectionContents = createHTMLElement('div');
  759. var addSectionTitle = createHTMLElement('h2', 'ADD CUSTOM BUTTON', null,
  760. [{ n: 'style', v: style + 'margin-top: 25px' }]);
  761. var title = createHTMLElement('h3', 'Title', null,
  762. [{ n: 'style', v: style + 'margin-top: 20px' }]);
  763. var titleInput = createHTMLElement('input', null, 'titleInput',
  764. [{ n: 'placeholder', v: 'Button title' }, { n: 'style', v: style + 'width:80%' }]);
  765. var URLTitle = createHTMLElement('h3', 'Search URL', null,
  766. [{ n: 'style', v: style + 'margin-top: 20px' }]);
  767. var URLQm = createHTMLElement('i', null, 'URLQuestionmark questionmark fa fa-question-circle',
  768. [{ n: 'style', v: 'font-size:16px;margin-left:5px;' }]);
  769. var infoBoxStyle = 'width: 90%;display: inline-block;position: absolute;margin-left: 10px;background-color: white;border-radius: 8px;box-shadow: rgba(0,0,0, 0.3) 0px 0px 10px;transition: opacity 0.3s linear;opacity: 0;padding: 10px;font-weight: normal;font-size: medium;';
  770. var URLInfoBox = createHTMLElement('div', 'To get the search URL first go the site you want to add and search the term "ANIMENAME" in the search field. Then copy the full URL (including http://) in the field below. (exaple: https://myanimelist.net/search/all?q=ANIMENAME)', 'URLInfoBox infoBox',
  771. [{ n: 'style', v: infoBoxStyle }]);
  772. URLTitle.append(URLQm, URLInfoBox);
  773. var URLInput = createHTMLElement('input', null, 'URLInput',
  774. [{ n: 'placeholder', v: 'Search URL' }, { n: 'style', v: style + 'width:80%' }]);
  775. var iconTitle = createHTMLElement('h3', 'Icon URL', null,
  776. [{ n: 'style', v: style + 'margin-top: 20px' }]);
  777. var iconQm = createHTMLElement('i', null, 'iconQuestionmark questionmark fa fa-question-circle',
  778. [{ n: 'style', v: 'font-size:16px;margin-left:5px;' }]);
  779. var iconInfoBox = createHTMLElement('div', null, 'iconInfoBox infoBox',
  780. [{ n: 'style', v: infoBoxStyle }]);
  781. iconInfoBox.innerHTML = '(<b>Leave empty for automatic icon parse</b>)<br />Link to icon for the button. <br />The easiest way to get it is to copy this link "https://www.google.com/s2/favicons?domain=" and place the website url at the end (example: https://www.google.com/s2/favicons?domain=myanimelist.net).';
  782. iconTitle.append(iconQm, iconInfoBox);
  783. var iconInput = createHTMLElement('input', null, 'iconInput',
  784. [{ n: 'placeholder', v: 'Icon URL' }, { n: 'style', v: style + 'width:80%' }]);
  785.  
  786. var msgBoxDiv = createHTMLElement('div', null, 'addMsgBox',
  787. [{ n: 'style', v: 'width: 86%;position: absolute;margin-left: 7%;bottom: 150%;background-color: white;border-radius: 8px;box-shadow: rgba(0,0,0, 0.4) 0px 0px 15px;text-align: center;transition: opacity 0.2s linear;opacity:0' }]);
  788. var msgText = createHTMLElement('div', 'Button added succsessfully! Reload to see it!', 'addMgsText',
  789. [{ n: 'style', v: 'margin: 10px;' }]);
  790. var reloadButton = createHTMLElement('button', 'RELOAD', 'reloadButton',
  791. [{ n: 'style', v: 'margin: 10px;margin-right:0px;width:90px;' }]);
  792. var closeButton = createHTMLElement('button', 'CLOSE', 'closeButton',
  793. [{ n: 'style', v: 'margin: 10px;width:90px;' }]);
  794. msgBoxDiv.append(msgText, reloadButton, closeButton);
  795.  
  796. var buttonsDiv = createHTMLElement('div', null, 'addAndCancelButtons',
  797. [{ n: 'style', v: style + 'margin-bottom: 10px;' }]);
  798. var addButton = createHTMLElement('button', 'ADD', 'addButton',
  799. [{ n: 'style', v: 'width:90px;margin:5px' }]);
  800. var cancelButton = createHTMLElement('button', 'CANCEL', 'cancelButton',
  801. [{ n: 'style', v: 'width:90px;margin:5px' }]);
  802.  
  803. var editSection = createHTMLElement('div', null, 'editSection',
  804. [{ n: 'style', v: 'display: flex;flex-direction: column;height: calc(100% - 40px);width:100%;display:none;transition: all 0.2s linear 0s;' }]);
  805. var editSectionTitle = createHTMLElement('h2', 'EDIT CUSTOM BUTTONS', null,
  806. [{ n: 'style', v: style + 'margin-top: 25px' }]);
  807. var animeButtonsList = createHTMLElement('ul', null, 'buttonsList',
  808. [{ n: 'style', v: 'list-style: none;margin-top: 25px;padding-left: 40px;overflow: hidden;overflow-y: auto;flex: 1 1 auto;' }]);
  809. var animeButtons = document.querySelectorAll('.animeButton');
  810.  
  811. var settingsDiv = createHTMLElement('div', null, 'settingsDiv',
  812. [{ n: 'style', v: 'padding: 0px 30px;' }]);
  813. var hideEditCheckbox = createHTMLElement('input', null, 'editCheckbox',
  814. [{ n: 'id', v: 'editCheckbox' }, { n: 'type', v: 'checkbox' }, { n: 'value', v: 'editCheckbox' }]);
  815.  
  816. if (autoHide) {
  817. hideEditCheckbox.setAttribute('checked', true);
  818. }
  819.  
  820. var hideEditCheckboxLabel = createHTMLElement('label', 'Auto hide buttons (show on mouseover)', null,
  821. [{ n: 'for', v: 'editCheckbox' }, { n: 'style', v: 'padding-left:5px;' }]);
  822. var exportButton = createHTMLElement('button', 'Export custom buttons.', null,
  823. [{ n: 'style', v: 'padding: 4px;' }]);
  824. exportButton.addEventListener('click', exportCustomButtons);
  825.  
  826. var imortExportDiv = createHTMLElement('div', null, null,
  827. [{ n: 'style', v: 'display: flex;justify-content: space-evenly;margin: 7px 0;' }]);
  828. var importInput = createHTMLElement('input', null, null,
  829. [{ n: 'type', v: 'file' }, { n: 'accept', v: 'application/json' }, { n: 'style', v: 'display: none;' }]);
  830. importInput.addEventListener('change', onImportChange);
  831. var importButton = createHTMLElement('button', 'Import custom buttons.', null,
  832. [{ n: 'style', v: 'padding: 4px;' }]);
  833. importButton.addEventListener('click', () => { importInput.click(); });
  834. imortExportDiv.append(exportButton, importButton, importInput);
  835.  
  836. settingsDiv.append(hideEditCheckbox, hideEditCheckboxLabel, imortExportDiv);
  837.  
  838. var editButtonsDiv = createHTMLElement('div', null, 'addAndCancelButtons',
  839. [{ n: 'style', v: style + 'margin-bottom: 10px;' }]);
  840. var cancelButtonEdit = createHTMLElement('button', 'CLOSE', 'cancelButton',
  841. [{ n: 'style', v: 'width:90px;margin:5px' }]);
  842. editButtonsDiv.appendChild(cancelButtonEdit);
  843.  
  844. createAndAppendEditListEntry(animeButtonsList, animeButtons);
  845.  
  846. buttonsDiv.append(addButton, cancelButton);
  847. addSectionContents.append(addSectionTitle, title, titleInput, URLTitle, URLInput, iconTitle, iconInput);
  848. addSection.append(addSectionContents, buttonsDiv);
  849.  
  850. editSection.append(editSectionTitle, animeButtonsList, settingsDiv, editButtonsDiv);
  851.  
  852. popUp.append(tabs, addSection, editSection, msgBoxDiv);
  853. var html = getElement('html');
  854. html.appendChild(popUp);
  855.  
  856. buttonsDiv.addEventListener('click', addAndCancelButtonsHandler);
  857. editButtonsDiv.addEventListener('click', addAndCancelButtonsHandler);
  858. tabs.addEventListener('click', addAndEditTabButtonsHandler);
  859. animeButtonsList.addEventListener('click', hideAndDeleteHandler);
  860. msgBoxDiv.addEventListener('click', msgButtonsHandler);
  861. settingsDiv.addEventListener('click', settingsHandler);
  862. URLQm.addEventListener('mouseover', URLQuestionmarkHandler);
  863. iconQm.addEventListener('mouseover', iconQuestionmarkHandler);
  864. popUp.addEventListener('click', popupClickHandler);
  865. }
  866.  
  867. function createAndAppendEditListEntry(animeButtonsList, animeButtons) {
  868. animeButtons.forEach((b) => {
  869. var listEl = createHTMLElement('li', null, b.id,
  870. [{ n: 'style', v: 'width:90%;margin-top:5px;border-bottom-style: inset;border-bottom-width: thin;display:flex;' }]);
  871. var imgUrl = b.firstElementChild.getAttribute('src');
  872. var img = createHTMLElement('img', null, null,
  873. [{ n: 'src', v: imgUrl }, { n: 'style', v: 'width: 16px;height: 16px;' }]);
  874. var span = createHTMLElement('span', b.getAttribute('title'), null,
  875. [{ n: 'style', v: 'margin-left:5px;bottom: 2px;position: relative;flex: 1 1 0;' }]);
  876. var hideIcon = createHTMLElement('i', null, 'hideButton fa fa-eye',
  877. [{ n: 'title', v: 'Toggle Hide' }, { n: 'style', v: 'font-size:16px;position: relative;margin: 0 10px;' }]);
  878. var removeIcon = createHTMLElement('i', null, 'removeButton fa fa-trash-alt',
  879. [{ n: 'title', v: 'DELETE' }, { n: 'style', v: 'font-size:16px;position: relative;' }]);
  880.  
  881. if (b.style.display === 'none') {
  882. hideIcon.classList.replace('fa-eye', 'fa-eye-slash');
  883. }
  884.  
  885. listEl.append(img, span, hideIcon, removeIcon);
  886.  
  887. if (b.className.includes('stockButton')) {
  888. removeIcon.style.color = 'lightgray';
  889. removeIcon.classList.add('disabled');
  890. }
  891.  
  892. animeButtonsList.appendChild(listEl);
  893. });
  894. }
  895.  
  896. function onImportChange(e) {
  897. var reader = new FileReader();
  898. reader.readAsText(e.target.files[0]);
  899. reader.onload = (e) => {
  900. importCustomButtons(e.target.result);
  901. };
  902. }
  903.  
  904. function importCustomButtons(buttons) {
  905. GM_setValue('setting:buttonsNames', buttons);
  906. toggleMsgBox(true, 'Import successful, reload the page for it to take effect.', true);
  907. }
  908.  
  909. function exportCustomButtons() {
  910. var customButtonsStr = GM_getValue('setting:buttonsNames', '[]');
  911. downloadJson(`custom-buttons-plus-export-${new Date().toISOString()}`, customButtonsStr);
  912. }
  913.  
  914. function downloadJson(filename, jsonString) {
  915. var element = createHTMLElement('a', null, null,
  916. [{ n: 'href', v: 'data:application/json;charset=utf-8,' + encodeURIComponent(jsonString) },
  917. { n: 'download', v: `${filename}.json` }]);
  918.  
  919. element.style.display = 'none';
  920. document.body.appendChild(element);
  921. element.click();
  922. document.body.removeChild(element);
  923. }
  924.  
  925. function createHTMLElement(tag, textContent, className, attributes) {
  926. var element = document.createElement(tag);
  927.  
  928. if (className) {
  929. element.className = className;
  930. }
  931. if (textContent) {
  932. element.textContent = textContent;
  933. }
  934. if (attributes) {
  935. attributes.forEach((a) => {
  936. element.setAttribute(a.n, a.v);
  937. });
  938. }
  939.  
  940. return element;
  941. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址