User:Js/diffs.js
Appearance
< User:Js
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
![]() | This user script seems to have a documentation page at User:Js/diffs. |
$(function(){
var dfPopupSheet
var clickedLink
var dfPopups = []
mw.util.addCSS(
'a[href^="/w"][href*="diff="]' + (window.dfDiffLinksCSS || '{font-style:italic}') +
'a[href^="/w"][href*="diff="]:hover {color:red}' +
'.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')
}
$(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 ) return
var url = decodeURI(clickedLink.attr('href'))
if( /^#/.test(url) ) return
if( ! /diff=/.test(url) ) return
//we have diff link
e.preventDefault()
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> </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>' +
' (' + dfLink + ' ' + outputLink2(pgTitle, '?action=history', 'h') + ')' +
'</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 = ' '
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(/&/gi,'&').replace(/#.+$/, calcAnchor)
return '<a href="'+ 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.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')
}
})