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. |
//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)
}
})