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 22:15, 26 March 2008 (async Ajax call, interface changes, JS diff with full revisions, lots of new parameters (see top of the script), lots of other fixes). 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.
/*
Parameters, most should be set to true to override standard behaviour:
dfNoPopups: no popups
dfNoAjaxDiff: no quick next/prev loading on usual diff pages
dfNoArrowKeys : disable keyboard shortcuts (left/right arrow keys)  for  next/prev diff
dfNoWikiParsing : completely disable wikicode parsing in diffs
dfParseContext : also parse gray columns
dfNoLargerFont: set to true to keep normal small font for changed areas
dfNoWaitCursor : set to true to disable cursor changing to "wait"
wikEdFullDiff: do not shorten JS Diff Engine output (pretty much useless)

dfImproveAdvanced :  imrove diffs even more (make simple diffs as one column)
dfOneColumn : convert all diffs to one column view

dfDiffLinksCSS: set this to CSS string to replace default  "{font-style:italic}" for all diff links

*/




var diffScript = new function(){ //wrapper

var dfNoWikiParsing = window.dfNoWikiParsing
var currentLink, currentTable
var noImprove, curTitle
var requestedPages = {} //aray of retreived diffs, used as cache
var isIE = navigator.userAgent.indexOf('MSIE') != -1
var popupCount = 0 //, popupArray = []

this.start = function(){

 addHandler(document, 'click', onClick)
 if (!window.dfNoArrowKeys) addHandler(document, 'keyup', onKeyUp)
 if (!window.dfNoPopups) addCSS('a[href^="'+wgServer+'/w/index.php"][href*="diff="],\
  a[href^="/w/index.php"][href*="diff="]' + (window.dfDiffLinksCSS || '{font-style:italic}'))
 //if (isIE &&  isIE6) //no CSS3 support in IE6, should make links italic manually

 if (document.URL.indexOf('diff=') != -1){ //this is a diff page
   //find diff table
   var content = document.getElementById('content') || document.getElementById('mw_content') || document
   var i = 0, tables = content.getElementsByTagName('table')
   while (i < tables.length && tables[i].className != 'diff') i++
   if (i == tables.length) return  //then it's  div.firstrevisionheader
   currentTable = tables[i]
   //remember url and title
   currentTable.parentNode.diffURL = document.URL
   currentTable.parentNode.diffTitle = wgPageName
   //improve diff Table
   requestedPages[document.URL] = '<table class=diff">' + currentTable.innerHTML + '</table>'
   improveDiff()
   currentTable.parentNode.insertBefore(createToolbar(), currentTable.parentNode.firstChild)
 }

}



function onClick(e){
 cursorNormal() //cancel waiting indicator if something went wrong
 //check if this was on a diff link
 e = e || window.event
 if (e.shiftKey || e.button == 2) return // shift key or right click
 if ((isIE && e.button == 4) || (!isIE &&  e.button == 1)) return //middle click
 var lnk = e.target || e.srcElement
 var url = lnk.href
 if (!url || url.indexOf('diff=') == -1) return //not a diff
 if (url.indexOf(wgServer) != 0 && url.charAt(0) != '/') return //diff, but not to this server
 if (lnk.parentNode.className == 't-print') return
 //check if it was nex/prev diff link
 currentLink = lnk
 var isClickIntercepted = true
 if (/differences-(prev|next)link/.test(lnk.id)) isClickIntercepted = followNextLink()
 else if (!window.dfNoPopups) createPopup()
 else isClickIntercepted = false
 //do not follow the link
 if (isClickIntercepted){
   if (e.preventDefault) e.preventDefault(); else e.returnValue = false
   return false
 }else
   return true
}


function onClickError(msg){
 jsMsg(msg)
 markLink(currentLink, true)
 return false
}




function improveDiff(){

 if (noImprove || currentTable.innerHTML.length>70000) return
 addDiffTableCSS()

 var trs = currentTable.getElementsByTagName('tr'), tr, tds, td,  stripes = false
 curTitle = currentTable.parentNode.diffTitle //to be used in processHTML()
 //trs[0].className = 'df-header'

for (var i=1; i<trs.length; i++){

 tr = trs[i]
 tds = tr.getElementsByTagName('td')
 tr.className = 'df-change' //by default, as most common case

 if (tds.length == 1){  // 'One intermediate revision not shown'
   //tr.className = 'message'
   continue

 }else if (tds.length == 2){ // "Line xx:"
   tr.className = 'df-lineno'
   expandCell(tds[1])
   continue

 }else if (tds.length == 3 && tds[1].className == 'diff-deletedline'){
   if (tds[1].innerHTML.length==0) tds[1].innerHTML = '&nbsp;'
   expandCell(tds[1], 'df-deletedline') //new class, means the line was simply deleted, has pink background
   continue

 }else if (tds.length == 3 && tds[2].className == 'diff-addedline'){
   processHTML(tds[2])
   htm = tds[2].innerHTML
   if (htm.length==0) tds[2].innerHTML = '&nbsp;'
   if (stripes) tr.className += ' odd'
   if (/<span class="?sig"?>/i.test(htm)) stripes = !stripes
   if (/^<div>==.*== *<\/div>$/i.test(htm))
      tds[2].style.cssText = 'font-weight:bold; font-size:120%; padding-top:15px'
   expandCell(tds[2])
   continue

 }else if (tds[1].className == 'diff-context') {
   tr.className = 'df-context'
   if (window.dfParseContext) processHTML(tds[1])
   expandCell(tds[1])
   stripes = false
   continue
 }

 //from here we're left with normal yellow/green rows with 4 cells
 if (!window.dfImproveAdvanced) continue

 //check for simple  diffs
 var oldline = tds[1].innerHTML
 var newline = tds[3].innerHTML

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

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

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

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

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

} // for



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[1].colSpan = 4
 tds[3].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)
 i++ //increment i in for loop
}


} //improveDiff()



