User:Enterprisey/diff-context.js

// vim: ts=4 sw=4 et ai ( function {    var wikitextCache = {};    function getWikitext( pageName, revision ) {        if( wikitextCache[ revision ] ) {            return $.when( wikitextCache[ revision ] );        }        return $.getJSON( mw.config.get( 'wgScriptPath' ) + '/api.php', {               format: "json", action: "query", prop: "revisions", rvprop: "content", rvslots: "main", rvlimit: 1, rvstartid: revision, titles: pageName, formatversion: 2, }       ).then( function ( data ) { var revObj = data.query.pages[ Object.keys( data.query.pages )[0] ].revisions[0]; var wikitext = revObj.slots.main.content; wikitextCache[ revision ] = wikitext; return wikitext; } );   }

function lineNumFromLineNumCell( cell ) { return parseInt( cell.textContent.replace( /,/g, "" ).match( /Line (\d+):/ )[1] ); }

function findLineNumberRowIdx( diffTable, direction, rowIdx ) { var rows = Array.prototype.slice.call( diffTable.rows ); var searchRows; if( direction === "next" ) { searchRows = rows.slice( rowIdx ); } else { searchRows = rows.slice( 0, rowIdx ); searchRows.reverse; }       return searchRows.find( function ( row ) {            return row.cells[0].className === "diff-lineno";        } ); }

// I'm probably gonna keep finding off-by-one errors in this code until the heat death of the universe function showMore( revision, pageName, direction, rowWithButton, numRowsToShow, targetRow, diffTable ) { var rows = Array.prototype.slice.call( diffTable.rows ); var buttonRowIdx = rows.indexOf( rowWithButton ); if( direction === "next" ) { targetRow = findLineNumberRowIdx( diffTable, "previous", buttonRowIdx ); }       var currLineNum = lineNumFromLineNumCell( targetRow.cells[1] ); var targetRowIdx = Array.prototype.indexOf.call( diffTable.rows, targetRow ); var currRowIdx = direction === "next" ? buttonRowIdx - 1 : targetRowIdx + 1; if( direction === "next" ) { currLineNum += buttonRowIdx - rows.indexOf( targetRow ) - 2; }       return getWikitext( pageName, revision ).then( function ( wikitext ) {            var lines = wikitext.split( "\n" );            var startLineIdx, endLineIdx;            switch( direction ) {                case "next":                    startLineIdx = currLineNum + 1;                    endLineIdx = currLineNum + numRowsToShow + 1;

var nextLineNumberRow = findLineNumberRowIdx( diffTable, "next", buttonRowIdx ); if( nextLineNumberRow ) { var limitLineNum = lineNumFromLineNumCell( nextLineNumberRow.cells[1] ) - 1; endLineIdx = Math.min( endLineIdx, limitLineNum ); }                   break; case "previous": startLineIdx = currLineNum - numRowsToShow - 1; endLineIdx = currLineNum - 1;

