A Chrome extension that gives you finer control over MyAnimeList.net scores
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. /* Decimal Scores for MyAnimeList
  2. Copyright (C) 2014 Ben Kurtovic <ben.kurtovic@gmail.com>
  3. Distributed under the terms of the MIT License. See the LICENSE file for
  4. details.
  5. */
  6. /* -------------------------------- Globals -------------------------------- */
  7. var MAX_BUCKETS = 256;
  8. var LOADING_IMG = '<img src="http://cdn.myanimelist.net/images/xmlhttp-loader.gif" align="center">';
  9. /* ------------------------ Miscellaneous functions ------------------------ */
  10. /* Note: complaints about modifying objects we don't own are ignored since
  11. these changes are only executed within the context of our own extension. */
  12. String.prototype.contains = function(substr) {
  13. return this.indexOf(substr) != -1;
  14. };
  15. String.prototype.cut = function(after, before) {
  16. var str = this;
  17. if (str.contains(after)) {
  18. str = str.substr(str.indexOf(after) + after.length);
  19. if (str.contains(before))
  20. str = str.substr(0, str.indexOf(before));
  21. }
  22. return str;
  23. };
  24. function round_score(num) {
  25. num = Math.round(num * 10) / 10;
  26. if (isNaN(num))
  27. return num;
  28. if (num == Math.round(num))
  29. num += ".0";
  30. return num;
  31. }
  32. function get_score_from_element(elem) {
  33. var score = round_score(elem.val());
  34. if (isNaN(score) || ((score < 1 || score > 10) && score != 0)) {
  35. alert("Invalid score: must be a number between 1.0 and 10.0, or 0.");
  36. return null;
  37. }
  38. return score;
  39. }
  40. function load_score_into_element(data, anime_id, elem) {
  41. var bucket = data[(parseInt(anime_id) % MAX_BUCKETS).toString()];
  42. if (bucket !== undefined && bucket[anime_id] !== undefined)
  43. elem.text(bucket[anime_id] == 0 ? "-" : bucket[anime_id]);
  44. else {
  45. var current = parseInt(elem.text());
  46. if (!isNaN(current))
  47. elem.text(current + ".0");
  48. }
  49. }
  50. function update_shared_row_colors(row, our_pos) {
  51. var our_cell = $(row.find("td")[our_pos]);
  52. var their_cell = $(row.find("td")[our_pos == 1 ? 2 : 1]);
  53. var diff = our_cell.text() - their_cell.text();
  54. if (!diff) {
  55. row.find("td").css("background-color", "#f6f6f6");
  56. our_cell.add(their_cell).find("span").css("color", "");
  57. }
  58. else {
  59. row.find("td").css("background-color", "");
  60. our_cell.css("color", diff > 0 ? "#FF0000" : "#0000FF");
  61. their_cell.css("color", diff > 0 ? "#0000FF" : "#FF0000");
  62. }
  63. }
  64. /* --------------------------- Storage functions --------------------------- */
  65. function save_score(anime_id, score) {
  66. var bucket_id = (parseInt(anime_id) % MAX_BUCKETS).toString();
  67. chrome.storage.sync.get(bucket_id, function(data) {
  68. var bucket = data[bucket_id];
  69. if (bucket === undefined)
  70. bucket = data[bucket_id] = {};
  71. bucket[anime_id] = score;
  72. chrome.storage.sync.set(data);
  73. });
  74. }
  75. function retrieve_scores(anime_id, callback) {
  76. var bucket_id = null;
  77. if (anime_id !== null)
  78. bucket_id = (parseInt(anime_id) % MAX_BUCKETS).toString();
  79. chrome.storage.sync.get(bucket_id, function(data) {
  80. if (anime_id !== null) {
  81. var bucket = data[bucket_id];
  82. if (bucket !== undefined && bucket[anime_id] !== undefined)
  83. callback(bucket[anime_id]);
  84. else
  85. callback(null);
  86. }
  87. else
  88. callback(data);
  89. });
  90. }
  91. function remove_score(anime_id) {
  92. var bucket_id = (parseInt(anime_id) % MAX_BUCKETS).toString();
  93. chrome.storage.sync.get(bucket_id, function(data) {
  94. var bucket = data[bucket_id];
  95. if (bucket === undefined || bucket[anime_id] === undefined)
  96. return;
  97. delete bucket[anime_id];
  98. if ($.isEmptyObject(bucket))
  99. chrome.storage.sync.remove(bucket_id);
  100. else
  101. chrome.storage.sync.set(data);
  102. });
  103. }
  104. function export_scores() {
  105. chrome.storage.sync.get(null, function(dat) {
  106. var blob = new Blob([JSON.stringify(dat)], {type: "application/json"});
  107. $($("<a>")
  108. .attr("href", window.URL.createObjectURL(blob))
  109. .attr("download", "animelist_decimal_scores.json")
  110. .hide()
  111. .appendTo($("body"))[0].click()).remove();
  112. });
  113. }
  114. function validate_score_data(data) {
  115. if (!$.isPlainObject(data))
  116. throw "invalid data type: " + data;
  117. if (JSON.stringify(data).length > chrome.storage.sync.QUOTA_BYTES)
  118. throw "file too large";
  119. for (var bucket_id in data) {
  120. if (data.hasOwnProperty(bucket_id)) {
  121. if (isNaN(parseInt(bucket_id)) || bucket_id >= MAX_BUCKETS)
  122. throw "invalid bucket ID: " + bucket_id;
  123. var bucket = data[bucket_id];
  124. if (!$.isPlainObject(bucket))
  125. throw "invalid bucket type: " + bucket;
  126. for (var anime_id in bucket) {
  127. if (data.hasOwnProperty(anime_id)) {
  128. if (isNaN(parseInt(anime_id)))
  129. throw "invalid anime ID: " + anime_id;
  130. if (parseInt(anime_id) % MAX_BUCKETS != bucket_id)
  131. throw "anime is in the wrong bucket: " + anime_id;
  132. var score = parseFloat(bucket[anime_id]);
  133. if (isNaN(score))
  134. throw "score is not a number: " + score;
  135. if ((score < 1 || score > 10) && score != 0)
  136. throw "score out of range: " + score;
  137. }
  138. }
  139. }
  140. }
  141. }
  142. function import_scores(data, callback) {
  143. validate_score_data(data);
  144. chrome.storage.sync.clear(function() {
  145. chrome.storage.sync.set(data, callback);
  146. });
  147. }
  148. /* ----------------------- Event patches/injections ------------------------ */
  149. function update_list_score(anime_id) {
  150. var new_score = get_score_from_element($("#scoretext" + anime_id));
  151. if (new_score === null)
  152. return;
  153. var payload = {id: anime_id, score: Math.round(new_score)};
  154. $("#scorebutton" + anime_id).prop("disabled", true);
  155. $.post("/includes/ajax.inc.php?t=63", payload, function(data) {
  156. $("#scoreval" + anime_id).text(new_score == 0 ? "-" : new_score);
  157. $("#scoretext" + anime_id).val("");
  158. $("#scorediv" + anime_id).css("display", "none");
  159. $("#scorebutton" + anime_id).prop("disabled", false);
  160. sort_list();
  161. update_list_stats();
  162. });
  163. save_score(anime_id, new_score);
  164. }
  165. function update_anime_score(anime_id, is_new) {
  166. var new_score = get_score_from_element($("#myinfo_score"));
  167. if (new_score === null)
  168. return;
  169. var t_id, payload = {score: Math.round(new_score)};
  170. payload["status"] = $("#myinfo_status").val();
  171. payload["epsseen"] = $("#myinfo_watchedeps").val();
  172. if (is_new) {
  173. payload["aid"] = anime_id;
  174. t_id = "61";
  175. }
  176. else {
  177. payload["alistid"] = anime_id;
  178. payload["aid"] = $("#myinfo_anime_id").val();
  179. payload["astatus"] = $("#myinfo_curstatus").val();
  180. t_id = "62";
  181. }
  182. $("#myinfoDisplay").html(LOADING_IMG);
  183. $.post("/includes/ajax.inc.php?t=" + t_id, payload, function(data) {
  184. if (is_new) {
  185. $("#myinfoDisplay").html("");
  186. $("#addtolist").html(data);
  187. }
  188. else
  189. $("#myinfoDisplay").html(data);
  190. });
  191. save_score(anime_id, new_score);
  192. }
  193. function submit_add_form(submit_button) {
  194. var anime_id = $("input[name='series_title']").val();
  195. if (!anime_id)
  196. return submit_button[0].click();
  197. var new_score = get_score_from_element($("#score_input"));
  198. if (new_score === null)
  199. return;
  200. $("select[name='score']").val(Math.round(new_score));
  201. save_score(anime_id, new_score);
  202. submit_button[0].click();
  203. }
  204. function submit_edit_form(anime_id, submit_type, submit_button) {
  205. if (submit_type == 2) {
  206. var new_score = get_score_from_element($("#score_input"));
  207. if (new_score === null)
  208. return;
  209. $("select[name='score']").val(Math.round(new_score));
  210. save_score(anime_id, new_score);
  211. }
  212. else if (submit_type == 3)
  213. remove_score(anime_id);
  214. submit_button[0].click();
  215. }
  216. /* ------------------------ List stats and sorting ------------------------- */
  217. function compare_scores(row1, row2) {
  218. var r1 = $(row1).find("span[id^='scoreval']").text(),
  219. r2 = $(row2).find("span[id^='scoreval']").text();
  220. if (r1 == r2) {
  221. r1 = $(row1).find("a.animetitle span").text();
  222. r2 = $(row2).find("a.animetitle span").text();
  223. return r1 > r2 ? 1 : -1;
  224. }
  225. if (r1 == "-")
  226. return 1;
  227. if (r2 == "-")
  228. return -1;
  229. return r2 - r1;
  230. }
  231. function extract_progress(td) {
  232. var text = td.text();
  233. if (text.contains("/"))
  234. return [text.substr(0, text.indexOf("/")), text.cut("/", " ")];
  235. else
  236. return [text, text];
  237. }
  238. function compare_progress(row1, row2) {
  239. var header = $(row1).parent().prev();
  240. var column = header.find("td")
  241. .index(header.find("a:contains('Progress')").closest("td"));
  242. var r1 = extract_progress($($(row1).find("td")[column])),
  243. r2 = extract_progress($($(row2).find("td")[column]));
  244. if (r1[0] == r2[0])
  245. return r2[1] - r1[1];
  246. return (r2[0] == "-" ? 0 : r2[0]) - (r1[0] == "-" ? 0 : r1[0]);
  247. }
  248. function prepare_list() {
  249. var headers = [".header_cw", ".header_completed", ".header_onhold",
  250. ".header_dropped", ".header_ptw"];
  251. $.each(headers, function(i, header) {
  252. $(header).next()
  253. .nextUntil($(".category_totals").closest("table"))
  254. .wrapAll('<div class="list-chart-group"/>');
  255. });
  256. $(".list-chart-group table").each(function(i, row) {
  257. $(row).add($(row).next())
  258. .wrapAll('<div class="list-chart-row"/>');
  259. });
  260. $(".category_totals, #grand_totals").each(function(i, totals) {
  261. var text = $(totals).text();
  262. $(totals).empty()
  263. .append($("<span>").text(text))
  264. .append($("<span>").hide().text(text.cut("Score Dev.: ", "\n")));
  265. });
  266. }
  267. function sort_list() {
  268. var order = parseInt(window.location.href.cut("order=", "&")), cmp_func;
  269. switch (order) {
  270. case 4:
  271. cmp_func = compare_scores; break;
  272. case 12:
  273. cmp_func = compare_progress; break;
  274. default:
  275. return;
  276. }
  277. $(".list-chart-group").each(function(i, group) {
  278. $(group).find(".list-chart-row").sort(cmp_func).each(function(i, row) {
  279. $(group).append(row);
  280. });
  281. $(group).find(".list-chart-row").each(function(i, row) {
  282. $(row).find("tr").first().children().first().text(i + 1);
  283. $(row).find((i % 2) ? ".td1" : ".td2").toggleClass("td1 td2");
  284. });
  285. });
  286. }
  287. function apply_stats(elem, old_sum, new_sum, nums) {
  288. var text = elem.find(":first").text();
  289. var mean = round_score(new_sum / nums) || "0.0";
  290. var dev = parseFloat(elem.find(":first").next().text());
  291. dev = Math.round(((new_sum - old_sum) / nums + dev || 0) * 100) / 100;
  292. elem.find(":first").text(text
  293. .replace("Score: " + text.cut("Score: ", ","), "Score: " + mean)
  294. .replace("Dev.: " + text.cut("Dev.: ", "\n"), "Dev.: " + dev));
  295. }
  296. function update_list_stats() {
  297. var old_sum_all = 0, new_sum_all = 0, nums_all = 0;
  298. $(".category_totals").each(function(i, totals) {
  299. var group = $(totals).closest("table").prev();
  300. var old_sum = 0, new_sum = 0, nums = 0;
  301. group.find("span[id^='scoreval']").each(function(j, elem) {
  302. if ($(elem).text() != "-") {
  303. old_sum += parseFloat($(elem).next().text());
  304. new_sum += parseFloat($(elem).text());
  305. nums++;
  306. }
  307. });
  308. apply_stats($(totals), old_sum, new_sum, nums);
  309. old_sum_all += old_sum;
  310. new_sum_all += new_sum;
  311. nums_all += nums;
  312. });
  313. if ($("#grand_totals").length > 0)
  314. apply_stats($("#grand_totals"), old_sum_all, new_sum_all, nums_all);
  315. }
  316. /* ---------------------------- Extension hooks ---------------------------- */
  317. function hook_list() {
  318. retrieve_scores(null, function(data) {
  319. $("span[id^='scoreval']").each(function(i, elem) {
  320. var anime_id = elem.id.split("scoreval")[1];
  321. $(elem).after($("<span>").hide().text($(elem).text()));
  322. load_score_into_element(data, anime_id, $(elem));
  323. $("#scorediv" + anime_id)
  324. .after($("<div>")
  325. .attr("id", "scorediv" + anime_id)
  326. .hide()
  327. .append($('<input>')
  328. .attr("type", "text")
  329. .attr("id", "scoretext" + anime_id)
  330. .attr("size", "2")
  331. .keydown(function(a_id) {
  332. return function(ev) {
  333. if ((window.event ? window.event.keyCode : ev.which) == 13)
  334. update_list_score(a_id);
  335. else
  336. return true;
  337. }
  338. }(anime_id)))
  339. .append($("<input>")
  340. .attr("type", "button")
  341. .attr("id", "scorebutton" + anime_id)
  342. .attr("value", "Go")
  343. .click(function(a_id) {
  344. return function() { return update_list_score(a_id); }
  345. }(anime_id))))
  346. .remove();
  347. });
  348. prepare_list();
  349. sort_list();
  350. update_list_stats();
  351. });
  352. }
  353. function hook_anime(anime_id) {
  354. retrieve_scores(anime_id, function(score) {
  355. var old_input = $("#myinfo_score");
  356. var old_button = $("input[name='myinfo_submit']");
  357. var is_new = old_button.attr("value") == "Add";
  358. if (!is_new && score === null)
  359. score = parseInt(old_input.val()) + ".0";
  360. old_input.after($("<span> / 10.0</span>"))
  361. .after($("<input>")
  362. .attr("type", "text")
  363. .attr("id", "myinfo_score")
  364. .attr("name", "myinfo_score")
  365. .attr("class", "inputtext")
  366. .attr("value", (score === null || score == 0) ? "" : score)
  367. .attr("size", "3"))
  368. .remove();
  369. old_button.after($("<input>")
  370. .attr("type", "button")
  371. .attr("name", "myinfo_submit")
  372. .attr("value", old_button.attr("value"))
  373. .attr("class", "inputButton")
  374. .click(function(a_id, is_new) {
  375. return function() { return update_anime_score(a_id, is_new); }
  376. }(anime_id, is_new)))
  377. .remove();
  378. });
  379. }
  380. function hook_add() {
  381. var old_input = $("select[name='score']");
  382. var old_submit = $("input[type='button'][onclick='checkValidSubmit(1)']");
  383. old_input.after($("<span> / 10.0</span>"))
  384. .after($("<input>")
  385. .attr("type", "text")
  386. .attr("id", "score_input")
  387. .attr("class", "inputtext")
  388. .attr("size", "3"))
  389. .hide();
  390. old_submit.after($("<input>")
  391. .attr("type", "button")
  392. .attr("class", "inputButton")
  393. .attr("style", old_submit.attr("style"))
  394. .attr("value", old_submit.attr("value"))
  395. .click(function(button) {
  396. return function() { return submit_add_form(button); }
  397. }(old_submit)))
  398. .hide();
  399. }
  400. function hook_edit(anime_id) {
  401. retrieve_scores(anime_id, function(score) {
  402. var old_input = $("select[name='score']");
  403. var old_edit = $("input[type='button'][onclick='checkValidSubmit(2)']");
  404. var old_delete = $("input[type='button'][onclick='checkValidSubmit(3)']");
  405. if (score === null)
  406. score = parseInt(old_input.val()) + ".0";
  407. old_input.after($("<span> / 10.0</span>"))
  408. .after($("<input>")
  409. .attr("type", "text")
  410. .attr("id", "score_input")
  411. .attr("class", "inputtext")
  412. .attr("value", score == 0 ? "" : score)
  413. .attr("size", "3"))
  414. .hide();
  415. old_edit.after($("<input>")
  416. .attr("type", "button")
  417. .attr("class", "inputButton")
  418. .attr("style", old_edit.attr("style"))
  419. .attr("value", old_edit.attr("value"))
  420. .click(function(a_id, button) {
  421. return function() { return submit_edit_form(a_id, 2, button); }
  422. }(anime_id, old_edit)))
  423. .hide();
  424. old_delete.after($("<input>")
  425. .attr("type", "button")
  426. .attr("class", "inputButton")
  427. .attr("value", old_delete.attr("value"))
  428. .click(function(a_id, button) {
  429. return function() { return submit_edit_form(a_id, 3, button); }
  430. }(anime_id, old_delete)))
  431. .hide();
  432. });
  433. }
  434. function hook_shared() {
  435. var our_profile = $("#nav a:first").attr("href"), our_pos;
  436. var profile_links = $("#content h2:first").find("a").slice(1);
  437. var shared_table = $("#content h2:first").next(), unique_table;
  438. var shared_means = shared_table.find("tr:nth-last-child(2)");
  439. var mean_score, mean_diff;
  440. if ($(profile_links[0]).attr("href") == our_profile)
  441. our_pos = 1;
  442. else if ($(profile_links[1]).attr("href") == our_profile)
  443. our_pos = 2;
  444. else
  445. return;
  446. retrieve_scores(null, function(data) {
  447. var score_sum = 0, diff_sum = 0, score_nums = 0, diff_nums = 0;
  448. shared_table.find("tr").slice(1, -2).each(function(i, row) {
  449. var anime_id = $(row).find("a").attr("href").cut("/anime/", "/");
  450. var our_cell = $($(row).find("td")[our_pos]).find("span");
  451. var their_cell = $($(row).find("td")[our_pos == 1 ? 2 : 1]);
  452. var diff_cell = $($(row).find("td")[3]);
  453. load_score_into_element(data, anime_id, our_cell);
  454. if (our_cell.text() != "-") {
  455. score_sum += parseFloat(our_cell.text());
  456. score_nums++;
  457. }
  458. if (our_cell.text() != "-" && their_cell.text() != "-") {
  459. var diff = Math.abs(our_cell.text() - their_cell.text());
  460. diff_sum += diff;
  461. diff_cell.text(round_score(diff));
  462. diff_nums++;
  463. update_shared_row_colors($(row), our_pos);
  464. }
  465. });
  466. unique_table = $($("#content h2")[our_pos]).next();
  467. unique_table.find("tr").slice(1, -1).each(function(i, row) {
  468. var anime_id = $(row).find("a").attr("href").cut("/anime/", "/");
  469. var cell = $(row).find("td:nth(1)").find("span");
  470. load_score_into_element(data, anime_id, cell);
  471. });
  472. mean_score = round_score(score_sum / score_nums);
  473. if (!isNaN(mean_score)) {
  474. $(shared_means.find("td")[our_pos]).find("span").text(mean_score);
  475. update_shared_row_colors(shared_means, our_pos);
  476. }
  477. mean_diff = Math.round(diff_sum / diff_nums * 100) / 100;
  478. if (!isNaN(mean_diff))
  479. $(shared_means.find("td")[3]).text(mean_diff);
  480. });
  481. }
  482. function hook_addtolist() {
  483. /* TODO: this entry point is unimplemented - it's rarely used and difficult
  484. to inject into, so I'm avoiding it for now. */
  485. $("<p><b>Note:</b> For the time being, anime added through this " +
  486. "interface cannot be given scores on the 10.0-point scale (the old " +
  487. "10-point system is used).</p><p>To give a more specific number, " +
  488. "simply add the anime here, then go to its own page or to your list " +
  489. "page, and update the score.</p>").insertAfter($("#stype").parent());
  490. }
  491. function hook_export() {
  492. chrome.storage.sync.getBytesInUse(null, function(usage) {
  493. usage = Math.round(usage / 1024 * 10) / 10;
  494. usage += " KB / " + chrome.storage.sync.QUOTA_BYTES / 1024 + " KB";
  495. $("#dialog td")
  496. .append($("<hr>")
  497. .css("border", "none")
  498. .css("background-color", "#bebebe")
  499. .css("height", "1px"))
  500. .append($("<p>")
  501. .html("The regular list export above will only include " +
  502. "rounded scores. You can export your decimal scores " +
  503. "separately and " +
  504. '<a href="http://myanimelist.net/import.php">import ' +
  505. "them</a> later."))
  506. .append($("<div>")
  507. .attr("class", "spaceit")
  508. .html("Chrome Sync usage: " + usage))
  509. .append($("<input>")
  510. .attr("type", "submit")
  511. .attr("value", "Export Decimal Scores")
  512. .attr("class", "inputButton")
  513. .click(export_scores));
  514. });
  515. }
  516. function hook_import() {
  517. $("#content").append($("<hr>")
  518. .css("border", "none")
  519. .css("background-color", "#bebebe")
  520. .css("height", "1px"))
  521. .append($("<p>")
  522. .html("You can also import decimal scores here. Doing so will " +
  523. "erase any existing decimal scores."))
  524. .append($("<div>")
  525. .attr("class", "spaceit")
  526. .append($("<input>")
  527. .attr("id", "decimal-file")
  528. .attr("size", "60")
  529. .attr("type", "file")
  530. .attr("class", "inputtext")))
  531. .append($("<input>")
  532. .attr("id", "decimal-submit")
  533. .attr("type", "submit")
  534. .attr("value", "Import Decimal Scores")
  535. .attr("class", "inputButton")
  536. .click(function() {
  537. var filelist = $("#decimal-file")[0].files, file, reader;
  538. if (filelist.length != 1)
  539. return;
  540. file = filelist[0];
  541. if (file.type != "application/json") {
  542. alert("Invalid file type: must be .json.");
  543. return;
  544. }
  545. reader = new FileReader();
  546. reader.onload = function() {
  547. try {
  548. import_scores(JSON.parse(reader.result), function() {
  549. $("#decimal-file").after("<p>Success!</p>")
  550. .remove();
  551. $("#decimal-submit").remove();
  552. });
  553. } catch (exc) {
  554. if (typeof exc === "object")
  555. alert("The file could not be parsed as JSON.");
  556. else
  557. alert("Error validating data: " + exc);
  558. }
  559. };
  560. reader.readAsText(file);
  561. }));
  562. }
  563. /* ------------------------------- Main hook ------------------------------- */
  564. $(document).ready(function() {
  565. var href = window.location.href;
  566. if (href.contains("/animelist/")) {
  567. var list_info = $("#mal_cs_otherlinks div:first");
  568. if (list_info.text() == "You are viewing your anime list")
  569. hook_list();
  570. }
  571. else if ($("#malLogin").length == 0) {
  572. if (href.contains("/anime/") || href.contains("/anime.php"))
  573. hook_anime(href.cut("/anime/", "/").cut("id=", "&"));
  574. else if (href.contains("/panel.php") && href.contains("go=add"))
  575. hook_add();
  576. else if (href.contains("/editlist.php") && href.contains("type=anime"))
  577. hook_edit(href.cut("id=", "&"));
  578. else if (href.contains("/shared.php") && !href.contains("type=manga"))
  579. hook_shared();
  580. else if (href.contains("/addtolist.php"))
  581. hook_addtolist();
  582. else if (href.contains("/panel.php") && href.contains("go=export"))
  583. hook_export();
  584. else if (href.contains("/import.php"))
  585. hook_import();
  586. }
  587. });