function changeLevel(e){
 e = e || window.event
 var butt = e.target || e.srcElement
 currentTable = getClickedTable(e)
 var dfLevel = butt.innerHTML
 if (dfLevel == '-'){
   noImprove = false
   //dfNoWikiParsing = true
   butt.innerHTML = '='
   //restore
  improveDiff()
 }else if (dfLevel == '='){
//   dfNoWikiParsing = false
//   butt.innerHTML = 'Ξ'
// }else if (dfLevel == 'Ξ'){
   noImprove = true
   butt.innerHTML = '-'
   replaceTable(requestedPages[currentTable.parentNode.diffURL])
 }
}



function createToolbar(){
 var dToolbar = document.createElement('div')
 //dToolbar.diffTable = currentTable
 dToolbar.className = 'df-toolbar'
 dToolbar.appendChild(btn(changeLevel, '=', 'Enable/disable diff improvement'))
 dToolbar.appendChild(btn(diffJSEngine, 'js', 'Show diffs using JS engine'))
 dToolbar.appendChild(btn(highlightDiffs, 'hl', 'Highlight diffs with red border', 'df-but-highlight'))
 //references back and forth
 dToolbar.tableParent = currentTable.parentNode //reference so we can find table later
 return dToolbar
}

function btn(func, htm, tooltip, clss){ //creates <span> button
 var bt = document.createElement('span')
 bt.innerHTML = htm
 bt.onclick = func
 bt.title = tooltip
 bt.className = 'df-but'
 if (clss) bt.className += ' ' + clss
 return bt
}



function getClickedTable(e){
 e = e || window.event
 var button = e.target || e.srcElement
 return button.parentNode.tableParent.getElementsByTagName('table')[0]
}


function checkNOBRs(){
 var cntAdded = cntRemoved = 0
if (cntRemoved>0 || cntAdded>0){
  //hide context rows that are not needed anymore
 for (var i=1; i<trs.length-1; i++)
  if (tr.className.match(/df-context|df-lineno/)
    && !trs[i-1].className.match(/df-change/) && !trs[i+1].className.match(/df-change/))
    tr.style.display='none'

 //add notice that 0x0A changes are hidden
 var newtd = document.createElement('td')
 newtd.colSpan=4;  newtd.style.textAlign='center'; newtd.style.fontWeight = 'bold'
 var notice = (cntRemoved>0)?' задушено: '+cntRemoved:''
 notice += (cntAdded>0)?' вставлено: '+cntAdded:''
 newtd.innerHTML = '<span style="border:2px solid orange; padding:2px">Неразрывных пробелов' + notice + '</span>'
 trs[1].parentNode.insertBefore(newtd, trs[1])
}
}





