您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Integrates MyAnimeList anime history into posts
// ==UserScript== // @name ETI MAL Integration // @namespace pendevin // @description Integrates MyAnimeList anime history into posts // @include http://endoftheinter.net/inboxthread.php* // @include http://boards.endoftheinter.net/postmsg.php* // @include http://boards.endoftheinter.net/showmessages.php* // @include https://endoftheinter.net/inboxthread.php* // @include https://boards.endoftheinter.net/postmsg.php* // @include https://boards.endoftheinter.net/showmessages.php* // @require http://code.jquery.com/jquery-2.1.3.min.js // @version 3.3 // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // ==/UserScript== //enter the characters you want to replace with your last watched show //MAKE SURE THIS IS UNIQUE IN YOUR SIG const EPISODE_REPLACER='/et'; //enter the characters you want to replace with your episode count for the day //MAKE SURE THIS IS UNIQUE IN YOUR SIG const COUNT_REPLACER='/ec'; //enter the url for your MyAnimeList history page //if you only want to use anime or manga updates, add /anime or /manga to the end of the url const HISTORY_URL='http://myanimelist.net/history/pendevin'; //ll breaks without noconflict jquery this.$=this.jQuery=jQuery.noConflict(true); //i got this from shoecream's userscript autoupdater at http://userscripts.org/scripts/show/45904 var XHR={ // r.doc is the returned page // r.respose is the response element createDoc:function(response,callback,optional){ var doc=document.implementation.createDocument('','',null); var html=document.createElement("html"); html.innerHTML=response.responseText; doc.appendChild(html); var r={}; r.response=response; r.doc=doc; callback(r,optional); }, //sends the XHR request, callback is the function to call on the returned page get:function(url,callback,optional){ if(optional==undefined)optional=null; GM_xmlhttpRequest({ method:'GET', url:url, headers:{ 'User-Agent':navigator.userAgent, 'Content-Type':'application/x-www-form-urlencoded', 'Host':'myanimelist.net', 'Accept':'image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*', 'Pragma':'no-cache' }, onload:function(r){XHR.createDoc(r,callback,optional);} }); } }; //finds the last index of a regular expression value //takes a string and a regex object //kinda slow :( function reLastIndex(string,regex){ var index=-1; //we're going backwards from the end and searching for the first occurrence we find for(var i=string.length-1;i>0;i++){ //once we find it, we're outta here if(string.substring(i).search(regex)!=-1){ index=i; break; } } return index; } function reEscape(str){ var specials=new RegExp("[.*+?|()\\[\\]{}\\\\]","g"); // .*+?|()[]{}\ return str.replace(specials,"\\$&"); } //parse a mal date into a javascript date thingy //takses a string, motherfucker, and returns a Date object function parseMalDate(time){ var now=new Date; var clock; //some date case if(time.match(/\w\w\w \d\d?,/)){ clock=time.match(/(\w\w\w) (\d\d?), (\d\d?):(\d\d) ([AP]M)/); //adjust for am/pm clock[3]=parseInt(clock[3]!=12?clock[3]:0)+(clock[5]=='PM'?12:0); //gotta parse the month because ugh var monthKey={ 'Jan':0, 'Feb':1, 'Mar':2, 'Apr':3, 'May':4, 'Jun':5, 'Jul':6, 'Aug':7, 'Sep':8, 'Oct':9, 'Nov':10, 'Dec':11 }; clock[1]=monthKey[clock[1]]; //date object is (year (4 digits), month (0-11), date (1-31), hour (0-23), minute (0-59), second (0-59)) return new Date(now.getFullYear(),clock[1],parseInt(clock[2],10),clock[3],parseInt(clock[4]),0); } //some time yesterday case else if(time.match(/Yesterday,/)){ clock=time.match(/Yesterday, (\d\d?):(\d\d) ([AP]M)/); clock[1]=parseInt(clock[1]!=12?clock[1]:0)+(clock[3]=='PM'?12:0); return new Date(now.getFullYear(),now.getMonth(),now.getDate()-1,clock[1],parseInt(clock[2]),0); } //some time today case else if(time.match(/Today,/)){ clock=time.match(/Today, (\d\d?):(\d\d) ([AP]M)/); clock[1]=parseInt(clock[1]!=12?clock[1]:0)+(clock[3]=='PM'?12:0); return new Date(now.getFullYear(),now.getMonth(),now.getDate(),clock[1],parseInt(clock[2]),0); } //hour(s) ago case else if(time.match(/hours? ago/)){ return new Date(now.getTime()-parseInt(time.match(/\d\d?/))*3600000); } //minute(s) ago case else if(time.match(/minutes? ago/)){ return new Date(now.getTime()-parseInt(time.match(/\d\d?/))*60000); } //second(s) ago case else if(time.match(/seconds? ago/)){ return new Date(now.getTime()-parseInt(time.match(/\d\d?/))*1000); } return now; } //figure out how long ago something was, with expanding time scales //takes a Date object!!, and tells you how long ago it was in the form ' (<time(s)> ago)'' //now returns an object with the numeral ago and the unit as attributes function differenceEngine(then){ var diff={ diff:new Date().getTime()-then.getTime() }; //days difference if(diff.diff>86400000){ diff.numeral=(diff.diff-diff.diff%86400000)/86400000; diff.unit='day'+(diff.numeral>1?'s':''); } //hours difference else if(diff.diff>3600000){ diff.numeral=(diff.diff-diff.diff%3600000)/3600000; diff.unit='hour'+(diff.numeral>1?'s':''); } //minutes difference else if(diff.diff>60000){ diff.numeral=(diff.diff-diff.diff%60000)/60000; diff.unit='minute'+(diff.numeral>1?'s':''); } //seconds difference else if(diff.diff>1000){ diff.numeral=(diff.diff-diff.diff%1000)/1000; diff.unit='second'+(diff.numeral>1?'s':''); } //guess it could have been less than a second ago but i can't imagine why else{ diff.numeral=1; diff.unit='second'; } return diff; } //when you get the history response, insert that info into the quickpost box function malShit(r){ var doc=$(r.doc); //do shit //make sure there's a history if(doc.find('#horiznav_nav').next().text()=='No history found'){ return null; } //grab the rows and shit var episodes=doc.find('#horiznav_nav + div tr'); //find most recent anime update var anime={ raw:episodes.has('td:contains(" ep. ")').first() }; //parse anime update if(anime.raw.length){ anime.name=anime.raw.find('td:first-child>a').text(); anime.link=anime.raw.find('td:first-child>a').attr('href'); anime.episode=anime.raw.find('td:first-child>strong').text(); anime.date=parseMalDate(anime.raw.find('td:last-child').text()); anime.elapsed=differenceEngine(anime.date); anime.type='anime'; } //find most recent manga update var manga={ raw:episodes.has('td:contains(" chap. ")').first() }; //parse manga update if(manga.raw.length){ manga.name=manga.raw.find('td:first-child>a').text(); manga.link=manga.raw.find('td:first-child>a').attr('href'); manga.episode=manga.raw.find('td:first-child>strong').text(); manga.date=parseMalDate(manga.raw.find('td:last-child').text()); manga.elapsed=differenceEngine(manga.date); manga.type='manga'; } //get today's ep count //make sure that it's actually today' var count={ raw:episodes.find('div.normal_header:contains("Today") small'), }; count.number=count.raw[0]?count.raw.text().slice(1,-1):"0"; //which update is more recent and also really there //ugh this is ungainly var upd8=null; if(anime.raw[0]&&manga.raw[0]){ if(anime.elapsed.diff<manga.elapsed.diff){ upd8=anime; } else{ upd8=manga; } } else if(anime.raw[0]){ upd8=anime; } else if(manga.raw[0]){ upd8=manga; } //now that we've extracted our data, check to see if it's different from what we've got if(upd8&&(cachedData.name!=upd8.name||cachedData.episode!=upd8.episode||count.number!=cachedData.count)){ //if the episode changed if(upd8&&(cachedData.name!=upd8.name||cachedData.episode!=upd8.episode)){ //cache our shit cachedData=upd8; } //i suck cocks cachedData.count=count.number; //make a display string i guess var display=cachedData.name+(cachedData.type=='anime'?' ep. ':' chap. ')+cachedData.episode+' ('+cachedData.elapsed.numeral+' '+cachedData.elapsed.unit+' ago)'; //remember the quickpost box position var scrollPosition=quickpost.prop('scrollTop'); var cursorStart=quickpost.prop('selectionStart'); var cursorEnd=quickpost.prop('selectionEnd'); //insert the shit //gotta do our stored shit because fucking cocks quickpost.val(quickpost.val().replace(rxCocks,'$1'+(quickpost.remember.replace(rxEpisode,'$1'+display+'$2').replace(rxCount,'$1'+cachedData.count+'$2')))); //restore the quickpost box position quickpost.prop('scrollTop',scrollPosition); quickpost.prop('selectionStart',cursorStart); quickpost.prop('selectionEnd',cursorEnd); } //save the data var gmCache={ name:cachedData.name, link:cachedData.link, episode:cachedData.episode, date:cachedData.date.valueOf(), count:cachedData.count, type:cachedData.type } GM_setValue('cached',JSON.stringify(gmCache)); return null; } //when the quickpost box opens, send off a request to mal for the history function onFocus(e){ //don't do this too often pls quickpost.off('focus.mal'); //why the fuck doesn't this always work //maybe it has to do with gm hating event calls but not timeouts, which is whack if(!GM_getValue('test',true)){ window.setTimeout(onFocus,500); return null; } //get a new elapsed time since now is different cachedData.elapsed=differenceEngine(new Date(cachedData.date)); //make a display string i guess var display=cachedData.name!=''? cachedData.name+(cachedData.type=='anime'?' ep. ':' chap. ')+cachedData.episode+' ('+cachedData.elapsed.numeral+' '+cachedData.elapsed.unit+' ago)': HISTORY_URL ; //save the position of the cursor and scrollbar var scrollPosition=quickpost.prop('scrollTop'); var cursorStart=quickpost.prop('selectionStart'); var cursorEnd=quickpost.prop('selectionEnd'); //remember quickpost value because cocks quickpost.remember=quickpost.val().match(rxCocks)[2]; //insert our data quickpost.val(quickpost.val().replace(rxEpisode,'$1'+display+'$2').replace(rxCount,'$1'+cachedData.count+'$2')); //restore the position of the cursor and scrollbar quickpost.prop('scrollTop',scrollPosition); quickpost.prop('selectionStart',cursorStart); quickpost.prop('selectionEnd',cursorEnd); //send off our get request to mal XHR.get(HISTORY_URL,malShit); return null; } //find the quickpost box and listen for it to open //this gets the quickpost box and also the message box on postmsg.php var quickpost=$('.quickpost textarea[name="message"], textarea#message'); //null data var cachedData=JSON.parse(GM_getValue('cached','{"name":"","episode":"","date":0,"count":"0"}')); //make a regexps var rxEpisode=new RegExp('(---[\\w\\W]+)'+reEscape(EPISODE_REPLACER)+'([\\w\\W]*)'); var rxCount=new RegExp('(---[\\w\\W]+)'+reEscape(COUNT_REPLACER)+'([\\w\\W]*)'); var rxCocks=new RegExp('([\\w\\W]*)(---[\\w\\W]+)'); //listen for shit quickpost.on('focus.mal',onFocus); //reinitialize after posting $('form.quickpost input[name="post"]').on('click.mal',function(){ quickpost.on('focus.mal',onFocus); });