User:Js/diffs.js

//dfPinWatchlist = true

$(function{

var dfPopupSheet

mw.util.addCSS('\ td.diff-addedline:hover .diffchange, td.diff-deletedline:hover .diffchange {background:#fdd}\ ') var localDomain = new RegExp('^https?:' + mw.config.get('wgServer').replace(/([\.\/])/g,'\\$1') + '\/')

$(function{ var $tbl = $('table.diff') if( $tbl.length ){  improveTable($tbl)   dfAddToolbar($tbl, '#contentSub') } mw.util.$content.click(dfClick)

mw.util.addCSS('\ a[href*="diff="][href^="/w"],\ a[href*="diff="][href*="' + mw.config.get('wgServer') + '"]' + (window.dfDiffLinksCSS || '{font-style:italic}') +'\ a[href*="diff="][href^="/w"]:hover,\ a[href*="diff="][href*="' + mw.config.get('wgServer') + '"]:hover\ {color:red !important}\ .df-popup {margin:0.5em}\ ') }) $(document).keyup( function(e){ //close popup on Escape if( e.which == 27 ) popupClose('.df-instance:last') })

function dfClick(e){

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

//is it a diff link? var lnk = $(e.target).closest('a') if( !lnk.length ) return var url = lnk.attr('href').replace(localDomain, '/') if( ! /^\//.test(url) ) return //external URL var loadURL, dfContainer if( /[&?]diff=/.test(url) ){ // diff if( /differences-(next|prev)link/.test( lnk.attr('id') ) )//prev/next link in diff table dfContainer = lnk.closest('table.diff').parent else markClickedLink(lnk) if( /:Undelete/.test(url) ) loadURL = url + '&useskin=myskin #content' else loadURL = url + '&action=render&diffonly=true' }else if( mw.config.get('wgCanonicalSpecialPageName') == 'Watchlist'        && window.dfPinWatchlist //popup for any link         && ! lnk.closest('fieldset').length ){

if( ! /\?/.test(url) && ! /(special|служебная):/i.test(url) ) loadURL = url + '?action=render' else loadURL = url + '&useskin=myskin #content' }else{ return }  //load diff e.preventDefault cursorWait(true) dfContainer = dfContainer || popupCreate(e) dfContainer .attr('dfURL', url) .load( loadURL, afterDiffLoaded )

}

function afterDiffLoaded{

cursorWait var $container = $(this) var url = $container.attr('dfURL') var dfLink = outputLink2('', $container.attr('dfURL'), 'Δ', 'current diff')

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

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

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

dfAddToolbar($tbl, caption) $container.parent.appendTo('body') }else{ $('#contentSub') .children('.df-link').remove.end .append(    $(' ')     .append( dfLink )   ) }

}

function popupCreate(e){

//set CSS if( ! dfPopupSheet ){ dfPopupSheet = mw.util.addCSS('\ .df-clicked {background-color:#E0E0E0}\  a.df-clicked-last {background-color:#FFDDDD}\  .df-popup {position:absolute; z-index:5; width:95%; border:1px solid #000033; \    font-size:110%; background-color:white; padding:0 9px 9px 9px }\  .df-caption {background:#F0F0FF; border:1px outset gray; padding:2px; margin:0 -9px}\  .df-closer {position:absolute; z-index:10; border:2px outset gray;\    width:20px; height:20px; cursor:pointer; background:gray; opacity:0.5}') if( ! /[&?]diff=/.test(document.URL) ) importStylesheetURI('//bits.wikimedia.org/ru.wikipedia.org/load.php?modules=mediawiki.action.history.diff&only=styles') //importStylesheetURI('/skins-1.5/common/diff.css') }

//create popup var dfN = $('.df-popup').length var dfPopup = $(' ') .css({  top: $(window).scrollTop + 30  + dfN * 5 + 'px',   left: 10 + dfN * 5 + 'px' }) .click( function(e){ //close popup when clicking on edges and and caption   if( /df-(popup|caption)/.test(e.target.className) ) popupClose(this)    else $(this).addClass('persistent')  }) .click(dfClick) // .mouseleave( function(e){ //   if( ! /persistent/.test(this.className) ) popupClose(this) // })

//create 'closing' square on mouse position var dfCloser = $(' ') .css({top: e.pageY-10, left: e.pageX-10}) .mouseleave( function{ $(this).remove }) .click( function{ popupClose(this) } )

$(' ') //container for popup and closer .append(dfPopup, dfCloser) // if (isIE) hideAllSelectElements(true) // !!! return dfPopup

}

function popupClose(el){ $(el).closest('.df-instance').remove }

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

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

function markClickedLink(lnk){ //and mark link as "clicked" $('a.df-clicked-last').removeClass('df-clicked-last') //rm class from previos click lnk.addClass('df-clicked df-clicked-last') if( /Watchlist|Recentchanges/.test(mw.config.get('wgCanonicalSpecialPageName')) ) lnk.closest('li').add(lnk.closest('tr')).addClass('df-clicked') }

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; cursor:pointer}\ 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('dfURL') ) curStripes = false addDiffTableCSS $tbl.find('tr').each(improveRow) $tbl.click(tableOnclick) }

function dfAddToolbar($tbl, where){ //return $(' ') .append(  //btn(changeTable, '¤', 'Enable/disable improvements'),   btn(diffJSEngine, 'js', 'Javascript diff engine') ) .prependTo(where) function changeTable{alert(11)} function btn(func, txt, tip){ //creates button return $('' + txt + ' ') .click( { dTable: $tbl }, 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 = ' '    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 = ' '    if( curStripes ) tr.className += ' odd' if( / /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.innerHTML + ' ' }

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 = ' '

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

htm = htm.replace(/\u00A0/g, '\u00B7') 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, ' $& ') htm = htm.replace(/\{\{unsigned[^\}]\}\}/i, ' $& ')

//link var CatOrFileRegExp = RegExp('^('+mw.config.get('wgFormattedNamespaces')[6]+'|' +mw.config.get('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(mw.config.get('wgServer')+mw.config.get('wgScript')) == 0){ //local link    tip = tryDecodeURI(link.substring((mw.config.get('wgServer')+mw.config.get('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 + ']' })

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 '' + name + '' //' }

function calcAnchor(txt){ //try to create href anchor similar to Parser.php::guessSectionNameFromWikiText txt = $.trim(txt).replace(/#/g,'') .replace(/\[\[([^|]+\|)?([^\]]+)\]\]/g, '$2') //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.dfURL]) }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')

}

// *** JS Diff Engine *** function diffJSEngine(e){ var $tbl = e.data.dTable var htm = $tbl.data('origHTML') //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 var oldVer = , newVer = , txt, marker $tbl.find('td').each(function{   txt = $(this).data('origHTML') || this.innerHTML    txt = txt.replace(/<.+?>/g,'') + '\n'    switch( this.className ){    case 'diff-context':      marker = '\x03' + txt + '\x04\n'      oldVer += marker      newVer += marker      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':      oldVer += txt      break    case 'diff-addedline':      newVer += txt      break    }  }) //compare and display txt = WDiffString(oldVer, newVer) //txt = txt.replace(/\x03.*?\x04/g, '    ') txt = txt.replace(/\x03|\x04/g, '') txt = WDiffShortenOutput(txt) //txt = txt.replace(/¶/g,' ') txt = txt.replace(/&amp;/g,'&') //txt = txt.replace(/&lt;/g,'<').replace(/&gt;/g, '>') $(' JS Engine diff '     + txt + ' ') .insertAfter($tbl)[0].scrollIntoView cursorWait }

}

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

})