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 06:18, 15 October 2011 (https support). 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(){


var dfPopupSheet
var clickedLink
var dfPopups = []

mw.util.addCSS('\
a[href*="diff="][href^="/w"],\
a[href*="diff="][href*="' + wgServer + '"]' +
 (window.dfDiffLinksCSS || '{font-style:italic}') +'\
a[href*="diff="][href^="/w"]:hover,\
a[href*="diff="][href*="' + wgServer + '"]:hover\
 {color:green !important}\
.df-popup {margin:0.5em}\
')
 

mw.util.addCSS('\
td.diff-addedline:hover .diffchange, td.diff-deletedline:hover .diffchange {border-bottom:1px solid red}\
')
/*
, td.diff-deletedline:hover .diffchange 
tr.df-change:hover .diffchange-inline, tr.df-change:hover .diffchange {border-top:1px solid red}\

*/
 

 
 var $tbl = $('table.diff')
 if( $tbl.length ){
   improveTable($tbl)
   dfAddToolbar($tbl, '#contentSub')
 }

 var dfURLRegExp = new RegExp('^(https?:' +  wgServer.replace(/([\.\/])/g,'\\$1') + ')?\/.*[&?]diff=')

 
 $(document)
 .click(dfClick)
 .keyup( function(e){ //close popup on Escape
   if( e.which == 27 ) closePopup( dfPopups[dfPopups.length-1] )
 })



function dfClick(e){

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

 switch( e.target.className ){
   case 'df-popup': closePopup(e.target); return //close popup when clicking on edges
   case 'df-caption': closePopup(e.target.parentNode); return // ... and caption
   case 'df-close':  closePopup( $(e.target).data('popup') ); return //little square
 }

 //otherwise see if clicked on link
 var clickedLink = $(e.target).closest('a')
 if( !clickedLink.length ) return
 
 var url = clickedLink.attr('href')
 if( ! dfURLRegExp.test(url)  ) return

 //we have diff link
 e.preventDefault()
 
 url = url.replace(/^https?:\/\/[^\/]+\//, '/') //relative URL to be able to fetch http/https diffs
 //url = decodeURI(url) // ???

 if( /differences-nextlink|differences-prevlink/.test(clickedLink.attr('id')) )
   return ajaxLoadDiff( clickedLink.closest('table.diff').parent(), url )

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

 //create popup
 var dfPopup = $('<div class=df-popup style="display:none" />')
 .css({
   top: $(window).scrollTop() + 30  + dfPopups.length * 5 + 'px',
   left: 10 + dfPopups.length * 5 + 'px'
 })  
 .appendTo(document.body)

 //create 'closing' square
 var dfClose = $('<div style="position:absolute; z-index:10; border:2px outset gray; width:20px; height:20px; cursor:pointer; background:gray; opacity:0.5; display:none" class=df-close title=close>&nbsp;</div>')
 .css({top: e.pageY-10, left: e.pageX-10})
 .appendTo(document.body)
 .mouseleave(function(){ $(this).remove() })
 
 //misc
 dfPopups.push(dfPopup)
 dfPopup.data('close', dfClose)
 dfClose.data('popup', dfPopup)
 
 //request diff 
 ajaxLoadDiff( dfPopup, url )
 // if (isIE) hideAllSelectElements(true) // !!!
 
 
 return false
  /* 
 else if( ! /\?/.test(url) && ! /(special|служебная):/i.test(url) ){
    url += '?action=render'
 }else{
   url += (/\?/.test(url) ? '&' : '?') + 'useskin=myskin #content'
 }
 */
 
 

}


function ajaxLoadDiff($container, url){
 cursorWait(true)
 $container
 .attr('diffURL', url)
 .load( url + '&action=render&diffonly=true', afterDiffLoaded )
}


function afterDiffLoaded(){

 cursorWait()
 var $container = $(this)
 var $tbl = $container.find('table.diff')
 if( ! $tbl.length ) return
 var dfLink = outputLink2('', $container.attr('diffURL'), 'Δ', 'current diff')
 improveTable( $tbl )

 if ( $container.hasClass('df-popup') ){

   var pgTitle = getTitleFromURL( $tbl.find('td.diff-ntitle a').attr('href') )
   var caption = $(
    '<div class=df-caption>' + 
    '<b>' + outputLink2(pgTitle) + '</b>' +
    ' (' + outputLink2(pgTitle, '?action=history', 'h') + ' ' + dfLink  + ')' +
    '</div>'
   )
   .prependTo($container)

   dfAddToolbar($tbl, caption)
   $container.show()
   $container.data('close').show()
 
 }else{
   $('#contentSub')
   .children('.df-link').remove().end()
   .append(
     $('<span class=df-link style="margin-left:1em; font-weight:bold" />')
     .append( dfLink )
   )
 }

}






 
 
 



 



function closePopup(pp){
 pp = $(pp)
 if( pp.data('close') ) pp.data('close').remove()
 var idx = pp.data('idx')
 dfPopups.splice( pp.data('idx'), 1 )
 pp.remove()
}



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

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}\
div.df-popup{position:absolute; z-index:5; width:95%; border:1px solid #000033; \
 font-size:110%; background-color:white;}\
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% 1% 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 = getTitleFromURL( $tbl.parent().attr('diffURL') )
 curStripes = false
 addDiffTableCSS()
 $tbl.find('tr').each(improveRow)
 $tbl.click(tableOnclick)
}


function dfAddToolbar($tbl, where){
 return 
 $('<div class=df-toolbar style="float:right" />')
 .append(
   btn(changeTable, '¤', 'Enable/disable improvements')
 )
 .prependTo(where)
 /*
   $tbl.closest('.df-popup').length
   ?  $tbl.closest('.df-popup').find('df-caption')
   : '#contentSub'
  )
*/  
 //btn(diffJSEngine, 'Δ', 'Javascript diff engine'))
 function changeTable(){alert(11)}
 function btn(func, txt, tip){ //creates <span> button
  return $('<span class=df-btn title="' + tip + '">' + txt + '</span>').click(func)
 } 
}









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 = '<br />'
     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 ) tds[2].innerHTML = '<br />'
     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
 }else{ //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 ) improveRowMore(tr)
 return 





 
 
 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
  */
  tr.innerHTML = '<td colspan=4 class="' + td.className + (clss?' ' + clss:'') + '">'
   + td.innerHTML + '</td>'
 }


 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 ) return 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')

}




})