function processHTML(elem){

 if (dfNoWikiParsing) return

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

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

 //[[link]]
 html = html.replace(/\[\[([^\]><}{|]+)\|?([^\]><]*)?\]\]/g,
 function(wikicode,page,name){
  if (/http:\/\//i.test(page)) return wikicode //user made a mistake
  if (isImagePage(page) && name)  name = page + name  //display full image link, including "image:"
  if (!name) name = page
  if (page.substring(0,1)=='#' || page.substring(0,1)=='/') {
    page = curTitle + page //relative link
  }
  return outputLink(page, name, wikicode)
 })



 // [http://...]
 html = html.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>'
 })

 elem.innerHTML = html

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

}



function followNextLink(){ //loads next/prev diff using next/prev diff link
 //find old table
 var el = currentLink
 while (el && el.nodeName != 'TABLE') el = el.parentNode
 if (!el || el.className != 'diff') return false
 currentTable = el
 if (currentTable.parentNode.className != 'df-popup'){ //normal diff page, not a popup
   if (window.dfNoAjaxDiff || isImagePage(getTitleFromURL(currentLink.href))) return false
   if (el=document.getElementById('t-print')) el.style.display = 'none' //hide "printable" link which becomes outdated
 }
 //replace diff with new table
 loadDiff()
 return true
}



function replaceTable(html){
 var oldTable = currentTable
 //create temp div with new table
 var el, tempDiv = document.createElement('div')
 tempDiv.innerHTML = html
 currentTable = tempDiv.getElementsByTagName('table')[0]
 //insert temp div
 oldTable.parentNode.insertBefore(tempDiv, oldTable)
 //remove old table and (now outdated) prevew below
 while ((el=tempDiv.nextSibling) && !/visualClear|df-caption/.test(el.className))
   el.parentNode.removeChild(el)
 //get rid of temporary DIV
 while (el=tempDiv.firstChild)
   tempDiv.parentNode.insertBefore(el, tempDiv)
 tempDiv.parentNode.removeChild(tempDiv)
}










// *** POPUP ***


function createPopup(lnk){
 if (lnk) currentLink = lnk
 //unfortunately, image diffs do not work with &action=render
 if (isImagePage(getTitleFromURL(currentLink.href)))
   return onClickError('Sorry, diffPopup doesn\'t work with diffs to images pages')
 //initialize
 markLink(currentLink)
 addDiffPopupCSS()
 if (isIE) hideAllSelectElements()
 //create popup
 popupCount++
 var pop = document.createElement('div')
 pop.className = 'df-popup'
 pop.style.top = windowScrolled() + 30  + popupCount*10 + 'px'
 pop.style.left = 10 + popupCount*10 + 'px'
 //load diff
 currentTable = document.createElement('table')
 pop.appendChild(currentTable)
 loadDiff(createPopup2)
}


function createPopup2(){
 var pop = currentTable.parentNode
 //add bottom and top captions
 pop.appendChild(createCaption())
 pop.insertBefore(createCaption(), pop.firstChild)
 document.body.appendChild(pop)
 return true
 //if it's too wide
 //if (elementWidth(pop) +10 > windowWidth()) scrollBy(elementWidth(pop), 0)
 function createCaption(){ //for popup
  var dCaption = document.createElement('div')
  dCaption.className = 'df-caption'
  dCaption.onclick = captionClick
  //on special pages
  if (wgNamespaceNumber == -1){
   dCaption.appendChild( btn(function(e){neighborDiff(e,currentLink,  1)},
     '↓', 'Open next diff link on that page'))
   dCaption.appendChild( btn(function(e){neighborDiff(e,currentLink, -1)},
     '↑', 'Open previous diff link on that page'))
  }
  //add page title
  var sp = document.createElement('span')
  var dTitle = currentTable.parentNode.diffTitle
  sp.innerHTML = '<b>'+outputLink(dTitle)+'</b>'
   + ' (' + outputLink(dTitle+'?action=history', 'h') + ')'
  dCaption.appendChild(sp)
  //add toolbar
  dCaption.insertBefore(createToolbar(), dCaption.firstChild)
  return dCaption
 }
}


function captionClick(e){
 e = e || window.event
 var el = e.target || e.srcElement
 if (el && el.className && el.className == 'df-caption') closePopup(el.parentNode)
}

function closePopup(div){
 div.style.display = 'none'
 purge(div)
 div.parentNode.removeChild(div)
 popupCount--
 if (isIE && (popupCount==0)) hideAllSelectElements(true) //IE  select zIndex bug
}

