User:Js/diffs.js

From Wikipedia, the free encyclopedia

Note: After saving, you have to bypass your browser's cache to see the changes. In Internet Explorer and Firefox, hold down the Ctrl key and click the Refresh or Reload button. Opera users have to clear their caches through Tools→Preferences, see the instructions for Opera. Konqueror and Safari users can just click the Reload button.

/*
Switches
--------
(these should be set to true to override standard behaviour)
 
dfNoAjaxDiff: no quick next/prev loading on usual diff pages
dfNoPopups: no popups
dfNoArrowKeys : disable keyboard shortcuts (left/right arrow keys)  for  next/prev diff
dfNoWikiParsing : completely disable wikicode parsing in diffs
dfParseContext : also parse gray columns
dfNoWaitCursor : disable "wait" cursor
wikEdFullDiff: do not shorten JS Diff Engine output (seems useless)
 
CSS parameters
--------------
dfDiffLinksCSS: CSS for diff links, default: "font-style:italic" 
dfDiffTableCSS: extra CSS for diff table
 
*/
 
 
 
 
var currentLink, currentTable // !! debugging
 
 
var diffScript = new function(){ //wrapper
 
var dfNoWikiParsing = window.dfNoWikiParsing, dfNoPopups = window.dfNoPopups
var curTitle, tempDiv, curStripes
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 (!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>'
   improveCurrentTable()
   currentTable.parentNode.insertBefore(createToolbar(), currentTable.parentNode.firstChild)
 }
 
}
 
 
 
function onClick(e){
 cursorWait() //cancel waiting indicator if something went wrong
 
 //find event and click target
 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 targ = e.target || e.srcElement
 //log(targ)
 if (!targ) return
 
 //clicked on diff table
 // ...
 
 //clicked on diff popup
 if (targ.className)
 switch (targ.className){
  case 'df-popup': closePopup(targ); return
  case 'df-caption': closePopup(targ.parentNode); return
  case 'diff-lineno': changeBlock(targ.parentNode); return
  case 'diff-addedline': case 'diff-deletedline': case 'diff-context': processCell(targ); return
 }
/*
 if (!dfNoPopups && targ.className){
   if (targ.className=='df-popup') return closePopup(targ)
   if (targ.className=='df-caption') return closePopup(targ.parentNode)
 }
 if (targ.className == 'diff-lineno') return changeBlock(targ.parentNode)
 //else if (targ.className == 'diff-marker') return changeRow(targ.parentNode)
 */
 
 //clicked on a diff link
 var url = targ.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 (targ.parentNode.className == 't-print') return
 //check if it was nex/prev diff link
 currentLink = targ
 var isClickIntercepted = true
 if (/differences-(prev|next)link/.test(targ.id)) isClickIntercepted = followNextLink()
 else if (!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 improveCurrentTable(){
 if (currentTable.innerHTML.length>70000) return
 addDiffTableCSS()
 var trs = currentTable.getElementsByTagName('tr')
 curTitle = currentTable.parentNode.diffTitle //to be used in processHTML()
 curStripes = false
 for (var i=1; i<trs.length; i++) improveRow(trs[i])
}
 
 
 
function improveRow(tr){
 
 var tds = tr.getElementsByTagName('td'), td
 tr.className = 'df-change' //by default, as most common case
 
 if (tds.length == 1){  // 'One intermediate revision not shown'
   return //plus maybe //tr.className = 'df-message'
 
 }else if (tds.length == 2){ // "Line xx:"
   tr.className = 'df-lineno'
   return
 
 }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
   return
 
 }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 (curStripes) tr.className += ' odd'
   if (/<span class="?sig"?>/i.test(htm)) curStripes = !curStripes
   if (/^<div>==.*== *<\/div>$/i.test(htm))
      tds[2].style.cssText = 'font-weight:bold; font-size:120%; padding-top:15px'
   expandCell(tds[2])
   return
 
 }else if (tds[1].className == 'diff-context') {
   tr.className = 'df-context'
   if (window.dfParseContext) processHTML(tds[1])
   expandCell(tds[1])
   curStripes = false
   return
 }
 
 //from here we're left with normal yellow/green rows with 4 cells
 
 tds[1].colSpan = tds[3].colSpan = 2
 //tds[1].style.borderRight = '10px solid white'
 tr.removeChild(tds[2]); tr.removeChild(tds[0])
 return
 
 if (!window.dfImproveAdvanced) return
 
 //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])
   }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[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
 }
 
 
} //improveRow()
 
 
 
