MAL Voice Actor Filter

This script filters voice actor: "Voice Acting Roles" and anime staff: "Anime Staff Positions" by your anime list entries

目前为 2016-09-11 提交的版本。查看 最新版本

  1. // MAL Voice Actor Filter!
  2. // version 1.2
  3. // 2010-06-14
  4. // Copyright (c) 2009, Bastvera <bastvera@gmail.com>
  5. // Released under the GPL license
  6. // http://www.gnu.org/copyleft/gpl.html
  7. //
  8. // --------------------------------------------------------------------
  9. //
  10. // This is a Greasemonkey user script.
  11. //
  12. // To install, you need Greasemonkey: http://greasemonkey.mozdev.org/
  13. // Then restart Firefox and revisit this script.
  14. // Under Tools, there will be a new menu item to "Install User Script".
  15. // Accept the default configuration and install.
  16. //
  17. // To uninstall, go to Tools/Manage User Scripts,
  18. // select "MAL Voice Actor Filter", and click Uninstall.
  19. //
  20. // --------------------------------------------------------------------
  21. //
  22. // ==UserScript==
  23. // @name MAL Voice Actor Filter
  24. // @namespace http://thayanger.neostrada.pl
  25. // @include http://myanimelist.net/people/*
  26. // @include http://myanimelist.net/people.php?id=*
  27. // @include https://myanimelist.net/people/*
  28. // @include https://myanimelist.net/people.php?id=*
  29. // @description This script filters voice actor: "Voice Acting Roles" and anime staff: "Anime Staff Positions" by your anime list entries
  30. // @author Bastvera <bastvera@gmail.com>
  31. // @version 1.2.16
  32. // @grant GM_xmlhttpRequest
  33. // ==/UserScript==
  34.  
  35. setTimeout(function(){
  36. //All edit buttons in "Voice Acting Roles"
  37. var allEdits = document.evaluate(
  38. "//td[@style='padding-left: 5px;']//table[1]//a[@class='Lightbox_AddEdit button_edit']",
  39. document,
  40. null,
  41. XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
  42. null);
  43. //All edit buttons in "Anime Staff Positions"
  44. var allEditsStaff = document.evaluate(
  45. "//td[@style='padding-left: 5px;']//table[2]//a[@class='Lightbox_AddEdit button_edit']",
  46. document,
  47. null,
  48. XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
  49. null);
  50.  
  51. //Correct Table check
  52. var antyStaff = allEdits.snapshotItem(0);
  53. if (antyStaff == null) {
  54. antyStaff = allEditsStaff.snapshotItem(0);
  55. var staffIndex = 2;
  56. } else {
  57. var staffIndex = 1;
  58. }
  59. antyStaff = antyStaff.parentNode.parentNode.parentNode.parentNode.parentNode.previousSibling;
  60. var convert=antyStaff.innerHTML;
  61. var finder = convert.search("Voice Acting Roles");
  62. var finderStaff = convert.search("Anime Staff Positions");
  63.  
  64. if(finder!=-1 || finderStaff!=-1){
  65. var normalHeader = document.evaluate(
  66. "//div[@class='normal_header']",
  67. document,
  68. null,
  69. XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
  70. null);
  71. //Elements placing
  72. var checkboxAnchor = normalHeader.snapshotItem(0);
  73.  
  74. var newElement = document.createElement('BR');
  75. checkboxAnchor.appendChild(newElement);
  76.  
  77. var checkbox = document.createElement('input');
  78. checkbox.type = 'checkbox';
  79. checkbox.defaultChecked = false;
  80. checkboxAnchor.appendChild(checkbox);
  81.  
  82. newElement = document.createElement('label');
  83. newElement.setAttribute('for','firstName');
  84. newElement.appendChild(document.createTextNode('Filter entries by your Anime List.'));
  85. checkboxAnchor.appendChild(newElement);
  86. newElement.style.fontWeight="normal";
  87. newElement.style.fontSize="10px";
  88.  
  89. var newElement2 = document.createElement('label');
  90. newElement2.appendChild(document.createTextNode(' (Downloading Plan to Watch entries...)'));
  91. checkboxAnchor.appendChild(newElement2);
  92. newElement2.style.fontWeight="normal";
  93. newElement2.style.fontSize="10px";
  94.  
  95. if (finder!=-1){
  96. //Arrays for storing elements
  97. var editdiv = []; //Edit button Div
  98. var moe = []; //Char Name
  99. var role = []; //Main/Support Div
  100.  
  101. //Edit Entries Segments
  102. for (var i = 0; i < allEdits.snapshotLength; i++){
  103. var AnchorLink = allEdits.snapshotItem(i);
  104. editdiv[i] = AnchorLink.parentNode; //Edit button Div
  105. role[i] = editdiv[i].parentNode.nextSibling.nextSibling.lastChild; //Main/Support Div
  106. moe[i] = editdiv[i].parentNode.nextSibling.nextSibling.firstChild; //Char Name
  107. }
  108. } else {
  109. if (allEdits.snapshotItem(0) != null) {
  110. allEditsStaff = allEdits;
  111. }
  112. }
  113.  
  114. //Arrays for storing elements
  115. var editdivStaff = []; //Edit button Div
  116. var moe2 = []; //Anime Name
  117.  
  118. //Edit Entries Segments
  119. for (var i = 0; i < allEditsStaff.snapshotLength; i++){
  120. var AnchorLink = allEditsStaff.snapshotItem(i);
  121. editdivStaff[i] = AnchorLink; //Edit button Div
  122. moe2[i] = editdivStaff[i].parentNode.parentNode.firstChild; //Anime Name
  123. }
  124.  
  125. //All add buttons in "Voice Acting Roles"
  126. var allElements = document.evaluate(
  127. "//td[@style='padding-left: 5px;']//a[@class='Lightbox_AddEdit button_add']", //"//td[@style='padding-left: 5px;']//table[1]//a[@class='Lightbox_AddEdit button_add']",
  128. document,
  129. null,
  130. XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
  131. null);
  132.  
  133. //Array with add buttons div
  134. var addbutton = [];
  135. for (i = 0; i < allElements.snapshotLength; i++){
  136. AnchorLink = allElements.snapshotItem(i);
  137. addbutton[i] = AnchorLink.parentNode.parentNode.parentNode; //Main Div with Add button
  138. }
  139.  
  140. //Div backup
  141. var backup = [];
  142. var orginal = [];
  143. var backpos = 0;
  144.  
  145. //Get or Set status of checkbox
  146. var checkboxmem = (localStorage.getItem('checkboxmem_voice') === "true"); //Get chceckbox status
  147. if(checkboxmem==null){
  148. checkboxmem=false;
  149. localStorage.setItem('checkboxmem_voice', checkboxmem);
  150. checkbox.checked=checkboxmem;
  151. }
  152. else{
  153. checkbox.checked=checkboxmem;
  154. if(checkbox.checked==true){
  155. HideDivs();
  156. }
  157. }
  158.  
  159. //Collect Plan to Watch divs
  160. var editPlan = [];
  161. CollectPlan();
  162.  
  163. //Listener
  164. checkbox.addEventListener('change',function () {
  165.  
  166. if(checkbox.checked==true){
  167. HideDivs();
  168. }
  169.  
  170. if(checkbox.checked==false){
  171. RestoreDivs();
  172. }
  173.  
  174. localStorage.setItem('checkboxmem_voice', checkbox.checked);
  175.  
  176. },false)
  177. }
  178.  
  179. function CollectPlan(){
  180. //Get username
  181. var allNavs = document.evaluate(
  182. "//div[@id='menu_left']//ul[@id='nav']//a",
  183. document,
  184. null,
  185. XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
  186. null);
  187.  
  188. var userName;
  189. for (var i = 0; i < allNavs.snapshotLength; i++){
  190. var linkNav = allNavs.snapshotItem(i);
  191. var userTest=/\/profile\/.*/;
  192. var getName = userTest.exec(linkNav);
  193. getName = "" + getName;
  194. getName = getName.replace(/\/profile\//,"");
  195. if(getName!='null')
  196. userName=getName;
  197. }
  198.  
  199. var rssURL = "http://myanimelist.net/rss.php?type=rw&u=" + userName;
  200.  
  201. //RSS change check
  202. GM_xmlhttpRequest({
  203. method: 'GET',
  204. url: rssURL,
  205. headers: {
  206. 'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
  207. 'Accept': 'application/atom+xml,application/xml,text/xml',
  208. },
  209. onload: function(responseDetails){
  210.  
  211. //Cache test
  212. var cacheCheck = 0;
  213.  
  214. var lastTime = /<pubDate>.*<\/pubDate>/;
  215. var modTime = lastTime.exec(responseDetails.responseText);
  216. var saveTime = localStorage.getItem('saveTime');
  217. modTime = "" + modTime;
  218. if(modTime!=saveTime){ //Cache time check
  219. localStorage.setItem('saveTime', modTime);
  220. }
  221. else
  222. cacheCheck++;
  223.  
  224. if(cacheCheck==0){
  225. //User list
  226. var animeURL = "http://myanimelist.net/malappinfo.php?u=" + userName + "&status=all";
  227. GM_xmlhttpRequest({
  228. method: 'GET',
  229. url: animeURL,
  230. headers: {
  231. 'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
  232. 'Accept': 'application/atom+xml,application/xml,text/xml',
  233. },
  234. onload: function(responseDetails){
  235. var text = responseDetails.responseText;
  236.  
  237. var key = ";"; //Chaching string
  238.  
  239. //Edit Anime links formating
  240. var tagcount = 5000; //Max number of anime entries
  241. var plancount = 0; //Counter "Plan to Watch" entries
  242. var ListLinks = []; //Array for anime entries storing
  243. var anireg=/<series_animedb_id>\d{1,}<\/series_animedb_id>/g; //Anime entries links
  244. var anistatus=/<my_status>\d{1}<\/my_status>/g; //Anime entries status
  245. var exactlink=/\d{1,}/; //Anime exact link
  246.  
  247. for (var i = 0; i < tagcount; i++){
  248. var linkGet = anireg.exec(text);
  249. if(linkGet==null){
  250. tagcount=i;
  251. break;
  252. }
  253. else {
  254. var planDetect = anistatus.exec(text);
  255. if(planDetect=="<my_status>6</my_status>"){
  256. ListLinks[plancount] = linkGet;
  257. ListLinks[plancount] = exactlink.exec(ListLinks[plancount]);
  258. key = key + ListLinks[plancount] + ";";
  259. ListLinks[plancount] = "http://myanimelist.net/anime/" + ListLinks[plancount] + "/";
  260. plancount++;
  261. }
  262. }
  263. }
  264. localStorage.setItem('list', key); //Store Cache string
  265. CollectDivs(ListLinks);
  266. if(checkbox.checked==true){
  267. RestoreDivs();
  268. HideDivs();
  269. }
  270. newElement2.style.display="none";
  271. }
  272. });
  273. }
  274.  
  275. else{
  276. var ListLinks = []; //Anime Links Array
  277. var key = localStorage.getItem('list'); //Fetch link from Cache
  278. var exactlink=/\d{1,}/g;
  279. var tagcount = 5000;
  280. for (var i = 0; i < tagcount; i++){
  281. var linkGet = exactlink.exec(key);
  282. if(linkGet==null){
  283. tagcount=i;
  284. break;
  285. }
  286. else{
  287. ListLinks[i]=linkGet;
  288. ListLinks[i] = "http://myanimelist.net/anime/" + ListLinks[i] + "/";
  289. }
  290. }
  291. CollectDivs(ListLinks);
  292. if(checkbox.checked==true){
  293. RestoreDivs();
  294. HideDivs();
  295. }
  296. newElement2.style.display="none";
  297. }
  298. }
  299. });
  300.  
  301. function CollectDivs(ListLinks){
  302. var editSibling = []; //Anime Link href for comparing
  303.  
  304. if(finder!=-1){
  305. for (var i = 0; i < allEdits.snapshotLength; i++){
  306. editSibling[i] = editdiv[i].parentNode.firstChild; //Anime Name;
  307.  
  308. StorePlan(i);
  309. }
  310. }
  311.  
  312. for (var i = 0; i < allEditsStaff.snapshotLength; i++){
  313. editSibling[i] = moe2[i]; //Anime Name;
  314.  
  315. StorePlan(i);
  316. }
  317.  
  318. function StorePlan(i){
  319. //Store "Plan to Watch" Divs
  320. var convert = editSibling[i].href;
  321. var exactlink=/\d{1,}/;
  322. convert = "http://myanimelist.net/anime/" + exactlink.exec(convert) + "/"; //Anime Link;
  323.  
  324.  
  325. for (var tcount in ListLinks){
  326. var finderPlan = convert.search(ListLinks[tcount]);
  327. if(finderPlan!=-1){
  328. editPlan.push(editSibling[i]);
  329. break;
  330. }
  331. }
  332. }
  333. }
  334. }
  335.  
  336. function HideDivs(){
  337. //Hide all div with add
  338. for (var current in addbutton){
  339. addbutton[current].style.display="none";
  340. }
  341.  
  342. //Hide edit Div
  343. var current;
  344. for (current in editdivStaff){
  345. editdivStaff[current].style.display="none";
  346. }
  347.  
  348. for(current in moe){
  349.  
  350. var curpos = current;
  351. curpos++;
  352.  
  353. if(editdiv[current].parentNode.parentNode.getAttribute('style')!="display: none;"){ //Modify root entries only
  354.  
  355. //Div backup storage
  356. backup[backpos] = editdiv[current].parentNode.parentNode.cloneNode(true);
  357. orginal[backpos] = editdiv[current].parentNode.parentNode;
  358. backpos++;
  359.  
  360. //Root /Main/Support text add
  361. var temp = role[current].innerHTML; //Main/Support text
  362. temp = temp.replace(/&nbsp;/,""); //Main/Support clear
  363. var line = document.createTextNode('\n'+ temp);
  364. editdiv[current].parentNode.appendChild(line);
  365.  
  366. //Root hide elements
  367. role[current].style.display="none"; //Hide Main/Support Div
  368. editdiv[current].style.display="none"; //Hide edit and airing Div
  369.  
  370. var currentPlanned = false;
  371. for (var i in editPlan){
  372. if(editPlan[i] == editdiv[current].parentNode.firstChild.href){
  373. currentPlanned = true;
  374. editdiv[current].parentNode.parentNode.style.display="none";
  375. }
  376. }
  377.  
  378. if (!currentPlanned) {
  379. for( curpos ; curpos < allEdits.snapshotLength; curpos++){
  380. if(moe[curpos].href==moe[current].href){ //Compare entries by moe name ^_^
  381. var planned = false;
  382. for (var i in editPlan) {
  383. if(editPlan[i] == editdiv[curpos].parentNode.firstChild.href) {
  384. planned = true;
  385. }
  386. }
  387. if (!planned) {
  388. var br = document.createElement('br');
  389.  
  390. //Add Similar anime name
  391. editdiv[current].parentNode.appendChild(br);
  392. var newNode=editdiv[curpos].parentNode.firstChild.cloneNode(true);
  393. editdiv[current].parentNode.appendChild(newNode);
  394.  
  395. //Similar /Main/Support text add
  396. temp = role[curpos].innerHTML;
  397. temp = temp.replace(/&nbsp;/,"");
  398. line = document.createTextNode('\n'+temp);
  399. editdiv[current].parentNode.appendChild(line);
  400.  
  401. //Hide Similar Div
  402. editdiv[curpos].parentNode.parentNode.style.display="none";
  403. }
  404. }
  405. }
  406. }
  407. }
  408. }
  409.  
  410. for(current in moe2){
  411.  
  412. var curpos = current;
  413. curpos++;
  414.  
  415. if(editdivStaff[current].parentNode.parentNode.parentNode.getAttribute('style')!="display: none;"){ //Modify root entries only
  416.  
  417. //Div backup storage
  418. backup[backpos] = editdivStaff[current].parentNode.parentNode.parentNode.cloneNode(true);
  419. orginal[backpos] = editdivStaff[current].parentNode.parentNode.parentNode;
  420. backpos++;
  421.  
  422. for( curpos ; curpos < allEditsStaff.snapshotLength; curpos++){
  423. if(moe2[curpos].href==moe2[current].href){ //Compare entries by anime name ^_^
  424.  
  425. //Add Similar anime name
  426. var newNode=editdivStaff[curpos].parentNode.cloneNode(true);
  427. editdivStaff[current].parentNode.parentNode.appendChild(newNode);
  428.  
  429. //Hide Similar Div
  430. editdivStaff[curpos].parentNode.parentNode.parentNode.style.display="none";
  431. }
  432. }
  433.  
  434. for (var i in editPlan){
  435. if(editPlan[i] == moe2[current].href){
  436. editdivStaff[current].parentNode.parentNode.parentNode.style.display="none";
  437. }
  438. }
  439. }
  440. }
  441. }
  442.  
  443. function RestoreDivs(){
  444.  
  445. //Restore Modified Divs
  446. for(var current in backup){
  447. orginal[current].parentNode.replaceChild(backup[current],orginal[current]);
  448. }
  449.  
  450. //Unhide Similar Voice Actor
  451. for(current in editdiv){
  452. editdiv[current].parentNode.parentNode.removeAttribute('style');
  453. }
  454.  
  455. //Unhide Similar Staff
  456. for(current in editdivStaff){
  457. editdivStaff[current].parentNode.parentNode.parentNode.removeAttribute('style');
  458. }
  459.  
  460. //Unhide add entries
  461. for(current in addbutton){
  462. addbutton[current].removeAttribute('style');
  463. }
  464.  
  465. if (finder!=-1){
  466. //Rescan all edit entries
  467. allEdits = document.evaluate(
  468. "//td[@style='padding-left: 5px;']//table[1]//a[@class='Lightbox_AddEdit button_edit']",
  469. document,
  470. null,
  471. XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
  472. null);
  473.  
  474. for (var i = 0; i < allEdits.snapshotLength; i++){
  475. AnchorLink = allEdits.snapshotItem(i);
  476. editdiv[i] = AnchorLink.parentNode; //Edit button Div
  477. role[i] = editdiv[i].parentNode.nextSibling.nextSibling.lastChild; //Main/Support Div
  478. moe[i] = editdiv[i].parentNode.nextSibling.nextSibling.firstChild; //Char Name
  479. }
  480.  
  481. //Rescan all edit entries
  482. allEditsStaff = document.evaluate(
  483. "//td[@style='padding-left: 5px;']//table[2]//a[@class='Lightbox_AddEdit button_edit']",
  484. document,
  485. null,
  486. XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
  487. null);
  488. }
  489.  
  490. if (finderStaff!=-1) {
  491. //Rescan all edit entries
  492. allEditsStaff = document.evaluate(
  493. "//td[@style='padding-left: 5px;']//table["+staffIndex+"]//a[@class='Lightbox_AddEdit button_edit']",
  494. document,
  495. null,
  496. XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
  497. null);
  498. }
  499.  
  500. for (var i = 0; i < allEditsStaff.snapshotLength; i++){
  501. AnchorLink = allEditsStaff.snapshotItem(i);
  502. editdivStaff[i] = AnchorLink; //Edit button Div
  503. }
  504.  
  505. //Unhide edit entries
  506. for(current in editdivStaff){
  507. editdivStaff[current].removeAttribute('style');
  508. }
  509.  
  510. //Reset backups
  511. backup = [];
  512. orginal = [];
  513. backpos = 0;
  514. }
  515. }, 100);