Anime Website (Custom) Buttons Plus

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

当前为 2020-10-26 提交的版本,查看 最新版本

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