//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 = $(
'<div class=df-caption>' +
'<b>' + outputLink2(pgTitle) + '</b>' +
' (' + outputLink2(pgTitle, '?action=history', 'h') + ' ' + dfLink + ')' +
'</div>'
)
.prependTo($container)
dfAddToolbar($tbl, caption)
$container.parent().appendTo('body')
}else{
$('#contentSub')
.children('.df-link').remove().end()
.append(
$('<span class=df-link style="margin-left:1em; font-weight:bold" />')
.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 = $('<div class=df-popup />')
.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 = $('<div class=df-closer title=close> </div>')
.css({top: e.pageY-10, left: e.pageX-10})
.mouseleave( function(){ $(this).remove() })
.click( function(){ popupClose(this) } )
$('<div class=df-instance />') //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
$('<div class=df-toolbar style="float:right" />')
.append(
//btn(changeTable, '¤', 'Enable/disable improvements'),
btn(diffJSEngine, 'js', 'Javascript diff engine')
)
.prependTo(where)
function changeTable(){alert(11)}
function btn(func, txt, tip){ //creates <span> button
return $('<span class=df-btn title="' + tip + '">' + txt + '</span>')
.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 = '<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 = ' '
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('^('+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 + ']</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(/&/gi,'&').replace(/#.+$/, calcAnchor)
return '<a href="'+ mw.config.get('wgArticlePath').replace(/\$1/,'')
+ encodeURI(page).replace(/\?/g,'%3F')
+ (params||'')
+ '" title="' + (tooltip||name).replace(/"/g,'"') + '">' + 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.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, '<br><br><hr><br><br>')
txt = txt.replace(/\x03|\x04/g, '')
txt = WDiffShortenOutput(txt)
//txt = txt.replace(/¶/g,'<br>')
txt = txt.replace(/&/g,'&')
//txt = txt.replace(/</g,'<').replace(/>/g, '>')
$('<div style="padding:2px"><br><br><h3>JS Engine diff</h3><hr style="height:5px" />'
+ txt + '</div>')
.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)
}
})