function onKeyUp(e){
 e = e || window.event
 var lnk
 if (e.keyCode == 37) lnk = document.getElementById('differences-prevlink')
 else if (e.keyCode == 39) lnk = document.getElementById('differences-nextlink')
 if (!lnk) return
 currentLink = lnk
 if (!followNextLink()) document.location.href = lnk.href
}

function markLink(lnk, isError){ //mark link as 'clicked'. especially useful for watchlist/RC
 lnk.style.backgroundColor = isError ? '#FFDDDD' :'#DDDDFF'
}

function hideAllSelectElements(isRestore){ //workaround for IE  <select> zIndex bug
 var sels = document.getElementsByTagName('select')
 for (var i=0; i<sels.length; i++)
   sels[i].style.visibility = isRestore ? 'visible' : 'hidden'
}

function neighborDiff(e, lnk, dir){
 //close old popup
 e = e || window.event
 var el = e.target || e.srcElement
 closePopup (el.parentNode.parentNode)
 //try to find diff in neghbor TR (this happens inside expanded  section of enhanced RC/WL)
 if ((el=findParentWithTag(lnk,/TR/)) && (el=findNeighborBlock(el)) && (el=findDiffIn(el)))
   createPopup(el)
 //try to find diff in neghbor LI (on contrib page and simple RC)  or TABLE (on enhanced RC and WL)
 else if ((el=findParentWithTag(lnk,/LI|TABLE/)) && (el=findNeighborBlock(el)) && (el=findDiffIn(el)))
   createPopup(el)
 //functions
 function findParentWithTag(el, regexp){
   while ((el=el.parentNode) && !regexp.test(el.nodeName));
   return el
 }
 function findNeighborBlock(el){ //looks for next/prev block with the same tag
   var tag = el.nodeName
   do{ el = (dir == -1) ?  el.previousSibling : el.nextSibling } while (el && el.nodeName != tag)
   return el
 }
 function findDiffIn(block){
   var aa = block.getElementsByTagName('a')
   for (var i=0; i<aa.length; i++)
     if (/diff=/.test(aa[i].href)) return aa[i]
 }
}



// *** JS Diff Engine ***
function diffJSEngine(e){
 //create temporary table with original diff
 var dTable = getClickedTable(e)
 var curTitle = dTable.parentNode.diffTitle
 var tmpDiv = document.createElement('div')
 tmpDiv.innerHTML = requestedPages[dTable.parentNode.diffURL]
 var tempTable =  tmpDiv.getElementsByTagName('table')[0] 
 //create "full comparison" button
 var btFull = document.createElement('input');  btFull.type = 'button'
 btFull.value = 'Perform full comparison'
 btFull.title = 'Compare full revisions, in case MediaWiki diff engine is wrong'
 btFull.onclick = diffJSFull

 //call and run JS diff engine
 if (window.WDiffString) diffJSGo()
 else importScriptAndRun('http://en.wikipedia.org/w/index.php?title=User:Cacycle/diff.js', diffJSGo)

 function diffJSGo(){
  cursorWait()
  //var
  oldVer = newVer = ''
  tds = tempTable.getElementsByTagName('td')
  var isContent
  for (var i=1; i<tds.length; i++){
    td = tds[i]
    switch(td.className){
    case 'diff-context':
       //oldVer += td.innerHTML.replace(/<.+?>/g,'') + '<hr>'
       //newVer += td.innerHTML.replace(/<.+?>/g,'') + '<hr>'

     /*
     if (isContent){
       marker = '\x03' + td.innerHTML.replace(/<.+?>/g,'') + '\x04'
       oldVer += marker
       newVer += marker
       isContent = false
     }*/
     i++
     break
    case 'diff-lineno':
     break  // !!!
    case 'diff-deletedline':
     isContent = true
     oldVer += '\n' + td.innerHTML.replace(/<.+?>/g,'')
     break
    case 'diff-addedline':
     isContent = true
     newVer += '\n' +  td.innerHTML.replace(/<.+?>/g,'')
     break
    }
   }
  difDiv = document.createElement('div')
  difDiv.style.cssText = 'padding:2px'
  difDiv.innerHTML = '<br><br><h3>JS Engine diff</h3><hr style="height:5px" />'
  diffJSDisplay()
  difDiv.appendChild(document.createElement('hr'))
  difDiv.appendChild(btFull)
  dTable.parentNode.insertBefore(difDiv, dTable.nextSibling)
 }

 function diffJSFull(){
  btFull.parentNode.removeChild(btFull)
  //get versions Ids
  var ma, newId, oldId
  ma = tempTable.rows[0].cells[0].innerHTML.match(/oldid=(\d+)/)
  if (ma) oldId = ma[1]; else { difDiv.innerHTML += '<p class=error>Could not find oldid</p>'; return }
  ma = tempTable.rows[0].cells[1].innerHTML.match(/oldid=(\d+)/)
  if (ma) newId = ma[1]; else newId = ''
  //request versions
  difDiv.innerHTML += '<br><br><h3>Full revisions comparison</h3><hr style="height:5px" />'
  cursorWait()
  oldVer = newVer = null
  var url = wgServer + wgScript + '?action=raw&maxage=0&title=' + encodeURIComponent(curTitle)
  requestPage(url + '&oldid=' + oldId, function(txt) {
    oldVer = txt
    if (newVer) diffJSDisplay()
  })
  requestPage(url + '&oldid=' + newId, function(txt) {
	  newVer = txt
	  if (oldVer) diffJSDisplay()
  })
 }

 function diffJSDisplay(){
   var txt = WDiffString(oldVer, newVer)
  //txt = txt.replace(/\x03.*?\x04/g, '<br><br><hr><br><br>')
  if (!window.wikEdFullDiff ) txt = WDiffShortenOutput(txt)
  //txt = txt.replace(/¶/g,'<br>')
  txt = txt.replace(/&amp;/g,'&')
  //txt = txt.replace(/&lt;/g,'<').replace(/&gt;/g, '>')
  difDiv.innerHTML += txt
  cursorNormal()
 }

}//diffJSEngine






