MALstreaming

Adds various streaming links to MAL

当前为 2018-06-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name MALstreaming
  3. // @namespace https://github.com/mattiadr/MALstreaming
  4. // @version 4.0
  5. // @author https://github.com/mattiadr
  6. // @description Adds various streaming links to MAL
  7. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wQRDic4ysC1kQAAA+lJREFUWMPtlk1sVFUUx3/n3vvmvU6nnXbESkTCR9DYCCQSFqQiMdEY4zeJuiBhwUISAyaIHzHGaDTxKyzEr6ULNboiRonRhQrRCMhGiDFGA+WjhQ4NVKbtzJuP9969Lt4wlGnBxk03vZv3cu495/7u/5x7cmX1xk8dczjUXG4+DzAPMA8AYNoNIunXudnZ2+enrvkvn2kADkhiiwM8o6YEEuLE4pxDK0GakZUIoiCOHXFiW2uNEqyjZdNaIbMB0Ero7gwQ4OJEDa0VSoR6lNDT5eMZRaUa0YgSjFZU6zG1ekK+y6er00eJECWWchiRMYp8VwBAOYyw1l0dQIlQrcfcvKSHT968j+5chg+/OMoHnx9FCdwzsIRdz24gGxhe2v0Le74/htaKFYvzbNm4knWrF3J9IYtSQq0e8+C2r+jwDXvefYjEWja98B2DQyU6fINty8cVCigl9HYHiMCOzWs4/HuR4XNl3n5mPbmsB0DgGyYrDR69ewXvvXgXgW+oNxLOX6ySJJaebp/+ZQWOD5fIZT2cS5WddRGCw9oU5rVtA1SqEfmcTxRZPE8RxZbe7oBXnlpH4BtGx0Ke2PkNt624jte3DzBWqjF4ZhzP6GYBOtw1qtC07Y2I0IgTisUKtyztBaB4voLWQl8hS1iLuL2/j0V9OQC+/fkkx4ZK3L9hGQt6Oyj0BCiR1qZpwV5dgRn7gBLh1Y8OcmpkAoDndv3E6IUQgCRx9BWy6b91bH64n7P7tvL8lrU4l/pOi6dSRZWSaShmJgDPKIbPTfLy+wdYfEMXB46M0JXLNE8ElWoEQK0e8/fJi8SJpa+QZemi7hmiOSphxESlQRRb/IzGKMHNBOCaJwTI53wOHhnBM5pCPqDRSFIHrTh1drzls/2Nffx18h+efGwV7+y8kyi2l+O5VKW1KxeycEEn2Q6PPwfHKE3WMVpwrg1AAK1TkaxzBBlDEGiSxLXsgW84cWacE2fGWX5TnnsHlnB8qEQ2SG+J1qnM0lTLaMVbO+5AJL2ijzy9l7FSDaMV4FIAh0MpoRxGfL1vECRtHiK0Gsj+w8OcHpmkeKFCWIv54dAQWx9fxfo1N/Lxl38wVJzgx1+HCGsx1XoMwN79gy1VfU9zujjB2dFJfE9dLtKpb0JrHeUwzW8u66Gm3N9yGJEkls6sR5I4+pcX2PTArez+7DcmK+lcWIsRgc5mzyhXoivSq5W0+klL9fZH6SWpL9VCy64ERLDW4lyaorAaE2Q0xihE0kqnmfepsaZSJPYanXCmjVt265rnaAKJkM9lsM7hXLPg2nyvFuuaALMdjumn+T9jzh8k8wDzAPMAcw7wLz7iq04ifbsDAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE1LTA0LTE3VDE0OjM5OjU2LTA0OjAw6I0f5AAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNS0wNC0xN1QxNDozOTo1Ni0wNDowMJnQp1gAAAAASUVORK5CYII=
  8. // @run-at document-idle
  9. // @supportURL https://github.com/mattiadr/MALstreaming/issues
  10. // @match https://myanimelist.net/animelist/*
  11. // @match https://myanimelist.net/ownlist/anime/*/edit*
  12. // @match https://myanimelist.net/ownlist/anime/add?selected_series_id=*
  13. // @match http://kissanime.ru/
  14. // @require https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js
  15. // @grant GM_xmlhttpRequest
  16. // @grant GM_openInTab
  17. // @grant GM_setValue
  18. // @grant GM_getValue
  19. // @grant window.close
  20. // ==/UserScript==
  21.  
  22. /*
  23. HOW TO ADD A NEW STREAMING SERVICE:
  24. - add a new object to the streamingServices array with attributes id (unique id, must be a valid identifier) and name (display name)
  25. - create a new function in getEplistUrl that will simply return the full url from the partial url (saved in comments)
  26. - create a new function in getEpisodes that will accept dataStream and url,
  27. the function needs to callback to putEpisodes(dataStream, episodes, timeMillis)
  28. url is the url of the episode list provoded by getEplistUrl
  29. episodes needs to be an array of object with text and href attributes
  30. timeMillis can optionally be the time left until the next episode in milliseconds
  31. - create a new function in search that will accept id and title
  32. the function needs to callback to putResults(id, results, manualSearch)
  33. results needs to be an array of object with title (display title), href (the url that will be put in the comments) and fullhref (full url of page) attributes
  34. manualSearch needs to be an url to visit if search yields no results
  35. - if other utility is needed, add it in the service section and if you need to run a script on specific pages add another if in the "main"
  36. */
  37.  
  38. /* generic */
  39. /*******************************************************************************************************************************************************************/
  40. // contains all functions to execute on page load
  41. const pageLoad = {};
  42. // contains all functions to get the episodes list from the streaming services
  43. // must callback to putEpisodes(dataStream, episodes, timeMillis)
  44. const getEpisodes = {};
  45. // contains all functions to get the episode list url from the partial url
  46. const getEplistUrl = {};
  47. // contains all functions to execute the search on the streaming services
  48. // must callback to putResults(results)
  49. const searchSite = {};
  50. // is an array of valid streaming services names
  51. const streamingServices = [
  52. {id:"kissanime", name:"Kissanime"},
  53. {id:"nineanime", name:"9anime"}
  54. ];
  55. // return an array that contains the streaming service and url relative to that service or false if comment is not valid
  56. function getUrlFromComment(comment) {
  57. let c = comment.split(" ");
  58. if (c.length < 2) return false;
  59. for (let i = 0; i < streamingServices.length; i++) {
  60. if (streamingServices[i].id == c[0]) return c;
  61. }
  62. return false;
  63. }
  64.  
  65. /* kissanime */
  66. /*******************************************************************************************************************************************************************/
  67. const kissanime = {};
  68. kissanime.base = "http://kissanime.ru/";
  69. kissanime.anime = kissanime.base + "Anime/";
  70. kissanime.search = kissanime.base + "Search/Anime/";
  71. kissanime.server = "&s=rapidvideo";
  72. // blacklisted urls
  73. kissanime.epsBlacklist = [
  74. "/Anime/Macross/Bunny_Hat-Macross_Special_-4208D135?id=73054",
  75. "/Anime/Macross/Bunny_Hat_Raw-30th_Anniversary_Special_-0A1CD40E?id=73055",
  76. "/Anime/Macross/Episode-011-original?id=35423"
  77. ];
  78. // regexes
  79. kissanime.regexWhitelist = /episode|movie|special|OVA/i;
  80. kissanime.regexBlacklist = /\b_[a-z]+|recap|\.5/i;
  81. kissanime.regexCountdown = /\d+(?=\), function)/;
  82.  
  83. // loads kissanime cookies and then calls back
  84. function kissanime_loadCookies(callback, arg1, arg2) {
  85. if (!GM_getValue("KAloadcookies", false)) {
  86. GM_setValue("KAloadcookies", true);
  87. GM_openInTab(kissanime.base, true);
  88. }
  89. if (callback) {
  90. setTimeout(function() {
  91. callback(arg1, arg2);
  92. }, 6000);
  93. }
  94. }
  95.  
  96. // function to execute when scrip is run on kissanime
  97. pageLoad["kissanime"] = function() {
  98. if (GM_getValue("KAloadcookies", false) && document.title != "Please wait 5 seconds...") {
  99. GM_setValue("KAloadcookies", false);
  100. window.close();
  101. }
  102. }
  103.  
  104. getEpisodes["kissanime"] = function(dataStream, url) {
  105. GM_xmlhttpRequest({
  106. method: "GET",
  107. url: kissanime.anime + url,
  108. onload: function(resp) {
  109. if (resp.status == 503) {
  110. // loading CF cookies
  111. kissanime_loadCookies(getEpisodes["kissanime"], dataStream, url);
  112. } else if (resp.status == 200) {
  113. // OK
  114. let jqPage = $(resp.response);
  115. let episodes = [];
  116. // get anchors for the episodes
  117. let as = jqPage.find(".listing").find("tr > td > a");
  118. // filter and add to episodes array
  119. as.each(function(i, e) {
  120. // title must match regexWhitelist, must not match regexBlacklist and href must not be in epsBlacklist to be considered a valid episode
  121. if (kissanime.regexWhitelist.test(e.text) && !kissanime.regexBlacklist.test(e.text) && kissanime.epsBlacklist.indexOf(e.href) == -1) {
  122. // get tite to split episode name and leave only "Episode xx"
  123. let title = jqPage.find("#leftside > div:nth-child(1) > div.barContent > div:nth-child(2) > a").text();
  124. let t = e.text.split(title)[1].substring(1).replace(/ 0+(?=\d+)/, " ");
  125. // prepend new object to array
  126. episodes.unshift({
  127. text:t,
  128. href:kissanime.anime + e.href.split("/Anime/")[1] + kissanime.server
  129. });
  130. }
  131. });
  132. // get time until next episode
  133. let timeMillis = parseInt(kissanime.regexCountdown.exec(resp.responseText));
  134. // callback to insert episodes in list
  135. putEpisodes(dataStream, episodes, timeMillis);
  136. }
  137. }
  138. });
  139. }
  140.  
  141. getEplistUrl["kissanime"] = function(partialUrl) {
  142. return kissanime.anime + partialUrl;
  143. }
  144.  
  145. searchSite["kissanime"] = function(id, title) {
  146. GM_xmlhttpRequest({
  147. method: "POST",
  148. data: "type=Anime" + "&keyword=" + title,
  149. url: kissanime.search,
  150. headers: { "Content-Type": "application/x-www-form-urlencoded" },
  151. onload: function(resp) {
  152. if (resp.status == 503) {
  153. // loading CF cookies
  154. kissanime_loadCookies(search["kissanime"], title);
  155. } else if (resp.status == 200) {
  156. // OK
  157. let results = [];
  158. // if there is only one result, kissanime redirects to the only result page
  159. if (resp.finalUrl.indexOf(kissanime.search) == -1) {
  160. // only one result
  161. results.push({
  162. title:title,
  163. href:resp.finalUrl.split("/")[4],
  164. fullhref:kissanime.anime + resp.finalUrl.split("/")[4]
  165. });
  166. } else {
  167. // multiple results
  168. let list = $(resp.response).find("#leftside > div > div.barContent > div:nth-child(2) > table > tbody > tr").slice(2);
  169. list.each(function() {
  170. let a = $(this).find("a")[0];
  171. results.push({
  172. title:a.text.replace(/\n\s+/, ""), // regex is used to remove leading whitespace
  173. href:a.pathname.split("/")[2],
  174. fullhref:kissanime.anime + a.pathname.split("/")[2]
  175. });
  176. })
  177. }
  178. // callback
  179. putResults(id, results, kissanime.base);
  180. }
  181. }
  182. });
  183. }
  184.  
  185. /* 9anime */
  186. /*******************************************************************************************************************************************************************/
  187. const nineanime = {};
  188. nineanime.base = "https://www5.9anime.is/";
  189. nineanime.anime = nineanime.base + "watch/";
  190. nineanime.search = nineanime.base + "search?keyword=";
  191. nineanime.server = "33"; // RapidVideo = 33, MyCloud = 28, Streamango = 34, OpenLoad = 24
  192.  
  193. getEpisodes["nineanime"] = function(dataStream, url) {
  194. GM_xmlhttpRequest({
  195. method: "GET",
  196. url: nineanime.anime + url,
  197. onload: function(resp) {
  198. if (resp.status == 200) {
  199. // OK
  200. let jqPage = $(resp.response);
  201. let episodes = [];
  202. // get servers
  203. let servers = jqPage.find("#main > div > div.widget.servers > div.widget-body > .server");
  204. let server = null;
  205. // if possibe use specified server
  206. servers.each(function() {
  207. if ($(this).attr("data-name") == nineanime.server) {
  208. server = $(this);
  209. }
  210. });
  211. // else use first one
  212. if (!server) {
  213. server = servers.first();
  214. }
  215. // get anchors
  216. let as = server.find("li > a");
  217. as.each(function() {
  218. episodes.push({
  219. text:"Episode " + $(this).text().replace(/^0+(?=\d+)/, ""),
  220. href:nineanime.base + $(this).attr("href").substr(1)
  221. });
  222. });
  223. // get time if available
  224. let time = jqPage.find("#main > div > div.alert.alert-primary > i");
  225. let timeMillis;
  226. if (time.length !== 0) {
  227. // timer is present
  228. timeMillis = time.data("to") * 1000 - Date.now();
  229. } else {
  230. // timer is not present, estimating based on latest episode
  231. let timeStr = as.last().data("title").replace("-", "");
  232. timeMillis = Date.parse(timeStr) + 1000 * 60 * 60 * 24 * 7 - Date.now();
  233. }
  234. // callback
  235. putEpisodes(dataStream, episodes, timeMillis);
  236. }
  237. }
  238. });
  239. }
  240.  
  241. getEplistUrl["nineanime"] = function(partialUrl) {
  242. return nineanime.anime + partialUrl;
  243. }
  244.  
  245. searchSite["nineanime"] = function(id, title) {
  246. GM_xmlhttpRequest({
  247. method: "GET",
  248. url: nineanime.search + encodeURI(title),
  249. onload: function(resp) {
  250. if (resp.status == 200) {
  251. // OK
  252. let jqPage = $(resp.response);
  253. let results = [];
  254. // get results from response
  255. let list = jqPage.find("#main > div > div:nth-child(1) > div.widget-body > div.film-list > .item");
  256. list = list.slice(0, 10);
  257. // add to results
  258. list.each(function() {
  259. let a = $(this).find("a")[1];
  260. results.push({
  261. title:a.text,
  262. href:a.href.split("/")[4],
  263. fullhref:a.href
  264. });
  265. });
  266. // callback
  267. putResults(id, results, nineanime.base);
  268. }
  269. }
  270. });
  271. }
  272.  
  273. /* MAL animelist */
  274. /*******************************************************************************************************************************************************************/
  275. pageLoad["list"] = function() {
  276. // own list
  277. if ($(".header-menu.other").length !== 0) return;
  278. // watching page
  279. if ($(".list-unit.watching").length !== 1) return;
  280.  
  281. // force hide more-info
  282. const styleSheet = document.createElement("style");
  283. styleSheet.innerHTML =`
  284. .list-table .more-info {
  285. display: none!important;
  286. }
  287. `;
  288. document.body.appendChild(styleSheet);
  289.  
  290. // expand more-info
  291. $(".more > a").each(function(i, e) {
  292. e.click();
  293. });
  294.  
  295. // add col to table
  296. $("#list-container").find("th.header-title.title").after("<th class='header-title stream'>Watch</th>");
  297. $(".list-item").each(function() {
  298. $(this).find(".data.title").after("<td class='data stream'></td>");
  299. });
  300.  
  301. // style
  302. $(".data.stream").css("font-weight", "normal");
  303. $(".data.stream").css("line-height", "1.5em");
  304. $(".header-title.stream").css("min-width", "90px");
  305.  
  306. // wait
  307. setTimeout(function() {
  308. // collapse more-info
  309. $(".more-info").css("display", "none");
  310. // remove sheet
  311. document.body.removeChild(styleSheet);
  312.  
  313. // put comment into data("comment")
  314. $(".list-item").each(function() {
  315. let comment = $(this).find(".td1.borderRBL").html().match(/Comments: ([\S ]+)(?=&nbsp;)/g);
  316. if (comment) {
  317. // revome the first 10 characters to remove "Comments: " since js doesn't support lookbehinds
  318. comment = comment.toString().substring(10);
  319. } else {
  320. comment = null;
  321. }
  322.  
  323. $(this).find(".data.stream").data("comment", comment);
  324. });
  325.  
  326. // load links
  327. $(".header-title.stream").trigger("click");
  328. }, 1000);
  329.  
  330. // event listeners
  331. // column header
  332. $(".header-title.stream").on("click", function() {
  333. $(".data.stream").each(function() {
  334. $(this).trigger("click");
  335. });
  336. });
  337.  
  338. // table cell
  339. $(".data.stream").on("click", function() {
  340. updateList($(this), true, true);
  341. });
  342.  
  343. // complete one episode
  344. $(".icon-add-episode").on("click", function() {
  345. let dataStream = $(this).parents(".list-item").find(".data.stream");
  346. updateList(dataStream, false, true);
  347. });
  348.  
  349. // update timer
  350. setInterval(function() {
  351. $(".data.stream").trigger("update-time");
  352. }, 1000);
  353. }
  354.  
  355. // updates dataStream cell
  356. function updateList(dataStream, forceReload, canReload) {
  357. // remove old divs
  358. dataStream.find(".nextep").remove();
  359. dataStream.find(".loading").remove();
  360. dataStream.find(".timer").remove();
  361. dataStream.off("update-time");
  362. // get episode list from data
  363. let episodeList = dataStream.data("episodeList");
  364. if (episodeList && episodeList.length > 0 && !forceReload) {
  365. // episode list exists
  366. updateList_exists(dataStream);
  367. } else if (canReload) {
  368. // episode list doesn't exist or needs to be reloaded
  369. updateList_doesntExist(dataStream);
  370. } else {
  371. // broken link
  372. dataStream.prepend("<font color='red'>Broken link</font><br>");
  373. }
  374. }
  375.  
  376. function updateList_exists(dataStream) {
  377. // listitem
  378. let listitem = dataStream.parents(".list-item");
  379. // get current episode number
  380. let currEp = parseInt(listitem.find(".data.progress").find(".link").text());
  381. if (isNaN(currEp)) currEp = 0;
  382. // get episodes from data
  383. let episodes = dataStream.data("episodeList");
  384. // create new nextep
  385. let nextep = $("<div class='nextep'></div>");
  386. // add new nextep
  387. dataStream.prepend(nextep);
  388.  
  389. if (episodes.length > currEp) {
  390. // there are episodes available
  391. let isAiring = listitem.find("span.content-status:contains('Airing')").length !== 0;
  392. let t = episodes[currEp].text;
  393.  
  394. let a = $("<a></a>");
  395. a.text(t.length > 13 ? t.substr(0, 12) + "&hellip;" : t);
  396. if (t.length > 13) a.attr("title", t);
  397. a.attr("href", episodes[currEp].href);
  398. a.attr("target", "_blank");
  399. a.attr("class", isAiring ? "airing" : "non-airing");
  400. a.css("color", isAiring ? "#2db039" : "#ff730a");
  401. nextep.append(a);
  402.  
  403. if (episodes.length - currEp > 1) {
  404. // if there is more than 1 new ep then put the amount in parenthesis
  405. nextep.append(" (" + (episodes.length - currEp) + ")");
  406. }
  407. } else if (currEp > episodes.length) {
  408. // user has watched too many episodes
  409. nextep.append($("<div class='.epcount-error'>Ep. count Error</div>").css("color", "red"));
  410. } else {
  411. // there aren't episodes available, displaying timer
  412. // add update-time event
  413. dataStream.on("update-time", function() {
  414. // get time from data
  415. let timeMillis = dataStream.data("timeMillis");
  416. let time;
  417. if (!timeMillis || isNaN(timeMillis) || timeMillis < 1000) {
  418. time = "Not Yet Aired";
  419. } else {
  420. const d = Math.floor(timeMillis / (1000 * 60 * 60 * 24));
  421. const h = Math.floor((timeMillis % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
  422. const m = Math.floor((timeMillis % (1000 * 60 * 60)) / (1000 * 60));
  423. time = (h < 10 ? "0"+h : h) + "h:" + (m < 10 ? "0" + m : m) + "m";
  424. if (d > 0) {
  425. time = d + (d == 1 ? " day " : " days ") + time;
  426. }
  427. // subtract time
  428. dataStream.data("timeMillis", timeMillis - 1000);
  429. // if 0 is reached, stop it
  430. if (timeMillis < 1000) {
  431. dataStream.removeData("timeMillis");
  432. }
  433. }
  434. // if timer doesn't exist create it, otherwise update it
  435. if (dataStream.find(".timer").length === 0) {
  436. dataStream.prepend("<div class='timer'>" + time + "<div>");
  437. } else {
  438. dataStream.find(".timer").html(time);
  439. }
  440. });
  441.  
  442. dataStream.trigger("update-time");
  443. }
  444. }
  445.  
  446. function updateList_doesntExist(dataStream) {
  447. // check if comment exists and is correct
  448. let comment = dataStream.data("comment");
  449. if (comment) {
  450. // comment exists
  451. // url is and array that contains the streaming service and url relative to that service
  452. let url = getUrlFromComment(comment);
  453. if (url) {
  454. // comment valid
  455. // add loading
  456. dataStream.prepend("<div class='loading'>Loading...</div>");
  457. // add eplist to dataStream
  458. if (dataStream.find(".eplist").length === 0) {
  459. let eplistUrl = getEplistUrl[url[0]](url[1]);
  460. dataStream.append("<a class='eplist' target='_blank' href='" + eplistUrl + "'>Ep. list</a>");
  461. }
  462. // executes getEpisodes relative to url[0] passing dataStream and url[1]
  463. getEpisodes[url[0]](dataStream, url[1]);
  464. } else {
  465. // comment invalid
  466. dataStream.append("Invalid Link")
  467. }
  468. } else {
  469. // comment doesn't extst
  470. dataStream.append("No Link");
  471. }
  472. }
  473.  
  474. // save episodeList and timeMillis inside .data.stream of listitem
  475. function putEpisodes(dataStream, episodes, timeMillis) {
  476. dataStream.data("episodeList", episodes);
  477. dataStream.data("timeMillis", timeMillis);
  478. updateList(dataStream, false, false);
  479. }
  480.  
  481. /* MAL edit anime */
  482. /*******************************************************************************************************************************************************************/
  483. pageLoad["edit"] = function() {
  484. // get title
  485. const title = $("#main-form > table:nth-child(1) > tbody > tr:nth-child(1) > td:nth-child(2) > strong > a")[0].text;
  486. // add #search div
  487. let search = $("<div id='search' style='width: 420px'><b style='font-size: 110%; line-height: 180%;'>Search: </b></div>");
  488. $("#add_anime_comments").after(search);
  489. // add streamingServices
  490. for (let i = 0 ; i < streamingServices.length; i++) {
  491. let ss = streamingServices[i];
  492. if (i !== 0) search.append(", ");
  493. // new anchor
  494. let a = $("<a></a>");
  495. a.text(ss.name);
  496. a.attr("href", "#");
  497. // on anchor click
  498. a.on("click", function() {
  499. // remove old results
  500. search.find(".site").remove();
  501. // add new result box
  502. search.append("<div class='site " + ss.id + "'><div id='searching'>Searching...</div></div>");
  503. // execute search
  504. searchSite[ss.id](ss.id, title);
  505. // return
  506. return false;
  507. });
  508. search.append(a);
  509. }
  510. search.append("<br>");
  511. }
  512.  
  513. function putResults(id, results, manualSearch) {
  514. let siteDiv = $("#search").find("." + id);
  515. // if div with current id cant be found then don't add results
  516. if (siteDiv.length !== 0) {
  517. siteDiv.find("#searching").remove();
  518.  
  519. if (results.length === 0) {
  520. siteDiv.append("No Results. <a target='_blank' href='" + manualSearch + "'>Manual Search</a></div>");
  521. return;
  522. }
  523. // add results
  524. for (let i = 0; i < results.length; i++) {
  525. let r = results[i];
  526. let a = $("<a href='#'>Select</a>");
  527. a.on("click", function() {
  528. $("#add_anime_comments").val(id + " " + r.href);
  529. return false;
  530. });
  531. siteDiv.append("(").append(a).append(") ").append("<a target='_blank' href='" + r.fullhref + "'>" + r.title + "</a>").append("<br>");
  532. }
  533. }
  534. }
  535.  
  536. /* main */
  537. /*******************************************************************************************************************************************************************/
  538. (function($) {
  539. if (window.location.href == kissanime.base) {
  540. pageLoad["kissanime"]();
  541. } else if (window.location.href.indexOf("https://myanimelist.net/animelist/") != -1) {
  542. pageLoad["list"]();
  543. } else {
  544. pageLoad["edit"]();
  545. }
  546. })(jQuery);