function changeTable(e){
 e = e || window.event
 var butt = e.target || e.srcElement
 currentTable = getClickedTable(e)
 var dfLevel = butt.innerHTML
 if (dfImprovementSheet.disabled){
  improveCurrentTable()
  dfImprovementSheet.disabled = false
 }else{
   //butt.innerHTML = '-'
   replaceCurrentTable(requestedPages[currentTable.parentNode.diffURL])
   dfImprovementSheet.disabled = true
 }
}
 
 
function changeBlock(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')
 
}
 
 
function processCell(cell){ //parse wikicode in already improved row when clicked on white border above row
 if (cell.origHTML){ //restore
   cell.innerHTML = cell.origHTML
   cell.origHTML = null
 }else{
   processHTML(cell)
 }
}
 
 
// NOT USED
function changeRow(row){ //switch one rowl improvement level when clicked on +- marker on the left
 var dTbody = row.parentNode, rowIdx = 1
 //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-change'){
   var origTable = createTableFromHTML(requestedPages[dTbody.parentNode.parentNode.diffURL])
   dTbody.replaceChild(origTable.rows[rowIdx].cloneNode(true), row)
 }else{
   dfImprovementSheet.disabled = false
   improveRow(row)
   var tds = row.cells
   processHTML(tds[1])
   processHTML(tds[3])
 }  
}
 
 
function createToolbar(){
 var dToolbar = document.createElement('div')
 //dToolbar.diffTable = currentTable
 dToolbar.className = 'df-toolbar'
 dToolbar.appendChild(btn(changeTable, '¤', 'Enable/disable improvements', 'df-improve-btn')) //÷
 dToolbar.appendChild(btn(highlightDiffs, 'π', 'Highlight differences with red border', 'df-highlight-btn')) 
 dToolbar.appendChild(btn(diffJSEngine, 'Δ', 'Javascript diff engine'))
 //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 = clss || 'df-btn'
 return bt
}
 
 
 
function getClickedTable(e){
 e = e || window.event
 var button = e.target || e.srcElement
 return button.parentNode.tableParent.getElementsByTagName('table')[0]
}
 
 
 
 
 
 
 
 
function processHTML(elem){
 
 if (dfNoWikiParsing) return
 
 var html = elem.innerHTML
 elem.origHTML = html
 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 (/^image:|\.(jpg|png|svg|gif)$/i.test(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) 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 replaceCurrentTable(html){
 var oldTable = currentTable, parent = oldTable.parentNode, el
 //append new table
 currentTable = createTableFromHTML(html)
 parent.insertBefore(currentTable, oldTable)
 //remove everything below new table: old table, (now outdated) prevew, js engine diff
 while ((el=currentTable.nextSibling) && !/visualClear|df-caption/.test(el.className))
   parent.removeChild(el)
}
 
 
function createTableFromHTML(html){
 if (!tempDiv) tempDiv = document.createElement('div')
 tempDiv.innerHTML = html
 return tempDiv.getElementsByTagName('table')[0]
}
 
 
 
 
 
// *** POPUP ***
 
 
function createPopup(lnk){
 if (lnk) currentLink = lnk
 //unfortunately, image diffs do not work with &action=render
 //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
 document.body.appendChild(pop) //have to append now, otherwise table is broken in Safari 3 
 currentTable = document.createElement('table')
 pop.appendChild(currentTable)
 loadDiff(createPopup2)
}
 
 
function createPopup2(){
 var pop = currentTable.parentNode
 pop.style.visibility = 'visible'
 //add bottom and top captions
 pop.appendChild(createCaption())
 pop.insertBefore(createCaption(), pop.firstChild)
 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){  // !! obsolete?
 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 tempTable = createTableFromHTML(requestedPages[dTable.parentNode.diffURL])
 //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(true)
  //var
  oldVer = newVer = ''
  tds = tempTable.getElementsByTagName('td')
  var txt, isContent
  for (var i=1; i<tds.length; i++){
    txt = tds[i].innerHTML.replace(/<.+?>/g,'') + '\n'
    switch(tds[i].className){
    case 'diff-context':
      marker = '\x03' + txt + '\x04\n'
      oldVer += marker
      newVer += marker
      isContent = false
      i += 2 //skip other context cell
      break
    case 'diff-lineno':
      //oldVer += '\n\n\n\n\n\n'
      //newVer += '\n\n\n\n\n\n'
      break  // !!!
    case 'diff-deletedline':
      isContent = true
      oldVer += txt
      break
    case 'diff-addedline':
      isContent = true
      newVer +=  txt
      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)
  scrollToElement(difDiv)
 }
 
 
 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(true)
  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>')
  txt = txt.replace(/\x03|\x04/g, '')
  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
  cursorWait()
 }
 
}//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 scrollToElement (el){
 var yy = el.offsetTop
 while (el = el.offsetParent) yy += el.offsetTop
 scrollTo(0, yy)
}
 