// *** COMMON ***

function windowScrolled(){
if (self.pageYOffset) // all except Explorer
 return self.pageYOffset
else if (document.documentElement && document.documentElement.scrollTop)	// Explorer 6 Strict
 return document.documentElement.scrollTop
else if (document.body) // all other Explorers
 return document.body.scrollTop
}

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

function isImagePage(page){
 return /^image:|\.(jpg|png|svg|gif)$/i.test(page)
}

function outputLink(page, name, tooltip){
 if (!name) name = page
 if (!tooltip) tooltip = name //.replace(/'/g,'`')
 page = page.replace(/&amp;/gi,'&')
 var k = page.indexOf('#')
 if (k != -1) page = page.substring(0,k) + '#' + guessSectionName(page.substring(k+1))
 return '<a href="'+wgServer+wgArticlePath.replace('$1', page)
    +'" title="' + tooltip.replace(/"/g,'%22') + '">' + name + '</a>' //'
}

function guessTOCName(txt){ //make header into TOC item, will not work 100%
 txt = txt.replace(/^ */,'').replace(/ *$/,'') //trim spaces
 txt = txt.replace(/\[\[[^|]+\|([^\]]+)\]\]/g, '$1') //[[foo|bar]] -> bar
 txt = txt.replace(/\[\[([^\]]+)\]\]/g, '$1') //[[bar]] -> bar
 txt = txt.replace(/<.*?>/g, '').replace(/ /g,'_') //strip tags, spaces -> _
 return txt
}

function guessSectionName(txt){ //make header into URL anchor, will not work 100%
 //should behave like like Parser.php::guessSectionNameFromWikiText() and Sanitizer.php
 txt = guessTOCName(txt)
 //... skipping step "HTML entities are turned into their proper characters"
 txt = encodeURIComponent(txt)  //maybe encodeURI(p1.replace(/\?/g,'%3F').replace(/&/g,'%26'))
 txt = txt.replace('%3A', ':').replace(/%([0-9A-F][0-9A-F])/g, '.$1')
 return txt
}

function cursorWait(){
 if (!window.dfNoWaitCursor) document.body.style.cursor = 'wait' 
}
function cursorNormal(){
 if (!window.dfNoWaitCursor) document.body.style.cursor = '' 
}


// *** CSS/JS ***
var doneDiffTableCSS, doneDiffPopupCSS, highlighDiffCSS

function  addDiffTableCSS(){
//table.diff td {padding-left:3px}\
//td.diff-addedline, td.diff-deletedline, td.df-deletedline
 if (doneDiffTableCSS) return
 addCSS('\
table.diff, td.diff-otitle, td.diff-ntitle {background:#FBFBFB}\
tr.df-lineno td.diff-lineno {padding-top:30px; font-size:80%; font-weight:normal}\
td.df-deletedline {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%}\
div.df-toolbar{float:right; border:1px solid gray}\
span.df-but {cursor:pointer; padding:0 3px 0 3px; margin:0 3px 0 3px; border:1px dotted gray}'
+ (window.dfNoLargerFont ? '' : 'tr.df-change td {font-size:100%; padding-left:3px'))

 doneDiffTableCSS = true
}

