User:Stevage/EnhanceHistory.user.js
Appearance
From Wikipedia, the free encyclopedia
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump.
This code will be executed when previewing this page.
This code will be executed when previewing this page.
This user script seems to have a documentation page at User:Stevage/EnhanceHistory.user.
// ==UserScript== // @name Enhanced history display // @namespace stevage // @description Collapses consecutive edits from the same person into one, shows diffs on history page // @include *.wikipedia.org/*action=history // ==/UserScript== // This page should be found at http://en.wikipedia.org/wiki/User:Stevage/EnhanceHistory.user.js // Install it from http://en.wikipedia.org/w/index.php?action=raw&ctype=text/javascript&dontcountme=s&title=User:Stevage/EnhanceHistory.user.js ( function(){ if(typeofGM_log==='undefined')return; GM_log('in blank function'); functioncompress(){ GM_log('in compress function'); if(!document.getElementById('bodyContent')){ return; } this.add_buttons(); } compress.prototype.add_buttons=function(){ GM_log('in add_buttons'); // Create the compress buttion varbutton1=document.createElement('input'); button1.setAttribute('id','compress_button1'); button1.className='historysubmit'; button1.style.marginLeft='5px'; button1.setAttribute('type','button'); button1.value='Compress history'; button1.onclick=function(){compress.start();} // Create the ShowDiffs buttion varbutton1=document.createElement('input'); button1.setAttribute('id','showdiffs1'); button1.className='historysubmit'; button1.style.marginLeft='5px'; button1.setAttribute('type','button'); button1.value='Show diffs'; button1.onclick=function(){compress.showDiffs();} // Add the button to the page varhistory=document.getElementById('pagehistory'); history.parentNode.insertBefore(button1,history); } ///////////////////////////////////////////////////////// functiongetPlainText(s){ GM_log(">getPlainText"); if(s==null) return""; varlen=s.length; if(len>20){ return"<small>"+s.substr(0,10)+'...'+s.substr(len-10,10)+"</small>"; }else{ return"<small>"+s+"</small>"; } GM_log("<getPlainText"); } functiondiffString(text1,text2){ vard=diff(text1,text2); varhtml=''; for(varx=0;x<d.length;x++){ varm=d[x][0];// Mode (-1=delete, 0=copy, 1=add) vari=d[x][1];// Index of change. vart=d[x][2];// Text of change. t=t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); if(m==-1) html+="<DEL STYLE='background:#FFE6E6;' TITLE='i="+i+"'>"+t+"</DEL>"; elseif(m==1) html+="<INS STYLE='background:#E6FFE6;' TITLE='i="+i+"'>"+t+"</INS>"; else html+="<SPAN TITLE='i="+i+"'>"+getPlainText(t)+"</SPAN>"; } returnhtml; } // Find the differences between two texts. Return an array of changes. functiondiff(text1,text2){ // Check for equality (speedup) if(text1==text2) return[[0,0,text1]]; vara; // Trim off common prefix (speedup) a=diff_prefix(text1,text2); text1=a[0]; text2=a[1]; varcommonprefix=a[2]; // Trim off common suffix (speedup) a=diff_suffix(text1,text2); text1=a[0]; text2=a[1]; varcommonsuffix=a[2]; if(!text1){// Just add some text (speedup) a=[[1,commonprefix.length,text2]]; }elseif(!text2){// Just delete some text (speedup) a=[[-1,commonprefix.length,text1]]; }else{ // Check to see if the problem can be split in two. varlongtext=text1.length>text2.length?text1:text2; varshorttext=text1.length>text2.length?text2:text1; varhm=diff_halfmatch(longtext,shorttext,Math.ceil(longtext.length/4)); if(!hm) hm=diff_halfmatch(longtext,shorttext,Math.ceil(longtext.length/2)); if(hm){ if(text1.length>text2.length){ vartext1_a=hm[0]; vartext1_b=hm[1]; vartext2_a=hm[2]; vartext2_b=hm[3]; }else{ vartext2_a=hm[0]; vartext2_b=hm[1]; vartext1_a=hm[2]; vartext1_b=hm[3]; } varmid_common=hm[4]; varresult_a=diff(text1_a,text2_a); varresult_b=diff(text1_b,text2_b); if(commonprefix)// Shift the indicies forwards due to the commonprefix. for(varx=0;x<result_a.length;x++) result_a[x][1]+=commonprefix.length; result_a.push([0,commonprefix.length+text2_a.length,mid_common]); while(result_b.length){ result_b[0][1]+=commonprefix.length+text2_a.length+mid_common.length; result_a.push(result_b.shift()); } a=result_a; }else{ varresult=diff_map(text1,text2); if(result) a=diffchar2diffarray(result,commonprefix.length); else// No acceptable result. a=[[-1,commonprefix.length,text1],[1,commonprefix.length,text2]]; } } if(commonprefix) a.unshift([0,0,commonprefix]); if(commonsuffix) a.push([0,commonprefix.length+text2.length,commonsuffix]); returna; } functiondiff_map(text1,text2){ // Explore the intersection points between the two texts. varnow=newDate(); varms_end=now.getTime()+1000;// Don't run for more than one second. varmax=text1.length+text2.length; varv_map=newArray(); varv=newArray(); v[1]=0; varx,y; for(vard=0;d<=max;d++){ now=newDate(); if(now.getTime()>ms_end)// JavaScript timeout reached returnnull; v_map[d]=newObject; for(vark=-d;k<=d;k+=2){ if(k==-d||k!=d&&v[k-1]<v[k+1]) x=v[k+1]; else x=v[k-1]+1; y=x-k; while(x<text1.length&&y<text2.length&&text1.charAt(x)==text2.charAt(y)){ x++;y++; } v[k]=x; v_map[d][k]=x; if(x>=text1.length&&y>=text2.length){ varstr=diff_path(v_map,text1,text2); returnstr; } } } alert("No result. Can't happen. (diff_map)"); returnnull; } functiondiff_path(v_map,text1,text2){ // Work from the end back to the start to determine the path. varpath=''; varx=text1.length; vary=text2.length; for(vard=v_map.length-2;d>=0;d--){ while(1){ if(diff_match(v_map[d],x-1,y)){ x--; path="-"+text1.substring(x,x+1)+path; break; }elseif(diff_match(v_map[d],x,y-1)){ y--; path="+"+text2.substring(y,y+1)+path; break; }else{ x--; y--; //if (text1.substring(x, x+1) != text2.substring(y, y+1)) // return alert("No diagonal. Can't happen. (diff_path)"); path="="+text1.substring(x,x+1)+path; } } } returnpath; } functiondiff_match(v,x,y){ // Does the vector list contain an x/y coordinate? for(varkinv) if(v[k]==x&&x-k==y) returntrue; returnfalse; } functiondiff_prefix(text1,text2){ // Trim off common prefix varpointermin=0; varpointermax=Math.min(text1.length,text2.length); varpointermid=pointermax; while(pointermin<pointermid){ if(text1.substring(0,pointermid)==text2.substring(0,pointermid)) pointermin=pointermid; else pointermax=pointermid; pointermid=Math.floor((pointermax-pointermin)/2+pointermin); } varcommonprefix=text1.substring(0,pointermid); text1=text1.substring(pointermid); text2=text2.substring(pointermid); return[text1,text2,commonprefix]; } functiondiff_suffix(text1,text2){ // Trim off common suffix varpointermin=0; varpointermax=Math.min(text1.length,text2.length); varpointermid=pointermax; while(pointermin<pointermid){ if(text1.substring(text1.length-pointermid)==text2.substring(text2.length-pointermid)) pointermin=pointermid; else pointermax=pointermid; pointermid=Math.floor((pointermax-pointermin)/2+pointermin); } varcommonsuffix=text1.substring(text1.length-pointermid); text1=text1.substring(0,text1.length-pointermid); text2=text2.substring(0,text2.length-pointermid); return[text1,text2,commonsuffix]; } functiondiff_halfmatch(longtext,shorttext,i){ // Do the two texts share a substring which is at least half the length of the longer text? // Start with a 1/4 length substring at position i as a seed. if(longtext.length<10||shorttext.length<1) returnnull;// Pointless. varseed=longtext.substring(i,i+Math.floor(longtext.length/4)); varj=0; varj_index; varbest_common=''; while((j_index=shorttext.substring(j).indexOf(seed))!=-1){ j+=j_index; varmy_prefix=diff_prefix(longtext.substring(i),shorttext.substring(j)); varmy_suffix=diff_suffix(longtext.substring(0,i),shorttext.substring(0,j)); if(best_common.length<(my_suffix[2]+my_prefix[2]).length){ best_common=my_suffix[2]+my_prefix[2]; best_longtext_a=my_suffix[0]; best_longtext_b=my_prefix[0]; best_shorttext_a=my_suffix[1]; best_shorttext_b=my_prefix[1]; } j++; } if(best_common.length>=longtext.length/2) return[best_longtext_a,best_longtext_b,best_shorttext_a,best_shorttext_b,best_common]; else returnnull; } functiondiffchar2diffarray(text,offset){ // Convert '-h+c=a=t' into [[-1, 0, 'h'], [1, 0, 'c'], [0, 1, 'at']] // Old format: - remove char, = keep char, + add char // New format: array of [m, i, t] // Where m: -1 remove char, 0 keep char, 1 add char // Where i: index of change in first text // Where t: text to be added/kept/removed vari=0; if(offset)i+=offset; vara=newArray(); varm; varlast_m=null; for(varx=0;x<text.length;x+=2){ m="-=+".indexOf(text.substring(x,x+1))-1; if(m==-2)returnalert("Error: '"+text.substring(x,x+1)+"' is not one of '+=-'"); if(last_m===m){ a[a.length-1][2]+=text.substring(x+1,x+2); }else{ a[a.length]=newArray(m,i,text.substring(x+1,x+2)); } last_m=m; if(m!=-1)i++; } returna; } /* // JavaScript diff code thanks to John Resig (http://ejohn.org) // http://ejohn.org/files/jsdiff.js function diffString( o, n ) { GM_log(">diffstring " + o.length + "/" + n.length); var out = diff( o.split(/\s+/), n.split(/\s+/) ); GM_log("1diffstring"); var str = ""; GM_log("2diffstring"); var plaintext = ""; GM_log("3diffstring"); for ( var i = 0; i < out.n.length - 1; i++ ) { if ( out.n[i].text == null ) { if ( out.n[i].indexOf('"') == -1 && out.n[i].indexOf('<') == -1 && out.n[i].indexOf('=') == -1 ) { str += getPlainText(plaintext) + " " + "<b style='background:#E6FFE6;' class='diff'> " + out.n[i] +"</b>"; plaintext = ""; } else plaintext += " " + out.n[i]; } else { var pre = ""; if ( out.n[i].text.indexOf('"') == -1 && out.n[i].text.indexOf('<') == -1 && out.n[i].text.indexOf('=') == -1 ) { var n = out.n[i].row + 1; while ( n < out.o.length && out.o[n].text == null ) { if ( out.o[n].indexOf('"') == -1 && out.o[n].indexOf('<') == -1 && out.o[n].indexOf(':') == -1 && out.o[n].indexOf(';') == -1 && out.o[n].indexOf('=') == -1 ) pre += " <s style='background:#FFE6E6;' class='diff'>" + out.o[n] +" </s>"; n++; } } plaintext = plaintext + " " + out.n[i].text; if (pre!="") { str += getPlainText(plaintext) + " " + pre; plaintext = ""; } } // if } // for GM_log("<diffstring"); return str +" " +getPlainText(plaintext); } function diff( o, n ) { var ns = new Array(); var os = new Array(); for ( var i = 0; i < n.length; i++ ) { if ( ns[ n[i] ] == null ) ns[ n[i] ] = { rows: new Array(), o: null }; ns[ n[i] ].rows.push( i ); } for ( var i = 0; i < o.length; i++ ) { if ( os[ o[i] ] == null ) os[ o[i] ] = { rows: new Array(), n: null }; os[ o[i] ].rows.push( i ); } for ( var i in ns ) { if ( ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1 ) { n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] }; o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] }; } } for ( var i = 0; i < n.length - 1; i++ ) { if ( n[i].text != null && n[i+1].text == null && o[ n[i].row + 1 ].text == null && n[i+1] == o[ n[i].row + 1 ] ) { n[i+1] = { text: n[i+1], row: n[i].row + 1 }; o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1 }; } } for ( var i = n.length - 1; i > 0; i-- ) { if ( n[i].text != null && n[i-1].text == null && o[ n[i].row - 1 ].text == null && n[i-1] == o[ n[i].row - 1 ] ) { n[i-1] = { text: n[i-1], row: n[i].row - 1 }; o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1 }; } } return { o: o, n: n }; } */ functionstripHTML(oldString){ varnewString=""; varinTag=false; for(vari=0;i<oldString.length;i++){ if(oldString.charAt(i)=='<') inTag=true; if(oldString.charAt(i)=='>'){ inTag=false; i++; } if(!inTag) newString+=oldString.charAt(i); } returnnewString; } compress.prototype.mediawiki_content=function(text){ GM_log(">mw_content:"); if(text==""){ returntext; }else{ text=''+text; varstart=text.indexOf('<textarea'); start+=text.substr(start,1000).indexOf('>')+1; varend=text.indexOf('</textarea>'); GM_log("<mw_content"); text=text.substr(start,end-start); s=text.replace(/</g,"<"); s=s.replace(/>/g,">"); GM_log("Stripped: "+s.substr(0,50)); returns; } } compress.prototype.start=function(){ varhist=document.getElementById('pagehistory'); if(hist){ vardiffs; diffs=document.evaluate( "LI", hist, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null ); varlast='*x!',prevdiffcomment; for(vari=0;i<diffs.snapshotLength;i++){ vardiff=diffs.snapshotItem(i); varcomment=document.evaluate( 'SPAN[@class="comment"]', diff, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null ).snapshotItem(0); //GM_log(comment.innerHTML); vara=document.evaluate( "SPAN/A", diff, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null ); eacha=a.snapshotItem(0); if(eacha.title==last){ if(comment){ prevdiffcomment.innerHTML=prevdiffcomment.innerHTML+'//'+comment.innerHTML; }else{ prevdiffcomment.innerHTML=prevdiffcomment.innerHTML+'//---'; } diff.parentNode.removeChild(diff); }else{ last=eacha.title; if(!comment){ comment=document.createElement('SPAN'); comment.className='comment'; comment.innerHTML=' ---'; diff.insertBefore(comment,null); } prevdiffcomment=comment; }//if }//for }//if hist }// function 'start' compress.prototype.loadDiff=function(urlno){ GM_log("in loadDiff"); this.urlno=urlno; this.hostname="en.wikipedia.org"; varurl=this.urls[urlno]+'&action=edit'; if(this.urls[urlno]==null){ vardetails=newString(""); details.responseText="";// force comparison with blank text; compress.loadedDiff(details); return; } GM_log(">loading!"+url); GM_xmlhttpRequest({ method:'GET', url:url, headers:{ 'User-agent':'Mozilla/4.0 (compatible) Greasemonkey', 'Accept':'application/xml', }, onload:function(details){ //alert("hello " + details.status + '/' + details.statusText + '/' + details.responseHeaders); compress.loadedDiff(details); } }); GM_log("<loading!"+url); } compress.prototype.loadedDiff=function(details){ GM_log(">loadedDiff "+this.urlno); this.pages[this.urlno]=this.mediawiki_content(details.responseText); GM_log("-loadedDiff "+this.urlno); if(this.urlno>0){ s=diffString(this.pages[this.urlno],this.pages[this.urlno-1]); GM_log("done diff"); wh=document.getElementById(this.info[this.urlno-1]); span=document.createElement('span'); span.innerHTML=s; wh.insertBefore(span,null); } if(details.responseText!=""){ compress.loadDiff(this.urlno+1);// if blank text, stop. } GM_log("<loadedDiff"); } compress.prototype.showDiffs=function(){ varhist=document.getElementById('pagehistory'); if(hist){ vardiffs; diffs=document.evaluate( 'LI/A[text() != "cur" and text() != "last"][1]', hist, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null ); this.urls=newArray(diffs.snapshotLength); this.info=newArray(diffs.snapshotLength); this.pages=newArray(diffs.snapshotLength); GM_log("Number of A's: "+diffs.snapshotLength); for(vari=0;i<diffs.snapshotLength;i++){ vardiff=diffs.snapshotItem(i); diff.id="difflink"+i; diff.parentNode.id="diffli"+i; this.urls[i]=diff.href; this.info[i]="diffli"+i; if(i==0){ this.loadDiff(0); } }//for }//if hist }// function 'start' varcompress=newcompress(); document.compress=compress; }// unnamed function )();