function getTitleFromURL(url){
 var tt = url.match(/title=([^&>"]+)/) //"
 if (!tt) return ''
 else return decodeURIComponent(tt[1]).replace(/_/g,' ')
}
 
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(isWait){
 if (window.dfNoWaitCursor) return
 document.body.style.cursor = isWait ? 'wait' : ''
}
 
 
// *** CSS/JS ***
var dfHighlightSheet, dfImprovementSheet, dfPopupSheet
 
function  addDiffTableCSS(){
//table.diff td {padding-left:3px}\
//td.diff-addedline, td.diff-deletedline, td.df-deletedline
 if (dfImprovementSheet) return
 addCSS('\
div.df-toolbar {float:right}\
div.df-toolbar span\
 {cursor:pointer; padding:0 3px 0 3px; margin:0 3px 0 3px; border:1px solid #EEEEEE}\
')
 dfImprovementSheet = addCSS('\
table.diff {width:99%}\
table.diff td {padding-left:2px}\
table.diff, td.diff-otitle, td.diff-ntitle {background:#FBFBFB}\
table.diff td.diff-lineno {border-top: 25px solid #FBFBFB}\
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%}\
tr.df-change td {font-size:100%}\
div.df-toolbar span.df-improve-btn {border:1px inset #EEEEEE}\
table.diff td {padding-right:8px; cursor:help}\
table.diff td div, table.diff td.diff-multi {cursor:default}' //make TD clickable (sticking out from under DIV inside)
+ (window.dfDiffTableCSS ? dfDiffTableCSS : '')) //user CSS
 
//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 addDiffPopupCSS(){
 if (dfPopupSheet) return
 if (document.URL.indexOf('&diff=') == -1) importCSS('/skins-1.5/common/diff.css')
 dfPopupSheet = addCSS('\
div.df-popup{position:absolute; margin-right:15px; border:1px solid #000033; z-index:50; \
 font-size:130%; background-color:white; visibility:hidden; min-width:75%}\
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% }')
}
//div.df-popup div.df-toolbar {}\
//div.df-popup table.diff {width:auto}\
// min-width was added for Safari
 
function highlightDiffs(e){
 if (!dfHighlightSheet)
   dfHighlightSheet = addCSS('span.diffchange{border:1px solid red}\
 div.df-toolbar span.df-highlight-btn {border:1px inset #EEEEEE}')
 else dfHighlightSheet.disabled = !dfHighlightSheet.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){
 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
}
 
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(true)
 var url = currentLink.href + '&action=render&diffonly=yes'
 requestPage(url, loadDiff2)
 function loadDiff2(html){
  replaceCurrentTable(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
  improveCurrentTable()
  if (func) func()
  cursorWait()
 }
}
 
 
} //wrapper
 
 
 
 
if (doneOnloadHook) diffScript.start()
addOnloadHook(diffScript.start)