function addDiffPopupCSS(){
 if (doneDiffPopupCSS) return
 importCSS('/skins-1.5/common/diff.css')
 addCSS('\
div.df-popup{position:absolute; margin-right:15px; border:1px solid #000033; z-index:50; \
 font-size:130%; background-color:white}\
div.df-popup div.df-caption{float:none; background:#F0F0FF; font-size:120%;\
 border:1px outset gray; padding:2px; cursor:pointer}\
div.df-popup div.df-toolbar {border:none}')
 doneDiffPopupCSS = true
}
//div.df-popup table.diff {width:auto}\

function highlightDiffs(e){
 if (!highlighDiffCSS)
   highlighDiffCSS = addCSS('span.diffchange{border:1px solid red}\
 span.df-but-highlight {border:1px inset gray}')
 else highlighDiffCSS.disabled = !highlighDiffCSS.disabled
}

function importCSS(url){
 if (document.createStyleSheet) var s = document.createStyleSheet(url) //IE
 else {
   var s = document.createElement('style')
   s.type = 'text/css'
   s.appendChild (document.createTextNode('@import "' + url + '";'))
   document.getElementsByTagName('head')[0].appendChild(s)
 }
 return s
}

function addCSS(text){ //could use createStyleSheet for IE as well
 var s = document.createElement('style')
 s.setAttribute('type', 'text/css')
 if (s.styleSheet) s.styleSheet.cssText = text //IE
 else s.appendChild(document.createTextNode(text))
 document.getElementsByTagName('head')[0].appendChild(s)
 return s.sheet || s //so we can disable it later, Safari insists on .sheet
}

function importScriptAndRun(url, func) {
 var s = document.createElement('script')
 s.type = 'text/javascript'
 s.src = url + '&action=raw&ctype=text/javascript'
 if (isIE) s.onreadystatechange = function(){
     if (this.readyState == 'loaded' || this.readyState == 'complete') func()
   }
 else s.onload = func
 document.getElementsByTagName('head')[0].appendChild(s)
}



// *** AJAX ***
function requestPage(url, func){
 if (requestedPages[url]) {func(requestedPages[url]); return }
 var aj = sajax_init_object()
 aj.open('GET', url, true)
 aj.onreadystatechange=function() {
   if (aj.readyState != 4) return
   if (aj.status == 200) {
      requestedPages[url] = aj.responseText
      func(aj.responseText)
   }else func(null)
 }
 aj.send(null)
}



function loadDiff(func){
 currentTable.style.opacity = '0.5'
 cursorWait()
 var url = currentLink.href + '&action=render&diffonly=yes'
 requestPage(url, loadDiff2)
 function loadDiff2(html){
  replaceTable(html)
  //remember diff url and title
  currentTable.parentNode.diffURL = url
  var td = getElementsByClassName(currentTable, 'td', 'diff-ntitle')[0]
  currentTable.parentNode.diffTitle = td ? getTitleFromURL(td.getElementsByTagName('a')[1].href) : '??'
  //improve
  improveDiff()
  if (func) func()
  cursorNormal()
 }
}


/*
function dispDiffInfo(dTable){
 var td = getElementsByClassName(dTable, 'td', 'diff-ntitle')[0]
 if (!td) return
 // emphasize if this is current revision
 //var aa = tds[1].getElementsByTagName('a')[0]
 //if (!/oldid=/.test(aa.href)) aa.style.cssText = 'border:2px solid green; padding:0 2px 0 2px'
 //get title from (edit) link
 dTable.parentNode.diffTitle = getTitleFromURL(td.getElementsByTagName('a')[1].href)
}
*/

//http://javascript.crockford.com/memory/leak.html
function purge(d) {
    var a = d.attributes, i, l, n;
    if (a) {
        l = a.length;
        for (i = 0; i < l; i += 1) {
            n = a[i].name;
            if (typeof d[n] === 'function') {
                d[n] = null;
            }
        }
    }
    a = d.childNodes;
    if (a) {
        l = a.length;
        for (i = 0; i < l; i += 1) {
            purge(d.childNodes[i]);
        }
    }
}

} //wrapper





addOnloadHook(diffScript.start)