A Chrome extension that gives you finer control over MyAnimeList.net scores
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

443 lignes
15 KiB

  1. /* -------------------------------- Globals -------------------------------- */
  2. var MAX_BUCKETS = 256;
  3. var LOADING_IMG = '<img src="http://cdn.myanimelist.net/images/xmlhttp-loader.gif" align="center">';
  4. var should_sort = window.location.href.indexOf("order=4") != -1;
  5. /* ------------------------ Miscellaneous functions ------------------------ */
  6. String.prototype.cut_after = function(substr) {
  7. return this.substr(this.indexOf(substr) + substr.length)
  8. };
  9. String.prototype.cut_before = function(substr) {
  10. return this.substr(0, this.indexOf(substr));
  11. };
  12. function get_anime_id_from_href(href) {
  13. var anime_id;
  14. if (href.indexOf("/anime/") != -1)
  15. anime_id = href.cut_after("/anime/");
  16. else
  17. anime_id = href.cut_after("id=");
  18. if (anime_id.indexOf("/") != -1)
  19. anime_id = anime_id.cut_before("/");
  20. if (anime_id.indexOf("&") != -1)
  21. anime_id = anime_id.cut_before("&");
  22. return anime_id;
  23. }
  24. function get_edit_id_from_href(href) {
  25. var anime_id = href.cut_after("id=");
  26. if (anime_id.indexOf("&") != -1)
  27. anime_id = anime_id.cut_before("&");
  28. return anime_id;
  29. }
  30. function get_score_from_element(elem) {
  31. var score = Math.round(elem.val() * 10) / 10;
  32. if (isNaN(score) || ((score < 1 || score > 10) && score != 0)) {
  33. alert("Invalid score: must be a number between 1.0 and 10.0, or 0.");
  34. return null;
  35. }
  36. if (score == Math.round(score))
  37. score += ".0"
  38. return score;
  39. }
  40. /* --------------------------- Storage functions --------------------------- */
  41. function save_score(anime_id, score) {
  42. var bucket_id = (parseInt(anime_id) % MAX_BUCKETS).toString();
  43. chrome.storage.sync.get(bucket_id, function(data) {
  44. var bucket = data[bucket_id];
  45. if (bucket === undefined)
  46. bucket = data[bucket_id] = {};
  47. bucket[anime_id] = score;
  48. chrome.storage.sync.set(data);
  49. });
  50. }
  51. function retrieve_scores(anime_id, callback) {
  52. var bucket_id = null;
  53. if (anime_id !== null)
  54. bucket_id = (parseInt(anime_id) % MAX_BUCKETS).toString();
  55. chrome.storage.sync.get(bucket_id, function(data) {
  56. if (anime_id !== null) {
  57. var bucket = data[bucket_id];
  58. if (bucket !== undefined && bucket[anime_id] !== undefined)
  59. callback(bucket[anime_id]);
  60. else
  61. callback(null);
  62. }
  63. else
  64. callback(data);
  65. });
  66. }
  67. function remove_score(anime_id) {
  68. var bucket_id = (parseInt(anime_id) % MAX_BUCKETS).toString();
  69. chrome.storage.sync.get(bucket_id, function(data) {
  70. var bucket = data[bucket_id];
  71. if (bucket === undefined || bucket[anime_id] === undefined)
  72. return;
  73. delete bucket[anime_id];
  74. if ($.isEmptyObject(bucket))
  75. chrome.storage.sync.remove(bucket_id);
  76. else
  77. chrome.storage.sync.set(data);
  78. });
  79. }
  80. /* ----------------------- Event patches/injections ------------------------ */
  81. function update_list_score(anime_id) {
  82. var new_score = get_score_from_element($("#scoretext" + anime_id));
  83. if (new_score === null)
  84. return;
  85. var payload = {id: anime_id, score: Math.round(new_score)};
  86. $("#scorebutton" + anime_id).prop("disabled", true);
  87. $.post("/includes/ajax.inc.php?t=63", payload, function(data) {
  88. $("#scoreval" + anime_id).text(new_score == 0 ? "-" : new_score);
  89. $("#scoretext" + anime_id).val("");
  90. $("#scorediv" + anime_id).css("display", "none");
  91. $("#scorebutton" + anime_id).prop("disabled", false);
  92. if (should_sort)
  93. sort_list();
  94. update_list_stats();
  95. });
  96. save_score(anime_id, new_score);
  97. }
  98. function update_anime_score(anime_id, is_new) {
  99. var new_score = get_score_from_element($("#myinfo_score"));
  100. if (new_score === null)
  101. return;
  102. var t_id, payload = {score: Math.round(new_score)};
  103. payload["status"] = $("#myinfo_status").val();
  104. payload["epsseen"] = $("#myinfo_watchedeps").val();
  105. if (is_new) {
  106. payload["aid"] = anime_id;
  107. t_id = "61";
  108. }
  109. else {
  110. payload["alistid"] = anime_id;
  111. payload["aid"] = $("#myinfo_anime_id").val();
  112. payload["astatus"] = $("#myinfo_curstatus").val();
  113. t_id = "62";
  114. }
  115. $("#myinfoDisplay").html(LOADING_IMG);
  116. $.post("/includes/ajax.inc.php?t=" + t_id, payload, function(data) {
  117. if (is_new) {
  118. $("#myinfoDisplay").html("");
  119. $("#addtolist").html(data);
  120. }
  121. else
  122. $("#myinfoDisplay").html(data);
  123. });
  124. save_score(anime_id, new_score);
  125. }
  126. function submit_add_form(submit_button) {
  127. var anime_id = $("input[name='series_title']").val();
  128. if (!anime_id)
  129. return submit_button[0].click();
  130. var new_score = get_score_from_element($("#score_input"));
  131. if (new_score === null)
  132. return;
  133. $("select[name='score']").val(Math.round(new_score));
  134. save_score(anime_id, new_score);
  135. submit_button[0].click();
  136. }
  137. function submit_edit_form(anime_id, submit_type, submit_button) {
  138. if (submit_type == 2) {
  139. var new_score = get_score_from_element($("#score_input"));
  140. if (new_score === null)
  141. return;
  142. $("select[name='score']").val(Math.round(new_score));
  143. save_score(anime_id, new_score);
  144. }
  145. else if (submit_type == 3)
  146. remove_score(anime_id);
  147. submit_button[0].click();
  148. }
  149. /* ------------------------ List stats and sorting ------------------------- */
  150. function compare(row1, row2) {
  151. var r1 = $(row1).find("span[id^='scoreval']").text(),
  152. r2 = $(row2).find("span[id^='scoreval']").text();
  153. if (r1 == r2) {
  154. r1 = $(row1).find("a.animetitle span").text();
  155. r2 = $(row2).find("a.animetitle span").text();
  156. return r1 > r2 ? 1 : -1;
  157. }
  158. if (r1 == "-")
  159. return 1;
  160. if (r2 == "-")
  161. return -1;
  162. return r2 - r1;
  163. }
  164. function prepare_list() {
  165. var headers = [".header_cw", ".header_completed", ".header_onhold",
  166. ".header_dropped", ".header_ptw"];
  167. $.each(headers, function(i, header) {
  168. $(header).next()
  169. .nextUntil($(".category_totals").closest("table"))
  170. .wrapAll('<div class="list-chart-group"/>');
  171. });
  172. $(".list-chart-group table").each(function(i, row) {
  173. $(row).add($(row).next())
  174. .wrapAll('<div class="list-chart-row"/>');
  175. });
  176. }
  177. function sort_list() {
  178. $(".list-chart-group").each(function(i, group) {
  179. $(group).find(".list-chart-row").sort(compare).each(function(i, row) {
  180. $(group).append(row);
  181. });
  182. $(group).find(".list-chart-row").each(function(i, row) {
  183. $(row).find("tr").first().children().first().text(i + 1);
  184. $(row).find((i % 2) ? ".td1" : ".td2").toggleClass("td1 td2");
  185. });
  186. });
  187. }
  188. function apply_stats(elem, old_sum, new_sum, nums) {
  189. var old_score = elem.text().cut_after("Score: ").cut_before(",");
  190. var old_dev = elem.text().cut_after("Dev.: ").cut_before("\n");
  191. var mean, deviation;
  192. mean = Math.round(new_sum / nums * 10) / 10 || 0;
  193. if (mean == Math.round(mean))
  194. mean += ".0";
  195. deviation = (new_sum - old_sum) / nums + parseFloat(old_dev) || 0;
  196. deviation = Math.round(deviation * 100) / 100;
  197. elem.text(elem.text()
  198. .replace("Score: " + old_score, "Score: " + mean)
  199. .replace("Dev.: " + old_dev, "Dev.: " + deviation));
  200. }
  201. function update_list_stats() {
  202. var old_sum_all = 0, new_sum_all = 0, nums_all = 0;
  203. $(".category_totals").each(function(i, totals) {
  204. var group = $(totals).closest("table").prev();
  205. var old_sum = 0, new_sum = 0, nums = 0;
  206. group.find("span[id^='scoreval']").each(function(j, elem) {
  207. if ($(elem).text() != "-") {
  208. old_sum += parseFloat($(elem).next().text());
  209. new_sum += parseFloat($(elem).text());
  210. nums++;
  211. }
  212. });
  213. apply_stats($(totals), old_sum, new_sum, nums);
  214. old_sum_all += old_sum;
  215. new_sum_all += new_sum;
  216. nums_all += nums;
  217. });
  218. if ($("#grand_totals").length > 0)
  219. apply_stats($("#grand_totals"), old_sum_all, new_sum_all, nums_all);
  220. }
  221. /* ---------------------------- Extension hooks ---------------------------- */
  222. function hook_list() {
  223. retrieve_scores(null, function(data) {
  224. $("span[id^='scoreval']").each(function(i, elem) {
  225. var anime_id = elem.id.split("scoreval")[1];
  226. var bucket_id = (parseInt(anime_id) % MAX_BUCKETS).toString();
  227. var bucket = data[bucket_id];
  228. $(elem).after($("<span>")
  229. .css("display", "none").text($(elem).text()));
  230. if (bucket !== undefined && bucket[anime_id] !== undefined)
  231. $(elem).text(bucket[anime_id] == 0 ? "-" : bucket[anime_id]);
  232. else {
  233. var current = parseInt($(elem).text());
  234. if (!isNaN(current))
  235. $(elem).text(current + ".0");
  236. }
  237. $("#scorediv" + anime_id)
  238. .after($("<div>")
  239. .attr("id", "scorediv" + anime_id)
  240. .css("display", "none")
  241. .append($('<input>')
  242. .attr("type", "text")
  243. .attr("id", "scoretext" + anime_id)
  244. .attr("size", "2")
  245. .keydown(function(a_id) {
  246. return function(ev) {
  247. if ((window.event ? window.event.keyCode : ev.which) == 13)
  248. update_list_score(a_id);
  249. else
  250. return true;
  251. }
  252. }(anime_id)))
  253. .append($("<input>")
  254. .attr("type", "button")
  255. .attr("id", "scorebutton" + anime_id)
  256. .attr("value", "Go")
  257. .click(function(a_id) {
  258. return function() { return update_list_score(a_id); }
  259. }(anime_id))))
  260. .remove();
  261. });
  262. prepare_list();
  263. if (should_sort)
  264. sort_list();
  265. update_list_stats();
  266. });
  267. }
  268. function hook_anime(anime_id) {
  269. retrieve_scores(anime_id, function(score) {
  270. var old_input = $("#myinfo_score");
  271. var old_button = $("input[name='myinfo_submit']");
  272. var is_new = old_button.attr("value") == "Add";
  273. if (!is_new && score === null)
  274. score = parseInt(old_input.val()) + ".0";
  275. old_input.after($("<span> / 10.0</span>"))
  276. .after($("<input>")
  277. .attr("type", "text")
  278. .attr("id", "myinfo_score")
  279. .attr("name", "myinfo_score")
  280. .attr("class", "inputtext")
  281. .attr("value", (score === null || score == 0) ? "" : score)
  282. .attr("size", "3"))
  283. .remove();
  284. old_button.after($("<input>")
  285. .attr("type", "button")
  286. .attr("name", "myinfo_submit")
  287. .attr("value", old_button.attr("value"))
  288. .attr("class", "inputButton")
  289. .click(function(a_id, is_new) {
  290. return function() { return update_anime_score(a_id, is_new); }
  291. }(anime_id, is_new)))
  292. .remove();
  293. });
  294. }
  295. function hook_add() {
  296. var old_input = $("select[name='score']");
  297. var old_submit = $("input[type='button'][onclick='checkValidSubmit(1)']");
  298. old_input.after($("<span> / 10.0</span>"))
  299. .after($("<input>")
  300. .attr("type", "text")
  301. .attr("id", "score_input")
  302. .attr("class", "inputtext")
  303. .attr("size", "3"))
  304. .hide();
  305. old_submit.after($("<input>")
  306. .attr("type", "button")
  307. .attr("class", "inputButton")
  308. .attr("style", old_submit.attr("style"))
  309. .attr("value", old_submit.attr("value"))
  310. .click(function(button) {
  311. return function() { return submit_add_form(button); }
  312. }(old_submit)))
  313. .hide();
  314. }
  315. function hook_edit(anime_id) {
  316. retrieve_scores(anime_id, function(score) {
  317. var old_input = $("select[name='score']");
  318. var old_edit = $("input[type='button'][onclick='checkValidSubmit(2)']");
  319. var old_delete = $("input[type='button'][onclick='checkValidSubmit(3)']");
  320. if (score === null)
  321. score = parseInt(old_input.val()) + ".0";
  322. old_input.after($("<span> / 10.0</span>"))
  323. .after($("<input>")
  324. .attr("type", "text")
  325. .attr("id", "score_input")
  326. .attr("class", "inputtext")
  327. .attr("value", score == 0 ? "" : score)
  328. .attr("size", "3"))
  329. .hide();
  330. old_edit.after($("<input>")
  331. .attr("type", "button")
  332. .attr("class", "inputButton")
  333. .attr("style", old_edit.attr("style"))
  334. .attr("value", old_edit.attr("value"))
  335. .click(function(a_id, button) {
  336. return function() { return submit_edit_form(a_id, 2, button); }
  337. }(anime_id, old_edit)))
  338. .hide();
  339. old_delete.after($("<input>")
  340. .attr("type", "button")
  341. .attr("class", "inputButton")
  342. .attr("value", old_delete.attr("value"))
  343. .click(function(a_id, button) {
  344. return function() { return submit_edit_form(a_id, 3, button); }
  345. }(anime_id, old_delete)))
  346. .hide();
  347. });
  348. }
  349. function hook_shared() {
  350. }
  351. function hook_addtolist() {
  352. /* TODO: this entry point is unimplemented - it's rarely used and difficult
  353. to inject into, so I'm avoiding it for now. */
  354. $("<p><b>Note:</b> For the time being, anime added through this " +
  355. "interface cannot be given scores on the 10.0-point scale (the old " +
  356. "10-point system is used).</p><p>To give a more specific number, " +
  357. "simply add the anime here, then go to its own page or to your list " +
  358. "page, and update the score.</p>").insertAfter($("#stype").parent());
  359. }
  360. /* ------------------------------- Main hook ------------------------------- */
  361. $(document).ready(function() {
  362. var href = window.location.href;
  363. if (href.indexOf("/animelist/") != -1) {
  364. var list_info = $("#mal_cs_otherlinks div:first");
  365. if (list_info.text() == "You are viewing your anime list")
  366. hook_list();
  367. }
  368. else if ($("#malLogin").length == 0) {
  369. if (href.indexOf("/anime/") != -1 || href.indexOf("/anime.php") != -1)
  370. hook_anime(get_anime_id_from_href(href));
  371. else if (href.indexOf("/panel.php") != -1 && href.indexOf("go=add") != -1)
  372. hook_add();
  373. else if (href.indexOf("/editlist.php") != -1 && href.indexOf("type=anime") != -1)
  374. hook_edit(get_edit_id_from_href(href));
  375. else if (href.indexOf("/shared.php") != -1)
  376. hook_shared();
  377. else if (href.indexOf("/addtolist.php") != -1)
  378. hook_addtolist();
  379. }
  380. });