Jump to content

User:Js/diffs.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Js (talk | contribs) at 04:38, 7 October 2011 (old version is completely broken by MW 1.18, so (early alpha) new version can't make it worse, I guess). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
$(function(){

dfImproveAdvanced = true

var dfPopupSheet
var clickedLink



//if( wgCanonicalSpecialPageName == 'Recentchanges'){

 mw.util.addCSS(
  'a[href^="/w"][href*="diff="]' + (window.dfDiffLinksCSS || '{font-style:italic}') +
  'a[href^="/w"][href*="diff="]:hover {color:red}' +
  '.popup {margin:0.5em}'
 )
 mw.loader.load( 'jquery.ui.dialog' )
 setTimeout(addPopupLinks, 100)
 
//}

var tbl = $('table.diff')
if( tbl.length ) improveTable(tbl)

function addPopupLinks(){
 //mw.util.$content.find('a').click(clickPopup)
 mw.util.$content.click(dfClick)
}








function dfClick(e){

 cursorWait() //cancel waiting indicator if something went wrong
 if( e.shiftKey || e.which != 1) return

 var aa = $(e.target).closest('a')
 if( !aa ) return
 var url = decodeURI(aa.attr('href'))

 if( /^#/.test(url) ) return

 
 if( /diff=/.test(url) ) return requestDiff(aa, url)
  /* 
 else if( ! /\?/.test(url) && ! /(special|служебная):/i.test(url) ){
    url += '?action=render'
 }else{
   url += (/\?/.test(url) ? '&' : '?') + 'useskin=myskin #content'
 }
 */
}



function requestDiff(lnk, url){

 addDiffPopupCSS()
 addDiffTableCSS()
 
 //mark link as "clicked"
 clickedLink = lnk
 $('a.df-clicked-last').removeClass('df-clicked-last') //rm class from previos click
 lnk.addClass('df-clicked df-clicked-last')
 if( /Watchlist|Recentchanges/.test(wgCanonicalSpecialPageName) )
   lnk.closest('li').add(lnk.closest('tr')).addClass('df-clicked')

 url += '&action=render&diffonly=true'

 $('<div class=popup />').hide().insertAfter(lnk)
 .load(url, showPopup)

 return false
}




function showPopup(){
 improveTable( $(this).find('table') )

 $(this)
 .dialog(
   {width: $(window).width() - 100
   //,maxHeight: $(window).height() - 100
   }
 )
 .click( function(e){
   if( $(e.target).hasClass('ui-dialog-content') )
     $(this).dialog('close')
 })
 
 //.mouseleave( function(){   $(this).dialog('close') })
}






//******************************************************************************************

function cursorWait(isWait){
 if (window.dfNoWaitCursor) return
 document.body.style.cursor = isWait ? 'wait' : ''
}



function addDiffPopupCSS(){
 if (dfPopupSheet) return
 if (document.URL.indexOf('&diff=') == -1) importStylesheetURI('/skins-1.5/common/diff.css')
 dfPopupSheet = mw.util.addCSS('\
.df-clicked {background-color:#E0E0E0}\
a.df-clicked-last {background-color:#FFDDDD}\
.ui-widget-content a {color:#0645AD}\
')
}

/*



div.df-popup{position:absolute; z-index:5; width:95%; border:1px solid #000033; \
 font-size:110%; background-color:white; visibility:hidden;}\
div.df-popup div.df-caption{float:none; background:#F0F0FF; font-size:120%;\
 border:1px outset gray; padding:2px}\
div.df-popup table.diff {width:97%; margin:0 1% 0 1% }\
*/


var dfHighlightSheet, dfImprovementSheet
function  addDiffTableCSS(){
 if( dfImprovementSheet ) return

 dfImprovementSheet = mw.util.addCSS('\
td.diff-addedline, td.diff-deletedline {font-size:100%}\
table.diff {border-spacing:1px}\
table.diff td {vertical-align:top}\
table.diff {width:99%}\
body table.diff, td.diff-otitle, td.diff-ntitle {background:#FBFBFB}\
table.diff td.diff-lineno {border-top: 25px solid #FBFBFB}\
tr.df-deleted td {background-color:#FEC}\
table.diff td div {min-height:1em}\
td.df-deletedwords, td.df-addedwords\
 {background:white; border:1px dotted gray; padding:2px}\
td.df-deletedwords span.diffchange {background-color:#FFA}\
td.df-addedwords span.diffchange {background-color:#CFC; color:black; font-weight:normal}\
tr.odd td.diff-addedline {background-color:#BEB}\
span.sig {border:1px dotted gray; border-bottom:none; font-family:cursive; font-size:90%}\
td.df-header {font-weight:bold; font-size:120%; padding-top:15px}\
tr.df-change td {font-size:100%}\
tr.df-added ins.diffchange {color:inherit; font-weight:normal; border:none}\
div.df-toolbar span.df-improve-btn {border:1px inset #EEEEEE}\
.df-btn {padding:2px; border:1px solid gray; margin-right:2px}\
table.diff td {cursor:help}\
table.diff td div, table.diff td.diff-multi {margin:0 8px 0 2px; cursor:default}'
+ (window.dfDiffTableCSS || '')) //user CSS

//cursor stuff shows that TD is clickable (sticking out from under DIV inside)

//padding: 0 8px 0 2px

//different approach to make cells clickable
//+ 'table.diff {border-spacing:0} table.diff td {border-top:4px solid #FBFBFB} table.diff td.diff-deletedline {border-right: 6px solid #FBFBFB}'
}






//==============================================================================



function improveTable(tbl){
 if (window.dfMaxImproveSize && tbl.html().length>dfMaxImproveSize) return
 //improve rows
 //curTitle = tbl.parent()[0].diffTitle //needed in improveCell() for relative links
 curTitle = 'AA' // !!!!
 curStripes = false
 addDiffTableCSS()
 tbl.find('tr').each(improveRow)
 tbl.click(tableOnclick)
}



function improveRow(){
 
 var tr = this, tds = $(this).find('td'), td


 if (tds.length <= 2) return // 'diff info' or 'intermediate revisions' or 'Line xx:'
   //  case 'diff-otitle': case 'diff-multi': case 'diff-lineno':
  
 if (tds.length == 3){
   if (tds[1].className == 'diff-deletedline'){
     tr.className += ' df-deleted' //new class, means the line was simply deleted, has pink background
     //if (tds[1].innerHTML.length==0) tds[1].innerHTML = '<div>&nbsp;</div>'
     expandCell(tds[1]) 
     return
   }else if (tds[2].className == 'diff-addedline'){
     tr.className += ' df-added'
     improveCell(tds[2])
     htm = tds[2].innerHTML
     if (htm.length==0) tds[2].innerHTML = '<div>&nbsp;</div>'
     if (curStripes) tr.className += ' odd'
     if (/<span class="sig">/i.test(htm)) curStripes = !curStripes
     expandCell(tds[2])
     return
   }
 }else if (tds[1].className == 'diff-context'){
   tr.className = 'df-context'
   if (window.dfParseContext) improveCell(tds[1])
   expandCell(tds[1])
   curStripes = false
   return
 }
 //normal yellow/green rows with 4 cells
 tr.className = 'df-change'
 tds[1].colSpan = tds[3].colSpan = 2
 tr.removeChild(tds[2]); tr.removeChild(tds[0])
 if (!window.dfImproveAdvanced) return


 tds = tr.getElementsByTagName('td')
 var oldline = tds[0].innerHTML
 var newline = tds[1].innerHTML

 //check for simple  diffs
 if (oldline.length < 90 && oldline.replace(/<.+?>| |\n/g,'') == ''){ //old empty
   tds[1].className = 'diff-addedline'
   tds[1].innerHTML = newline.replace(/<span class="diffchange">/i,'').replace(/<\/span>/i,'')
   improveCell(tds[1])
   splitRowsUp(tds[0])

 }else if (newline.length < 90 && newline.replace(/<.+?>| |\n/g,'') == ''){ //new empty
   tds[0].className = 'df-deletedline' // !!! replace wih tr-deleted
   tds[0].innerHTML = oldline.replace(/<span class="diffchange">/i,'').replace(/<\/span>/i,'')
   splitRowsUp(tds[1])

 }else if (!/<span/i.test(newline)){ //simple change: something removed
   improveCell(tds[0])
   expandCell(tds[0], 'df-deletedwords')

 }else if (!/<span/i.test(oldline)){ //simple change: something added
   improveCell(tds[1])
   expandCell(tds[1], 'df-addedwords')

  }else{ //complex case
   //improveCell(tds[1])
   //improveCell(tds[3])
   if (window.dfOneColumn){ // separate into different rows anyway
    tds[0].firstChild.style.borderTop = '10px solid #FBFBFB'
    tds[1].firstChild.style.borderBottom = '10px solid #FBFBFB'
    splitRowsUp(tds[0])
   }else{
    //remove + / - markers ?
   }
 }

function expandCell(td, clss){
 while (td.nextSibling) tr.removeChild(td.nextSibling)
 while (td.previousSibling) tr.removeChild(td.previousSibling)
 td.colSpan = 4
 if (clss) td.className = clss
}


 function splitRowsUp(tdGoesUp){
  tds[0].colSpan = 4
  tds[1].colSpan = 4
  //tr.removeChild(tds[2])
  //tr.removeChild(tds[0])
  var trnew = document.createElement('tr')
  trnew.className = 'df-change'
  trnew.appendChild(tdGoesUp)
  tr.parentNode.insertBefore(trnew, tr)
 }

} //improveRow()









function improveCell(cell){
 if (window.dfNoWikiParsing) return

 cell = $(cell)
 var htm = cell.html()
 if (htm.length == 0) return  //cell.innerHTML = '&nbsp;'

 cell.data('origHTML', htm)
 if (/^==.*== *$/i.test(cell.text())) cell.addClass('df-header') 
       

 htm = htm.replace(/\u00A0/g, '<b>\u00B7</b>')
 htm = htm.replace(/({\{u(?:ser(?:links)?)?\|)([^}]+)}\}/g, function(str,tmpl,user){
  return tmpl + outputLink2('special:contributions/'+user,'',user) + '}}' })

 //mark signatures
 htm = htm.replace(/(\[\[[^\[]{4,65})?\d\d:\d\d, \d\d? \S{3,9} 20\d\d \(UTC\)/g, '<span class="sig">$&</span>')
 htm = htm.replace(/\{\{unsigned[^\}]\}\}/i, '<span class="sig">$&</span>')

 //[[link]]
 var CatOrFileRegExp = RegExp('^('+wgFormattedNamespaces[6]+'|'
  +wgFormattedNamespaces[14]+'|category|image|file):|\.(jpg|png|svg|gif)$','i')
 htm = htm.replace(/\[\[([^\]><}{|]+)\|?([^\]><]*)?\]\]/g,
 function(wikicode,page,name){
  if (/http:\/\//i.test(page)) return wikicode //user made a mistake
  if (CatOrFileRegExp.test(page)) name = page+(name?'|'+name:'') //file or category, show in full
  else if (!name) name = page
  if (/^[#\/]/.test(page)) page = curTitle + page //relative link
  else if (/^[a-z]{2,7}:/.test(page)) page = 'Special:Search/'+page //possible interproject link, some are not "local"
  return outputLink2(page, '', name, wikicode)
 })

 // [http://...]
 htm = htm.replace(/\[(https?:\/\/[^ \]><]*)( [^\]]*)?\]/g, //
 function  (str,link,name){
  var output = '<a href=' + link, title, tip, nameWas = name
  if (link.indexOf(wgServer+wgScript) == 0){ //local link
    tip = tryDecodeURI(link.substring((wgServer+wgScript).length+1))
    if (!name){
      name = getTitleFromURL(link) || tip
      if (/diff=/.test(link)) name = 'diff: ' + name
      else if (tip.match(/action=history/)) name = 'hist: ' + name
      else if (tip.match(/oldid=/)) name = 'oldid: ' + name
      else name = 'wiki: ' + name
    }
  } else { //ext link
    tip = tryDecodeURI(link.substring(7))
    output += ' class="external text"'
    if (!name) name = tip
  }
  if (!nameWas && (name.length > 70)) name = name.substring(0,60) + '… …'
  return output + ' title="' + tip + '">[' + name + ']</a>'
 })

 cell.html(htm)

function tryDecodeURI(s){ try{s=decodeURIComponent(s)}catch (e){}; return s }

}




function outputLink2(page, params, name, tooltip){
 name = name || page
 page = page.replace(/&amp;/gi,'&').replace(/#.+$/, calcAnchor)
 return '<a href="'+ wgArticlePath.replace(/\$1/,'')
  + encodeURI(page).replace(/\?/g,'%3F') 
  + (params||'')
  + '" title="' + (tooltip||name).replace(/"/g,'&quot;') + '">' + name + '</a>' //'
}


function calcAnchor(txt){ //try to create href anchor similar to Parser.php::guessSectionNameFromWikiText() 
 txt = $.trim(txt).replace(/#/g,'')
      .replace(/\[\[([^|]+\|)?([^\]]+)\]\]/g, '$2') //[[foo|bar]] -> bar
      .replace(/<.*?>/g, '').replace(/ /g,'_')
       // (we skip step "HTML entities are turned into their proper characters")
 return '#' + encodeURIComponent(txt).replace('%3A', ':').replace(/%([0-9A-F][0-9A-F])/g, '.$1')
 //maybe encodeURI(p1.replace(/\?/g,'%3F').replace(/&/g,'%26'))  ?
}


function getTitleFromURL(url){
 var tt = /title=([^&>"]+)/.exec(url) //"
 if( tt ) decodeURIComponent(tt[1]).replace(/_/g,' ')
 else return ''
}


function tableOnclick(e){
 var trg = e.target
 if( trg.className ) switch ( trg.className ){
  //case 'diff-lineno': changeBlock(trg.parentNode); return
  //case 'diff-otitle': case 'diff-ntitle': return
  case 'diff-addedline': case 'diff-deletedline': case 'diff-context':
   //if( trg.nodeName != 'TD' || $(trg).parents('table.diff').length == 0 ) break 
   //parse wikicode in already improved row when clicked on white border above row
   var orig = $(trg).data('origHTML')
   if( orig ) $(trg).html(orig).data('origHTML','')
   else improveCell(trg)
   return
 }
}


function changeBlockXXX(row){ //switch improvement level for this part of diff table
 var dTbody = row.parentNode, rowIdx = 1, isImproved
 //find clicked row number
 while (rowIdx < dTbody.rows.length && dTbody.rows[rowIdx] != row) rowIdx++
 if (dTbody.rows[rowIdx] != row) return
 //check if rows are improved or not
 if (row.className == 'df-lineno'){
   isImproved = true
   var origTable = createTableFromHTML(requestedPages[dTbody.parentNode.parentNode.diffURL])
 }else
   dfImprovementSheet.disabled = false
 //improve / de-improve rows
 do{
   if (isImproved) dTbody.replaceChild(origTable.rows[rowIdx].cloneNode(true), row)
   else improveRow(row)
 }while((row=dTbody.rows[++rowIdx]) && row.cells[0].className != 'diff-lineno')

}




})