var prevLineNumberRow = findLineNumberRowIdx( diffTable, "previous", buttonRowIdx ); if( prevLineNumberRow ) { var prevLineNumberRowIdx = rows.indexOf( prevLineNumberRow ); var limitLineNum = lineNumFromLineNumCell( prevLineNumberRow.cells[1] ) + ( buttonRowIdx - prevLineNumberRowIdx ) - 2; startLineIdx = Math.max( startLineIdx, limitLineNum ); }                   break; }           startLineIdx = Math.max( 0, startLineIdx ); endLineIdx = Math.min( lines.length - 1, endLineIdx ); if( endLineIdx - startLineIdx === 0 ) { return true; // no more work can be done }           var actualNumLines = endLineIdx - startLineIdx; var linesToShow = lines.slice( startLineIdx, endLineIdx ); if( direction === "previous" ) { linesToShow.reverse; }           var insertionRow = currRowIdx + +( direction === "next" ); for( var lineCounter = 0; lineCounter < actualNumLines; lineCounter++ ) { var currLine = " " + linesToShow[ lineCounter ].replace( /</g, "&lt;" ) + " "; var newRow = diffTable.insertRow( insertionRow ); newRow.insertCell( 0 ).appendChild( document.createTextNode( "." ) );               var newCell = newRow.insertCell( 1 ); newCell.className = "diff-context"; newCell.innerHTML = currLine; newRow.insertCell( 2 ).appendChild( document.createTextNode( "." ) );               var newCell2 = newRow.insertCell( 3 ); newCell2.className = "diff-context"; newCell2.innerHTML = currLine; }           if( direction === "previous" ) { targetRow.cells[0].textContent = "Line " + ( currLineNum - actualNumLines ) + ":"; targetRow.cells[1].textContent = "Line " + ( currLineNum - actualNumLines ) + ":"; }           mw.hook( "diff-update" ).fire( diffTable ); if( actualNumLines < numRowsToShow ) { return true; // we're done }           return false; } );   }

function makeDropdown { var sel = document.createElement( "select" ); function add( num ) { var opt = document.createElement( "option" ); opt.value = num; opt.textContent = num; sel.appendChild( opt ); }       add( 1 ); add( 10 ); add( 50 ); add( 100 ); //add( "all" ); return sel; }

function addLinks( diffTable ) { if( !diffTable.querySelector ) {

// Assume it's a jquery object diffTable = diffTable.get( 0 ); }

if( diffTable.getElementsByClassName( "diff-context-container" ).length > 0 ) {

// We already ran on this diff return; }

function makeRow( index, direction, targetRow ) { var newRow = diffTable.insertRow( index ); newRow.className = "context " + direction; var newCell = newRow.insertCell( 0 ); newCell.setAttribute( "colspan", "4" ); var container = document.createElement( "div" ); container.className = "diff-context-container"; container.appendChild( document.createTextNode( "Show " + direction + " " ) ); var dropdown = makeDropdown; container.appendChild( dropdown ); container.appendChild( document.createTextNode( " lines: " ) ); var go = document.createElement( "button" ); go.textContent = "Go"; var revision = mw.config.get( "wgDiffNewId" ); var pageName = mw.config.get( "wgPageName" );

if( diffTable.parentNode && diffTable.parentNode.dataset.mwRevid ) { // User contribs page or normal watchlist or history page revision = diffTable.parentNode.dataset.mwRevid; if( pageName.indexOf( "Special:Contributions" ) === 0 ) { pageName = diffTable.parentNode.querySelector( "a.mw-contributions-title" ).textContent; }           } else if( diffTable.parentNode && diffTable.parentNode.className === "mw-enhanced-rc-nested" ) { // Enhanced ("show all changes") watchlist, when multiple revisions are shown for one page revision = diffTable.parentNode.parentNode.dataset.mwRevid; pageName = diffTable.parentNode.dataset.targetPage; } else if( diffTable.id.substring( diffTable.id.length - 7 ) === "display" && diffTable.previousElementSibling.dataset.mwRevid ) { // Enhanced watchlist, only one revision shown for a page revision = diffTable.previousElementSibling.dataset.mwRevid; pageName = diffTable.previousElementSibling.getElementsByClassName( "mw-changeslist-line-inner" )[0].dataset.targetPage; }

go.addEventListener( "click", function ( evt ) {               var numRowsToShow = parseInt( dropdown.value );                showMore( revision, pageName, direction, newRow, numRowsToShow, targetRow, diffTable ).then( function ( done ) { if( done ) { this.disabled = true; }               }.bind( this ) );                evt.preventDefault;            }, true );

container.appendChild( go ); newCell.appendChild( container ); }

var firstTime = true; for( var rowIdx = 0, rowCount = diffTable.rows.length; rowIdx < rowCount; rowIdx++ ) { var row = diffTable.rows[rowIdx]; if( row.cells[0].className !== "diff-lineno" ) continue; if( !firstTime ) { makeRow( rowIdx, "next", row ); rowIdx++; } else { firstTime = false; }           makeRow( rowIdx, "previous", row ); rowIdx++; }       var lineNumCells = diffTable.querySelectorAll( "td.diff-lineno" ); if( lineNumCells.length ) { var lastLineNumRow = Array.prototype.slice.call( lineNumCells, -1 )[0].parentNode; makeRow( -1, "next", lastLineNumRow ); }   }

$( function {        var diffTable = document.querySelector( "table.diff" );        if( mw.config.get( "wgDiffNewId" ) && diffTable && diffTable.rows.length > 2 ) {            mw.loader.addStyleTag( "tr.context td { text-align: center; }" );            mw.loader.addStyleTag( "tr.context td div.diff-context-container { display: inline-block; border: thin solid #ddd; padding: 0.1em 0.5em; }" );            addLinks( diffTable );        }        mw.hook( "wikipage.diff" ).add( addLinks );        mw.hook( "new-diff-table" ).add( addLinks );    } ); } );