Revision as of 02:17, 6 July 2006 view sourceLupin (talk | contribs)19,513 edits tweaks and bugfixes, nostalgia structure← Previous edit |
Latest revision as of 14:34, 9 February 2021 view source Xaosflux (talk | contribs)Edit filter managers, Autopatrolled, Bureaucrats, Importers, Interface administrators, Oversighters, Administrators83,950 edits expand directions |
(23 intermediate revisions by 15 users not shown) |
Line 1: |
Line 1: |
|
|
var popScript = '//en.wikipedia.org/search/?action=raw&ctype=text/javascript&title=MediaWiki:Gadget-popups.js'; |
|
var popupVersion="Sun Jul 2 18:23:28 EDT 2006"; |
|
|
|
var popStyleSheet = '//en.wikipedia.org/search/?action=raw&ctype=text/css&title=MediaWiki:Gadget-navpop.css'; |
|
// STARTFILE: main.js |
|
|
|
if ( window.localCSS ) { popStyleSheet = 'http://localhost:8080/js/navpop.css'; } |
|
// ********************************************************************** |
|
|
// ** ** |
|
|
// ** changes to this file affect many users. ** |
|
|
// ** please discuss on the talk page before editing ** |
|
|
// ** ** |
|
|
// ********************************************************************** |
|
|
// ** ** |
|
|
// ** if you do edit this file, be sure that your editor recognizes it ** |
|
|
// ** as utf8, or the weird and wonderful characters in the namespaces ** |
|
|
// ** below will be completely broken. You can check with the show ** |
|
|
// ** changes button before submitting the edit. ** |
|
|
// ** test: مدیا מיוחד Мэдыя ** |
|
|
// ** ** |
|
|
// ********************************************************************** |
|
|
|
|
|
|
|
function popups_importScriptURI(url) { |
|
//////////////////////////////////////////////////////////////////// |
|
|
|
var s = document.createElement('script'); |
|
// Import stylesheet(s) |
|
|
|
s.setAttribute('src',url); |
|
// |
|
|
|
s.setAttribute('type','text/javascript'); |
|
|
|
|
|
document.getElementsByTagName('head').appendChild(s); |
|
|
|
|
|
return s; |
|
if ( window.localCSS ) { |
|
|
document.write('<link rel="stylesheet" type="text/css" href="http://localhost:8080/js/navpop.css">'); |
|
|
} else { |
|
|
document.write('<link rel="stylesheet" type="text/css" href="' + |
|
|
'http://en.wikipedia.org/search/?title=User:Lupin/navpop.css' + |
|
|
'&action=raw&ctype=text/css&dontcountme=s">'); |
|
|
} |
|
} |
|
|
|
|
|
|
function popups_importStylesheetURI(url) { |
|
////////////////////////////////////////////////// |
|
|
|
return document.createStyleSheet ? document.createStyleSheet(url) : popups_appendCSS('@import "' + url + '";'); |
|
// Globals |
|
|
// |
|
|
|
|
|
// Trying to shove as many of these as possible into the pg (popup globals) object |
|
|
window.pg = { |
|
|
re: {}, // regexps |
|
|
ns: {}, // namespaces |
|
|
string: {}, // translatable strings |
|
|
wiki: {}, // local site info |
|
|
misc: {}, // YUCK PHOOEY |
|
|
option: {}, // options, see newOption etc |
|
|
optionDefault: {}, // default option values |
|
|
flag: {}, // misc flags |
|
|
cache: {}, // page and image cache |
|
|
structures: {}, // navlink structures |
|
|
timer: {}, // all sorts of timers (too damn many) |
|
|
counter: {}, // .. and all sorts of counters |
|
|
current: {}, // state info |
|
|
endoflist: null |
|
|
}; |
|
|
window.pop = { // wrap various functions in here |
|
|
init: {}, |
|
|
util: {}, |
|
|
endoflist: null |
|
|
}; |
|
|
function popupsReady() { |
|
|
if (!window.pg) { return false; } |
|
|
if (!pg.flag) { return false; } |
|
|
if (!pg.flag.finishedLoading) { return false; } |
|
|
return true; |
|
|
} |
|
} |
|
//////////////////////////////////////////////////////////////////// |
|
|
// Run things |
|
|
//////////////////////////////////////////////////////////////////// |
|
|
|
|
|
|
|
function popups_appendCSS(text) { |
|
addOnloadHook(setupPopups); |
|
|
|
var s = document.createElement('style'); |
|
|
|
|
|
s.type = 'text/css'; |
|
/// Local Variables: /// |
|
|
|
s.rel = 'stylesheet'; |
|
/// mode:c /// |
|
|
|
if (s.styleSheet) s.styleSheet.cssText = text //IE |
|
/// End: /// |
|
|
|
else s.appendChild(document.createTextNode(text + '')) //Safari sometimes borks on null |
|
// ENDFILE: main.js |
|
|
|
document.getElementsByTagName('head').appendChild(s); |
|
// STARTFILE: actions.js |
|
|
|
return s; |
|
function setupTooltips(container, remove) { |
|
|
log('setupTooltips, container='+container+', remove='+remove); |
|
|
if (!container) { |
|
|
//<NOLITE> |
|
|
// the main initial call |
|
|
if (getValueOf('popupOnEditSelection') && window.doSelectionPopup ) { |
|
|
try { |
|
|
document.editform.wpTextbox1.onmouseup=function() { doSelectionPopup(); }; |
|
|
} catch (neverMind) {} |
|
|
} |
|
|
//</NOLITE> |
|
|
// article/content is a structure-dependent thing |
|
|
container = defaultPopupsContainer(); |
|
|
} |
|
|
|
|
|
if (!remove && container.ranSetupTooltipsAlready) { return; } |
|
|
container.ranSetupTooltipsAlready = !remove; |
|
|
|
|
|
var anchors; |
|
|
anchors=container.getElementsByTagName('A'); |
|
|
setupTooltipsLoop(anchors, 0, 250, 100, remove); |
|
|
} |
|
} |
|
|
|
|
|
|
popups_importStylesheetURI(popStyleSheet); |
|
function defaultPopupsContainer() { |
|
|
|
popups_importScriptURI(popScript); |
|
if (getValueOf('popupOnlyArticleLinks')) { |
|
|
return document.getElementById('article') || |
|
|
document.getElementById('content') || document; |
|
|
} |
|
|
return document; |
|
|
} |
|
|
|
|
|
|
|
if ( typeof mw !== 'undefined' ) { |
|
function setupTooltipsLoop(anchors,begin,howmany,sleep, remove) { |
|
|
|
mw.loader.using( , function() { |
|
log(simplePrintf('setupTooltipsLoop(%s,%s,%s,%s,%s)', arguments)); |
|
|
|
var k = 'User:Lupin/popups.js', |
|
var finish=begin+howmany; |
|
|
|
t = 'Information: You are importing User:Lupin/popups.js' + |
|
var loopend = min(finish, anchors.length); |
|
|
|
' into your User:USERNAME/common.js or User:USERNAME/<skin>.js!\n' + |
|
var j=loopend - begin; |
|
|
|
'This script is unmaintained. Please remove this inclusion and enable the Navigation popups Gadget in the preferences of your account instead.', |
|
log ('setupTooltips: anchors.length=' + anchors.length + ', begin=' + begin + ', howmany=' + howmany + ', loopend=' + loopend + ', remove=' + remove); |
|
|
|
x = mw.storage.get( k ); |
|
var doTooltip= remove ? removeTooltip : addTooltip; |
|
|
// try a faster (?) loop construct |
|
|
if (j > 0) { |
|
|
do { |
|
|
var a=anchors; |
|
|
if (!a || !a.href) { |
|
|
log('got null anchor at index ' + loopend - j); |
|
|
continue; |
|
|
} |
|
|
doTooltip(a); |
|
|
} while (--j); |
|
|
} |
|
|
if (finish < anchors.length) { |
|
|
setTimeout(function() {setupTooltipsLoop(anchors,finish,howmany,sleep,remove);}, sleep); |
|
|
} else { |
|
|
// now eliminate popups from the TOC |
|
|
if ( !remove && ! getValueOf('popupTocLinks')) { |
|
|
var toc=document.getElementById('toc'); |
|
|
if (toc) { |
|
|
var tocLinks=toc.getElementsByTagName('A'); |
|
|
var tocLen = tocLinks.length; |
|
|
for (j=0; j<tocLen; ++j) { |
|
|
log ('killing popup for toclinks'); |
|
|
doTooltip(tocLinks, true); |
|
|
} |
|
|
// looks like we just killed any onclick stuff that used to be going on in the toc |
|
|
// life is hard |
|
|
} |
|
|
} |
|
|
pg.flag.finishedLoading=true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
if ( !x ) { |
|
function addTooltip(a) { |
|
|
|
mw.storage.set( k, 1 ); |
|
if ( !isPopupLink(a) ) { return; } |
|
|
|
alert( t ); |
|
var remTitles = getValueOf('removeTitles'); |
|
|
a.onmouseover=mouseOverWikiLink; |
|
|
a.onmouseout= mouseOutWikiLink; |
|
|
a.onclick= killPopup; |
|
|
a.hasPopup = true; |
|
|
if (remTitles && typeof a.originalTitle=='undefined') { |
|
|
a.originalTitle=a.title; |
|
|
a.title=''; |
|
|
} |
|
|
} |
|
|
|
|
|
//<NOLITE> |
|
|
function removeTooltip(a) { |
|
|
if ( !a.hasPopup ) { return; } |
|
|
a.onmouseover = null; |
|
|
a.onmouseout = null; |
|
|
if (a.originalTitle) { a.title = a.originalTitle; } |
|
|
a.hasPopup=false; |
|
|
} |
|
|
//</NOLITE> |
|
|
|
|
|
function registerHooks(np) { |
|
|
var popupMaxWidth=getValueOf('popupMaxWidth'); |
|
|
|
|
|
if (typeof popupMaxWidth == 'number') { |
|
|
var setMaxWidth = function () { |
|
|
np.mainDiv.style.maxWidth = popupMaxWidth + 'px'; |
|
|
np.maxWidth = popupMaxWidth; |
|
|
|
|
|
// hack for IE |
|
|
// see http://www.svendtofte.com/code/max_width_in_ie/ |
|
|
// use setExpression as documented here on msdn: http://tinyurl dot com/dqljn |
|
|
|
|
|
if (np.mainDiv.style.setExpression) { |
|
|
np.mainDiv.style.setExpression('width', 'document.body.clientWidth > ' + |
|
|
popupMaxWidth + ' ? "' +popupMaxWidth + 'px": "auto"'); |
|
|
} |
|
|
}; |
|
|
np.addHook(setMaxWidth, 'unhide', 'before'); |
|
|
} |
|
|
//<NOLITE> |
|
|
if (window.addPopupShortcuts && window.rmPopupShortcuts) { |
|
|
np.addHook(addPopupShortcuts, 'unhide', 'after'); |
|
|
np.addHook(rmPopupShortcuts, 'hide', 'before'); |
|
|
} |
|
|
//</NOLITE> |
|
|
} |
|
|
|
|
|
|
|
|
function mouseOverWikiLink() { |
|
|
if (!window.popupsReady || !window.popupsReady()) { return; } |
|
|
return mouseOverWikiLink2(this); |
|
|
} |
|
|
|
|
|
function footnoteTarget(a) { |
|
|
var aTitle=Title.fromAnchor(a); |
|
|
// We want ".3A" rather than "%3A" or "?" here, so use the anchor property directly |
|
|
var anch = aTitle.anchor; |
|
|
if ( ! /^(_note-|endnote)/.test(anch) ) { |
|
|
return false; |
|
|
} |
|
|
|
|
|
var lTitle=Title.fromURL(location.href); |
|
|
if ( lTitle.toString(true) != aTitle.toString(true) ) { |
|
|
return false; |
|
|
} |
|
|
var el=document.getElementById(anch); |
|
|
while ( el && typeof el.nodeName == 'string') { |
|
|
var nt = el.nodeName.toLowerCase(); |
|
|
if ( nt == 'li' ) { return el; } |
|
|
else if ( nt == 'body' ) { return false; } |
|
|
else if ( el.parentNode ) { el=el.parentNode; } |
|
|
else { return false; } |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
function footnotePreview(x) { |
|
|
setPopupHTML('<hr>' + x.innerHTML, 'popupPreview', pg.idNumber, |
|
|
getValueOf('popupSubpopups') ? function() { |
|
|
setupTooltips(document.getElementById('popupPreview' + pg.idNumber)); |
|
|
} : null); |
|
|
} |
|
|
|
|
|
function mouseOverWikiLink2(a) { |
|
|
// FIXME: should not generate the HTML until the delay has elapsed, |
|
|
// and then popup immediately. Can be a CPU hog otherwise. |
|
|
|
|
|
//log('mouseOverWikiLink: a='+a+', pg.current.link='+pg.current.link); |
|
|
|
|
|
// try not to duplicate effort |
|
|
if ( a==pg.current.link && a.navpopup && a.navpopup.isVisible() ) { return; } |
|
|
pg.current.link=a; |
|
|
|
|
|
if (getValueOf('simplePopups') && pg.option.popupStructure===null) { |
|
|
// reset *default value* of popupStructure |
|
|
//log ('simplePopups is true and no popupStructure selected. Defaulting to "original"'); |
|
|
setDefault('popupStructure', 'original'); |
|
|
} |
|
|
|
|
|
var article=(new Title()).fromAnchor(a); |
|
|
// set global variable (ugh) to hold article (wikipage) |
|
|
pg.current.article = article; |
|
|
var diff=null; |
|
|
var history=null; |
|
|
var params=parseParams(a.href); |
|
|
var oldid=(typeof params.oldid=='undefined' ? null : params.oldid); |
|
|
//<NOLITE> |
|
|
if(getValueOf('popupPreviewDiffs') && window.loadDiff) { |
|
|
diff=params.diff; // alert(params.diff); |
|
|
} |
|
|
if(getValueOf('popupPreviewHistory') && getValueOf('popupUseQueryInterface')) { |
|
|
history=(params.action=='history'); |
|
|
} |
|
|
//</NOLITE> |
|
|
if (pg.timer.image !== null) { |
|
|
clearInterval(pg.timer.image); |
|
|
pg.timer.image=null; |
|
|
pg.counter.checkImages=0; |
|
|
} |
|
|
|
|
|
if (!a.navpopup) { |
|
|
log ('mouseoverwikilink2: creating new Navpopup'); |
|
|
a.navpopup = new Navpopup(); |
|
|
a.navpopup.fuzz=5; |
|
|
a.navpopup.delay=getValueOf('popupDelay')*1000; |
|
|
// increment global counter now |
|
|
a.navpopup.popupIdNumber = ++pg.idNumber; |
|
|
a.navpopup.parentAnchor = a; |
|
|
registerHooks(a.navpopup); |
|
|
pg.current.links.push(a); |
|
|
} |
|
|
if (a.navpopup.pending===null || a.navpopup.pending!==0) { |
|
|
// either fresh popups or those with unfinshed business are redone from scratch |
|
|
log ('doing popup content from scratch; a.navpopup.pending='+a.navpopup.pending); |
|
|
a.navpopup.setInnerHTML(popupHTML(a)); |
|
|
fillEmptySpans({navpopup:a.navpopup}); |
|
|
} else { |
|
} else { |
|
|
x++; |
|
log ('using existing popup content - just showing'); |
|
|
|
mw.storage.set( k, x ); |
|
} |
|
|
|
if ( x % 25 === 0 ) { |
|
a.navpopup.showSoonIfStable(a.navpopup.delay); |
|
|
|
mw.notify( t ); |
|
|
|
|
getValueOf('popupInitialWidth'); |
|
|
|
|
|
if (typeof pg.timer.checkPopupPosition==typeof 1) { clearInterval(pg.timer.checkPopupPosition); } |
|
|
pg.timer.checkPopupPosition=setInterval(checkPopupPosition, 600); |
|
|
|
|
|
//<NOLITE> |
|
|
if (getValueOf('popupLiveOptions')) { |
|
|
setPopupHTML(popupLiveOptionsHTML(), 'popupLiveOptions', pg.idNumber, |
|
|
function () { popupToggleShowOptions(true); } ); |
|
|
} |
|
|
if (getValueOf('popupRedlinkRemoval') && a.className=='new') { |
|
|
setPopupHTML('<br>'+popupRedlinkHTML(), 'popupRedlink', pg.idNumber); |
|
|
} |
|
|
//</NOLITE> |
|
|
|
|
|
if(getValueOf('simplePopups')) { return; } |
|
|
|
|
|
if (a.navpopup.pending!==0 ) { |
|
|
a.navpopup.pending=0; |
|
|
log('running unsimplify block'); |
|
|
var previewImage=true; |
|
|
var x; |
|
|
pg.misc.gImage=null; |
|
|
//alert(diff+'\n'+oldid); |
|
|
if (x=footnoteTarget(a)) { |
|
|
footnotePreview(x); |
|
|
//<NOLITE> |
|
|
} else if ( diff || diff === 0 ) { |
|
|
//alert(); |
|
|
loadDiff(article, oldid, diff, a.navpopup); |
|
|
} else if ( history && getValueOf('popupUseQueryInterface') ) { |
|
|
loadQueryPreview('history', article, a.navpopup); |
|
|
} else if ( pg.re.contribs.test(a.href) && getValueOf('popupUseQueryInterface')) { |
|
|
loadQueryPreview('contribs', article, a.navpopup); |
|
|
} else if ( // FIXME should be able to get all preview combinations with options |
|
|
article.namespace()==pg.ns.image && |
|
|
( getValueOf('imagePopupsForImages') || ! anchorContainsImage(a) ) |
|
|
) { |
|
|
if (getValueOf('popupUseQueryInterface') && getValueOf('popupImageLinks')) { |
|
|
loadQueryPreview('imagelinks', article, a.navpopup); |
|
|
} |
|
|
startArticlePreview(article, oldid, a.navpopup); |
|
|
loadImages(article); |
|
|
//</NOLITE> |
|
|
} else if (article.namespace() == pg.ns.category && |
|
|
getValueOf('popupCategoryMembers')) { |
|
|
getValueOf('popupUseQueryInterface') && |
|
|
loadQueryPreview('category', article, a.navpopup); |
|
|
startArticlePreview(article, oldid, a.navpopup); |
|
|
} |
|
|
else if (!article.namespace()!=pg.ns.image && previewImage ) { |
|
|
startArticlePreview(article, oldid, a.navpopup); |
|
|
} |
|
} |
|
} |
|
} |
|
|
}); |
|
} |
|
} |
|
|
|
|
function pendingNavpopTask(navpop) { |
|
|
if (navpop && navpop.pending===null) { navpop.pending=0; } |
|
|
++navpop.pending; |
|
|
} |
|
|
|
|
|
function completedNavpopTask(navpop) { |
|
|
if (navpop && navpop.pending) { --navpop.pending; } |
|
|
} |
|
|
|
|
|
function startArticlePreview(article, oldid, navpop) { |
|
|
navpop.redir=0; |
|
|
loadPreview(article, oldid, navpop); |
|
|
} |
|
|
|
|
|
function loadPreview(article, oldid, navpop) { |
|
|
log('loadPreview(' + article + ', ' + oldid + ', ' + navpop + ')'); |
|
|
pendingNavpopTask(navpop); |
|
|
navpop.originalArticle=article; |
|
|
getWiki(article, insertPreview, oldid, navpop); |
|
|
} |
|
|
|
|
|
function loadPreviewFromRedir(redirMatch, navpop) { |
|
|
// redirMatch is a regex match |
|
|
var target = new Title().fromWikiText(redirMatch); |
|
|
var trailingRubbish=redirMatch; |
|
|
navpop.redir++; |
|
|
navpop.redirTarget=target; |
|
|
//<NOLITE> |
|
|
if (window.redirLink) { |
|
|
var warnRedir = redirLink(target); |
|
|
setPopupHTML(warnRedir, 'popupWarnRedir'); |
|
|
} |
|
|
//</NOLITE> |
|
|
fillEmptySpans({redir: true, redirTarget: target, navpopup:navpop}); |
|
|
return loadPreview(target, null, navpop); |
|
|
} |
|
|
|
|
|
function insertPreview(download) { |
|
|
if (!download.owner) { return; } |
|
|
var wikiText=download.data; |
|
|
var navpop=download.owner; |
|
|
var redirMatch = pg.re.redirect.exec(wikiText); |
|
|
completedNavpopTask(navpop); |
|
|
var art=navpop.redirTarget || navpop.originalArticle; |
|
|
|
|
|
if (navpop.redir===0 && redirMatch) { |
|
|
pg.current.redirSource=pg.current.article; |
|
|
loadPreviewFromRedir(redirMatch, navpop); |
|
|
return; |
|
|
} |
|
|
|
|
|
//<NOLITE> |
|
|
if ( window.makeFixDabs ) { |
|
|
if (navpop.redir===0) { // not a redir, so we don't have to specify an oldTarget |
|
|
makeFixDabs(wikiText); |
|
|
} else { |
|
|
makeFixDabs(wikiText, navpop.originalArticle); |
|
|
} |
|
|
} |
|
|
|
|
|
if (getValueOf('popupSummaryData') && window.getPageInfo) { |
|
|
var pgInfo=getPageInfo(wikiText, download); |
|
|
setPopupTrailer(pgInfo); |
|
|
} |
|
|
|
|
|
if (window.getValidImageFromWikiText) { |
|
|
var imagePage=getValidImageFromWikiText(wikiText); |
|
|
if(imagePage) { |
|
|
loadImages(Title.fromWikiText(imagePage)); |
|
|
} |
|
|
} |
|
|
//</NOLITE> |
|
|
|
|
|
if (getValueOf('popupPreviews')) { |
|
|
if (download && typeof download.data == typeof ''){ |
|
|
if (art.namespace()==pg.ns.template && getValueOf('popupPreviewRawTemplates')) { |
|
|
// FIXME compare/consolidate with diff escaping code for wikitext |
|
|
var h='<hr><tt>' + download.data.entify().split('\\n').join('<br>\\n') + '</tt>'; |
|
|
setPopupHTML(h, 'popupPreview'); |
|
|
} |
|
|
else { |
|
|
// deal with tricksy anchors |
|
|
var anch=art.anchorString(); |
|
|
var d=anchorize(download.data, anch); |
|
|
var urlBase=joinPath(); |
|
|
var p=new Previewmaker(d.substring(0,10000), urlBase); |
|
|
p.showPreview(); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
function anchorize(d, anch) { |
|
|
if (!anch) { return d; } |
|
|
var anchRe=RegExp('=+\\s*' + literalizeRegex(anch).replace(//g, '') + '\\s*=+'); |
|
|
var match=d.match(anchRe); |
|
|
if(match && match.length > 0 && match) { return d.substring(d.indexOf(match)); } |
|
|
|
|
|
// now try to deal with == foo ] boom == -> #foo_baz_boom |
|
|
var lines=d.split('\n'); |
|
|
for (var i=0; i<lines.length; ++i) { |
|
|
lines=lines.replace(RegExp('{2}(]*?)?(.*?)]{2}', 'g'), '$2'); |
|
|
if (lines.match(anchRe)) { |
|
|
return d.split('\n').slice(i).join('\n').replace(RegExp('^*'), ''); |
|
|
} |
|
|
} |
|
|
return d; |
|
|
} |
|
|
|
|
|
function killPopup() { |
|
|
if (getValueOf('popupShortcutKeys') && window.rmPopupShortcuts) { rmPopupShortcuts(); } |
|
|
if (!pg) { return; } |
|
|
pg.current.link && pg.current.link.navpopup && pg.current.link.navpopup.banish(); |
|
|
pg.current.link=null; |
|
|
abortAllDownloads(); |
|
|
window.stopImagesDownloading && stopImagesDownloading(); |
|
|
if (pg.timer.checkPopupPosition !== null) { |
|
|
clearInterval(pg.timer.checkPopupPosition); |
|
|
pg.timer.checkPopupPosition=null; |
|
|
} |
|
|
if (pg.timer.checkImages !== null) { clearInterval(pg.timer.checkImages); pg.timer.checkImages=null; } |
|
|
if (pg.timer.image !== null) { clearInterval(pg.timer.image); pg.timer.image=null; } |
|
|
return true; // preserve default action (eg from |
|
|
} |
|
|
// ENDFILE: actions.js |
|
|
// STARTFILE: domdrag.js |
|
|
/** |
|
|
@fileoverview |
|
|
The {@link Drag} object, which enables objects to be dragged around. |
|
|
|
|
|
<pre> |
|
|
************************************************* |
|
|
dom-drag.js |
|
|
09.25.2001 |
|
|
www.youngpup.net |
|
|
************************************************** |
|
|
10.28.2001 - fixed minor bug where events |
|
|
sometimes fired off the handle, not the root. |
|
|
************************************************* |
|
|
Pared down, some hooks added by ] |
|
|
|
|
|
Copyright Aaron Boodman. |
|
|
Saying stupid things daily since March 2001. |
|
|
</pre> |
|
|
*/ |
|
|
|
|
|
/** |
|
|
Creates a new Drag object. This is used to make various DOM elements draggable. |
|
|
@constructor |
|
|
*/ |
|
|
function Drag () { |
|
|
/** |
|
|
Condition to determine whether or not to drag. This function should take one parameter, an Event. |
|
|
To disable this, set it to <code>null</code>. |
|
|
@type Function |
|
|
*/ |
|
|
this.startCondition = null; |
|
|
/** |
|
|
Hook to be run when the drag finishes. This is passed the final coordinates of |
|
|
the dragged object (two integers, x and y). To disables this, set it to <code>null</code>. |
|
|
@type Function |
|
|
*/ |
|
|
this.endHook = null; |
|
|
} |
|
|
|
|
|
/** |
|
|
Gets an event in a cross-browser manner. |
|
|
@param {Event} e |
|
|
@private |
|
|
*/ |
|
|
Drag.prototype.fixE = function(e) { |
|
|
if (typeof e == 'undefined') { e = window.event; } |
|
|
if (typeof e.layerX == 'undefined') { e.layerX = e.offsetX; } |
|
|
if (typeof e.layerY == 'undefined') { e.layerY = e.offsetY; } |
|
|
return e; |
|
|
}; |
|
|
/** |
|
|
Initialises the Drag instance by telling it which object you want to be draggable, and what you want to drag it by. |
|
|
@param {DOMElement} o The "handle" by which <code>oRoot</code> is dragged. |
|
|
@param {DOMElement} oRoot The object which moves when <code>o</code> is dragged, or <code>o</code> if omitted. |
|
|
*/ |
|
|
Drag.prototype.init = function(o, oRoot) { |
|
|
var dragObj = this; |
|
|
this.obj = o; |
|
|
o.onmousedown = function(e) { dragObj.start.apply( dragObj, ); }; |
|
|
o.dragging = false; |
|
|
o.draggable = true; |
|
|
o.hmode = true; |
|
|
o.vmode = true; |
|
|
|
|
|
o.root = oRoot && oRoot !== null ? oRoot : o ; |
|
|
|
|
|
if (isNaN(parseInt(o.root.style.left, 10))) { o.root.style.left = "0px"; } |
|
|
if (isNaN(parseInt(o.root.style.top, 10))) { o.root.style.top = "0px"; } |
|
|
|
|
|
o.root.onthisStart = function(){}; |
|
|
o.root.onthisEnd = function(){}; |
|
|
o.root.onthis = function(){}; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Starts the drag. |
|
|
@private |
|
|
@param {Event} e |
|
|
*/ |
|
|
Drag.prototype.start = function(e) { |
|
|
var o = this.obj; // = this; |
|
|
e = this.fixE(e); |
|
|
if (this.startCondition && !this.startCondition(e)) { return; } |
|
|
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10); |
|
|
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10); |
|
|
o.root.onthisStart(x, y); |
|
|
|
|
|
o.lastMouseX = e.clientX; |
|
|
o.lastMouseY = e.clientY; |
|
|
|
|
|
var dragObj = this; |
|
|
o.onmousemoveDefault = document.onmousemove; |
|
|
o.dragging = true; |
|
|
document.onmousemove = function(e) { dragObj.drag.apply( dragObj, ); }; |
|
|
document.onmouseup = function(e) { dragObj.end.apply( dragObj, ); }; |
|
|
return false; |
|
|
}; |
|
|
/** |
|
|
Does the drag. |
|
|
@param {Event} e |
|
|
@private |
|
|
*/ |
|
|
Drag.prototype.drag = function(e) { |
|
|
e = this.fixE(e); |
|
|
var o = this.obj; |
|
|
|
|
|
var ey = e.clientY; |
|
|
var ex = e.clientX; |
|
|
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10); |
|
|
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10 ); |
|
|
var nx, ny; |
|
|
|
|
|
nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1)); |
|
|
ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1)); |
|
|
|
|
|
this.obj.root.style = nx + "px"; |
|
|
this.obj.root.style = ny + "px"; |
|
|
this.obj.lastMouseX = ex; |
|
|
this.obj.lastMouseY = ey; |
|
|
|
|
|
this.obj.root.onthis(nx, ny); |
|
|
return false; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Ends the drag. |
|
|
@private |
|
|
*/ |
|
|
Drag.prototype.end = function() { |
|
|
document.onmousemove=this.obj.onmousemoveDefault; |
|
|
document.onmouseup = null; |
|
|
this.obj.dragging = false; |
|
|
if (this.endHook) { |
|
|
this.endHook( parseInt(this.obj.root.style, 10), |
|
|
parseInt(this.obj.root.style, 10)); |
|
|
} |
|
|
}; |
|
|
// ENDFILE: domdrag.js |
|
|
// STARTFILE: liveoptions.js |
|
|
//<NOLITE> |
|
|
////////////////////////////////////////////////// |
|
|
// live options nonsense |
|
|
// |
|
|
|
|
|
function popupToggleVar(varstring) { |
|
|
pg.option = ! getValueOf(varstring); |
|
|
if (getValueOf('popupCookies')) { |
|
|
Cookie.create(pg.option, String(pg.option)); |
|
|
} |
|
|
} |
|
|
|
|
|
function popupToggleShowOptions (dummy) { |
|
|
// just update state if dummy is true |
|
|
getValueOf('popupLiveOptionsExpanded'); |
|
|
if (!dummy) { pg.option.popupLiveOptionsExpanded=!pg.option.popupLiveOptionsExpanded; } |
|
|
setPopupHTML((pg.option.popupLiveOptionsExpanded) ? '<<' : '>>', |
|
|
'optionPopped'); |
|
|
var s=document.getElementById('popupOptionsDiv'); |
|
|
// if (!s) return; |
|
|
if (pg.option.popupLiveOptionsExpanded) { s.style.display='inline'; } |
|
|
else { s.style.display='none'; } |
|
|
} |
|
|
|
|
|
function popupOptionsCheckboxHTML(varstring, label, title) { |
|
|
var html='<br>'; |
|
|
html += '<span title="'+title+'">'; |
|
|
html += '<input type="checkbox" id="'+varstring+'Checkbox" '; |
|
|
html += (window) ? 'checked="checked" ' : ''; |
|
|
html += 'onClick="javascript:popupToggleVar(' + "'" + varstring + "'" + |
|
|
')">' + label + '</input></span>'; |
|
|
return html; |
|
|
} |
|
|
|
|
|
function popupLiveOptionsHTML() { |
|
|
var html = ''; |
|
|
html += '<br>'; |
|
|
html += '<span title="' + popupString('Show/hide options') + '" '; |
|
|
html += 'style="border: thin dotted black; cursor: pointer" '; |
|
|
html += 'onClick="javascript:popupToggleShowOptions()">'; |
|
|
html += 'Options <span id="optionPopped' + pg.idNumber + '"></span>'; |
|
|
html += '</span>'; |
|
|
html += '<div style="display: none" id="popupOptionsDiv">'; |
|
|
html += popupOptionsCheckboxHTML('simplePopups', popupString('Simple popups'), |
|
|
popupString('Never download extra stuff for images/previews')); |
|
|
html += popupOptionsCheckboxHTML('popupNavLinks', popupString('Show navigation links'), |
|
|
popupString('Display navigation links at the top of the popup')); |
|
|
html += popupOptionsCheckboxHTML('popupImages', popupString('Show image previews'), |
|
|
popupString('Load images')); |
|
|
html += popupOptionsCheckboxHTML('popupSummaryData', popupString('Show summary data'), |
|
|
popupString('Show page summary data')); |
|
|
html += popupOptionsCheckboxHTML('popupPreviews', popupString('Show text previews'), |
|
|
popupString('Show previews')); |
|
|
|
|
|
var extraOptions=[ |
|
|
'imagePopupsForImages', |
|
|
'popupAdminLinks', |
|
|
'popupAppendRedirNavLinks', |
|
|
'popupCookies', |
|
|
'popupFixDabs', |
|
|
'popupFixRedirs', |
|
|
'popupHistoricalLinks', |
|
|
'popupImagesFromThisWikiOnly', |
|
|
'popupImagesToggleSize', |
|
|
'popupLastEditLink', |
|
|
'popupLiveOptions', |
|
|
'popupLoadImagesSequentially', |
|
|
'popupNeverGetThumbs', |
|
|
'popupOnlyArticleLinks', |
|
|
'popupPreviewKillTemplates', |
|
|
'popupPreviewFirstParOnly', |
|
|
'popupShortcutKeys', |
|
|
'popupSimplifyMainLink', |
|
|
'removeTitles' // no , |
|
|
]; |
|
|
for (var i=0; i<extraOptions.length; ++i) { |
|
|
html += popupOptionsCheckboxHTML(extraOptions, extraOptions, popupString('Toggle this option')); |
|
|
} |
|
|
|
|
|
html += '</div>'; |
|
|
return html; |
|
|
} |
|
|
//</NOLITE> |
|
|
// ENDFILE: liveoptions.js |
|
|
// STARTFILE: structures.js |
|
|
//<NOLITE> |
|
|
pg.structures.original={}; |
|
|
pg.structures.original.popupLayout=function () { |
|
|
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle', |
|
|
'popupData', 'popupOtherLinks', |
|
|
'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', |
|
|
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'], |
|
|
'popupMiscTools', , |
|
|
'popupPrePreviewSep', 'popupPreview', 'popupPostPreview', 'popupFixDab']; |
|
|
}; |
|
|
pg.structures.original.popupRedirSpans=function () { |
|
|
return ['popupRedir', 'popupWarnRedir', 'popupRedirTopLinks', |
|
|
'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks']; |
|
|
}; |
|
|
pg.structures.original.popupTitle=function (x) { |
|
|
log ('defaultstructure.popupTitle'); |
|
|
if (!getValueOf('popupNavLinks')) { |
|
|
return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.oldid); |
|
|
} |
|
|
return ''; |
|
|
}; |
|
|
pg.structures.original.popupTopLinks=function (x) { |
|
|
log ('defaultstructure.popupTopLinks'); |
|
|
if (getValueOf('popupNavLinks')) { return navLinksHTML(x.article, x.hint, x.oldid); } |
|
|
return ''; |
|
|
}; |
|
|
pg.structures.original.popupImage=function(x) { |
|
|
log ('original.popupImage, x.article='+x.article+', pg.idNumber='+pg.idNumber); |
|
|
return imageHTML(x.article); |
|
|
}; |
|
|
pg.structures.original.popupRedirTitle=pg.structures.original.popupTitle; |
|
|
pg.structures.original.popupRedirTopLinks=pg.structures.original.popupTopLinks; |
|
|
|
|
|
|
|
|
function copyStructure(oldStructure, newStructure) { |
|
|
pg.structures={}; |
|
|
for (var prop in pg.structures) { |
|
|
pg.structures=pg.structures; |
|
|
} |
|
|
} |
|
|
|
|
|
copyStructure('original', 'nostalgia'); |
|
|
pg.structures.nostalgia.popupTopLinks=function(x) { |
|
|
var str=''; |
|
|
str += '<b><<mainlink|shortcut= >></b>'; |
|
|
|
|
|
// user links |
|
|
// contribs - log - count - email - block |
|
|
// count only if applicable; block only if popupAdminLinks |
|
|
str += 'if(user){<br><<contribs|shortcut=c>>'; |
|
|
str+='if(wikimedia){*<<count|shortcut=#>>}'; |
|
|
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>}}'; |
|
|
|
|
|
// editing links |
|
|
// talkpage -> edit|new - history - un|watch - article|edit |
|
|
// other page -> edit - history - un|watch - talk|edit|new |
|
|
var editstr='<<edit|shortcut=e>>'; |
|
|
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' |
|
|
+ editstr + '}' |
|
|
var historystr='<<history|shortcut=h>>'; |
|
|
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>'; |
|
|
|
|
|
str+='<br>if(talk){' + |
|
|
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' + |
|
|
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' + |
|
|
'}else{' + // not a talk page |
|
|
editOldidStr + '*' + historystr + '*' + watchstr + '*' + |
|
|
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' |
|
|
+ '}'; |
|
|
|
|
|
// misc links |
|
|
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>'; |
|
|
str += 'if(admin){<br>}else{*}<<move|shortcut=m>>'; |
|
|
|
|
|
// admin links |
|
|
str += 'if(admin){*<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*' + |
|
|
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>}'; |
|
|
return navlinkStringToHTML(str, x.article, x.oldid); |
|
|
}; |
|
|
pg.structures.nostalgia.popupRedirTopLinks=pg.structures.nostalgia.popupTopLinks; |
|
|
|
|
|
/** -- fancy -- **/ |
|
|
copyStructure('original', 'fancy'); |
|
|
pg.structures.fancy.popupTitle=function (x) { |
|
|
return navlinkStringToHTML('<font size=+0><<mainlink>></font>',x.article,x.oldid); |
|
|
}; |
|
|
pg.structures.fancy.popupTopLinks=function(x) { |
|
|
var hist='<<history|shortcut=h|hist>>|<<lastEdit|shortcut=/|last>>if(mainspace_en){|<<editors|shortcut=E|eds>>}'; |
|
|
var watch='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>'; |
|
|
var move='<<move|shortcut=m|move>>'; |
|
|
return navlinkStringToHTML('if(talk){' + |
|
|
'<<edit|shortcut=e>>|<<new|shortcut=+|+>>*' + hist + '*' + |
|
|
'<<article|shortcut=a>>|<<editArticle|edit>>' + '*' + watch + '*' + move + |
|
|
'}else{<<edit|shortcut=e>>*' + hist + |
|
|
'*<<talk|shortcut=t|>>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' + |
|
|
'*' + watch + '*' + move+'}<br>', x.article, x.oldid); |
|
|
}; |
|
|
pg.structures.fancy.popupOtherLinks=function(x) { |
|
|
var admin='<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*<<undelete|undeleteShort>>|<<delete|shortcut=d|del>>'; |
|
|
var user='<<contribs|shortcut=c>>if(wikimedia){|<<count|shortcut=#|#>>}'; |
|
|
user+='if(ipuser){|<<arin>>}else{*<<email|shortcut=E|'+ |
|
|
popupString('email')+'>>}if(admin){*<<block|shortcut=b>>}'; |
|
|
|
|
|
var normal='<<whatLinksHere|shortcut=l|links here>>*<<relatedChanges|shortcut=r|related>>'; |
|
|
return navlinkStringToHTML('<br>if(user){' + user + '*}if(admin){'+admin+'if(user){<br>}else{*}}' + normal, |
|
|
x.article, x.oldid); |
|
|
}; |
|
|
pg.structures.fancy.popupRedirTitle=pg.structures.fancy.popupTitle; |
|
|
pg.structures.fancy.popupRedirTopLinks=pg.structures.fancy.popupTopLinks; |
|
|
pg.structures.fancy.popupRedirOtherLinks=pg.structures.fancy.popupOtherLinks; |
|
|
|
|
|
|
|
|
/** -- fancy2 -- **/ |
|
|
// hack for ] |
|
|
copyStructure('fancy', 'fancy2'); |
|
|
pg.structures.fancy2.popupTopLinks=function(x) { // hack out the <br> at the end and put one at the beginning |
|
|
return '<br>'+pg.structures.fancy.popupTopLinks(x).replace(RegExp('<br>$','i'),''); |
|
|
}; |
|
|
pg.structures.fancy2.popupLayout=function () { // move toplinks to after the title |
|
|
return ['popupError', 'popupImage', 'popupTitle', 'popupData', 'popupTopLinks', 'popupOtherLinks', |
|
|
'popupRedir', , |
|
|
'popupMiscTools', , |
|
|
'popupPrePreviewSep', 'popupPreview', 'popupPostPreview', 'popupFixDab']; |
|
|
}; |
|
|
|
|
|
/** -- menus -- **/ |
|
|
copyStructure('original', 'menus'); |
|
|
pg.structures.menus.popupLayout=function () { |
|
|
return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle', 'popupOtherLinks', |
|
|
'popupRedir', , |
|
|
'popupData', 'popupMiscTools', , |
|
|
'popupPrePreviewSep', 'popupPreview', 'popupPostPreview', 'popupFixDab']; |
|
|
}; |
|
|
function toggleSticky(uid) { |
|
|
var popDiv=document.getElementById('navpopup_maindiv'+uid); |
|
|
if (!popDiv) { return; } |
|
|
if (!popDiv.navpopup.sticky) { popDiv.navpopup.stick(); } |
|
|
else { |
|
|
popDiv.navpopup.unstick(); |
|
|
popDiv.navpopup.hide(); |
|
|
} |
|
|
} |
|
|
pg.structures.menus.popupTopLinks = function (x, shorter) { |
|
|
// FIXME this stuff should be cached |
|
|
var s=; |
|
|
var dropdiv='<div class="popup_drop">'; |
|
|
var enddiv='</div>'; |
|
|
var endspan='</span>'; |
|
|
var hist='<<history|shortcut=h>>'; |
|
|
if (!shorter) { hist = '<menurow>' + hist + |
|
|
'|<<historyfeed|rss>>if(mainspace_en){|<<editors|shortcut=E>>}</menurow>'; } |
|
|
var lastedit='<<lastEdit|shortcut=/|show last edit>>'; |
|
|
var jsHistory='<<lastContrib|last set of edits>><<sinceMe|changes since mine>>'; |
|
|
var linkshere='<<whatLinksHere|shortcut=l|what links here>>'; |
|
|
var related='<<relatedChanges|shortcut=r|related changes>>'; |
|
|
var search='<menurow><<search|shortcut=s>>if(wikimedia){|<<globalsearch|shortcut=g|global>>}' + |
|
|
'|<<google|shortcut=G|web>></menurow>'; |
|
|
var watch='<menurow><<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>></menurow>'; |
|
|
var protect='<<unprotect|unprotectShort>>|<<protect|shortcut=p>>|<<protectlog|log>>'; |
|
|
var del='<<undelete|undeleteShort>>|<<delete|shortcut=d>>|<<deletelog|log>>'; |
|
|
var move='<<move|shortcut=m|move page>>'; |
|
|
var nullPurge='<menurow><<nullEdit|shortcut=n|null edit>>|<<purge|shortcut=P>></menurow>'; |
|
|
var viewOptions='<menurow><<view|shortcut=v>>|<<render|shortcut=S>>|<<raw>></menurow>'; |
|
|
var editRow='if(oldid){<menurow><<edit|shortcut=e>>|<<editOld|shortcut=e|this revision>></menurow>' + |
|
|
'<<revert|shortcut=v>>' + '}else{<<edit|shortcut=e>>}'; |
|
|
var newTopic='if(talk){<<new|shortcut=+|new topic>>}'; |
|
|
var protectDelete='if(admin){<menurow>' + protect + '</menurow><menurow>' + del + '</menurow>}'; |
|
|
|
|
|
if (getValueOf('popupActionsMenu')) { |
|
|
s.push( '<<mainlink>>*' + dropdiv + '<a href="#">'+popupString('actions') + '</a>'); |
|
|
} else { |
|
|
s.push( dropdiv + '<<mainlink>>'); |
|
|
} |
|
|
s.push( '<menu>') |
|
|
s.push( editRow + newTopic + hist + lastedit ) |
|
|
if (!shorter) { s.push(jsHistory); } |
|
|
s.push( move + linkshere + related) |
|
|
if (!shorter) { s.push(nullPurge + search); } |
|
|
if (!shorter) { s.push(viewOptions); } |
|
|
s.push('<hr>' + watch + protectDelete); |
|
|
s.push('<hr>' + |
|
|
'if(talk){<<article|shortcut=a|view article>><<editArticle|edit article>>}' + |
|
|
'else{<<talk|shortcut=t|talk page>><<editTalk|edit talk>>' + |
|
|
'<<newTalk|shortcut=+|new topic>>}</menu>' + enddiv); |
|
|
|
|
|
// user menu starts here |
|
|
var email='<<email|shortcut=E|email user>>'; |
|
|
var contribs= 'if(wikimedia){<menurow>}<<contribs|shortcut=c|contributions>>' + |
|
|
'if(wikimedia){|<<contribsTree|tree>></menurow>}'; |
|
|
|
|
|
s.push('if(user){*' + dropdiv + '<a href="#">'+popupString('user')+'</a>'); |
|
|
s.push('<menu>'); + |
|
|
s.push('<menurow><<userPage|shortcut=u|user page>>|<<userSpace|space>></menurow>'); |
|
|
s.push('<<userTalk|shortcut=t|user talk>><<editUserTalk|edit user talk>>' + |
|
|
'<<newUserTalk|shortcut=+|leave comment>>'); |
|
|
if(!shorter) { s.push( 'if(ipuser){<<arin>>}else{' + email + '}') } |
|
|
else { s.push( 'if(ipuser){}else{' + email + '}') } |
|
|
s.push('<hr>' + contribs + '<<userlog|shortcut=L|user log>>'); |
|
|
s.push('if(wikimedia){<<count|shortcut=#|edit counter>>}'); |
|
|
s.push('if(admin){<menurow><<unblock|unblockShort>>|<<block|shortcut=b|block user>></menurow>}'); |
|
|
s.push('<<blocklog|shortcut=B|block log>>' + getValueOf('popupExtraUserMenu')); |
|
|
s.push('</menu>' + enddiv + '}'); |
|
|
|
|
|
// popups menu starts here |
|
|
if (getValueOf('popupSetupMenu')) { |
|
|
s.push('*' + dropdiv + '<a href="#">' + popupString('popupsMenu') + '</a><menu>'); |
|
|
s.push('<<togglePreviews|toggle previews>>'); |
|
|
s.push('<<purgePopups|reset>>'); |
|
|
s.push('<<disablePopups|disable>>'); |
|
|
s.push('</menu>'+enddiv); |
|
|
} |
|
|
return navlinkStringToHTML(s.join(''), x.article, x.oldid); |
|
|
}; |
|
|
|
|
|
pg.structures.menus.popupRedirTitle=pg.structures.menus.popupTitle; |
|
|
pg.structures.menus.popupRedirTopLinks=pg.structures.menus.popupTopLinks; |
|
|
|
|
|
copyStructure('menus', 'shortmenus'); |
|
|
pg.structures.shortmenus.popupTopLinks=function(x) { |
|
|
return pg.structures.menus.popupTopLinks(x,true); |
|
|
}; |
|
|
pg.structures.shortmenus.popupRedirTopLinks=pg.structures.shortmenus.popupTopLinks; |
|
|
|
|
|
//</NOLITE> |
|
|
pg.structures.lite={}; |
|
|
pg.structures.lite.popupLayout=function () { |
|
|
return ; |
|
|
}; |
|
|
pg.structures.lite.popupTitle=function (x) { |
|
|
log (x.article + ': structures.lite.popupTitle'); |
|
|
//return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.oldid); |
|
|
return '<div><span class="popup_mainlink"><b>' + x.article.toString() + '</b></span></div>'; |
|
|
}; |
|
|
// ENDFILE: structures.js |
|
|
// STARTFILE: autoedit.js |
|
|
//<NOLITE> |
|
|
function getParamValue(paramName, h) { |
|
|
if (typeof h == 'undefined' ) { h = document.location.href; } |
|
|
var cmdRe=RegExp(''+paramName+'=(*)'); |
|
|
var m=cmdRe.exec(h); |
|
|
if (m) { |
|
|
try { |
|
|
return decodeURI(m); |
|
|
} catch (someError) {} |
|
|
} |
|
|
return null; |
|
|
} |
|
|
|
|
|
function substitute(data,cmdBody) { |
|
|
// alert('sub\nfrom: '+cmdBody.from+'\nto: '+cmdBody.to+'\nflags: '+cmdBody.flags); |
|
|
var fromRe=RegExp(cmdBody.from, cmdBody.flags); |
|
|
return data.replace(fromRe, cmdBody.to); |
|
|
} |
|
|
|
|
|
function execCmds(data, cmdList) { |
|
|
for (var i=0; i<cmdList.length; ++i) { |
|
|
data=cmdList.action(data, cmdList); |
|
|
} |
|
|
return data; |
|
|
} |
|
|
|
|
|
function parseCmd(str) { |
|
|
// returns a list of commands |
|
|
if (!str.length) { return ; } |
|
|
var p=false; |
|
|
switch (str.charAt(0)) { |
|
|
case 's': |
|
|
p=parseSubstitute(str); |
|
|
break; |
|
|
default: |
|
|
return false; |
|
|
} |
|
|
if (p) { return .concat(parseCmd(p.remainder)); } |
|
|
return false; |
|
|
} |
|
|
|
|
|
function unEscape(str, sep) { |
|
|
return str.split('\\\\').join('\\').split('\\'+sep).join(sep).split('\\n').join('\n'); |
|
|
} |
|
|
|
|
|
|
|
|
function parseSubstitute(str) { |
|
|
// takes a string like s/a/b/flags;othercmds and parses it |
|
|
|
|
|
var from,to,flags,tmp; |
|
|
|
|
|
if (str.length<4) { return false; } |
|
|
var sep=str.charAt(1); |
|
|
str=str.substring(2); |
|
|
|
|
|
tmp=skipOver(str,sep); |
|
|
if (tmp) { from=tmp.segment; str=tmp.remainder; } |
|
|
else { return false; } |
|
|
|
|
|
tmp=skipOver(str,sep); |
|
|
if (tmp) { to=tmp.segment; str=tmp.remainder; } |
|
|
else { return false; } |
|
|
|
|
|
flags=''; |
|
|
if (str.length) { |
|
|
tmp=skipOver(str,';') || skipToEnd(str, ';'); |
|
|
if (tmp) {flags=tmp.segment; str=tmp.remainder; } |
|
|
} |
|
|
|
|
|
return {action: substitute, from: from, to: to, flags: flags, remainder: str}; |
|
|
|
|
|
} |
|
|
|
|
|
function skipOver(str,sep) { |
|
|
var endSegment=findNext(str,sep); |
|
|
if (endSegment<0) { return false; } |
|
|
var segment=unEscape(str.substring(0,endSegment), sep); |
|
|
return {segment: segment, remainder: str.substring(endSegment+1)}; |
|
|
} |
|
|
|
|
|
function skipToEnd(str,sep) { |
|
|
return {segment: str, remainder: ''}; |
|
|
} |
|
|
|
|
|
function findNext(str, ch) { |
|
|
for (var i=0; i<str.length; ++i) { |
|
|
if (str.charAt(i)=='\\') { i+=2; } |
|
|
if (str.charAt(i)==ch) { return i; } |
|
|
} |
|
|
return -1; |
|
|
} |
|
|
|
|
|
function setCheckbox(param, box) { |
|
|
var val=getParamValue(param); |
|
|
if (val!==null) { |
|
|
switch (val) { |
|
|
case '1': case 'yes': case 'true': |
|
|
box.checked=true; |
|
|
break; |
|
|
case '0': case 'no': case 'false': |
|
|
box.checked=false; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
function autoEdit() { |
|
|
if (!document.editform) { return false; } |
|
|
if (window.autoEdit.alreadyRan) { return false; } |
|
|
window.autoEdit.alreadyRan=true; |
|
|
var cmdString=getParamValue('autoedit'); |
|
|
if (cmdString) { |
|
|
try { |
|
|
var editbox=document.editform.wpTextbox1; |
|
|
} catch (dang) { return; } |
|
|
var cmdList=parseCmd(cmdString); |
|
|
var input=editbox.value; |
|
|
var output=execCmds(input, cmdList); |
|
|
editbox.value=output; |
|
|
} |
|
|
setCheckbox('autominor', document.editform.wpMinoredit); |
|
|
setCheckbox('autowatch', document.editform.wpWatchthis); |
|
|
|
|
|
var rvid = getParamValue('autorv'); |
|
|
if (getValueOf('popupUseQueryInterface') && getParamValue('autorv')) { |
|
|
var url=pg.wiki.wikibase + '/query.php?format=json&what=revisions&revids='+rvid; |
|
|
startDownload(url, null, autoEdit2); |
|
|
} else { autoEdit2(); } |
|
|
} |
|
|
|
|
|
function autoEdit2(d) { |
|
|
var summary=getParamValue('autosummary'); |
|
|
var summaryprompt=getParamValue('autosummaryprompt'); |
|
|
var summarynotice=''; |
|
|
if (d && d.data && getParamValue('autorv')) { |
|
|
var s = getRvSummary(summary, d.data); |
|
|
if (s===false) { |
|
|
summaryprompt=true; |
|
|
summarynotice=popupString('Failed to get revision information, please edit manually.\n\n'); |
|
|
summary = simplePrintf(summary, ); |
|
|
} else { summary = s; } |
|
|
} |
|
|
if (summaryprompt) { |
|
|
var txt= summarynotice + popupString('Enter a non-empty edit summary or press cancel to abort'); |
|
|
var response=prompt(txt, summary); |
|
|
if (response) { summary=response; } |
|
|
else { return; } |
|
|
} |
|
|
if (summary) { document.editform.wpSummary.value=summary; } |
|
|
// Attempt to avoid possible premature clicking of the save button |
|
|
// (maybe delays in updates to the DOM are to blame?? or a red herring) |
|
|
setTimeout(autoEdit3, 100); |
|
|
} |
|
|
|
|
|
function autoEdit3() { |
|
|
var btn=getParamValue('autoclick'); |
|
|
if (btn) { |
|
|
if (document.editform && document.editform) { |
|
|
var headings=document.getElementsByTagName('h1'); |
|
|
if (headings) { |
|
|
var div=document.createElement('div'); |
|
|
var button=document.editform; |
|
|
div.innerHTML='<font size=+1><b>' + |
|
|
tprintf('The %s button has been automatically clicked. Please wait for the next page to load.', |
|
|
) + '</b></font>'; |
|
|
document.title='('+document.title+')'; |
|
|
headings.parentNode.insertBefore(div, headings); |
|
|
button.click(); |
|
|
} |
|
|
} else { |
|
|
alert(tprintf('Could not find button %s. Please check the settings in your javascript file.', )); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
function getRvSummary(template, json) { |
|
|
var o=getJsObj(json); |
|
|
try { |
|
|
var edit = anyChild(o.pages).revisions; |
|
|
} catch (badness) {return false;} |
|
|
var timestamp = edit.timestamp.split(//g).join(' ').replace(/^ *| *$/g, ''); |
|
|
return simplePrintf(template, ); |
|
|
} |
|
|
|
|
|
addOnloadHook(autoEdit); |
|
|
//</NOLITE> |
|
|
// ENDFILE: autoedit.js |
|
|
// STARTFILE: downloader.js |
|
|
/** |
|
|
@fileoverview |
|
|
{@link Downloader}, a xmlhttprequest wrapper, and helper functions. |
|
|
*/ |
|
|
|
|
|
/** |
|
|
Creates a new Downloader |
|
|
@constructor |
|
|
@class The Downloader class. Create a new instance of this class to download stuff. |
|
|
@param {String} url The url to download. This can be omitted and supplied later. |
|
|
*/ |
|
|
function Downloader(url) { |
|
|
// Source: http://jibbering.com/2002/4/httprequest.html |
|
|
/** xmlhttprequest object which we're wrapping */ |
|
|
this.http = false; |
|
|
|
|
|
/*@cc_on @*/ |
|
|
/*@if (@_jscript_version >= 5) |
|
|
// JScript gives us Conditional compilation, |
|
|
// we can cope with old IE versions. |
|
|
// and security blocked creation of the objects. |
|
|
try { |
|
|
this.http = new ActiveXObject("Msxml2.XMLHTTP"); |
|
|
} catch (e) { |
|
|
try { |
|
|
this.http = new ActiveXObject("Microsoft.XMLHTTP"); |
|
|
} catch (E) { |
|
|
// this.http = false; |
|
|
} |
|
|
} |
|
|
@end @*/ |
|
|
|
|
|
if (! this.http && typeof XMLHttpRequest!='undefined') { this.http = new XMLHttpRequest(); } |
|
|
/** |
|
|
The url to download |
|
|
@type String |
|
|
*/ |
|
|
this.url = url; |
|
|
/** |
|
|
A universally unique ID number |
|
|
@type integer |
|
|
*/ |
|
|
this.id=null; |
|
|
/** |
|
|
Modification date, to be culled from the incoming headers |
|
|
@type Date |
|
|
@private |
|
|
*/ |
|
|
this.lastModified = null; |
|
|
/** |
|
|
What to do when the download completes successfully |
|
|
@type Function |
|
|
@private |
|
|
*/ |
|
|
this.callbackFunction = null; |
|
|
/** |
|
|
What to do on failure |
|
|
@type Function |
|
|
@private |
|
|
*/ |
|
|
this.onFailure = null; |
|
|
/** |
|
|
Flag set on <code>abort</code> |
|
|
@type boolean |
|
|
*/ |
|
|
this.aborted = false; |
|
|
/** |
|
|
HTTP method. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html for possibilities. |
|
|
@type String |
|
|
*/ |
|
|
this.method='GET'; |
|
|
/** |
|
|
Async flag. |
|
|
@type boolean |
|
|
*/ |
|
|
this.async=true; |
|
|
} |
|
|
|
|
|
new Downloader(); |
|
|
|
|
|
/** Submits the http request. */ |
|
|
Downloader.prototype.send = function (x) { |
|
|
if (!this.http) { return null; } |
|
|
return this.http.send(x); |
|
|
}; |
|
|
/** Aborts the download, setting the <code>aborted</code> field to true. */ |
|
|
Downloader.prototype.abort = function () { |
|
|
if (!this.http) { return null; } |
|
|
this.aborted=true; |
|
|
return this.http.abort(); |
|
|
}; |
|
|
/** Returns the downloaded data. */ |
|
|
Downloader.prototype.getData = function () {if (!this.http) { return null; } return this.http.responseText;}; |
|
|
/** Prepares the download. */ |
|
|
Downloader.prototype.setTarget = function () { |
|
|
if (!this.http) { return null; } |
|
|
this.http.open(this.method, this.url, this.async); |
|
|
}; |
|
|
/** Gets the state of the download. */ |
|
|
Downloader.prototype.getReadyState=function () {if (!this.http) { return null; } return this.http.readyState;}; |
|
|
|
|
|
pg.misc.downloadsInProgress = { }; |
|
|
|
|
|
/** Starts the download. |
|
|
Note that setTarget {@link Downloader#setTarget} must be run first |
|
|
*/ |
|
|
Downloader.prototype.start=function () { |
|
|
if (!this.http) { return; } |
|
|
pg.misc.downloadsInProgress = this; |
|
|
this.http.send(null); |
|
|
}; |
|
|
|
|
|
/** Gets the 'Last-Modified' date from the download headers. |
|
|
Should be run after the download completes. |
|
|
Returns <code>null</code> on failure. |
|
|
@return {Date} |
|
|
*/ |
|
|
Downloader.prototype.getLastModifiedDate=function () { |
|
|
if(!this.http) { return null; } |
|
|
var lastmod=null; |
|
|
try { |
|
|
lastmod=this.http.getResponseHeader('Last-Modified'); |
|
|
} catch (err) {} |
|
|
if (lastmod) { return new Date(lastmod); } |
|
|
return null; |
|
|
}; |
|
|
|
|
|
/** Sets the callback function. |
|
|
@param {Function} f callback function, called as <code>f(this)</code> on success |
|
|
*/ |
|
|
Downloader.prototype.setCallback = function (f) { |
|
|
if(!this.http) { return; } |
|
|
this.http.onreadystatechange = f; |
|
|
}; |
|
|
|
|
|
Downloader.prototype.getStatus = function() { if (!this.http) { return null; } return this.http.status; }; |
|
|
|
|
|
////////////////////////////////////////////////// |
|
|
// helper functions |
|
|
|
|
|
/** Creates a new {@link Downloader} and prepares it for action. |
|
|
@param {String} url The url to download |
|
|
@param {integer} id The ID of the {@link Downloader} object |
|
|
@param {Function} callback The callback function invoked on success |
|
|
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser |
|
|
*/ |
|
|
function newDownload(url, id, callback, onfailure) { |
|
|
var d=new Downloader(url); |
|
|
if (!d.http) { return 'ohdear'; } |
|
|
d.id=id; |
|
|
d.setTarget(); |
|
|
if (!onfailure) { |
|
|
onfailure=2; |
|
|
} |
|
|
var f = function () { |
|
|
if (d.getReadyState() == 4) { |
|
|
delete pg.misc.downloadsInProgress; |
|
|
try { |
|
|
if ( d.getStatus() == 200 ) { |
|
|
d.data=d.getData(); |
|
|
d.lastModified=d.getLastModifiedDate(); |
|
|
callback(d); |
|
|
} else if (typeof onfailure == typeof 1) { |
|
|
if (onfailure > 0) { |
|
|
// retry |
|
|
newDownload(url, id, callback, onfailure - 1); |
|
|
} |
|
|
} else if (typeof onfailure == 'function') { |
|
|
onfailure(d,url,id,callback); |
|
|
} |
|
|
} catch (somerr) { /* ignore it */ } |
|
|
} |
|
|
}; |
|
|
d.setCallback(f); |
|
|
return d; |
|
|
} |
|
|
/** Simulates a download from cached data. |
|
|
The supplied data is put into a {@link Downloader} as if it had downloaded it. |
|
|
@param {String} url The url. |
|
|
@param {integer} id The ID. |
|
|
@param {Function} callback The callback, which is invoked immediately as <code>callback(d)</code>, |
|
|
where <code>d</code> is the new {@link Downloader}. |
|
|
@param {String} data The (cached) data. |
|
|
@param {Date} lastModified The (cached) last modified date. |
|
|
*/ |
|
|
function fakeDownload(url, id, callback, data, lastModified, owner) { |
|
|
var d=newDownload(url,callback); |
|
|
d.owner=owner; |
|
|
d.id=id; d.data=data; |
|
|
d.lastModified=lastModified; |
|
|
return callback(d); |
|
|
} |
|
|
|
|
|
/** |
|
|
Starts a download. |
|
|
@param {String} url The url to download |
|
|
@param {integer} id The ID of the {@link Downloader} object |
|
|
@param {Function} callback The callback function invoked on success |
|
|
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser |
|
|
*/ |
|
|
function startDownload(url, id, callback) { |
|
|
var d=newDownload(url, id, callback); |
|
|
if (typeof d == typeof '' ) { return d; } |
|
|
d.start(); |
|
|
return d; |
|
|
} |
|
|
|
|
|
/** |
|
|
Aborts all downloads which have been started. |
|
|
*/ |
|
|
function abortAllDownloads() { |
|
|
for ( var x in pg.misc.downloadsInProgress ) { |
|
|
try { |
|
|
pg.misc.downloadsInProgress.aborted=true; |
|
|
pg.misc.downloadsInProgress.abort(); |
|
|
delete pg.misc.downloadsInProgress; |
|
|
} catch (e) { } |
|
|
} |
|
|
} |
|
|
// ENDFILE: downloader.js |
|
|
// STARTFILE: livepreview.js |
|
|
// TODO: location is often not correct (eg relative links in previews) |
|
|
|
|
|
/** |
|
|
* InstaView - a Mediawiki to HTML converter in JavaScript |
|
|
* Version 0.6.1 |
|
|
* Copyright (C) Pedro Fayolle 2005-2006 |
|
|
* http://en.wikipedia.org/User:Pilaf |
|
|
* Distributed under the BSD license |
|
|
* |
|
|
* Changelog: |
|
|
* |
|
|
* 0.6.1 |
|
|
* - Fixed problem caused by \r characters |
|
|
* - Improved inline formatting parser |
|
|
* |
|
|
* 0.6 |
|
|
* - Changed name to InstaView |
|
|
* - Some major code reorganizations and factored out some common functions |
|
|
* - Handled conversion of relative links (i.e. ]) |
|
|
* - Fixed misrendering of adjacent definition list items |
|
|
* - Fixed bug in table headings handling |
|
|
* - Changed date format in signatures to reflect Mediawiki's |
|
|
* - Fixed handling of ] |
|
|
* - Updated MD5 function (hopefully it will work with UTF-8) |
|
|
* - Fixed bug in handling of links inside images |
|
|
* |
|
|
* To do: |
|
|
* - Better support for <math> |
|
|
* - Full support for <nowiki> |
|
|
* - Parser-based (as opposed to RegExp-based) inline wikicode handling (make it one-pass and bullet-proof) |
|
|
* - Support for templates (through AJAX) |
|
|
* - Support for coloured links (AJAX) |
|
|
*/ |
|
|
|
|
|
|
|
|
var Insta = {} |
|
|
|
|
|
function setupLivePreview() { |
|
|
|
|
|
// options |
|
|
Insta.conf = |
|
|
{ |
|
|
baseUrl: '', |
|
|
|
|
|
user: {}, |
|
|
|
|
|
wiki: { |
|
|
lang: pg.wiki.lang, |
|
|
interwiki: pg.wiki.interwiki, |
|
|
default_thumb_width: 180 |
|
|
}, |
|
|
|
|
|
paths: { |
|
|
articles: '/' + joinPath() + '/', |
|
|
math: '/math/', // FIXME |
|
|
images: ( window.getImageUrlStart ? getImageUrlStart(pg.wiki.hostname) : ''), |
|
|
images_fallback: 'http://upload.wikimedia.org/wikipedia/commons/', |
|
|
magnify_icon: 'skins/common/images/magnify-clip.png' |
|
|
}, |
|
|
|
|
|
locale: { |
|
|
user: pg.ns.user, |
|
|
image: pg.ns.image, |
|
|
category: pg.ns.category, |
|
|
// shouldn't be used in popup previews, i think |
|
|
months: |
|
|
} |
|
|
} |
|
|
|
|
|
// options with default values or backreferences |
|
|
with (Insta.conf) { |
|
|
user.name = user.name || 'Wikipedian' |
|
|
user.signature = ']' |
|
|
//paths.images = 'http://upload.wikimedia.org/wikipedia/' + wiki.lang + '/' |
|
|
} |
|
|
|
|
|
// define constants |
|
|
Insta.BLOCK_IMAGE = new RegExp('^\\[\\['+Insta.conf.locale.image+ |
|
|
':.*?\\|.*?(?:frame|thumbnail|thumb|none|right|left|center)', 'i'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
Insta.dump = function(from, to) |
|
|
{ |
|
|
if (typeof from == 'string') from = document.getElementById(from) |
|
|
if (typeof to == 'string') to = document.getElementById(to) |
|
|
to.innerHTML = this.convert(from.value) |
|
|
} |
|
|
|
|
|
Insta.convert = function(wiki) |
|
|
{ |
|
|
var ll = (typeof wiki == 'string')? wiki.replace(/\r/g,'').split(/\n/): wiki, // lines of wikicode |
|
|
o='', // output |
|
|
p=0, // para flag |
|
|
$r // result of passing a regexp to $() |
|
|
|
|
|
// some shorthands |
|
|
function remain() { return ll.length } |
|
|
function sh() { return ll.shift() } // shift |
|
|
function ps(s) { o+=s } // push |
|
|
|
|
|
function f() // similar to C's printf, uses ? as placeholders, ?? to escape question marks |
|
|
{ |
|
|
var i=1,a=arguments,f=a,o='',c,p |
|
|
for (;i<a.length; i++) if ((p=f.indexOf('?'))+1) { |
|
|
// allow character escaping |
|
|
i -= c=f.charAt(p+1)=='?'?1:0 |
|
|
o += f.substring(0,p)+(c?'?':a) |
|
|
f=f.substr(p+1+c) |
|
|
} else break; |
|
|
return o+f |
|
|
} |
|
|
|
|
|
function html_entities(s) { return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">") } |
|
|
|
|
|
function max(a,b) { return (a>b)?a:b } |
|
|
function min(a,b) { return (a<b)?a:b } |
|
|
|
|
|
// return the first non matching character position between two strings |
|
|
function str_imatch(a, b) |
|
|
{ |
|
|
for (var i=0, l=min(a.length, b.length); i<l; i++) if (a.charAt(i)!=b.charAt(i)) break |
|
|
return i |
|
|
} |
|
|
|
|
|
// compare current line against a string or regexp |
|
|
// if passed a string it will compare only the first string.length characters |
|
|
// if passed a regexp the result is stored in $r |
|
|
function $(c) { return (typeof c == 'string') ? (ll.substr(0,c.length)==c) : ($r = ll.match(c)) } |
|
|
|
|
|
function $$(c) { return ll==c } // compare current line against a string |
|
|
function _(p) { return ll.charAt(p) } // return char at pos p |
|
|
|
|
|
function endl(s) { ps(s); sh() } |
|
|
|
|
|
function parse_list() |
|
|
{ |
|
|
var prev=''; |
|
|
|
|
|
while (remain() && $(/^(+)(.*)$/)) { |
|
|
|
|
|
var l_match = $r |
|
|
|
|
|
sh() |
|
|
|
|
|
var ipos = str_imatch(prev, l_match) |
|
|
|
|
|
// close uncontinued lists |
|
|
for (var i=prev.length-1; i >= ipos; i--) { |
|
|
|
|
|
var pi = prev.charAt(i) |
|
|
|
|
|
if (pi=='*') ps('</ul>') |
|
|
else if (pi=='#') ps('</ol>') |
|
|
// close a dl only if the new item is not a dl item (:, ; or empty) |
|
|
else switch (l_match.charAt(i)) { case'':case'*':case'#': ps('</dl>') } |
|
|
} |
|
|
|
|
|
// open new lists |
|
|
for (var i=ipos; i<l_match.length; i++) { |
|
|
|
|
|
var li = l_match.charAt(i) |
|
|
|
|
|
if (li=='*') ps('<ul>') |
|
|
else if (li=='#') ps('<ol>') |
|
|
// open a new dl only if the prev item is not a dl item (:, ; or empty) |
|
|
else switch(prev.charAt(i)) { case'':case'*':case'#': ps('<dl>') } |
|
|
} |
|
|
|
|
|
switch (l_match.charAt(l_match.length-1)) { |
|
|
|
|
|
case '*': case '#': |
|
|
ps('<li>' + parse_inline_nowiki(l_match)); break |
|
|
|
|
|
case ';': |
|
|
ps('<dt>') |
|
|
|
|
|
var dt_match |
|
|
|
|
|
// handle ;dt :dd format |
|
|
if (dt_match = l_match.match(/(.*?)(:.*?)$/)) { |
|
|
|
|
|
ps(parse_inline_nowiki(dt_match)) |
|
|
ll.unshift(dt_match) |
|
|
|
|
|
} else ps(parse_inline_nowiki(l_match)) |
|
|
|
|
|
break |
|
|
|
|
|
case ':': |
|
|
ps('<dd>' + parse_inline_nowiki(l_match)) |
|
|
} |
|
|
|
|
|
prev=l_match |
|
|
} |
|
|
|
|
|
// close remaining lists |
|
|
for (var i=prev.length-1; i>=0; i--) |
|
|
ps(f('</?>', (prev.charAt(i)=='*')? 'ul': ((prev.charAt(i)=='#')? 'ol': 'dl'))) |
|
|
} |
|
|
|
|
|
function parse_table() |
|
|
{ |
|
|
endl(f('<table?>', $(/^\{\|( .*)$/)? $r: '')) |
|
|
|
|
|
for (;remain();) if ($('|')) switch (_(1)) { |
|
|
case '}': endl('</table>'); return |
|
|
case '-': endl(f('<tr ?>', $(/\|-*(.*)/))); break |
|
|
default: parse_table_data() |
|
|
} |
|
|
else if ($('!')) parse_table_data() |
|
|
else sh() |
|
|
} |
|
|
|
|
|
function parse_table_data() |
|
|
{ |
|
|
var td_line, match_i |
|
|
|
|
|
// 1: "|+", '|' or '+' |
|
|
// 2: ?? |
|
|
// 3: attributes ?? |
|
|
// TODO: finish commenting this regexp |
|
|
var td_match = sh().match(/^(\|\+|\||!)((?:(*?)\|(?!\|))?(.*))$/) |
|
|
|
|
|
if (td_match == '|+') ps('<caption'); |
|
|
else ps('<t' + ((td_match=='|')?'d':'h')) |
|
|
|
|
|
if (typeof td_match != 'undefined') { |
|
|
|
|
|
ps(' ' + td_match) |
|
|
match_i = 4 |
|
|
|
|
|
} else match_i = 2 |
|
|
|
|
|
ps('>') |
|
|
|
|
|
if (td_match != '|+') { |
|
|
|
|
|
// use || or !! as a cell separator depending on context |
|
|
// NOTE: when split() is passed a regexp make sure to use non-capturing brackets |
|
|
td_line = td_match.split((td_match == '|')? '||': /(?:\|\||!!)/) |
|
|
|
|
|
ps(parse_inline_nowiki(td_line.shift())) |
|
|
|
|
|
while (td_line.length) ll.unshift(td_match + td_line.pop()) |
|
|
|
|
|
} else ps(td_match) |
|
|
|
|
|
var tc = 0, td = |
|
|
|
|
|
for (;remain(); td.push(sh())) |
|
|
if ($('|')) { |
|
|
if (!tc) break // we're at the outer-most level (no nested tables), skip to td parse |
|
|
else if (_(1)=='}') tc-- |
|
|
} |
|
|
else if (!tc && $('!')) break |
|
|
else if ($('{|')) tc++ |
|
|
|
|
|
if (td.length) ps(Insta.convert(td)) |
|
|
} |
|
|
|
|
|
function parse_pre() |
|
|
{ |
|
|
ps('<pre>') |
|
|
do endl(parse_inline_nowiki(ll.substring(1)) + "\n"); while (remain() && $(' ')) |
|
|
ps('</pre>') |
|
|
} |
|
|
|
|
|
function parse_block_image() |
|
|
{ |
|
|
ps(parse_image(sh())) |
|
|
} |
|
|
|
|
|
function parse_image(str) |
|
|
{ |
|
|
//<NOLITE> |
|
|
// get what's in between "]" |
|
|
var tag = str.substring(Insta.conf.locale.image.length + 3, str.length - 2); |
|
|
|
|
|
var width; |
|
|
var attr = , filename, caption = ''; |
|
|
var thumb=0, frame=0, center=0; |
|
|
var align=''; |
|
|
|
|
|
if (tag.match(/\|/)) { |
|
|
// manage nested links |
|
|
var nesting = 0; |
|
|
var last_attr; |
|
|
for (var i = tag.length-1; i > 0; i--) { |
|
|
if (tag.charAt(i) == '|' && !nesting) { |
|
|
last_attr = tag.substr(i+1); |
|
|
tag = tag.substring(0, i); |
|
|
break; |
|
|
} else switch (tag.substr(i-1, 2)) { |
|
|
case ']]': |
|
|
nesting++; |
|
|
i--; |
|
|
break; |
|
|
case '[[': |
|
|
nesting--; |
|
|
i--; |
|
|
} |
|
|
} |
|
|
|
|
|
attr = tag.split(/\s*\|\s*/); |
|
|
attr.push(last_attr); |
|
|
filename = attr.shift(); |
|
|
|
|
|
var w_match; |
|
|
|
|
|
for (;attr.length; attr.shift()) |
|
|
if (w_match = attr.match(/^(\d*)px$/)) width = w_match |
|
|
else switch(attr) { |
|
|
case 'thumb': |
|
|
case 'thumbnail': |
|
|
thumb=true; |
|
|
case 'frame': |
|
|
frame=true; |
|
|
break; |
|
|
case 'none': |
|
|
case 'right': |
|
|
case 'left': |
|
|
center=false; |
|
|
align=attr; |
|
|
break; |
|
|
case 'center': |
|
|
center=true; |
|
|
align='none'; |
|
|
break; |
|
|
default: |
|
|
if (attr.length == 1) caption = attr; |
|
|
} |
|
|
|
|
|
} else filename = tag; |
|
|
|
|
|
|
|
|
var o=''; |
|
|
|
|
|
if (frame) { |
|
|
|
|
|
if (align=='') align = 'right'; |
|
|
|
|
|
o += f("<div class='thumb t?'>", align); |
|
|
|
|
|
if (thumb) { |
|
|
if (!width) width = Insta.conf.wiki.default_thumb_width; |
|
|
|
|
|
o += f("<div style='width:?px;'>?", 2+width*1, make_image(filename, caption, width)) + |
|
|
f("<div class='thumbcaption'><div class='magnify' style='float:right'><a href='?' class='internal' title='Enlarge'><img src='?'></a></div>?</div>", |
|
|
Insta.conf.paths.articles + Insta.conf.locale.image + ':' + filename, |
|
|
Insta.conf.paths.magnify_icon, |
|
|
parse_inline_nowiki(caption) |
|
|
) |
|
|
} else { |
|
|
o += '<div>' + make_image(filename, caption) + f("<div class='thumbcaption'>?</div>", parse_inline_nowiki(caption)) |
|
|
} |
|
|
|
|
|
o += '</div></div>'; |
|
|
|
|
|
} else if (align != '') { |
|
|
o += f("<div class='float?'><span>?</span></div>", align, make_image(filename, caption, width)); |
|
|
} else { |
|
|
return make_image(filename, caption, width); |
|
|
} |
|
|
|
|
|
return center? f("<div class='center'>?</div>", o): o; |
|
|
//</NOLITE> |
|
|
} |
|
|
|
|
|
function parse_inline_nowiki(str) |
|
|
{ |
|
|
var start, lastend=0 |
|
|
var substart=0, nestlev=0, open, close, subloop; |
|
|
var html=''; |
|
|
|
|
|
while (-1 != (start = str.indexOf('<nowiki>', substart))) { |
|
|
html += parse_inline_wiki(str.substring(lastend, start)); |
|
|
start += 8; |
|
|
substart = start; |
|
|
subloop = true; |
|
|
do { |
|
|
open = str.indexOf('<nowiki>', substart); |
|
|
close = str.indexOf('</nowiki>', substart); |
|
|
if (close<=open || open==-1) { |
|
|
if (close==-1) { |
|
|
return html + html_entities(str.substr(start)); |
|
|
} |
|
|
substart = close+9; |
|
|
if (nestlev) { |
|
|
nestlev--; |
|
|
} else { |
|
|
lastend = substart; |
|
|
html += html_entities(str.substring(start, lastend-9)); |
|
|
subloop = false; |
|
|
} |
|
|
} else { |
|
|
substart = open+8; |
|
|
nestlev++; |
|
|
} |
|
|
} while (subloop) |
|
|
} |
|
|
|
|
|
return html + parse_inline_wiki(str.substr(lastend)); |
|
|
} |
|
|
|
|
|
function make_image(filename, caption, width) |
|
|
{ |
|
|
//<NOLITE> |
|
|
// uppercase first letter in file name |
|
|
filename = filename.charAt(0).toUpperCase() + filename.substr(1); |
|
|
// replace spaces with underscores |
|
|
filename = filename.replace(/ /g, '_'); |
|
|
|
|
|
caption = strip_inline_wiki(caption); |
|
|
|
|
|
var md5 = hex_md5(filename); |
|
|
|
|
|
var source = md5.charAt(0) + '/' + md5.substr(0,2) + '/' + filename; |
|
|
|
|
|
if (width) width = "width='" + width + "px'"; |
|
|
|
|
|
var img = f("<img onerror=\"this.onerror=null;this.src='?'\" src='?' ? ?>", Insta.conf.paths.images_fallback + source, Insta.conf.paths.images + source, (caption!='')? "alt='" + caption + "'" : '', width); |
|
|
|
|
|
return f("<a class='image' ? href='?'>?</a>", (caption!='')? "title='" + caption + "'" : '', Insta.conf.paths.articles + Insta.conf.locale.image + ':' + filename, img); |
|
|
//</NOLITE> |
|
|
} |
|
|
|
|
|
function parse_inline_images(str) |
|
|
{ |
|
|
//<NOLITE> |
|
|
var start, substart=0, nestlev=0; |
|
|
var loop, close, open, wiki, html; |
|
|
|
|
|
while (-1 != (start=str.indexOf('[[', substart))) { |
|
|
if(str.substr(start+2).match(RegExp('^' + Insta.conf.locale.image + ':','i'))) { |
|
|
loop=true; |
|
|
substart=start; |
|
|
do { |
|
|
substart+=2; |
|
|
close=str.indexOf(']]',substart); |
|
|
open=str.indexOf('[[',substart); |
|
|
if (close<=open||open==-1) { |
|
|
if (close==-1) return str; |
|
|
substart=close; |
|
|
if (nestlev) { |
|
|
nestlev--; |
|
|
} else { |
|
|
wiki=str.substring(start,close+2); |
|
|
html=parse_image(wiki); |
|
|
str=str.replace(wiki,html); |
|
|
substart=start+html.length; |
|
|
loop=false; |
|
|
} |
|
|
} else { |
|
|
substart=open; |
|
|
nestlev++; |
|
|
} |
|
|
} while (loop) |
|
|
|
|
|
} else break; |
|
|
} |
|
|
|
|
|
//</NOLITE> |
|
|
return str; |
|
|
} |
|
|
|
|
|
// the output of this function doesn't respect the FILO structure of HTML |
|
|
// but since most browsers can handle it I'll save myself the hassle |
|
|
function parse_inline_formatting(str) |
|
|
{ |
|
|
var em,st,i,li,o=''; |
|
|
while ((i=str.indexOf("''",li))+1) { |
|
|
o += str.substring(li,i); |
|
|
li=i+2; |
|
|
if (str.charAt(i+2)=="'") { |
|
|
li++; |
|
|
st=!st; |
|
|
o+=st?'<strong>':'</strong>'; |
|
|
} else { |
|
|
em=!em; |
|
|
o+=em?'<em>':'</em>'; |
|
|
} |
|
|
} |
|
|
return o+str.substr(li); |
|
|
} |
|
|
|
|
|
function parse_inline_wiki(str) |
|
|
{ |
|
|
var aux_match; |
|
|
|
|
|
str = parse_inline_images(str); |
|
|
str = parse_inline_formatting(str); |
|
|
|
|
|
// math |
|
|
while (aux_match = str.match(/<(?:)math>(.*?)<\/math>/i)) { |
|
|
var math_md5 = hex_md5(aux_match); |
|
|
str = str.replace(aux_match, f("<img src='?.png'>", Insta.conf.paths.math+math_md5)); |
|
|
} |
|
|
|
|
|
// Build a Mediawiki-formatted date string |
|
|
var date = new Date; |
|
|
var minutes = date.getUTCMinutes(); |
|
|
if (minutes < 10) minutes = '0' + minutes; |
|
|
var date = f("?:?, ? ? ? (UTC)", date.getUTCHours(), minutes, date.getUTCDate(), Insta.conf.locale.months, date.getUTCFullYear()); |
|
|
|
|
|
// text formatting |
|
|
return str. |
|
|
// signatures |
|
|
replace(/~{5}(?!~)/g, date). |
|
|
replace(/~{4}(?!~)/g, Insta.conf.user.name+' '+date). |
|
|
replace(/~{3}(?!~)/g, Insta.conf.user.name). |
|
|
|
|
|
// ], ], etc... |
|
|
replace(RegExp('\\\\]','gi'), "<a href='"+Insta.conf.paths.articles+"$1'>$1</a>"). |
|
|
replace(RegExp('\\\\]','gi'),''). |
|
|
|
|
|
// ] |
|
|
replace(/\*?)\]\]/g, f("<a href='?$1'>$1</a>", Insta.conf.baseUrl)). |
|
|
|
|
|
// ] |
|
|
replace(/\\]/g, f("<a href='?$1'>$2</a>", Insta.conf.baseUrl)). |
|
|
|
|
|
// ] |
|
|
replace(/\*?)\]\](\w*)/g, f("<a href='?$1'>$1$2</a>", Insta.conf.paths.articles)). |
|
|
|
|
|
// ] |
|
|
replace(/\]+?)\]\](\w*)/g, f("<a href='?$1'>$2$3</a>", Insta.conf.paths.articles)). |
|
|
|
|
|
// ] |
|
|
replace(/\]*?:)?(.*?)( *\(.*?\))?\|\]\]/g, f("<a href='?$1$2$3'>$2</a>", Insta.conf.paths.articles)). |
|
|
|
|
|
// External links |
|
|
replace(/\]*?) (.*?)\]/g, "<a class='external' href='$1:$2$3'>$4</a>"). |
|
|
replace(/\/g, "<a class='external' href='http://$1'></a>"). |
|
|
replace(/\/g, "<a class='external' href='$1:$2$3'>$1:$2$3</a>"). |
|
|
replace(/(^| )(https?|news|ftp|mailto|gopher|irc):(\/*)(*)/g, "$1<a class='external' href='$2:$3$4'>$2:$3$4</a>"). |
|
|
|
|
|
replace('__NOTOC__',''). |
|
|
replace('__NOEDITSECTION__',''); |
|
|
} |
|
|
|
|
|
function strip_inline_wiki(str) |
|
|
{ |
|
|
return str |
|
|
.replace(/\]*\|(.*?)\]\]/g,'$1') |
|
|
.replace(/\\]/g,'$1') |
|
|
.replace(/''(.*?)''/g,'$1'); |
|
|
} |
|
|
|
|
|
// begin parsing |
|
|
for (;remain();) if ($(/^(={1,6})(.*)\1(.*)$/)) { |
|
|
p=0 |
|
|
endl(f('<h?>?</h?>?', $r.length, parse_inline_nowiki($r), $r.length, $r)) |
|
|
|
|
|
} else if ($(/^/)) { |
|
|
p=0 |
|
|
parse_list() |
|
|
|
|
|
} else if ($(' ')) { |
|
|
p=0 |
|
|
parse_pre() |
|
|
|
|
|
} else if ($('{|')) { |
|
|
p=0 |
|
|
parse_table() |
|
|
|
|
|
} else if ($(/^----+$/)) { |
|
|
p=0 |
|
|
endl('<hr>') |
|
|
|
|
|
} else if ($(Insta.BLOCK_IMAGE)) { |
|
|
p=0 |
|
|
parse_block_image() |
|
|
|
|
|
} else { |
|
|
|
|
|
// handle paragraphs |
|
|
if ($$('')) { |
|
|
if (p = (remain()>1 && ll==(''))) endl('<p><br>') |
|
|
} else { |
|
|
if(!p) { |
|
|
ps('<p>') |
|
|
p=1 |
|
|
} |
|
|
ps(parse_inline_nowiki(ll) + ' ') |
|
|
} |
|
|
|
|
|
sh(); |
|
|
} |
|
|
|
|
|
return o |
|
|
}; |
|
|
|
|
|
window.wiki2html=function(txt,baseurl) { |
|
|
Insta.conf.baseUrl=baseurl; |
|
|
return Insta.convert(txt); |
|
|
}; |
|
|
// ENDFILE: livepreview.js |
|
|
// STARTFILE: pageinfo.js |
|
|
//<NOLITE> |
|
|
function popupFilterPageSize(data) { |
|
|
return formatBytes(data.length); |
|
|
} |
|
|
|
|
|
function popupFilterCountLinks(data) { |
|
|
var num=countLinks(data); |
|
|
return String(num) + ' ' + ((num!=1)?popupString('wikiLinks'):popupString('wikiLink')); |
|
|
} |
|
|
|
|
|
function popupFilterCountImages(data) { |
|
|
var num=countImages(data); |
|
|
return String(num) + ' ' + ((num!=1)?popupString('images'):popupString('image')); |
|
|
} |
|
|
|
|
|
function popupFilterCountCategories(data) { |
|
|
var num=countCategories(data); |
|
|
return String(num) + ' ' + ((num!=1)?popupString('categories'):popupString('category')); |
|
|
} |
|
|
|
|
|
|
|
|
function popupFilterLastModified(data,download) { |
|
|
var lastmod=download.lastModified; |
|
|
var now=new Date(); |
|
|
var age=now-lastmod; |
|
|
if (lastmod && getValueOf('popupLastModified')) { |
|
|
return (tprintf('%s old', )).replace(RegExp(' ','g'), ' '); |
|
|
} |
|
|
return ''; |
|
|
} |
|
|
|
|
|
function formatAge(age) { |
|
|
// coerce into a number |
|
|
var a=0+age, aa=a; |
|
|
|
|
|
var seclen = 1000; |
|
|
var minlen = 60*seclen; |
|
|
var hourlen = 60*minlen; |
|
|
var daylen = 24*hourlen; |
|
|
var weeklen = 7*daylen; |
|
|
|
|
|
var numweeks = (a-a%weeklen)/weeklen; a = a-numweeks*weeklen; var sweeks = addunit(numweeks, 'week'); |
|
|
var numdays = (a-a%daylen)/daylen; a = a-numdays*daylen; var sdays = addunit(numdays, 'day'); |
|
|
var numhours = (a-a%hourlen)/hourlen; a = a-numhours*hourlen; var shours = addunit(numhours,'hour'); |
|
|
var nummins = (a-a%minlen)/minlen; a = a-nummins*minlen; var smins = addunit(nummins, 'minute'); |
|
|
var numsecs = (a-a%seclen)/seclen; a = a-numsecs*seclen; var ssecs = addunit(numsecs, 'second'); |
|
|
|
|
|
if (aa > 4*weeklen) { return sweeks; } |
|
|
if (aa > weeklen) { return sweeks + ' ' + sdays; } |
|
|
if (aa > daylen) { return sdays + ' ' + shours; } |
|
|
if (aa > 6*hourlen) { return shours; } |
|
|
if (aa > hourlen) { return shours + ' ' + smins; } |
|
|
if (aa > 10*minlen) { return smins; } |
|
|
if (aa > minlen) { return smins + ' ' + ssecs; } |
|
|
return ssecs; |
|
|
} |
|
|
|
|
|
function addunit(num,str) { return '' + num + ' ' + ((num!=1) ? popupString(str+'s') : popupString(str)) ;} |
|
|
|
|
|
function runPopupFilters(list, data, download) { |
|
|
var ret=; |
|
|
for (var i=0; i<list.length; ++i) { |
|
|
if (list && typeof list == 'function') { |
|
|
var s=list(data, download); |
|
|
if (s) { ret.push(s); } |
|
|
} |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function getPageInfo(data, download) { |
|
|
if (!data || data.length === 0) { return popupString('Empty page'); } |
|
|
|
|
|
var popupFilters=getValueOf('popupFilters') || ; |
|
|
var extraPopupFilters = getValueOf('extraPopupFilters') || ; |
|
|
var pageInfoArray = runPopupFilters(popupFilters.concat(extraPopupFilters), data, download); |
|
|
|
|
|
var pageInfo=pageInfoArray.join(', '); |
|
|
if (pageInfo !== '' ) { pageInfo = upcaseFirst(pageInfo); } |
|
|
return pageInfo; |
|
|
} |
|
|
|
|
|
|
|
|
// this could be improved! |
|
|
function countLinks(wikiText) { return wikiText.split('[[').length - 1; } |
|
|
|
|
|
// if N = # matches, n = # brackets, then |
|
|
// String.parenSplit(regex) intersperses the N+1 split elements |
|
|
// with Nn other elements. So total length is |
|
|
// L= N+1 + Nn = N(n+1)+1. So N=(L-1)/(n+1). |
|
|
|
|
|
function countImages(wikiText) { |
|
|
return (wikiText.parenSplit(pg.re.image).length - 1) / (pg.re.imageBracketCount + 1); |
|
|
} |
|
|
|
|
|
function countCategories(wikiText) { |
|
|
return (wikiText.parenSplit(pg.re.category).length - 1) / (pg.re.categoryBracketCount + 1); |
|
|
} |
|
|
|
|
|
function popupFilterStubDetect(data) { |
|
|
return (isStub(data)) ? popupString('stub') : ''; |
|
|
} |
|
|
function popupFilterDisambigDetect(data) { |
|
|
return (isDisambig(data)) ? popupString('disambig') : ''; |
|
|
} |
|
|
|
|
|
function formatBytes(num) { |
|
|
return (num > 949) ? (Math.round(num/100)/10+popupString('kB')) : (num +' ' + popupString('bytes')) ; |
|
|
} |
|
|
//</NOLITE> |
|
|
// ENDFILE: pageinfo.js |
|
|
// STARTFILE: titles.js |
|
|
/** |
|
|
@fileoverview Defines the {@link Title} class, and associated crufty functions. |
|
|
|
|
|
<code>Title</code> deals with article titles and their various |
|
|
forms. {@link Stringwrapper} is the parent class of |
|
|
<code>Title</code>, which exists simply to make things a little |
|
|
neater. |
|
|
|
|
|
*/ |
|
|
|
|
|
/** |
|
|
Creates a new Stringwrapper. |
|
|
@constructor |
|
|
|
|
|
@class the Stringwrapper class. This base class is not really |
|
|
useful on its own; it just wraps various common string operations. |
|
|
*/ |
|
|
function Stringwrapper() { |
|
|
/** |
|
|
Wrapper for this.toString().indexOf() |
|
|
@param {String} x |
|
|
@type integer |
|
|
*/ |
|
|
this.indexOf=function(x){return this.toString().indexOf(x);}; |
|
|
/** |
|
|
Returns this.value. |
|
|
@type String |
|
|
*/ |
|
|
this.toString=function(){return this.value;}; |
|
|
/** |
|
|
Wrapper for {@link String#parenSplit} applied to this.toString() |
|
|
@param {RegExp} x |
|
|
@type Array |
|
|
*/ |
|
|
this.parenSplit=function(x){return this.toString().parenSplit(x);}; |
|
|
/** |
|
|
Wrapper for this.toString().substring() |
|
|
@param {String} x |
|
|
@param {String} y (optional) |
|
|
@type String |
|
|
*/ |
|
|
this.substring=function(x,y){ |
|
|
if (typeof y=='undefined') { return this.toString().substring(x); } |
|
|
return this.toString().substring(x,y); |
|
|
}; |
|
|
/** |
|
|
Wrapper for this.toString().split() |
|
|
@param {String} x |
|
|
@type Array |
|
|
*/ |
|
|
this.split=function(x){return this.toString().split(x);}; |
|
|
/** |
|
|
Wrapper for this.toString().replace() |
|
|
@param {String} x |
|
|
@param {String} y |
|
|
@type String |
|
|
*/ |
|
|
this.replace=function(x,y){ return this.toString().replace(x,y); }; |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
Creates a new <code>Title</code>. |
|
|
@constructor |
|
|
|
|
|
@class The Title class. Holds article titles and converts them into |
|
|
various forms. Also deals with anchors, by which we mean the bits |
|
|
of the article URL after a # character, representing locations |
|
|
within an article. |
|
|
|
|
|
@param {String} value The initial value to assign to the |
|
|
article. This must be the canonical title (see {@link |
|
|
Title#value}. Omit this in the constructor and use another function |
|
|
to set the title if this is unavailable. |
|
|
*/ |
|
|
function Title(val) { |
|
|
/** |
|
|
The canonical article title. This must be in UTF-8 with no |
|
|
entities, escaping or nasties. Also, underscores should be |
|
|
replaced with spaces. |
|
|
@type String |
|
|
@private |
|
|
*/ |
|
|
this.value=null; |
|
|
/** |
|
|
The canonical form of the anchor. This should be exactly as |
|
|
it appears in the URL, i.e. with the .C3.0A bits in. |
|
|
@type String |
|
|
*/ |
|
|
this.anchor=''; |
|
|
|
|
|
this.setUtf(val); |
|
|
} |
|
|
Title.prototype=new Stringwrapper(); |
|
|
/** |
|
|
Returns the canonical representation of the article title, optionally without anchor. |
|
|
@param {boolean} omitAnchor |
|
|
@fixme Decide specs for anchor |
|
|
@return String The article title and the anchor. |
|
|
*/ |
|
|
Title.prototype.toString=function(omitAnchor) { |
|
|
return this.value + ( (!omitAnchor && this.anchor) ? '#' + this.anchorString() : '' ); |
|
|
}; |
|
|
Title.prototype.anchorString=function() { |
|
|
if (!this.anchor) { return ''; } |
|
|
|
|
|
var split=this.anchor.parenSplit(/((?:{2})+)/); |
|
|
var len=split.length; |
|
|
for (var j=1; j<len; j+=2) { |
|
|
// FIXME s/decodeURI/decodeURIComponent/g ? |
|
|
split=decodeURIComponent(split.split('.').join('%')).split('_').join(' '); |
|
|
} |
|
|
return split.join(''); |
|
|
}; |
|
|
Title.prototype.urlAnchor=function() { |
|
|
var split=this.anchor.parenSplit('/((?:{2})+)/'); |
|
|
var len=split.length; |
|
|
for (var j=1; j<len; j+=2) { |
|
|
split=split.split('%').join('.'); |
|
|
} |
|
|
return split.join(''); |
|
|
}; |
|
|
Title.fromURL=function(h) { |
|
|
return new Title().fromURL(h); |
|
|
}; |
|
|
Title.prototype.fromURL=function(h) { |
|
|
if (typeof h != 'string') { |
|
|
this.value=null; |
|
|
return this; |
|
|
} |
|
|
|
|
|
// NOTE : playing with decodeURI, encodeURI, escape, unescape, |
|
|
// we seem to be able to replicate the IE borked encoding |
|
|
|
|
|
// IE doesn't do this new-fangled utf-8 thing. |
|
|
// and it's worse than that. |
|
|
// IE seems to treat the query string differently to the rest of the url |
|
|
// the query is treated as bona-fide utf8, but the first bit of the url is pissed around with |
|
|
|
|
|
// we fix up & for all browsers, just in case. |
|
|
var splitted=h.split('?'); |
|
|
splitted=splitted.split('&').join('%26'); |
|
|
|
|
|
if (pg.flag.linksLikeIE) { |
|
|
splitted=encodeURI(decode_utf8(splitted)); |
|
|
} |
|
|
|
|
|
h=splitted.join('?'); |
|
|
|
|
|
var contribs=pg.re.contribs.exec(h); |
|
|
if (contribs !== null) { |
|
|
if (contribs=='title=') { contribs=contribs.split('+').join(' '); } |
|
|
this.setUtf(this.decodeNasties(pg.ns.user + ':' + contribs)); |
|
|
return this; |
|
|
} |
|
|
|
|
|
var email=pg.re.email.exec(h); |
|
|
if (email !== null) { |
|
|
this.setUtf(this.decodeNasties(pg.ns.user + ':' + email)); |
|
|
return this; |
|
|
} |
|
|
|
|
|
// no more special cases to check -- |
|
|
// hopefully it's not a disguised user-related page |
|
|
var m=pg.re.main.exec(h); |
|
|
if(m===null) { this.value=null; } |
|
|
else { |
|
|
var fromBotInterface = /(.+)?title=/.test(h); |
|
|
if (fromBotInterface) { |
|
|
m=m.split('+').join('_'); |
|
|
} |
|
|
var extracted = m + (m ? '#' + m : ''); |
|
|
this.setUtf(this.decodeNasties(extracted)); |
|
|
} |
|
|
return this; |
|
|
}; |
|
|
Title.prototype.decodeNasties=function(txt) { |
|
|
var ret= this.decodeEscapes(decodeURI(txt)); |
|
|
ret = ret.replace(/*$/, ''); |
|
|
return ret; |
|
|
}; |
|
|
Title.prototype.decodeEscapes=function(txt) { |
|
|
var split=txt.parenSplit(/((?:{2})+)/); |
|
|
var len=split.length; |
|
|
for (var i=1; i<len; i=i+2) { |
|
|
// FIXME is decodeURIComponent better? |
|
|
split=unescape(split); |
|
|
} |
|
|
return split.join(''); |
|
|
}; |
|
|
Title.fromAnchor=function(a) { |
|
|
return new Title().fromAnchor(a); |
|
|
}; |
|
|
Title.prototype.fromAnchor=function(a) { |
|
|
if (!a) { this.value=null; return this; } |
|
|
return this.fromURL(a.href); |
|
|
}; |
|
|
Title.fromWikiText=function(txt) { |
|
|
return new Title().fromWikiText(txt); |
|
|
}; |
|
|
Title.prototype.fromWikiText=function(txt) { |
|
|
// FIXME - testing needed |
|
|
if (!pg.flag.linksLikeIE) { txt=myDecodeURI(txt); } |
|
|
this.setUtf(txt); |
|
|
return this; |
|
|
}; |
|
|
Title.prototype.hintValue=function(){ |
|
|
if(!this.value) { return ''; } |
|
|
return safeDecodeURI(this.value); |
|
|
}; |
|
|
//<NOLITE> |
|
|
Title.prototype.toUserName=function(withNs) { |
|
|
if (!this.value) { return null; } |
|
|
var i=this.value.indexOf(pg.ns.user); |
|
|
var j=this.value.indexOf(':'); |
|
|
if (i !== 0 || j == -1) { |
|
|
this.value=null; |
|
|
return null; |
|
|
} |
|
|
var k=this.value.indexOf('/'); |
|
|
if (k==-1) { this.value=this.value.substring(j+1); } |
|
|
else { this.value=this.value.substring(j+1,k); } |
|
|
if (withNs) { this.value = pg.ns.user + ':' + this.value; } |
|
|
return this.value; |
|
|
}; |
|
|
Title.prototype.userName=function(withNs) { |
|
|
var t=(new Title(this.value)); |
|
|
t.toUserName(withNs); |
|
|
if (t.value) { return t; } |
|
|
return null; |
|
|
}; |
|
|
Title.prototype.toTalkPage=function() { |
|
|
// convert article to a talk page, or if we can't return null |
|
|
// or, in other words, return null if this ALREADY IS a talk page |
|
|
// and return the corresponding talk page otherwise |
|
|
if (this.value===null) { return null; } |
|
|
var talkRegex=namespaceListToRegex(pg.ns.talkList); |
|
|
if (talkRegex.exec(this.value)) { this.value=null; return null;} |
|
|
|
|
|
var nsRegex=namespaceListToRegex(pg.ns.withTalkList); |
|
|
var splitted=this.value.parenSplit(nsRegex); |
|
|
if (splitted.length<2) { |
|
|
this.value= (pg.ns.talkList+':'+this.value).split(' ').join('_'); |
|
|
return this.value; |
|
|
} |
|
|
for (var i=0; i< pg.ns.withTalkList.length; ++i) { |
|
|
if (splitted==pg.ns.withTalkList) { |
|
|
splitted=pg.ns.talkList; |
|
|
this.value=splitted.join(':').substring(1).split(' ').join('_'); |
|
|
return this.value; |
|
|
} |
|
|
} |
|
|
this.value=null; |
|
|
return null; |
|
|
}; |
|
|
//</NOLITE> |
|
|
Title.prototype.namespace=function() { |
|
|
var n=this.value.indexOf(':'); |
|
|
if (n<0) { return ''; } |
|
|
var list=pg.ns.list; |
|
|
for (var i=0; i<list.length; ++i) { |
|
|
if (upcaseFirst(list) == this.value.substring(0,n)) { return list; } |
|
|
} |
|
|
return ''; |
|
|
}; |
|
|
//<NOLITE> |
|
|
Title.prototype.talkPage=function() { |
|
|
var t=new Title(this.value); |
|
|
t.toTalkPage(); |
|
|
if (t.value) { return t; } |
|
|
return null; |
|
|
}; |
|
|
Title.prototype.isTalkPage=function() { |
|
|
if (this.talkPage()===null) { return true; } |
|
|
return false; |
|
|
}; |
|
|
Title.prototype.toArticleFromTalkPage=function() { |
|
|
var talkRegex=namespaceListToRegex(pg.ns.talkList); |
|
|
var splitted=this.value.parenSplit(talkRegex); |
|
|
if (splitted.length < 2 || splitted.length > 0) { this.value=null; return null; } |
|
|
if (splitted==pg.ns.talkList) { |
|
|
splitted=''; |
|
|
this.value=splitted.join(':').substring(2).split(' ').join('_'); |
|
|
return this.value; |
|
|
} |
|
|
for (var i=1; i< pg.ns.talkList.length; ++i) { |
|
|
if (splitted==pg.ns.talkList || splitted==pg.ns.talkList.split(' ').join('_')) { |
|
|
splitted=pg.ns.withTalkList; |
|
|
this.value= splitted.join(':').substring(1).split(' ').join('_'); |
|
|
return this.value; |
|
|
} |
|
|
} |
|
|
this.value=null; |
|
|
return this.value; |
|
|
}; |
|
|
Title.prototype.articleFromTalkPage=function() { |
|
|
var t=new Title(this.value); |
|
|
t.toArticleFromTalkPage(); |
|
|
if (t.value) { return t; } |
|
|
return null; |
|
|
}; |
|
|
Title.prototype.articleFromTalkOrArticle=function() { |
|
|
var t=new Title(this.value); |
|
|
if ( t.toArticleFromTalkPage() ) { return t; } |
|
|
return this; |
|
|
}; |
|
|
Title.prototype.isIpUser=function() { |
|
|
return pg.re.ipUser.test(this.userName()); |
|
|
}; |
|
|
//</NOLITE> |
|
|
Title.prototype.stripNamespace=function(){ // returns a string, not a Title |
|
|
// this isn't very sophisticated |
|
|
// it just removes everything up to the final : |
|
|
// BUG: probably does silly things for images with colons in the name - check it out |
|
|
var list = this.value.split(':'); |
|
|
return list; |
|
|
}; |
|
|
Title.prototype.setUtf=function(value){ |
|
|
if (!value) { this.value=''; return; } |
|
|
var anch=value.indexOf('#'); |
|
|
if(anch < 0) { this.value=value.split('_').join(' '); this.anchor=''; return; } |
|
|
this.value=value.substring(0,anch).split('_').join(' '); |
|
|
this.anchor=value.substring(anch+1); |
|
|
this.ns=null; // wait until namespace() is called |
|
|
}; |
|
|
Title.prototype.setUrl=function(urlfrag) { |
|
|
var anch=urlfrag.indexOf('#'); |
|
|
this.value=safeDecodeURI(urlfrag.substring(0,anch)); |
|
|
this.anchor=value.substring(anch+1); |
|
|
}; |
|
|
Title.prototype.append=function(x){ |
|
|
this.setUtf(this.value + x); |
|
|
}; |
|
|
Title.prototype.urlString=function(x) { |
|
|
x || ( x={} ); |
|
|
var v=this.toString(true); |
|
|
if (!x.omitAnchor && this.anchor) { v+= '#' + this.urlAnchor(); } |
|
|
if (!x.keepSpaces) { v=v.split(' ').join('_'); } |
|
|
return encodeURI(v).split('&').join('%26').split('?').join('%3F').split('+').join('%2B'); |
|
|
}; |
|
|
Title.prototype.removeAnchor=function() { |
|
|
return new Title(this.toString(true)); |
|
|
}; |
|
|
|
|
|
|
|
|
function paramValue(param, url) { |
|
|
var s=url.parenSplit(RegExp('' + literalizeRegex(param) + '=(*)')); |
|
|
if (!url) { return null; } |
|
|
return s || null; |
|
|
} |
|
|
|
|
|
function parseParams(url) { |
|
|
var ret={}; |
|
|
if (url.indexOf('?')==-1) { return ret; } |
|
|
var s=url.split('?').slice(1).join(); |
|
|
var t=s.split('&'); |
|
|
for (var i=0; i<t.length; ++i) { |
|
|
var z=t.split('='); |
|
|
z.push(null); |
|
|
ret]=z; |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
// all sorts of stuff here |
|
|
// FIXME almost everything needs to be rewritten |
|
|
|
|
|
function oldidFromAnchor(a) { return paramValue('oldid', a.href); } |
|
|
//function diffFromAnchor(a) { return paramValue('diff', a.href); } |
|
|
|
|
|
|
|
|
function wikiMarkupToAddressFragment (str) { // for images |
|
|
var ret = safeDecodeURI(str); |
|
|
ret = ret.split(' ').join('_'); |
|
|
ret = encodeURI(ret); |
|
|
return ret; |
|
|
} |
|
|
|
|
|
// (a) myDecodeURI (first standard decodeURI, then pg.re.urlNoPopup) |
|
|
// (b) change spaces to underscores |
|
|
// (c) encodeURI (just the straight one, no pg.re.urlNoPopup) |
|
|
|
|
|
function myDecodeURI (str) { |
|
|
var ret; |
|
|
// FIXME decodeURIComponent?? |
|
|
try { ret=decodeURI(str.toString()); } |
|
|
catch (summat) { return str; } |
|
|
for (var i=0; i<pg.misc.decodeExtras.length; ++i) { |
|
|
var from=pg.misc.decodeExtras.from; |
|
|
var to=pg.misc.decodeExtras.to; |
|
|
ret=ret.split(from).join(to); |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function safeDecodeURI(str) { var ret=myDecodeURI(str); return ret || str; } |
|
|
|
|
|
/////////// |
|
|
// TESTS // |
|
|
/////////// |
|
|
|
|
|
//<NOLITE> |
|
|
function isIpUser(user) {return pg.re.ipUser.test(user);} |
|
|
|
|
|
function isStub(data) { return pg.re.stub.test(data); } |
|
|
function isDisambig(data) { |
|
|
return ! pg.current.article.isTalkPage() && pg.re.disambig.test(data); |
|
|
} |
|
|
|
|
|
function isValidImageName(str){ // extend as needed... |
|
|
return ( str.indexOf('{') == -1 ); |
|
|
} |
|
|
|
|
|
function isInStrippableNamespace(article) { |
|
|
return ( findInArray( pg.ns.nonArticleList, article.namespace() ) > -1 ); |
|
|
} |
|
|
|
|
|
function isInMainNamespace(article) { return !isInStrippableNamespace(article); } |
|
|
|
|
|
function anchorContainsImage(a) { |
|
|
// iterate over children of anchor a |
|
|
// see if any are images |
|
|
if (a===null) { return false; } |
|
|
kids=a.childNodes; |
|
|
for (var i=0; i<kids.length; ++i) { if (kids.nodeName=='IMG') { return true; } } |
|
|
return false; |
|
|
} |
|
|
//</NOLITE> |
|
|
function isPopupLink(a) { |
|
|
// NB for performance reasons, TOC links generally return true |
|
|
// they should be stripped out later |
|
|
|
|
|
// FIXME is this faster inline? |
|
|
if (a.onclick) { return false; } |
|
|
var h=a.href; |
|
|
if ( (h.indexOf(pg.wiki.titlebase) === 0 || h.indexOf(pg.wiki.articlebase) === 0 || |
|
|
h.indexOf(pg.wiki.titlebase2) === 0 ) && |
|
|
!pg.re.urlNoPopup.test(h) ) { |
|
|
return true; |
|
|
} |
|
|
return ( |
|
|
(pg.re.email.test(h) || pg.re.contribs.test(h)) && |
|
|
h.indexOf('&limit=') == -1 ); |
|
|
} |
|
|
// ENDFILE: titles.js |
|
|
// STARTFILE: cookies.js |
|
|
//<NOLITE> |
|
|
////////////////////////////////////////////////// |
|
|
// Cookie handling |
|
|
// from http://www.quirksmode.org/js/cookies.html |
|
|
|
|
|
var Cookie= { |
|
|
create: function(name,value,days) |
|
|
{ |
|
|
var expires; |
|
|
if (days) |
|
|
{ |
|
|
var date = new Date(); |
|
|
date.setTime(date.getTime()+(days*24*60*60*1000)); |
|
|
expires = "; expires="+date.toGMTString(); |
|
|
} |
|
|
else { expires = ""; } |
|
|
document.cookie = name+"="+value+expires+"; path=/"; |
|
|
}, |
|
|
|
|
|
read: function(name) |
|
|
{ |
|
|
var nameEQ = name + "="; |
|
|
var ca = document.cookie.split(';'); |
|
|
for(var i=0;i < ca.length;i++) |
|
|
{ |
|
|
var c = ca; |
|
|
while (c.charAt(0)==' ') { c = c.substring(1,c.length); } |
|
|
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length,c.length); } |
|
|
} |
|
|
return null; |
|
|
}, |
|
|
|
|
|
erase: function(name) |
|
|
{ |
|
|
Cookie.create(name,"",-1); |
|
|
} |
|
|
}; |
|
|
//</NOLITE> |
|
|
// ENDFILE: cookies.js |
|
|
// STARTFILE: getpage.js |
|
|
////////////////////////////////////////////////// |
|
|
// Wiki-specific downloading |
|
|
// |
|
|
|
|
|
// Schematic for a getWiki call |
|
|
// |
|
|
// getWiki->-getPageWithCaching |
|
|
// | |
|
|
// false | true |
|
|
// getPage<-->-onComplete(a fake download) |
|
|
// \. |
|
|
// (async)->addPageToCache(download)->-onComplete(download) |
|
|
|
|
|
function getWiki(article, onComplete, oldid, owner) { |
|
|
// NB wikipage is a Title object |
|
|
log('getWiki, article='+article); |
|
|
// set ctype=text/css to get around opera gzip bug |
|
|
var url = pg.wiki.titlebase + article.removeAnchor().urlString() + '&action=raw&ctype=text/css'; |
|
|
if (oldid || oldid===0 || oldid==='0') { url += '&oldid='+oldid; } |
|
|
url += '&maxage=0&smaxage=0'; |
|
|
|
|
|
getPageWithCaching(url, onComplete, owner); |
|
|
} |
|
|
|
|
|
// check cache to see if page exists |
|
|
|
|
|
function getPageWithCaching(url, onComplete, owner) { |
|
|
log('getPageWithCaching, url='+url); |
|
|
var i=findInPageCache(url); |
|
|
if (i > -1) { |
|
|
var d=fakeDownload(url, pg.idNumber, onComplete, |
|
|
pg.cache.pages.data, pg.cache.pages.lastModified, |
|
|
owner); |
|
|
} else { |
|
|
var d=getPage(url, onComplete, owner); |
|
|
if (d && owner && owner.addDownload) { |
|
|
owner.addDownload(d); |
|
|
d.owner=owner; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
function getPage(url, onComplete, owner) { |
|
|
log('getPage'); |
|
|
var callback= function (d) { if (!d.aborted) {addPageToCache(d); onComplete(d);} }; |
|
|
return startDownload(url, pg.idNumber, callback); |
|
|
} |
|
|
|
|
|
function findInPageCache(url) { |
|
|
for (var i=0; i<pg.cache.pages.length; ++i) { |
|
|
if (url==pg.cache.pages.url) { return i; } |
|
|
} |
|
|
return -1; |
|
|
} |
|
|
|
|
|
function addPageToCache(download) { |
|
|
log('addPageToCache '+download.url); |
|
|
var page = {url: download.url, data: download.data, lastModified: download.lastModified}; |
|
|
return pg.cache.pages.push(page); |
|
|
} |
|
|
// ENDFILE: getpage.js |
|
|
// STARTFILE: md5-2.2alpha.js |
|
|
//<NOLITE> |
|
|
/* |
|
|
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message |
|
|
* Digest Algorithm, as defined in RFC 1321. |
|
|
* Version 2.2-alpha Copyright (C) Paul Johnston 1999 - 2005 |
|
|
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet |
|
|
* Distributed under the BSD License |
|
|
* See http://pajhome.org.uk/crypt/md5 for more info. |
|
|
*/ |
|
|
|
|
|
/* |
|
|
* Configurable variables. You may need to tweak these to be compatible with |
|
|
* the server-side, but the defaults work in most cases. |
|
|
*/ |
|
|
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ |
|
|
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ |
|
|
|
|
|
/* |
|
|
* These are the functions you'll usually want to call |
|
|
* They take string arguments and return either hex or base-64 encoded strings |
|
|
*/ |
|
|
function hex_md5(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); } |
|
|
function b64_md5(s) { return rstr2b64(rstr_md5(str2rstr_utf8(s))); } |
|
|
function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); } |
|
|
function hex_hmac_md5(k, d) |
|
|
{ return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); } |
|
|
function b64_hmac_md5(k, d) |
|
|
{ return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); } |
|
|
function any_hmac_md5(k, d, e) |
|
|
{ return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); } |
|
|
|
|
|
/* |
|
|
* Perform a simple self-test to see if the VM is working |
|
|
*/ |
|
|
function md5_vm_test() |
|
|
{ |
|
|
return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72"; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Calculate the MD5 of a raw string |
|
|
*/ |
|
|
function rstr_md5(s) |
|
|
{ |
|
|
return binl2rstr(binl_md5(rstr2binl(s), s.length * 8)); |
|
|
} |
|
|
|
|
|
/* |
|
|
* Calculate the HMAC-MD5, of a key and some data (raw strings) |
|
|
*/ |
|
|
function rstr_hmac_md5(key, data) |
|
|
{ |
|
|
var bkey = rstr2binl(key); |
|
|
if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8); |
|
|
|
|
|
var ipad = Array(16), opad = Array(16); |
|
|
for(var i = 0; i < 16; i++) |
|
|
{ |
|
|
ipad = bkey ^ 0x36363636; |
|
|
opad = bkey ^ 0x5C5C5C5C; |
|
|
} |
|
|
|
|
|
var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); |
|
|
return binl2rstr(binl_md5(opad.concat(hash), 512 + 128)); |
|
|
} |
|
|
|
|
|
/* |
|
|
* Convert a raw string to a hex string |
|
|
*/ |
|
|
function rstr2hex(input) |
|
|
{ |
|
|
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; |
|
|
var output = ""; |
|
|
var x; |
|
|
for(var i = 0; i < input.length; i++) |
|
|
{ |
|
|
x = input.charCodeAt(i); |
|
|
output += hex_tab.charAt((x >>> 4) & 0x0F) |
|
|
+ hex_tab.charAt( x & 0x0F); |
|
|
} |
|
|
return output; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Convert a raw string to a base-64 string |
|
|
*/ |
|
|
function rstr2b64(input) |
|
|
{ |
|
|
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
|
|
var output = ""; |
|
|
var len = input.length; |
|
|
for(var i = 0; i < len; i += 3) |
|
|
{ |
|
|
var triplet = (input.charCodeAt(i) << 16) |
|
|
| (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0) |
|
|
| (i + 2 < len ? input.charCodeAt(i+2) : 0); |
|
|
for(var j = 0; j < 4; j++) |
|
|
{ |
|
|
if(i * 8 + j * 6 > input.length * 8) output += b64pad; |
|
|
else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F); |
|
|
} |
|
|
} |
|
|
return output; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Convert a raw string to an arbitrary string encoding |
|
|
*/ |
|
|
function rstr2any(input, encoding) |
|
|
{ |
|
|
var divisor = encoding.length; |
|
|
var remainders = Array(); |
|
|
var i, q, x, quotient; |
|
|
|
|
|
/* Convert to an array of 16-bit big-endian values, forming the dividend */ |
|
|
var dividend = Array(input.length / 2); |
|
|
for(i = 0; i < dividend.length; i++) |
|
|
{ |
|
|
dividend = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); |
|
|
} |
|
|
|
|
|
/* |
|
|
* Repeatedly perform a long division. The binary array forms the dividend, |
|
|
* the length of the encoding is the divisor. Once computed, the quotient |
|
|
* forms the dividend for the next step. We stop when the dividend is zero. |
|
|
* All remainders are stored for later use. |
|
|
*/ |
|
|
while(dividend.length > 0) |
|
|
{ |
|
|
quotient = Array(); |
|
|
x = 0; |
|
|
for(i = 0; i < dividend.length; i++) |
|
|
{ |
|
|
x = (x << 16) + dividend; |
|
|
q = Math.floor(x / divisor); |
|
|
x -= q * divisor; |
|
|
if(quotient.length > 0 || q > 0) |
|
|
quotient = q; |
|
|
} |
|
|
remainders = x; |
|
|
dividend = quotient; |
|
|
} |
|
|
|
|
|
/* Convert the remainders to the output string */ |
|
|
var output = ""; |
|
|
for(i = remainders.length - 1; i >= 0; i--) |
|
|
output += encoding.charAt(remainders); |
|
|
|
|
|
return output; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Encode a string as utf-8. |
|
|
* For efficiency, this assumes the input is valid utf-16. |
|
|
*/ |
|
|
function str2rstr_utf8(input) |
|
|
{ |
|
|
var output = ""; |
|
|
var i = -1; |
|
|
var x, y; |
|
|
|
|
|
while(++i < input.length) |
|
|
{ |
|
|
/* Decode utf-16 surrogate pairs */ |
|
|
x = input.charCodeAt(i); |
|
|
y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0; |
|
|
if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) |
|
|
{ |
|
|
x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF); |
|
|
i++; |
|
|
} |
|
|
|
|
|
/* Encode output as utf-8 */ |
|
|
if(x <= 0x7F) |
|
|
output += String.fromCharCode(x); |
|
|
else if(x <= 0x7FF) |
|
|
output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F), |
|
|
0x80 | ( x & 0x3F)); |
|
|
else if(x <= 0xFFFF) |
|
|
output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), |
|
|
0x80 | ((x >>> 6 ) & 0x3F), |
|
|
0x80 | ( x & 0x3F)); |
|
|
else if(x <= 0x1FFFFF) |
|
|
output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), |
|
|
0x80 | ((x >>> 12) & 0x3F), |
|
|
0x80 | ((x >>> 6 ) & 0x3F), |
|
|
0x80 | ( x & 0x3F)); |
|
|
} |
|
|
return output; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Encode a string as utf-16 |
|
|
*/ |
|
|
function str2rstr_utf16le(input) |
|
|
{ |
|
|
var output = ""; |
|
|
for(var i = 0; i < input.length; i++) |
|
|
output += String.fromCharCode( input.charCodeAt(i) & 0xFF, |
|
|
(input.charCodeAt(i) >>> 8) & 0xFF); |
|
|
return output; |
|
|
} |
|
|
|
|
|
function str2rstr_utf16be(input) |
|
|
{ |
|
|
var output = ""; |
|
|
for(var i = 0; i < input.length; i++) |
|
|
output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, |
|
|
input.charCodeAt(i) & 0xFF); |
|
|
return output; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Convert a raw string to an array of little-endian words |
|
|
* Characters >255 have their high-byte silently ignored. |
|
|
*/ |
|
|
function rstr2binl(input) |
|
|
{ |
|
|
var output = Array(input.length >> 2); |
|
|
for(var i = 0; i < output.length; i++) |
|
|
output = 0; |
|
|
for(var i = 0; i < input.length * 8; i += 8) |
|
|
output |= (input.charCodeAt(i / 8) & 0xFF) << (i%32); |
|
|
return output; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Convert an array of little-endian words to a string |
|
|
*/ |
|
|
function binl2rstr(input) |
|
|
{ |
|
|
var output = ""; |
|
|
for(var i = 0; i < input.length * 32; i += 8) |
|
|
output += String.fromCharCode((input >>> (i % 32)) & 0xFF); |
|
|
return output; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Calculate the MD5 of an array of little-endian words, and a bit length. |
|
|
*/ |
|
|
function binl_md5(x, len) |
|
|
{ |
|
|
/* append padding */ |
|
|
x |= 0x80 << ((len) % 32); |
|
|
x = len; |
|
|
|
|
|
var a = 1732584193; |
|
|
var b = -271733879; |
|
|
var c = -1732584194; |
|
|
var d = 271733878; |
|
|
|
|
|
for(var i = 0; i < x.length; i += 16) |
|
|
{ |
|
|
var olda = a; |
|
|
var oldb = b; |
|
|
var oldc = c; |
|
|
var oldd = d; |
|
|
|
|
|
a = md5_ff(a, b, c, d, x, 7 , -680876936); |
|
|
d = md5_ff(d, a, b, c, x, 12, -389564586); |
|
|
c = md5_ff(c, d, a, b, x, 17, 606105819); |
|
|
b = md5_ff(b, c, d, a, x, 22, -1044525330); |
|
|
a = md5_ff(a, b, c, d, x, 7 , -176418897); |
|
|
d = md5_ff(d, a, b, c, x, 12, 1200080426); |
|
|
c = md5_ff(c, d, a, b, x, 17, -1473231341); |
|
|
b = md5_ff(b, c, d, a, x, 22, -45705983); |
|
|
a = md5_ff(a, b, c, d, x, 7 , 1770035416); |
|
|
d = md5_ff(d, a, b, c, x, 12, -1958414417); |
|
|
c = md5_ff(c, d, a, b, x, 17, -42063); |
|
|
b = md5_ff(b, c, d, a, x, 22, -1990404162); |
|
|
a = md5_ff(a, b, c, d, x, 7 , 1804603682); |
|
|
d = md5_ff(d, a, b, c, x, 12, -40341101); |
|
|
c = md5_ff(c, d, a, b, x, 17, -1502002290); |
|
|
b = md5_ff(b, c, d, a, x, 22, 1236535329); |
|
|
|
|
|
a = md5_gg(a, b, c, d, x, 5 , -165796510); |
|
|
d = md5_gg(d, a, b, c, x, 9 , -1069501632); |
|
|
c = md5_gg(c, d, a, b, x, 14, 643717713); |
|
|
b = md5_gg(b, c, d, a, x, 20, -373897302); |
|
|
a = md5_gg(a, b, c, d, x, 5 , -701558691); |
|
|
d = md5_gg(d, a, b, c, x, 9 , 38016083); |
|
|
c = md5_gg(c, d, a, b, x, 14, -660478335); |
|
|
b = md5_gg(b, c, d, a, x, 20, -405537848); |
|
|
a = md5_gg(a, b, c, d, x, 5 , 568446438); |
|
|
d = md5_gg(d, a, b, c, x, 9 , -1019803690); |
|
|
c = md5_gg(c, d, a, b, x, 14, -187363961); |
|
|
b = md5_gg(b, c, d, a, x, 20, 1163531501); |
|
|
a = md5_gg(a, b, c, d, x, 5 , -1444681467); |
|
|
d = md5_gg(d, a, b, c, x, 9 , -51403784); |
|
|
c = md5_gg(c, d, a, b, x, 14, 1735328473); |
|
|
b = md5_gg(b, c, d, a, x, 20, -1926607734); |
|
|
|
|
|
a = md5_hh(a, b, c, d, x, 4 , -378558); |
|
|
d = md5_hh(d, a, b, c, x, 11, -2022574463); |
|
|
c = md5_hh(c, d, a, b, x, 16, 1839030562); |
|
|
b = md5_hh(b, c, d, a, x, 23, -35309556); |
|
|
a = md5_hh(a, b, c, d, x, 4 , -1530992060); |
|
|
d = md5_hh(d, a, b, c, x, 11, 1272893353); |
|
|
c = md5_hh(c, d, a, b, x, 16, -155497632); |
|
|
b = md5_hh(b, c, d, a, x, 23, -1094730640); |
|
|
a = md5_hh(a, b, c, d, x, 4 , 681279174); |
|
|
d = md5_hh(d, a, b, c, x, 11, -358537222); |
|
|
c = md5_hh(c, d, a, b, x, 16, -722521979); |
|
|
b = md5_hh(b, c, d, a, x, 23, 76029189); |
|
|
a = md5_hh(a, b, c, d, x, 4 , -640364487); |
|
|
d = md5_hh(d, a, b, c, x, 11, -421815835); |
|
|
c = md5_hh(c, d, a, b, x, 16, 530742520); |
|
|
b = md5_hh(b, c, d, a, x, 23, -995338651); |
|
|
|
|
|
a = md5_ii(a, b, c, d, x, 6 , -198630844); |
|
|
d = md5_ii(d, a, b, c, x, 10, 1126891415); |
|
|
c = md5_ii(c, d, a, b, x, 15, -1416354905); |
|
|
b = md5_ii(b, c, d, a, x, 21, -57434055); |
|
|
a = md5_ii(a, b, c, d, x, 6 , 1700485571); |
|
|
d = md5_ii(d, a, b, c, x, 10, -1894986606); |
|
|
c = md5_ii(c, d, a, b, x, 15, -1051523); |
|
|
b = md5_ii(b, c, d, a, x, 21, -2054922799); |
|
|
a = md5_ii(a, b, c, d, x, 6 , 1873313359); |
|
|
d = md5_ii(d, a, b, c, x, 10, -30611744); |
|
|
c = md5_ii(c, d, a, b, x, 15, -1560198380); |
|
|
b = md5_ii(b, c, d, a, x, 21, 1309151649); |
|
|
a = md5_ii(a, b, c, d, x, 6 , -145523070); |
|
|
d = md5_ii(d, a, b, c, x, 10, -1120210379); |
|
|
c = md5_ii(c, d, a, b, x, 15, 718787259); |
|
|
b = md5_ii(b, c, d, a, x, 21, -343485551); |
|
|
|
|
|
a = safe_add(a, olda); |
|
|
b = safe_add(b, oldb); |
|
|
c = safe_add(c, oldc); |
|
|
d = safe_add(d, oldd); |
|
|
} |
|
|
return Array(a, b, c, d); |
|
|
} |
|
|
|
|
|
/* |
|
|
* These functions implement the four basic operations the algorithm uses. |
|
|
*/ |
|
|
function md5_cmn(q, a, b, x, s, t) |
|
|
{ |
|
|
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); |
|
|
} |
|
|
function md5_ff(a, b, c, d, x, s, t) |
|
|
{ |
|
|
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); |
|
|
} |
|
|
function md5_gg(a, b, c, d, x, s, t) |
|
|
{ |
|
|
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); |
|
|
} |
|
|
function md5_hh(a, b, c, d, x, s, t) |
|
|
{ |
|
|
return md5_cmn(b ^ c ^ d, a, b, x, s, t); |
|
|
} |
|
|
function md5_ii(a, b, c, d, x, s, t) |
|
|
{ |
|
|
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); |
|
|
} |
|
|
|
|
|
/* |
|
|
* Add integers, wrapping at 2^32. This uses 16-bit operations internally |
|
|
* to work around bugs in some JS interpreters. |
|
|
*/ |
|
|
function safe_add(x, y) |
|
|
{ |
|
|
var lsw = (x & 0xFFFF) + (y & 0xFFFF); |
|
|
var msw = (x >> 16) + (y >> 16) + (lsw >> 16); |
|
|
return (msw << 16) | (lsw & 0xFFFF); |
|
|
} |
|
|
|
|
|
/* |
|
|
* Bitwise rotate a 32-bit number to the left. |
|
|
*/ |
|
|
function bit_rol(num, cnt) |
|
|
{ |
|
|
return (num << cnt) | (num >>> (32 - cnt)); |
|
|
} |
|
|
//</NOLITE> |
|
|
// ENDFILE: md5-2.2alpha.js |
|
|
// STARTFILE: parensplit.js |
|
|
////////////////////////////////////////////////// |
|
|
// parenSplit |
|
|
|
|
|
// String.prototype.parenSplit should do what ECMAscript says |
|
|
// String.prototype.split does, interspersing paren matches between |
|
|
// the split elements |
|
|
|
|
|
if (String('abc'.split(/(b)/))!='a,b,c') { |
|
|
// broken String.split, e.g. konq, IE |
|
|
String.prototype.parenSplit=function (re) { |
|
|
re=nonGlobalRegex(re); |
|
|
var s=this; |
|
|
var m=re.exec(s); |
|
|
var ret=; |
|
|
while (m && s) { |
|
|
// without the following loop, we have |
|
|
// 'ab'.parenSplit(/a|(b)/) != 'ab'.split(/a|(b)/) |
|
|
for(var i=0; i<m.length; ++i) { |
|
|
if (typeof m=='undefined') m=''; |
|
|
} |
|
|
ret.push(s.substring(0,m.index)); |
|
|
ret = ret.concat(m.slice(1)); |
|
|
s=s.substring(m.index + m.length); |
|
|
m=re.exec(s); |
|
|
} |
|
|
ret.push(s); |
|
|
return ret; |
|
|
}; |
|
|
} else { |
|
|
String.prototype.parenSplit=function (re) { |
|
|
return this.split(re); |
|
|
}; |
|
|
} |
|
|
|
|
|
function nonGlobalRegex(re) { |
|
|
var s=re.toString(); |
|
|
flags=''; |
|
|
for (var j=s.length; s.charAt(j) != '/'; --j) { |
|
|
if (s.charAt(j) != 'g') { flags += s.charAt(j); } |
|
|
} |
|
|
var t=s.substring(1,j); |
|
|
return RegExp(t,flags); |
|
|
} |
|
|
// ENDFILE: parensplit.js |
|
|
// STARTFILE: tools.js |
|
|
// IE madness with encoding |
|
|
// ======================== |
|
|
// |
|
|
// suppose throughout that the page is in utf8, like wikipedia |
|
|
// |
|
|
// if a is an anchor DOM element and a.href should consist of |
|
|
// |
|
|
// http://host.name.here/foo?bar=baz |
|
|
// |
|
|
// then IE gives foo as "latin1-encoded" utf8; we have foo = decode_utf8(decodeURI(foo_ie)) |
|
|
// but IE gives bar=baz correctly as plain utf8 |
|
|
// |
|
|
// --------------------------------- |
|
|
// |
|
|
// IE's xmlhttp doesn't understand utf8 urls. Have to use encodeURI here. |
|
|
// |
|
|
// --------------------------------- |
|
|
// |
|
|
// summat else |
|
|
|
|
|
// Source: http://aktuell.de.selfhtml.org/artikel/javascript/utf8b64/utf8.htm |
|
|
|
|
|
//<NOLITE> |
|
|
function encode_utf8(rohtext) { |
|
|
// dient der Normalisierung des Zeilenumbruchs |
|
|
rohtext = rohtext.replace(/\r\n/g,"\n"); |
|
|
var utftext = ""; |
|
|
for(var n=0; n<rohtext.length; n++) |
|
|
{ |
|
|
// ermitteln des Unicodes des aktuellen Zeichens |
|
|
var c=rohtext.charCodeAt(n); |
|
|
// alle Zeichen von 0-127 => 1byte |
|
|
if (c<128) |
|
|
utftext += String.fromCharCode(c); |
|
|
// alle Zeichen von 127 bis 2047 => 2byte |
|
|
else if((c>127) && (c<2048)) { |
|
|
utftext += String.fromCharCode((c>>6)|192); |
|
|
utftext += String.fromCharCode((c&63)|128);} |
|
|
// alle Zeichen von 2048 bis 66536 => 3byte |
|
|
else { |
|
|
utftext += String.fromCharCode((c>>12)|224); |
|
|
utftext += String.fromCharCode(((c>>6)&63)|128); |
|
|
utftext += String.fromCharCode((c&63)|128);} |
|
|
} |
|
|
return utftext; |
|
|
} |
|
|
|
|
|
|
|
|
function getJsObj(json) { |
|
|
var jsobj; |
|
|
try { |
|
|
eval('jsobj='+json); |
|
|
return jsobj; |
|
|
} catch (someError) { |
|
|
log('Something went wrong with getJsobj, json='+json); |
|
|
return 1; |
|
|
} |
|
|
} |
|
|
|
|
|
function anyChild(obj) { |
|
|
for (var p in obj) { |
|
|
return obj; |
|
|
} |
|
|
return null; |
|
|
} |
|
|
//</NOLITE> |
|
|
|
|
|
function decode_utf8(utftext) { |
|
|
var plaintext = ""; var i=0; var c=c1=c2=0; |
|
|
// while-Schleife, weil einige Zeichen uebersprungen werden |
|
|
while(i<utftext.length) |
|
|
{ |
|
|
c = utftext.charCodeAt(i); |
|
|
if (c<128) { |
|
|
plaintext += String.fromCharCode(c); |
|
|
i++;} |
|
|
else if((c>191) && (c<224)) { |
|
|
c2 = utftext.charCodeAt(i+1); |
|
|
plaintext += String.fromCharCode(((c&31)<<6) | (c2&63)); |
|
|
i+=2;} |
|
|
else { |
|
|
c2 = utftext.charCodeAt(i+1); c3 = utftext.charCodeAt(i+2); |
|
|
plaintext += String.fromCharCode(((c&15)<<12) | ((c2&63)<<6) | (c3&63)); |
|
|
i+=3;} |
|
|
} |
|
|
return plaintext; |
|
|
} |
|
|
|
|
|
|
|
|
function upcaseFirst(str) { |
|
|
if (typeof str != typeof '' || str=='') return ''; |
|
|
return str.charAt(0).toUpperCase() + str.substring(1); |
|
|
} |
|
|
|
|
|
|
|
|
function findInArray(arr, foo) { |
|
|
if (!arr || !arr.length) { return -1; } |
|
|
var len=arr.length; |
|
|
for (var i=0; i<len; ++i) { if (arr==foo) { return i; } } |
|
|
return -1; |
|
|
} |
|
|
|
|
|
function nextOne (array, value) { |
|
|
// NB if the array has two consecutive entries equal |
|
|
// then this will loop on successive calls |
|
|
var i=findInArray(array, value); |
|
|
if (i<0) { return null; } |
|
|
return array; |
|
|
} |
|
|
|
|
|
function literalizeRegex(str){ |
|
|
return str.replace(RegExp('(])', 'g'), '\\$1'); |
|
|
} |
|
|
|
|
|
String.prototype.entify=function() { |
|
|
//var shy='­'; |
|
|
return this.split('&').join('&').split('<').join('<').split('>').join('>'/*+shy*/).split('"').join('"'); |
|
|
}; |
|
|
|
|
|
function findThis(array, value) { |
|
|
if (typeof array.length == 'undefined') { return null; } |
|
|
for (var i=0; i<array.length; ++i) { |
|
|
if (array==value) { return i; } |
|
|
} |
|
|
return null; |
|
|
} |
|
|
|
|
|
function removeNulls(list) { |
|
|
var ret=; |
|
|
for (var i=0; i<list.length; ++i) { |
|
|
if (list) { |
|
|
ret.push(list); |
|
|
} |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
function joinPath(list) { |
|
|
return removeNulls(list).join('/'); |
|
|
} |
|
|
|
|
|
|
|
|
function simplePrintf(str, subs) { |
|
|
if (!str || !subs) { return str; } |
|
|
var ret=; |
|
|
var s=str.parenSplit(/(%s|\$+)/); |
|
|
var i=0; |
|
|
do { |
|
|
ret.push(s.shift()); |
|
|
if ( !s.length ) { break; } |
|
|
var cmd=s.shift(); |
|
|
if (cmd == '%s') { |
|
|
if ( i < subs.length ) { ret.push(subs); } else { ret.push(cmd); } |
|
|
++i; |
|
|
} else { |
|
|
var j=parseInt( cmd.replace('$', ''), 10 ) - 1; |
|
|
if ( j > -1 && j < subs.length ) { ret.push(subs); } else { ret.push(cmd); } |
|
|
} |
|
|
} while (s.length > 0); |
|
|
return ret.join(''); |
|
|
} |
|
|
|
|
|
function max(a,b){return a<b ? b : a;} |
|
|
function min(a,b){return a>b ? b : a;} |
|
|
|
|
|
function isString(x) { return (typeof x === 'string' || x instanceof String); } |
|
|
//function isNumber(x) { return (typeof x === 'number' || x instanceof Number); } |
|
|
function isRegExp(x) { return x instanceof RegExp; } |
|
|
function isArray (x) { return x instanceof Array; } |
|
|
function isObject(x) { return x instanceof Object; } |
|
|
function isFunction(x) { |
|
|
return !isRegExp(x) && (typeof x === 'function' || x instanceof Function); |
|
|
} |
|
|
// ENDFILE: tools.js |
|
|
// STARTFILE: dab.js |
|
|
//<NOLITE> |
|
|
////////////////////////////////////////////////// |
|
|
// Dab-fixing code |
|
|
// |
|
|
|
|
|
|
|
|
function retargetDab(newTarget, oldTarget, friendlyCurrentArticleName) { |
|
|
log('retargetDab: newTarget='+newTarget + ' oldTarget=' + oldTarget); |
|
|
return changeLinkTargetLink( |
|
|
{newTarget: newTarget, |
|
|
text: newTarget.split(' ').join(' '), |
|
|
hint: tprintf('disambigHint', ), |
|
|
summary: simplePrintf( |
|
|
getValueOf('popupFixDabsSummary'), ), |
|
|
clickButton: 'wpDiff', minor: true, oldTarget: oldTarget, |
|
|
watch: getValueOf('popupWatchDisambiggedPages') |
|
|
}); |
|
|
} |
|
|
|
|
|
function listLinks(wikitext, oldTarget) { |
|
|
var reg=RegExp('\\*?)(\\||\\]\\])', 'gi'); |
|
|
var ret=; |
|
|
var splitted=wikitext.parenSplit(reg); |
|
|
// ^+ should match interwiki links, hopefully (case-insensitive) |
|
|
// and ^* should match those and ] style links too |
|
|
var omitRegex=RegExp('^*:|^pecial:|^mage|^ategory'); |
|
|
var friendlyCurrentArticleName=pg.current.article.split('_').join(' '); |
|
|
|
|
|
for (var i=1; i<splitted.length; i=i+3) { |
|
|
if (typeof splitted == typeof 'string' && splitted.length>0 && !omitRegex.test(splitted)) { |
|
|
ret.push( retargetDab(splitted, oldTarget, friendlyCurrentArticleName) ); |
|
|
} /* if */ |
|
|
} /* for loop */ |
|
|
|
|
|
ret = rmDupesFromSortedList(ret.sort()); |
|
|
|
|
|
var wikTarget='wiktionary:' + friendlyCurrentArticleName.replace( RegExp('^(.+)\\s++\\s*$'), '$1' ); |
|
|
ret.push( retargetDab(wikTarget, oldTarget, friendlyCurrentArticleName) ); |
|
|
|
|
|
ret.push(changeLinkTargetLink( |
|
|
{ newTarget: null, |
|
|
text: popupString('remove this link').split(' ').join(' '), |
|
|
hint: popupString("remove all links to this disambig page from this article"), |
|
|
clickButton: "wpDiff", oldTarget: oldTarget, |
|
|
summary: simplePrintf(getValueOf('popupRmDabLinkSummary'), ), |
|
|
watch: getValueOf('popupWatchDisambiggedPages')})); |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function rmDupesFromSortedList(list) { |
|
|
var ret=; |
|
|
for (var i=0; i<list.length; ++i) { |
|
|
if (ret.length===0 || list!=ret) { ret.push(list); } |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function makeFixDab(data, oldTarget) { |
|
|
var list=listLinks(data, oldTarget); |
|
|
if (list.length===0) { log('listLinks returned empty list'); return null; } |
|
|
var html='<hr>' + popupString('Click to disambiguate this link to:') + '<br>'; |
|
|
html+=list; |
|
|
for (var i=1; i<list.length; ++i) { html += ', '+list; } |
|
|
return html; |
|
|
} |
|
|
|
|
|
|
|
|
function makeFixDabs(wikiText, oldTarget) |
|
|
{ |
|
|
if (getValueOf('popupFixDabs') && isDisambig(wikiText) && |
|
|
location.href.indexOf(pg.ns.special+':') == -1 && |
|
|
pg.current.article.talkPage() ) { |
|
|
setPopupHTML(makeFixDab(wikiText, oldTarget), 'popupFixDab', pg.idNumber); |
|
|
} |
|
|
} |
|
|
|
|
|
function popupRedlinkHTML() { |
|
|
var friendlyCurrentArticleName=pg.current.article.split('_').join(' '); |
|
|
|
|
|
return changeLinkTargetLink( |
|
|
{ newTarget: null, text: popupString('remove this link').split(' ').join(' '), |
|
|
hint: popupString("remove all links to this page from this article"), |
|
|
clickButton: "wpDiff", |
|
|
//oldTarget: oldTarget, |
|
|
summary: simplePrintf(getValueOf('popupRedlinkSummary'), )}); |
|
|
} |
|
|
//</NOLITE> |
|
|
// ENDFILE: dab.js |
|
|
// STARTFILE: htmloutput.js |
|
|
// this has to use a timer loop as we don't know if the DOM element exists when we want to set the text |
|
|
function setPopupHTML (str, elementId, popupId, onSuccess) { |
|
|
//log('setPopupHTML, str='+str+', \n elementId='+elementId+', popupId='+popupId); |
|
|
if (typeof popupId === 'undefined') { popupId = pg.idNumber; } |
|
|
var timer; |
|
|
|
|
|
if (typeof pg.timer.popupHTML == 'undefined') { timer=null; } |
|
|
else { timer=pg.timer.popupHTML; } |
|
|
|
|
|
var popupElement=document.getElementById(elementId+popupId); |
|
|
if (popupElement) { |
|
|
if(timer) { clearInterval(timer); } |
|
|
pg.timer.popupHTML=null; |
|
|
popupElement.innerHTML=str; |
|
|
if (onSuccess) { onSuccess(); } |
|
|
setTimeout(checkPopupPosition, 100); |
|
|
return true; |
|
|
} else { |
|
|
// call this function again in a little while... |
|
|
var loopFunction=function() { setPopupHTML(str,elementId,popupId,onSuccess);}; |
|
|
pg.misc.popupHTMLLoopFunctions = loopFunction; |
|
|
if (!timer) { |
|
|
var doThis = 'pg.misc.popupHTMLLoopFunctions()'; |
|
|
pg.timer.popupHTML = setInterval(doThis, 600); |
|
|
} |
|
|
} |
|
|
return null; |
|
|
} |
|
|
|
|
|
//<NOLITE> |
|
|
function setImageStatus(str, id) {return; } // setPopupHTML(str, 'popupImageStatus', id);} |
|
|
function setPopupTrailer(str,id) {return setPopupHTML(str, 'popupData', id);} |
|
|
//</NOLITE> |
|
|
|
|
|
function fillEmptySpans(args) { return fillEmptySpans2(args); } |
|
|
|
|
|
function fillEmptySpans2(args) { // if redir is present and true then redirTarget is mandatory |
|
|
var redir=true; |
|
|
if (typeof args != 'object' || typeof args.redir == 'undefined' || !args.redir) { redir=false; } |
|
|
var a=pg.current.link; |
|
|
if (args && args.navpopup && args.navpopup.parentAnchor) { a=args.navpopup.parentAnchor; } |
|
|
if (!a) { log('*****\nfillEmptySpans: a is no good\n*****'); return; } |
|
|
|
|
|
var article, hint, oldid; |
|
|
if (redir && typeof args.redirTarget == typeof {}) { |
|
|
article=args.redirTarget; hint=null; |
|
|
} else { |
|
|
article=(new Title()).fromAnchor(a); |
|
|
hint=a.originalTitle || article.hintValue(); |
|
|
oldid=(getValueOf('popupHistoricalLinks')) ? oldidFromAnchor(a) : null; |
|
|
} |
|
|
var x={ article:article, hint: hint, oldid: oldid }; |
|
|
|
|
|
var structure=pg.structures; |
|
|
if (typeof structure != 'object') { |
|
|
setPopupHTML('popupError', 'Unknown structure (this should never happen): '+ |
|
|
pg.option.popupStructure); |
|
|
return; |
|
|
} |
|
|
var spans=flatten(pg.misc.layout); |
|
|
var numspans = spans.length; |
|
|
var redirs=pg.misc.redirSpans; |
|
|
|
|
|
for (var i=0; i<numspans; ++i) { |
|
|
var f=findThis(redirs, spans); |
|
|
//log('redir='+redir+', f='+f+', spans='+spans); |
|
|
if ( (f!==null && !redir) || (f===null && redir) ) { |
|
|
//log('skipping this set of the loop'); |
|
|
continue; |
|
|
} |
|
|
var structurefn=structure]; |
|
|
switch (typeof structurefn) { |
|
|
case 'function': |
|
|
//log('running '+spans+'({article:'+x.article+', hint:'+x.hint+', oldid: '+x.oldid+'})'); |
|
|
setPopupHTML(structurefn(x), spans); |
|
|
break; |
|
|
case 'string': |
|
|
setPopupHTML(structurefn, spans); |
|
|
break; |
|
|
default: |
|
|
errlog('unknown thing with label '+spans); |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
// flatten an array |
|
|
function flatten(list, start) { |
|
|
var ret=; |
|
|
if (typeof start == 'undefined') { start=0; } |
|
|
for (var i=start; i<list.length; ++i) { |
|
|
if (typeof list == typeof ) { |
|
|
return ret.concat(flatten(list)).concat(flatten(list, i+1)); |
|
|
} |
|
|
else { ret.push(list); } |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
// Generate html for whole popup |
|
|
function popupHTML (a) { |
|
|
getValueOf('popupStructure'); |
|
|
var structure=pg.structures; |
|
|
if (typeof structure != 'object') { |
|
|
//return 'Unknown structure: '+pg.option.popupStructure; |
|
|
// override user choice |
|
|
pg.option.popupStructure=pg.optionDefault.popupStructure; |
|
|
return popupHTML(a); |
|
|
} |
|
|
if (typeof structure.popupLayout != 'function') { return 'Bad layout'; } |
|
|
pg.misc.layout=structure.popupLayout(); |
|
|
if (typeof structure.popupRedirSpans == 'function') { pg.misc.redirSpans=structure.popupRedirSpans(); } |
|
|
else { pg.misc.redirSpans=; } |
|
|
return makeEmptySpans(pg.misc.layout); |
|
|
} |
|
|
|
|
|
function makeEmptySpans (list) { |
|
|
var ret=''; |
|
|
for (var i=0; i<list.length; ++i) { |
|
|
if (typeof list == typeof '') { |
|
|
ret += emptySpanHTML(list, pg.idNumber, 'div'); |
|
|
} else if (typeof list == typeof && list.length > 0 ) { |
|
|
ret = ret.parenSplit(RegExp('(</*?>$)')).join(makeEmptySpans(list)); |
|
|
} else if (typeof list == typeof {} && list.nodeType ) { |
|
|
ret += emptySpanHTML(list.name, pg.idNumber, list.nodeType); |
|
|
} |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
|
|
|
function emptySpanHTML(name, id, tag, classname) { |
|
|
tag = tag || 'span'; |
|
|
classname = classname || name; |
|
|
return simplePrintf('<%s id="%s" class="%s"></%s>', ); |
|
|
} |
|
|
|
|
|
// generate html for popup image |
|
|
// <a id="popupImageLinkn"><img id="popupImagen"> |
|
|
// where n=pg.idNumber |
|
|
function imageHTML(article) { |
|
|
return simplePrintf('<a id="popupImageLink%s"><img align="right" valign="top" id="popupImg%s" style="display: none;"></img></a>', |
|
|
); |
|
|
} |
|
|
|
|
|
|
|
|
function popTipsSoonFn(id, when) { |
|
|
when || ( when=250 ); |
|
|
//console.log('popTipsSoonFn, id='+id+', when='+when); |
|
|
var popTips=function(){ setupTooltips(document.getElementById(id)); }; |
|
|
return function() { setTimeout( popTips, when ); }; |
|
|
} |
|
|
|
|
|
function setPopupTipsAndHTML(html, divname, idnumber) { |
|
|
setPopupHTML(html, divname, idnumber, |
|
|
getValueOf('popupSubpopups') ? popTipsSoonFn(divname + idnumber) : null); |
|
|
} |
|
|
// ENDFILE: htmloutput.js |
|
|
// STARTFILE: mouseout.js |
|
|
////////////////////////////////////////////////// |
|
|
// fuzzy checks |
|
|
|
|
|
function fuzzyCursorOffMenus(x,y, fuzz, parent) { |
|
|
if (!parent) parent=over; |
|
|
if (!parent) return null; |
|
|
var uls=parent.getElementsByTagName('ul'); |
|
|
for (var i=0; i<uls.length; ++i) { |
|
|
if (uls.className=='popup_menu') { |
|
|
if (uls.offsetWidth > 0) return false; |
|
|
} // else {document.title+='.';} |
|
|
} |
|
|
return true; |
|
|
} |
|
|
|
|
|
function checkPopupPosition () { // stop the popup running off the right of the screen |
|
|
// FIXME avoid pg.current.link |
|
|
pg.current.link && pg.current.link.navpopup && |
|
|
pg.current.link.navpopup.limitHorizontalPosition(); |
|
|
} |
|
|
|
|
|
function mouseOutWikiLink () { |
|
|
if (!window.popupsReady || !window.popupsReady()) { return; } |
|
|
log ('mouseOutWikiLink'); |
|
|
var a=this; |
|
|
if (a.navpopup==null) return; |
|
|
if ( ! a.navpopup.isVisible() ) { |
|
|
a.navpopup.banish(); |
|
|
return; |
|
|
} |
|
|
Navpopup.tracker.addHook(posCheckerHook(a.navpopup)); |
|
|
} |
|
|
|
|
|
function posCheckerHook(navpop) { |
|
|
return function() { |
|
|
log('posCheckerHook'); |
|
|
if (!navpop.isVisible()) { return true; /* remove this hook */ } |
|
|
var x=Navpopup.tracker.x, y=Navpopup.tracker.y; |
|
|
var mouseOverNavpop = navpop.isWithin(x,y,navpop.fuzz, navpop.mainDiv) || !fuzzyCursorOffMenus(x,y,navpop.fuzz, navpop.mainDiv); |
|
|
|
|
|
// FIXME it'd be prettier to do this internal to the Navpopup objects |
|
|
var t=getValueOf('popupHideDelay'); |
|
|
if (t) { t = t * 1000; } |
|
|
if (!t) { |
|
|
if(!mouseOverNavpop) { navpop.banish(); return true; /* remove this hook */ } |
|
|
return false; |
|
|
} |
|
|
// we have a hide delay set |
|
|
var d=+(new Date()); |
|
|
if ( !navpop.mouseLeavingTime ) { |
|
|
navpop.mouseLeavingTime = d; |
|
|
return false; |
|
|
} |
|
|
if ( mouseOverNavpop ) { |
|
|
navpop.mouseLeavingTime=null; |
|
|
return false; |
|
|
} |
|
|
if (d - navpop.mouseLeavingTime > t) { |
|
|
navpop.banish(); return true; /* remove this hook */ |
|
|
} |
|
|
return false; |
|
|
}; |
|
|
} |
|
|
|
|
|
function runStopPopupTimer(navpop) { |
|
|
// at this point, we should have left the link but remain within the popup |
|
|
// so we call this function again until we leave the popup. |
|
|
if (!navpop.stopPopupTimer) { |
|
|
navpop.stopPopupTimer=setInterval(posCheckerHook(navpop), 500); |
|
|
navpop.addHook(function(){clearInterval(navpop.stopPopupTimer);}, 'hide', 'before'); |
|
|
} |
|
|
} |
|
|
// ENDFILE: mouseout.js |
|
|
// STARTFILE: previewmaker.js |
|
|
/** |
|
|
@fileoverview |
|
|
Defines the {@link Previewmaker} object, which generates short previews from wiki markup. |
|
|
*/ |
|
|
|
|
|
/** |
|
|
Creates a new Previewmaker |
|
|
@constructor |
|
|
@class The Previewmaker class. Use an instance of this to generate short previews from Wikitext. |
|
|
@param {String} wikiText The Wikitext source of the page we wish to preview. |
|
|
@param {String} baseUrl The url we should prepend when creating relative urls. |
|
|
*/ |
|
|
function Previewmaker(wikiText, baseUrl) { |
|
|
/** The wikitext which is manipulated to generate the preview. */ |
|
|
this.data=wikiText; |
|
|
this.baseUrl=baseUrl; |
|
|
} |
|
|
/** Remove HTML comments |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killComments = function () { |
|
|
// this also kills trailing spaces and one trailing newline, eg ] |
|
|
this.data=this.data.replace(RegExp('<!--*?--> *\\n?', 'g'), ''); |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killDivs = function () { |
|
|
// say goodbye, divs (can be nested, so use * not *?) |
|
|
this.data=this.data.replace(RegExp('< *div* *>*?< */ *div *>', |
|
|
'gi'), ''); |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killGalleries = function () { |
|
|
this.data=this.data.replace(RegExp('< *gallery* *>*?< */ *gallery *>', |
|
|
'gi'), ''); |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.kill = function(opening, closing, subopening, repl) { |
|
|
var oldk=this.data; |
|
|
var k=this.killStuff(this.data, opening, closing, subopening, repl); |
|
|
while (k.length < oldk.length) { |
|
|
oldk=k; |
|
|
k=this.killStuff(k, opening, closing, subopening, repl); |
|
|
} |
|
|
this.data=k; |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killStuff = function (txt, opening, closing, subopening, repl) { |
|
|
var op=this.makeRegexp(opening); |
|
|
var cl=this.makeRegexp(closing, '^'); |
|
|
var sb=subopening ? this.makeRegexp(subopening, '^') : null; |
|
|
repl = repl || ''; |
|
|
if (!op || !cl) { |
|
|
alert('Navigation Popups error: op or cl is null! something is wrong.'); |
|
|
return; |
|
|
} |
|
|
if (!op.test(txt)) { return txt; } |
|
|
var ret=''; |
|
|
var opResult = op.exec(txt); |
|
|
ret = txt.substring(0,opResult.index); |
|
|
txt=txt.substring(opResult.index+opResult.length); |
|
|
var depth = 1; |
|
|
while (txt.length > 0) { |
|
|
//console.log('depth: '+depth + ', txt='+txt); |
|
|
var removal=0; |
|
|
if (cl.test(txt)) { |
|
|
depth--; |
|
|
removal=cl.exec(txt).length; |
|
|
} else if (sb && sb.test(txt)) { |
|
|
depth++; |
|
|
removal=sb.exec(txt).length; |
|
|
} |
|
|
if ( !removal ) { removal = 1; } |
|
|
txt=txt.substring(removal); |
|
|
if (depth==0) { break; } |
|
|
} |
|
|
return ret + repl + txt; |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.makeRegexp = function (x, prefix, suffix) { |
|
|
prefix = prefix || ''; |
|
|
suffix = suffix || ''; |
|
|
var reStr=''; |
|
|
var flags=''; |
|
|
if (isString(x)) { |
|
|
reStr=prefix + literalizeRegex(x) + suffix; |
|
|
} else if (isRegExp(x)) { |
|
|
var s=x.toString().substring(1); |
|
|
var sp=s.split('/'); |
|
|
flags=sp; |
|
|
sp=''; |
|
|
s=sp.join('/'); |
|
|
s=s.substring(0,s.length-1); |
|
|
reStr= prefix + s + suffix; |
|
|
} else { |
|
|
log ('makeRegexp failed'); |
|
|
} |
|
|
|
|
|
log ('makeRegexp: got reStr=' + reStr + ', flags=' + flags); |
|
|
return RegExp(reStr, flags); |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killBoxTemplates = function () { |
|
|
|
|
|
// taxobox removal... in fact, there's a saudiprincebox_begin, so let's be more general |
|
|
// also, have float_begin, ... float_end |
|
|
this.kill(RegExp('*?(float|box)(begin|start)', 'i'), /\s*/, '{{'); |
|
|
|
|
|
// infoboxes etc |
|
|
// from ]: kill frames too |
|
|
this.kill(RegExp('*?(infobox|elementbox|frame)', 'i'), /\s*/, '{{'); |
|
|
|
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killTemplates = function () { |
|
|
this.kill('{{', '}}', '{{', ' '); |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killTables = function () { |
|
|
// tables are bad, too |
|
|
// this can be slow, but it's an inprovement over a browser hang |
|
|
// torture test: ] |
|
|
this.kill('{|', '|}', '{|'); |
|
|
this.kill(/<table.*?>/i, /<\/table.*?>/i, /<table.*?>/i); |
|
|
// remove lines starting with a pipe for the hell of it (?) |
|
|
this.data=this.data.replace(RegExp('^.*$', 'mg'), ''); |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killImages = function () { |
|
|
// images and categories are a nono |
|
|
this.kill(RegExp('\\s*(' + pg.ns.image + '|' + pg.ns.category + ')\\s*:', 'i'), |
|
|
/\]\]\s*/, '[['); |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killHTML = function () { |
|
|
// kill <ref ...>...</ref> |
|
|
this.kill(/<ref\b.*?>/i, /<\/ref>/i); |
|
|
|
|
|
// let's also delete entire lines starting with <. it's worth a try. |
|
|
this.data=this.data.replace(RegExp('(^|\\n) *<.*', 'g'), '\n'); |
|
|
|
|
|
// and those pesky html tags, but not <nowiki> |
|
|
var splitted=this.data.parenSplit(/(<.*?>)/); |
|
|
var len=splitted.length; |
|
|
for (var i=1; i<len; i=i+2) { |
|
|
switch (splitted) { |
|
|
case '<nowiki>': |
|
|
case '</nowiki>': |
|
|
break; |
|
|
default: |
|
|
splitted=''; |
|
|
} |
|
|
} |
|
|
this.data=splitted.join(''); |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killChunks = function() { // heuristics alert |
|
|
// chunks of italic text? you crazy, man? |
|
|
var italicChunkRegex=new RegExp |
|
|
("((^|\\n)\\s*:*\\s*''(|'''|'){20}(.|\\n)*''*\\n)+", 'g'); |
|
|
// keep stuff separated, though, so stick in \n (fixes ]? |
|
|
this.data=this.data.replace(italicChunkRegex, '\n'); |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.mopup = function () { |
|
|
// we simply *can't* be doing with horizontal rules right now |
|
|
this.data=this.data.replace(RegExp('^-{4,}','mg'),''); |
|
|
|
|
|
// no indented lines |
|
|
this.data=this.data.replace(RegExp('(^|\\n) *:*','g'), '\n'); |
|
|
|
|
|
// replace __TOC__, __NOTOC__ and whatever else there is |
|
|
// this'll probably do |
|
|
this.data=this.data.replace(RegExp('^__*__ *$', 'gmi'),''); |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.firstBit = function () { |
|
|
// dont't be givin' me no subsequent paragraphs, you hear me? |
|
|
/// first we "normalize" section headings, removing whitespace after, adding before |
|
|
var d=this.data; |
|
|
|
|
|
if (getValueOf('popupPreviewCutHeadings')) { |
|
|
this.data=this.data.replace(RegExp('\\s*(==+*==+)\\s*', 'g'), '\n\n$1 '); |
|
|
/// then we want to get rid of paragraph breaks whose text ends badly |
|
|
this.data=this.data.replace(RegExp('() *\\n{2,}', 'g'), '$1\n'); |
|
|
|
|
|
this.data=this.data.replace(RegExp('^*'), ''); |
|
|
stuff=(RegExp('^(|\\n)*')).exec(this.data); |
|
|
if (stuff) { d = stuff; } |
|
|
if (!getValueOf('popupPreviewFirstParOnly')) { d = this.data; } |
|
|
|
|
|
/// now put \n\n after sections so that bullets and numbered lists work |
|
|
d=d.replace(RegExp('(==+*==+)\\s*', 'g'), '$1\n\n'); |
|
|
} |
|
|
|
|
|
// superfluous sentences are RIGHT OUT. |
|
|
// note: exactly 1 set of parens here needed to make the slice work |
|
|
d = d.parenSplit(RegExp('(+*\\s)','g')); |
|
|
// leading space is bad, mmkay? |
|
|
d=d.replace(RegExp('^\\s*'), ''); |
|
|
|
|
|
var notSentenceEnds=RegExp('(|etc|sic|Dr|Mr|Mrs|Ms|St|\\]*|\\s)$', 'i'); |
|
|
|
|
|
d = this.fixSentenceEnds(d, notSentenceEnds); |
|
|
|
|
|
var maxChars=getValueOf('popupMaxPreviewCharacters'); |
|
|
var n=getValueOf('popupMaxPreviewSentences'); |
|
|
var dd; |
|
|
|
|
|
do {dd=this.firstSentences(d,n); --n; } |
|
|
while ( dd.length > maxChars && n > 0 ); |
|
|
|
|
|
this.data = dd; |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.fixSentenceEnds = function(strs, reg) { |
|
|
// take an array of strings, strs |
|
|
// join strs to strs & strs if strs matches regex reg |
|
|
|
|
|
for (var i=0; i<strs.length-2; ++i) { |
|
|
if (reg.test(strs)) { |
|
|
a=; |
|
|
for (var j=0; j<strs.length; ++j) { |
|
|
if (j<i) a=strs; |
|
|
if (j==i) a=strs+strs+strs; |
|
|
if (j>i+2) a=strs; |
|
|
} |
|
|
return this.fixSentenceEnds(a,reg); |
|
|
} |
|
|
} |
|
|
return strs; |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.firstSentences = function(strs, howmany) { |
|
|
var t=strs.slice(0, 2*howmany); |
|
|
return t.join(''); |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killBadWhitespace = function() { |
|
|
this.data=this.data.replace(/^ *$/gm, ''); |
|
|
}; |
|
|
/** |
|
|
Runs the various methods to generate the preview. |
|
|
The preview is stored in the <code>html</html> field. |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.makePreview = function() { |
|
|
if (pg.current.article.namespace()!=pg.ns.template) { |
|
|
this.killComments(); |
|
|
this.killDivs(); |
|
|
this.killGalleries(); |
|
|
this.killBoxTemplates(); |
|
|
|
|
|
if (getValueOf('popupPreviewKillTemplates')) { |
|
|
this.killTemplates(); |
|
|
} else { |
|
|
this.killMultilineTemplates(); |
|
|
} |
|
|
|
|
|
this.killTables(); |
|
|
this.killImages(); |
|
|
this.killHTML(); |
|
|
this.killChunks(); |
|
|
this.mopup(); |
|
|
|
|
|
this.firstBit(); |
|
|
this.killBadWhitespace(); |
|
|
} |
|
|
|
|
|
this.html=wiki2html(this.data, this.baseUrl); // needs livepreview |
|
|
this.fixHTML(); |
|
|
this.stripLongTemplates(); |
|
|
}; |
|
|
|
|
|
//<NOLITE> |
|
|
/** Test function for debugging preview problems one step at a time. |
|
|
*/ |
|
|
function previewSteps(txt) { |
|
|
try { |
|
|
txt=txt || document.editform.wpTextbox1.value; |
|
|
} catch (err) { |
|
|
if (pg.cache.pages.length > 0) { |
|
|
txt=pg.cache.pages.data; |
|
|
} else { |
|
|
alert('provide text or use an edit page'); |
|
|
} |
|
|
} |
|
|
txt=txt.substring(0,10000); |
|
|
var base=pg.wiki.articlebase + Title.fromURL(document.location.href).urlString(); |
|
|
var p=new Previewmaker(txt, base); |
|
|
if (pg.current.article.namespace() != pg.ns.template) { |
|
|
p.killComments(); if (!confirm('done killComments(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.killDivs(); if (!confirm('done killDivs(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.killGalleries(); if (!confirm('done killGalleries(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.killBoxTemplates(); if (!confirm('done killBoxTemplates(). Continue?\n---\n' + p.data)) { return; } |
|
|
|
|
|
if (getValueOf('popupPreviewKillTemplates')) { |
|
|
p.killTemplates(); if (!confirm('done killTemplates(). Continue?\n---\n' + p.data)) { return; } |
|
|
} else { |
|
|
p.killMultilineTemplates(); if (!confirm('done killMultilineTemplates(). Continue?\n---\n' + p.data)) { return; } |
|
|
} |
|
|
|
|
|
p.killTables(); if (!confirm('done killTables(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.killImages(); if (!confirm('done killImages(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.killHTML(); if (!confirm('done killHTML(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.killChunks(); if (!confirm('done killChunks(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.mopup(); if (!confirm('done mopup(). Continue?\n---\n' + p.data)) { return; } |
|
|
|
|
|
p.firstBit(); if (!confirm('done firstBit(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.killBadWhitespace(); if (!confirm('done killBadWhitespace(). Continue?\n---\n' + p.data)) { return; } |
|
|
} |
|
|
|
|
|
p.html=wiki2html(p.data, base); // needs livepreview |
|
|
p.fixHTML(); if (!confirm('done fixHTML(). Continue?\n---\n' + p.html)) { return; } |
|
|
p.stripLongTemplates(); if (!confirm('done stripLongTemplates(). Continue?\n---\n' + p.html)) { return; } |
|
|
alert('finished preview - end result follows.\n---\n' + p.html); |
|
|
} |
|
|
//</NOLITE> |
|
|
|
|
|
/** |
|
|
Works around a quoting bug in livepreview. |
|
|
<code>wiki2html(']')</code> gives @literal{<a href='Foo's "bar"'>} |
|
|
which doesn't do very well. We change this into @literal{<a href="Foo's %22bar%22">} |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.fixHTML = function() { |
|
|
if(!this.html) return; |
|
|
// all links seem to have potential issues with quotation marks |
|
|
var splitted=this.html.parenSplit(/href='(*)'/g); |
|
|
var ret=''; |
|
|
for (var i=0; i<splitted.length; ++i) { |
|
|
if(i%2==0) { ret += splitted; continue; } |
|
|
if(i%2==1) { ret += 'href="' + splitted.split('"').join('%22') + '"'; } |
|
|
} |
|
|
// fix question marks in wiki links |
|
|
// maybe this'll break some stuff :-( |
|
|
ret=ret.replace(RegExp('\(<a href="/' + pg.wiki.articlePath + '/*\)\(.*?"\)', 'g'), '$1%3F$2'); |
|
|
// FIXME fix up % too |
|
|
this.html=ret; |
|
|
}; |
|
|
/** |
|
|
Generates the preview and displays it in the current popup. |
|
|
|
|
|
Does nothing if the generated preview is invalid or consists of whitespace only. |
|
|
Also activates wikilinks in the preview for subpopups if the popupSubpopups option is true. |
|
|
*/ |
|
|
Previewmaker.prototype.showPreview = function () { |
|
|
this.makePreview(); |
|
|
if (typeof this.html != typeof '') return; |
|
|
if (RegExp('^\\s*$').test(this.html)) return; |
|
|
setPopupHTML('<hr>', 'popupPrePreviewSep', pg.idNumber); |
|
|
setPopupTipsAndHTML(this.html, 'popupPreview', pg.idNumber); |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.stripLongTemplates = function() { |
|
|
// operates on the HTML! |
|
|
this.html=this.html.replace(RegExp('^.{0,1000}*?(<(p|br)( /)?>\\s*){2,}(*?)?', 'gi'), ''); |
|
|
this.html=this.html.split('\n').join(' '); // workaround for <pre> templates |
|
|
this.html=this.html.replace(RegExp('*<pre>*','gi'), ''); |
|
|
}; |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killMultilineTemplates = function() { |
|
|
this.kill(RegExp('\\s**\\n'), '}}', '{{'); |
|
|
}; |
|
|
// ENDFILE: previewmaker.js |
|
|
// STARTFILE: querypreview.js |
|
|
//<NOLITE> |
|
|
function loadQueryPreview(queryType, article, navpop) { |
|
|
var art=new Title(article).urlString(); |
|
|
var url=pg.wiki.wikibase + '/query.php?format=json&'; |
|
|
var htmlGenerator=function(a,d){alert('invalid html generator');}; |
|
|
switch (queryType) { |
|
|
case 'history': |
|
|
url += 'titles=' + art + '&what=revisions&rvcomments&rvlimit=' + |
|
|
getValueOf('popupHistoryPreviewLimit'); |
|
|
htmlGenerator=historyPreviewHTML; |
|
|
break; |
|
|
case 'imagelinks': |
|
|
url += 'titles=' + art + '&what=imagelinks&ilfilter=all'; |
|
|
htmlGenerator=imagelinksPreviewHTML; |
|
|
break; |
|
|
case 'category': |
|
|
url += 'what=category&cptitle=' + art; |
|
|
htmlGenerator=categoryPreviewHTML; |
|
|
break; |
|
|
case 'contribs': |
|
|
url += 'what=usercontribs&titles=' + art + '&uccomments' + |
|
|
'&uclimit=' + getValueOf('popupContribsPreviewLimit'); |
|
|
htmlGenerator=contribsPreviewHTML; |
|
|
break; |
|
|
} |
|
|
pendingNavpopTask(navpop); |
|
|
//alert(htmlGenerator); |
|
|
return startDownload(url, pg.idNumber + queryType, |
|
|
function(d){showQueryPreview(queryType, htmlGenerator(article,d), pg.idNumber, navpop, d); }); |
|
|
} |
|
|
|
|
|
function showQueryPreview(queryType, html, id, navpop, download) { |
|
|
if (pg.idNumber + queryType != download.id) { return; } |
|
|
var target='popupPreview'; |
|
|
switch (queryType) { |
|
|
case 'imagelinks': |
|
|
case 'category': |
|
|
target='popupPostPreview'; break; |
|
|
} |
|
|
completedNavpopTask(navpop); |
|
|
setPopupTipsAndHTML(html, target, id); |
|
|
} |
|
|
|
|
|
|
|
|
function imagelinksPreviewHTML(article, download) { |
|
|
var jsobj=getJsObj(download.data); |
|
|
try { |
|
|
var list=anyChild(jsobj); |
|
|
if (!list) { return popupString('No image links found'); } |
|
|
} catch(someError) { return 'Preview generation failed :( Is the query.php extension installed?'; } |
|
|
var ret=; |
|
|
for (var i=0; i<list.length; ++i) { ret.push(list); } |
|
|
if (ret.length === 0) { return popupString('No image links found'); } |
|
|
return '<h2>' + popupString('File links') + '</h2>' + linkList(ret); |
|
|
} |
|
|
|
|
|
function categoryPreviewHTML(article, download) { |
|
|
var jsobj=getJsObj(download.data); |
|
|
try{ var list=jsobj; } |
|
|
catch(someError) { return 'Category preview failed :( Is the query.php extension installed?'; } |
|
|
var ret=; |
|
|
for (var p in list) { ret.push(list); } |
|
|
if (ret.length === 0) { return popupString('Empty category'); } |
|
|
return '<h2>' + tprintf('Category members (%s shown)', ) + '</h2>' +linkList(ret); |
|
|
} |
|
|
|
|
|
function linkList(list) { |
|
|
list.sort(function(x,y) { return (x==y ? 0 : (x<y ? -1 : 1)); }); |
|
|
var buf=; |
|
|
for (var i=0; i<list.length; ++i) { |
|
|
buf.push(wikiLink({article: new Title(list), |
|
|
text: list.split(' ').join(' '), |
|
|
action: 'view'})); |
|
|
} |
|
|
return buf.join(', '); |
|
|
} |
|
|
|
|
|
function contribsPreviewHTML(article, download) { |
|
|
return historyPreviewHTML(article, download, true); |
|
|
} |
|
|
|
|
|
function historyPreviewHTML(article, download, reallyContribs) { |
|
|
var jsobj=getJsObj(download.data); |
|
|
try { |
|
|
var p=jsobj; |
|
|
var edits = anyChild(p); |
|
|
} catch (someError) { |
|
|
log('historyPreivewHTML failed.'); |
|
|
return 'Preview failed :-( Is the query.php extension installed?'; |
|
|
} |
|
|
var ret=editPreviewTable(article, edits, reallyContribs); |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function editPreviewTable(article, h, reallyContribs) { |
|
|
var ret='<table>'; |
|
|
var day=null; |
|
|
var curart=article; |
|
|
for (var i=0; i<h.length; ++i) { |
|
|
if (reallyContribs) { var page=h; curart = new Title(page); } |
|
|
var minor=typeof h=='undefined' ? '' : '<b>m </b>'; |
|
|
var d=h.split(//g); |
|
|
var thisDay=d; |
|
|
if (thisDay==day) { d=''; } |
|
|
day=thisDay; |
|
|
if (d) { |
|
|
ret += '<tr><td colspan=3><span class="popup_history_date">' + |
|
|
d+'</span></td></tr>'; |
|
|
} |
|
|
ret +='<tr class="popup_history_row_' + ( (i%2) ? 'odd' : 'even') + '">'; |
|
|
ret +='<td>(<a href="' + pg.wiki.titlebase + new Title(curart).urlString() + |
|
|
'&diff=prev&oldid=' + h + '">' + popupString('last') + '</a>)</td>'; |
|
|
ret +='<td>' + |
|
|
'<a href="' + pg.wiki.titlebase + new Title(curart).urlString() + |
|
|
'&oldid=' + h + '">' + d + '</a></td>'; |
|
|
var col3url='', col3txt=''; |
|
|
if (!reallyContribs) { |
|
|
var user=h; |
|
|
col3url=pg.wiki.titlebase + pg.ns.user + ':' + new Title(user).urlString(); |
|
|
col3txt=user; |
|
|
} else { |
|
|
col3url=pg.wiki.titlebase + curart.urlString(); |
|
|
col3txt=page; // FIXME do we have to escape this? |
|
|
} |
|
|
ret +='<td>' + (reallyContribs ? minor : '') + |
|
|
'<a href="' + col3url + '">' + col3txt + '</a></td>'; |
|
|
var comment=''; |
|
|
if (typeof h != 'undefined') { comment=h.entify();} |
|
|
else { comment=h; } |
|
|
ret +='<td>' + (!reallyContribs ? minor : '') + comment + '</td>'; |
|
|
ret +='</tr>'; |
|
|
} |
|
|
ret+='</table>'; |
|
|
return ret; |
|
|
} |
|
|
|
|
|
//</NOLITE> |
|
|
// ENDFILE: querypreview.js |
|
|
// STARTFILE: debug.js |
|
|
//////////////////////////////////////////////////////////////////// |
|
|
// Debugging functions |
|
|
//////////////////////////////////////////////////////////////////// |
|
|
|
|
|
function setupDebugging() { |
|
|
//<NOLITE> |
|
|
// debugging - change DEBUG to NONE to switch off |
|
|
if (window.popupDebug) { // popupDebug is set from .version |
|
|
window.log=function(x) { //if(gMsg!='')gMsg += '\n'; gMsg+=time() + ' ' + x; }; |
|
|
if (pg.debugLevel != log.None) { window.logger.debug(x); } |
|
|
} |
|
|
window.errlog=function(x) { |
|
|
if (pg.debugLevel != log.None) { window.logger.error(x); } |
|
|
} |
|
|
pg.debugLevel=Log.DEBUG; |
|
|
window.logger = new Log(pg.debugLevel, Log.popupLogger); |
|
|
log('Initializing logger'); |
|
|
} else { |
|
|
//</NOLITE> |
|
|
window.log = function(x) {}; |
|
|
window.errlog = function(x) {}; |
|
|
//<NOLITE> |
|
|
} |
|
|
//</NOLITE> |
|
|
} |
|
|
// ENDFILE: debug.js |
|
|
// STARTFILE: images.js |
|
|
//<NOLITE> |
|
|
// How the URLs for images in the popup come about |
|
|
|
|
|
// loadPreview |
|
|
// | |
|
|
// getWiki |
|
|
// |<----------------see other schematic for details |
|
|
// insertPreview (insertPreview = onComplete) |
|
|
// | |
|
|
// | insertPreview gets a wikiText fragment from |
|
|
// | the wikiText downloaded by getWiki |
|
|
// | |
|
|
// |
|
|
// | |
|
|
// | mouseOverWikiLink (gets an "address fragment", |
|
|
// | | no processing needed) |
|
|
// \->-*loadThisImage---<----loadImages |
|
|
// | |
|
|
// -->--hopefully valid image urls |
|
|
|
|
|
|
|
|
function sequentialLoadThisImage (image) { |
|
|
if (!getValueOf('popupImages')) { return false; } |
|
|
if (!isValidImageName(image)) { return false; } |
|
|
|
|
|
var imageUrls=getImageUrls(image); |
|
|
if (!imageUrls) { return null; } |
|
|
|
|
|
var img=new Image(); |
|
|
img.isNew=true; |
|
|
img.pg.idNumber=pg.idNumber; |
|
|
img.counter=1; |
|
|
|
|
|
img.onload = function () { |
|
|
// clear status thingy |
|
|
setImageStatus(''); |
|
|
|
|
|
var i=findThis(imageUrls, this.src); |
|
|
var goodSrc=this.src; |
|
|
|
|
|
var setPopupImage=function () { |
|
|
var popupImage=document.getElementById("popupImage"+this.pg.idNumber); |
|
|
if (popupImage && typeof popupImage.src != 'undefined') { |
|
|
clearInterval(pg.timer.image); |
|
|
popupImage.src=goodSrc; |
|
|
popupImage.width=getValueOf('popupImageSize'); |
|
|
popupImage.style.display='inline'; |
|
|
setPopupImageLink(image, pg.wiki.imageSources.wiki); |
|
|
return true; |
|
|
} else { return false; } |
|
|
}; |
|
|
pg.timer.image=setInterval(setPopupImage, 250); |
|
|
pg.cache.images.push(goodSrc); |
|
|
}; |
|
|
|
|
|
img.onerror = function () { |
|
|
pg.cache.badImageUrls.push(this.src); |
|
|
// if the popup is visible, move on |
|
|
if (over && over.style.visibility=='visible' && this.pg.idNumber==pg.idNumber) { this.setNext(); } |
|
|
}; |
|
|
|
|
|
img.setNext = function () { |
|
|
var currentSrc=null; |
|
|
var newSrc; |
|
|
if (!this.isNew) { currentSrc=this.src; } |
|
|
this.isNew=false; |
|
|
|
|
|
newSrc= (currentSrc) ? nextOne(imageUrls, currentSrc) : imageUrls; |
|
|
|
|
|
while (findThis(pg.cache.badImageUrls, newSrc)) { |
|
|
newSrc=nextOne(imageUrls, newSrc); |
|
|
if (!newSrc) { |
|
|
setImageStatus (' :-('); |
|
|
return; |
|
|
} |
|
|
} |
|
|
setImageStatus(' '+findThis(imageUrls, newSrc)); |
|
|
this.src=newSrc; |
|
|
}; |
|
|
|
|
|
// start the ball rolling |
|
|
img.setNext(); |
|
|
|
|
|
} |
|
|
|
|
|
function loadThisImageAtThisUrl(image, url) { |
|
|
log('loading "best" image:\n'+url); |
|
|
pg.misc.gImage=new Title(image.toString()); |
|
|
pg.misc.imageArray = ; |
|
|
pg.misc.imageArray = new Image(); |
|
|
pg.misc.imageArray.src=url; |
|
|
if (pg.timer.image || pg.timer.image===0) { |
|
|
clearInterval(pg.timer.image); |
|
|
pg.counter.checkImages=0; |
|
|
} |
|
|
pg.timer.image=setInterval(checkImages, 250); |
|
|
return; |
|
|
} |
|
|
|
|
|
// methinks this is unbelievably silly |
|
|
// it dovetails with the parallel image loader function |
|
|
function checkImages() { |
|
|
//log('checkImages: pg.counter.loop='+pg.counter.loop+'; pg.counter.checkImages='+pg.counter.checkImages); |
|
|
if (pg.timer.checkImages || pg.timer.checkImages===0) { |
|
|
clearInterval(pg.timer.checkImages); |
|
|
pg.timer.checkImages=null; |
|
|
if (pg.counter.loop > 10) {pg.counter.loop=0; log('too many iterations of checkImages'); return;} |
|
|
pg.counter.loop++; |
|
|
} else pg.counter.checkImages++; |
|
|
|
|
|
var status = ( pg.counter.checkImages % 2 ) ? ':' : '.' ; |
|
|
setImageStatus(status); |
|
|
|
|
|
if (pg.counter.checkImages > 100) { |
|
|
pg.counter.checkImages = 0; |
|
|
log ('pg.counter.checkImages too big in checkImages; returning'); |
|
|
clearInterval(pg.timer.image); |
|
|
} |
|
|
|
|
|
var popupImage=null; |
|
|
popupImage=document.getElementById("popupImg"+pg.idNumber); |
|
|
if (popupImage == null) { |
|
|
// this doesn't seem to happen any more in practise for some reason |
|
|
// still, I'll leave it in |
|
|
log('checkImages: document.getElementById("popupImg'+pg.idNumber+'") is null! retrying in 333ms...'); |
|
|
pg.timer.checkImages=setInterval("checkImages()",333); |
|
|
return; |
|
|
} |
|
|
|
|
|
log('checkImages: found element popupImg'+pg.idNumber+', and src='+popupImage.src); |
|
|
|
|
|
// get the first image to successfully load |
|
|
// and put it in the popupImage |
|
|
for(var i = 0; i < pg.misc.imageArray.length; ++i) { |
|
|
if(isImageOk(pg.misc.imageArray)) { |
|
|
// stop all the gubbins, assign the image and return |
|
|
|
|
|
log('checkImages: got at pos '+i+', src='+pg.misc.imageArray.src); |
|
|
clearInterval(pg.timer.image); |
|
|
|
|
|
if(pg.misc.gImage && pg.misc.gImage.namespace() == pg.ns.image) { |
|
|
popupImage.src=pg.misc.imageArray.src; |
|
|
popupImage.width=getValueOf('popupImageSize'); |
|
|
popupImage.style.display='inline'; |
|
|
// should we check to see if it's already there? maybe... |
|
|
pg.cache.images.push(pg.misc.imageArray.src); |
|
|
|
|
|
setPopupImageLink(pg.misc.gImage, pg.wiki.imageSources.wiki); |
|
|
stopImagesDownloading(); |
|
|
} |
|
|
|
|
|
setImageStatus(''); |
|
|
|
|
|
// reset evil nonconstant globals |
|
|
delete pg.misc.imageArray; pg.misc.imageArray=; |
|
|
pg.timer.image=null; |
|
|
|
|
|
pg.counter.checkImages=0; |
|
|
pg.counter.loop=0; |
|
|
|
|
|
return popupImage.src; |
|
|
} |
|
|
} |
|
|
log('checkImages: no good image found. retrying in a tic...'); |
|
|
pg.timer.checkImages=setInterval("checkImages()",333); |
|
|
} |
|
|
|
|
|
function stopImagesDownloading() { |
|
|
pg.misc.gImage=null; |
|
|
if (pg.misc.imageArray == null) { return null; } |
|
|
for (var i=0; i<pg.misc.imageArray.length; ++i) { |
|
|
//pg.misc.imageArray.src=''; // this is a REALLY BAD IDEA |
|
|
delete pg.misc.imageArray; |
|
|
//pg.misc.imageArray = new Image(); |
|
|
} |
|
|
pg.misc.imageArray = ; |
|
|
} |
|
|
|
|
|
function toggleSize() { |
|
|
var imgContainer=this; |
|
|
if (!imgContainer) { alert('imgContainer is null :/'); return;} |
|
|
img=imgContainer.firstChild; |
|
|
if (!img) { alert('img is null :/'); return;} |
|
|
if (!img.style.width || img.style.width=='') { img.style.width='100%'; } |
|
|
else { img.style.width=''; } |
|
|
} |
|
|
|
|
|
function setPopupImageLink (img, wiki) { |
|
|
if( wiki === null || img === null ) { return null; } |
|
|
|
|
|
var a=document.getElementById("popupImageLink"+pg.idNumber); |
|
|
if (a === null) { return null; } |
|
|
|
|
|
var linkURL = imageURL(img, wiki); |
|
|
if (linkURL != null) { |
|
|
if (getValueOf('popupImagesToggleSize')) { a.onclick=toggleSize; a.title=popupString('Toggle image size'); } |
|
|
else { a.href=linkURL; a.title=popupString('Open full-size image'); } |
|
|
} |
|
|
return linkURL; |
|
|
} |
|
|
|
|
|
function isImageOk(img) { |
|
|
// IE test |
|
|
if (!img.complete) { return false; } |
|
|
|
|
|
// gecko test |
|
|
if (typeof img.naturalWidth != "undefined" && img.naturalWidth == 0) { return false; } |
|
|
|
|
|
// test for konqueror and opera |
|
|
|
|
|
// note that img.width must not be defined in the html with a width="..." |
|
|
// for this to work. |
|
|
|
|
|
// konq seems to give "broken images" width 16, presumably an icon width |
|
|
// this test would probably work in gecko too, *except for very small images* |
|
|
if (typeof img.width == 'undefined' || img.width <= 16) { return false; } |
|
|
|
|
|
// No other way of checking: assume it's ok. |
|
|
return true; |
|
|
} |
|
|
|
|
|
// those odd a/a5/ bits of image urls |
|
|
function imagePathComponent(article) { // article is string, no namespace |
|
|
// FIXME needs testing with odd characters |
|
|
var forhash=article.split(' ').join('_'); |
|
|
var hash=hex_md5(forhash); |
|
|
return hash.substring(0,1) + '/' + hash.substring(0,2) + '/'; |
|
|
} |
|
|
|
|
|
function getImageUrlStart(wiki) { // this returns a trailing slash |
|
|
switch (wiki) { |
|
|
case 'en.wikipedia.org': return 'http://upload.wikimedia.org/wikipedia/en/'; |
|
|
case pg.wiki.commons: return 'http://upload.wikimedia.org/wikipedia/commons/'; |
|
|
case 'en.wiktionary.org': return 'http://en.wiktionary.org/upload/en/'; |
|
|
case 'secure.wikimedia.org': |
|
|
return joinPath() + '/'; break; |
|
|
default: // unsupported - take a guess |
|
|
if (pg.wiki.wikimedia) { |
|
|
return 'http://upload.wikimedia.org/wikipedia/' + pg.wiki.lang +'/'; |
|
|
} |
|
|
/* this should work for wikicities */ |
|
|
return 'http://' + wiki + '/images/'; |
|
|
} |
|
|
} |
|
|
|
|
|
function imageURL(img, wiki) { |
|
|
if (getValueOf('popupImagesFromThisWikiOnly') && wiki != pg.wiki.hostname) return null; |
|
|
var imgurl=null; |
|
|
var pathcpt = imagePathComponent(img.stripNamespace()); |
|
|
imgurl=getImageUrlStart(wiki) + pathcpt + img.stripNamespace(); |
|
|
return imgurl; |
|
|
} |
|
|
|
|
|
function imageThumbURL(img, wiki, width) { |
|
|
// |
|
|
// eg http://upload.wikimedia.org/wikipedia/en/thumb/6/61/ |
|
|
// Rubiks_cube_solved.jpg/120px-Rubiks_cube_solved.jpg |
|
|
// ^^^^^^^^^^^^^^^^^^^^^^^ |
|
|
// wikicities omits this bit |
|
|
// AND wikicities needs a new pathcpt for each filename including thumbs |
|
|
|
|
|
if (getValueOf('popupImagesFromThisWikiOnly') && wiki != pg.wiki.hostname) return null; |
|
|
if (getValueOf('popupNeverGetThumbs')) return null; |
|
|
|
|
|
var imgurl=null; |
|
|
var stripped=img.stripNamespace(); |
|
|
var pathcpt; |
|
|
if (pg.wiki.wikimedia) pathcpt = imagePathComponent(stripped); |
|
|
else pathcpt = imagePathComponent(width+'px-'+stripped); |
|
|
imgurl=getImageUrlStart(wiki) + "thumb/" + pathcpt; |
|
|
if (pg.wiki.wikimedia) imgurl += stripped + '/'; |
|
|
imgurl += width +"px-" + stripped; |
|
|
return imgurl; |
|
|
} |
|
|
|
|
|
function loadImages(image) { |
|
|
if (typeof image.stripNamespace != 'function') { alert('loadImages bad'); } |
|
|
if (getValueOf('popupLoadImagesSequentially')) { return sequentialLoadThisImage(image); } |
|
|
return parallelLoadThisImage(image); |
|
|
} |
|
|
|
|
|
function getImageUrls(image) { |
|
|
if (typeof image.stripNamespace != 'function') { alert('getImageUrls bad'); } |
|
|
var imageUrls=; |
|
|
for (var i=0; i<pg.wiki.imageSources.length; ++i) { |
|
|
var url; |
|
|
if (pg.wiki.imageSources.thumb) { |
|
|
url=imageThumbURL(image, pg.wiki.imageSources.wiki, pg.wiki.imageSources.width); |
|
|
} else { url=imageURL(image, pg.wiki.imageSources.wiki); } |
|
|
for (var j=0; j<pg.cache.images.length; ++j) { |
|
|
if (url == pg.cache.images) { |
|
|
loadThisImageAtThisUrl(image, url); |
|
|
return null; |
|
|
} |
|
|
} |
|
|
if (url!=null) imageUrls.push(url); |
|
|
} |
|
|
return imageUrls; |
|
|
} |
|
|
|
|
|
|
|
|
// this is probably very wasteful indeed of bandwidth |
|
|
// hey ho |
|
|
|
|
|
function parallelLoadThisImage (image) { |
|
|
if (typeof image.stripNamespace != 'function') { alert('parallelLoadThisImage bad'); } |
|
|
if (!getValueOf('popupImages')) return; |
|
|
if (!isValidImageName(image)) return false; |
|
|
|
|
|
var imageUrls=getImageUrls(image); |
|
|
if (!imageUrls) return null; |
|
|
|
|
|
for (var i=0; i<imageUrls.length; ++i) { |
|
|
var url = imageUrls; |
|
|
pg.misc.imageArray=new Image(); |
|
|
pg.misc.imageArray.src=url; |
|
|
} |
|
|
if (pg.timer.image != null) { |
|
|
clearInterval(pg.timer.image); |
|
|
pg.counter.checkImages=0; |
|
|
} |
|
|
pg.misc.gImage=new Title(image.toString()); |
|
|
pg.timer.image=setInterval("checkImages()", 250); |
|
|
return true; |
|
|
} |
|
|
|
|
|
function getValidImageFromWikiText(wikiText) { |
|
|
var imagePage=null; |
|
|
// nb in pg.re.image we're interested in the second bracketed expression |
|
|
// this may change if the regex changes :-( |
|
|
//var match=pg.re.image.exec(wikiText); |
|
|
var matched=null; |
|
|
var match; |
|
|
// strip html comments, used by evil bots :-( |
|
|
var t = removeMatchesUnless(wikiText, /(<!--*?-->)/, 1, /^<!--*popup/i); |
|
|
|
|
|
while ( match = pg.re.image.exec(t) ) { |
|
|
// now find a sane image name - exclude templates by seeking { |
|
|
var m = match || pg.ns.image + ':' + match; |
|
|
var pxWidth=match; |
|
|
if ( isValidImageName(m) && |
|
|
(!pxWidth || parseInt(pxWidth,10) >= getValueOf('popupMinImageWidth')) ) { |
|
|
matched=m; |
|
|
break; |
|
|
} |
|
|
} |
|
|
pg.re.image.lastIndex=0; |
|
|
if (!matched) { return null; } |
|
|
return pg.ns.image+':'+upcaseFirst(matched); |
|
|
} |
|
|
|
|
|
function removeMatchesUnless(str, re1, parencount, re2) { |
|
|
var split=str.parenSplit(re1); |
|
|
var c=parencount + 1; |
|
|
for (var i=0; i<split.length; ++i) { |
|
|
if ( i%c === 0 || re2.test(split) ) { continue; } |
|
|
split=''; |
|
|
} |
|
|
return split.join(''); |
|
|
} |
|
|
|
|
|
//</NOLITE> |
|
|
// ENDFILE: images.js |
|
|
// STARTFILE: namespaces.js |
|
|
// Set up namespaces and other non-strings.js localization |
|
|
// (currently that means redirs too) |
|
|
|
|
|
// Put the right namespace list into pg.ns.list, based on pg.wiki.lang |
|
|
// Default to english if nothing seems to fit |
|
|
function setNamespaceList() { |
|
|
var m="Media"; |
|
|
var list = ; |
|
|
var nsLists = { |
|
|
//<NOLITE> |
|
|
"af": , |
|
|
"als": , |
|
|
"ar": , |
|
|
"ast": , |
|
|
"be": , |
|
|
"bg": , |
|
|
"bm": , |
|
|
"bn": , |
|
|
"br": , |
|
|
"ca": , |
|
|
"cs": , |
|
|
"csb": , |
|
|
"cv": , |
|
|
"cy": , |
|
|
"da": , |
|
|
"de": , |
|
|
"el": , |
|
|
"eo": , |
|
|
"es": , |
|
|
"et": , |
|
|
"eu": , |
|
|
"fa": , |
|
|
"fi": , |
|
|
"fo": , |
|
|
"fr": , |
|
|
"fur": , |
|
|
"fy": , |
|
|
"ga": , |
|
|
"gu": , |
|
|
"he": , |
|
|
"hi": , |
|
|
"hr": , |
|
|
"hu": , |
|
|
"ia": , |
|
|
"id": , |
|
|
"is": , |
|
|
"it": , |
|
|
"ja": , |
|
|
"ka": , |
|
|
"ko": , |
|
|
"ku": , |
|
|
"la": , |
|
|
"li": , |
|
|
"lt": , |
|
|
"mk": , |
|
|
"ms": , |
|
|
"mt": , |
|
|
"nap": , |
|
|
"nds": , |
|
|
"nl": , |
|
|
"nn": , |
|
|
"no": , |
|
|
"nv": , |
|
|
"oc": , |
|
|
"os": , |
|
|
"pa": , |
|
|
"pl": , |
|
|
"pt": , |
|
|
"ro": , |
|
|
"ru": , |
|
|
"sc": , |
|
|
"sk": , |
|
|
"sl": , |
|
|
"sq": , |
|
|
"sr": , |
|
|
"sv": , |
|
|
"ta": , |
|
|
"th": , |
|
|
"tlh": , |
|
|
"tr": , |
|
|
"tt": , |
|
|
"uk": , |
|
|
"vi": , |
|
|
"wa": |
|
|
//</NOLITE> |
|
|
}; |
|
|
pg.ns.list = nsLists || list; |
|
|
} |
|
|
|
|
|
function namespaceListToRegex(list) {return RegExp('^('+list.join('|').split(' ').join('')+'):');}; |
|
|
// function setNamespaceList is ugly as sin, moved to later in the code |
|
|
|
|
|
function setNamespaces() { |
|
|
setNamespaceList(); |
|
|
pg.ns.withTalkList=; // NB root (article) corresponds with this entry, null |
|
|
pg.ns.talkList=]; |
|
|
|
|
|
// if the number of namespaces changes then this will have to be changed |
|
|
// maybe the easiest way is to specify the arrays by hand as in the comments following the loop |
|
|
|
|
|
for (var i=3; i+1<pg.ns.list.length; i=i+2) { |
|
|
pg.ns.withTalkList.push(pg.ns.list); |
|
|
pg.ns.talkList.push(pg.ns.list); |
|
|
} |
|
|
|
|
|
// ALERT! SILLY HARDCODED VALUES FOLLOW! |
|
|
pg.ns.special = pg.ns.list; |
|
|
pg.ns.image = pg.ns.list; |
|
|
pg.ns.user = pg.ns.list; |
|
|
pg.ns.category = pg.ns.list; |
|
|
pg.ns.template = pg.ns.list; |
|
|
pg.ns.nonArticleList=pg.ns.list.slice(0,2).concat(pg.ns.list.slice(2)); |
|
|
} |
|
|
|
|
|
|
|
|
function setRedirs() { |
|
|
var r='redirect'; |
|
|
var R='REDIRECT'; |
|
|
var redirLists={ |
|
|
//<NOLITE> |
|
|
'be': , |
|
|
'bg': , |
|
|
'cs': , |
|
|
'cy': , |
|
|
'et': , |
|
|
'ga': , |
|
|
'is': , |
|
|
'mk': , |
|
|
'nds': , |
|
|
'nn': , |
|
|
'pt': , |
|
|
'ru': , |
|
|
'sk': , |
|
|
'sr': , |
|
|
'tt': , |
|
|
'vi': // no comma |
|
|
//</NOLITE> |
|
|
}; |
|
|
var redirList=redirLists || ; |
|
|
// Mediawiki is very tolerant about what comes after the #redirect at the start |
|
|
pg.re.redirect=RegExp('^\\s*(' + redirList.join('|') + ').*?\\]*)(|]*)?\\]{2}\\s*(.*)', 'i'); |
|
|
} |
|
|
|
|
|
function setInterwiki() { |
|
|
if (pg.wiki.wikimedia) { |
|
|
pg.wiki.interwiki='aa|ab|af|ak|als|am|an|ang|ar|arc|as|ast|av|ay|az|ba|be|ber|bg|bh|bi|bm|bn|bdf|bo|br|bs|ca|ce|ceb|ch|cho|chr|chy|co|commons|cr|cs|csb|cu|cv|cy|da|de|dv|dz|el|en|eo|es|et|eu|fa|ff|fi|fiu-vro|fj|fo|fr|fur|fy|ga|gd|gil|gl|gn|got|gu|gv|ha|haw|he|hi|ho|hr|ht|hu|hy|hz|ia|id|ie|ig|ii|ik|io|is|it|iu|ja|jbo|jv|ka|kg|ki|kj|kk|kl|km|kn|ko|kr|ks|ku|kv|kw|ky|la|lad|lan|lb|lg|li|ln|lo|lt|lu|lv|mg|mh|mi|mk|ml|mn|mo|mr|ms|mt|mus|my|na|nah|nap|nb|nd|nds|ne|ng|nl|nn|no|nr|nv|ny|oc|oj|om|or|os|pa|pam|pi|pl|ps|pt|qu|rm|rn|ro|roa-rup|ru|rw|sa|sc|scn|sco|sd|se|sg|sh|si|simple|sk|sl|sm|smg|sn|so|sq|sr|ss|st|su|sv|sw|ta|te|tg|th|ti|tk|tl|tlh|tn|to|tpi|tr|ts|tt|tum|tw|ty|ug|uk|ur|uz|ve|vi|vk|vo|wa|war|wen|wo|xh|yi|yo|za|zh|zh-min-nan|zu'; |
|
|
pg.re.interwiki=RegExp('^'+pg.wiki.interwiki+':'); |
|
|
} else { |
|
|
pg.wiki.interwiki=null; |
|
|
pg.re.interwiki=RegExp('^$'); |
|
|
} |
|
|
} |
|
|
// ENDFILE: namespaces.js |
|
|
// STARTFILE: selpop.js |
|
|
//<NOLITE> |
|
|
function getEditboxSelection() { |
|
|
// see http://www.webgurusforum.com/8/12/0 |
|
|
try { |
|
|
var editbox=document.editform.wpTextbox1; |
|
|
} catch (dang) { return; } |
|
|
// IE, Opera |
|
|
if (document.selection) { return document.selection.createRange().text; } |
|
|
// Mozilla |
|
|
var selStart = editbox.selectionStart; |
|
|
var selEnd = editbox.selectionEnd; |
|
|
return (editbox.value).substring(selStart, selEnd); |
|
|
} |
|
|
|
|
|
function doSelectionPopup() { |
|
|
// popup if the selection looks like [[foo|anything afterwards at all |
|
|
// or ]text without ']]' |
|
|
// or ] |
|
|
var sel=getEditboxSelection(); |
|
|
var open=sel.indexOf('[['); |
|
|
var pipe=sel.indexOf('|'); |
|
|
var close=sel.indexOf(']]'); |
|
|
if (open == -1 || ( pipe == -1 && close == -1) ) { return; } |
|
|
if (pipe != -1 && open > pipe || close != -1 && open > close) { return; } |
|
|
var article=new Title(sel.substring(open+2, (pipe < 0) ? close : pipe)).urlString(); |
|
|
if (close > 0 && sel.substring(close+2).indexOf('[[') >= 0) { return; } |
|
|
var a=document.createElement('a'); |
|
|
a.href=pg.wiki.titlebase + article; |
|
|
mouseOverWikiLink2(a); |
|
|
if (a.navpopup) { |
|
|
a.navpopup.addHook(function(){runStopPopupTimer(a.navpopup);}, 'unhide', 'after'); |
|
|
} |
|
|
} |
|
|
//</NOLITE> |
|
|
// ENDFILE: selpop.js |
|
|
// STARTFILE: navpopup.js |
|
|
/** |
|
|
@fileoverview Defines two classes: {@link Navpopup} and {@link Mousetracker}. |
|
|
|
|
|
<code>Navpopup</code> describes popups: when they appear, where, what |
|
|
they look like and so on. |
|
|
|
|
|
<code>Mousetracker</code> "captures" the mouse using |
|
|
<code>document.onmousemove</code>. |
|
|
*/ |
|
|
|
|
|
|
|
|
/** |
|
|
Creates a new Mousetracker. |
|
|
@constructor |
|
|
@class The Mousetracker class. This monitors mouse movements and manages associated hooks. |
|
|
*/ |
|
|
function Mousetracker() { |
|
|
/** |
|
|
Interval to regularly run the hooks anyway, in milliseconds. |
|
|
@type Integer |
|
|
*/ |
|
|
this.loopDelay=400; |
|
|
|
|
|
/** |
|
|
Timer for the loop. |
|
|
@type Timer |
|
|
*/ |
|
|
this.timer=null; |
|
|
|
|
|
/** |
|
|
Flag - are we switched on? |
|
|
@type Boolean |
|
|
*/ |
|
|
this.active=false; |
|
|
/** |
|
|
Array of hook functions. |
|
|
@private |
|
|
@type Array |
|
|
*/ |
|
|
this.hooks=; |
|
|
} |
|
|
|
|
|
/** |
|
|
Adds a hook, to be called when we get events. |
|
|
@param {Function} f A function which is called as |
|
|
<code>f(x,y)</code>. It should return <code>true</code> when it |
|
|
wants to be removed, and <code>false</code> otherwise. |
|
|
*/ |
|
|
Mousetracker.prototype.addHook = function (f) { |
|
|
this.hooks.push(f); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Runs hooks, passing them the x |
|
|
and y coords of the mouse. Hook functions that return true are |
|
|
passed to {@link Mousetracker#removeHooks} for removal. |
|
|
@private |
|
|
*/ |
|
|
Mousetracker.prototype.runHooks = function () { |
|
|
if (!this.hooks || !this.hooks.length) { return; } |
|
|
//log('Mousetracker.runHooks; we got some hooks to run'); |
|
|
var remove=false; |
|
|
var removeObj={}; |
|
|
// this method gets called a LOT - |
|
|
// pre-cache some variables |
|
|
var x=this.x, y=this.y, len = this.hooks.length; |
|
|
|
|
|
for (var i=0; i<len; ++i) { |
|
|
//~ run the hook function, and remove it if it returns true |
|
|
if (this.hooks(x, y)===true) { |
|
|
remove=true; |
|
|
removeObj=true; |
|
|
} |
|
|
} |
|
|
if (remove) { this.removeHooks(removeObj); } |
|
|
}; |
|
|
|
|
|
/** |
|
|
Removes hooks. |
|
|
@private |
|
|
@param {Object} removeObj An object whose keys are the index |
|
|
numbers of functions for removal, with values that evaluate to true |
|
|
*/ |
|
|
Mousetracker.prototype.removeHooks = function(removeObj) { |
|
|
var newHooks=; |
|
|
var len = this.hooks.length; |
|
|
for (var i=0; i<len; ++i) { |
|
|
if (! removeObj) { newHooks.push(this.hooks); } |
|
|
} |
|
|
this.hooks=newHooks; |
|
|
}; |
|
|
|
|
|
|
|
|
/** |
|
|
Event handler for mouse wiggles. |
|
|
We simply grab the event, set x and y and run the hooks. |
|
|
This makes the cpu all hot and bothered :-( |
|
|
@private |
|
|
@param {Event} e Mousemove event |
|
|
*/ |
|
|
Mousetracker.prototype.track=function (e) { |
|
|
//log ('Mousetracker.track'); |
|
|
//~ Apparently this is needed in IE. |
|
|
e = e || window.event; |
|
|
var x, y; |
|
|
if (e) { |
|
|
if (e.pageX) { x=e.pageX; y=e.pageY; } |
|
|
else if (typeof e.clientX!='undefined') { |
|
|
var left, top, docElt = window.document.documentElement; |
|
|
|
|
|
if (docElt) { left=docElt.scrollLeft; } |
|
|
left = left || window.document.body.scrollLeft || window.document.scrollLeft || 0; |
|
|
|
|
|
if (docElt) { top=docElt.scrollTop; } |
|
|
top = top || window.document.body.scrollTop || window.document.scrollTop || 0; |
|
|
|
|
|
x=e.clientX + left; |
|
|
y=e.clientY + top; |
|
|
} else { return; } |
|
|
this.x = x; |
|
|
this.y = y; |
|
|
if (this.hooks.length === 0) { return; } |
|
|
if (typeof this.lastHook_x != 'number') { this.lastHook_x = -100; this.lastHook_y=-100; } |
|
|
var diff = (this.lastHook_x - x)*(this.lastHook_y - y); |
|
|
diff = (diff >= 0) ? diff : -diff; |
|
|
if ( diff > 1 ) { |
|
|
this.lastHook_x=x; |
|
|
this.lastHook_y=y; |
|
|
this.runHooks(); |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
/** |
|
|
Sets things in motion, unless they are already that is, registering an event handler on <code>document.onmousemove</code>. |
|
|
A half-hearted attempt is made to preserve the old event handler if there is one. |
|
|
*/ |
|
|
Mousetracker.prototype.enable = function () { |
|
|
if (this.active) { return; } |
|
|
this.active=true; |
|
|
//~ Save the current handler for mousemove events. This isn't too |
|
|
//~ robust, of course. |
|
|
this.savedHandler=document.onmousemove; |
|
|
//~ Gotta save @tt{this} again for the closure, and use apply for |
|
|
//~ the member function. |
|
|
var savedThis=this; |
|
|
document.onmousemove=function (e) {savedThis.track.apply(savedThis, );}; |
|
|
if (this.loopDelay) { this.timer = setInterval(function() { //log('loop delay in mousetracker is working'); |
|
|
savedThis.runHooks();}, this.loopDelay); } |
|
|
}; |
|
|
|
|
|
/** |
|
|
Disables the tracker, removing the event handler. |
|
|
*/ |
|
|
Mousetracker.prototype.disable = function () { |
|
|
if (!this.active) { return; } |
|
|
if (typeof this.savedHandler=='function') { |
|
|
document.onmousemove=this.savedHandler; |
|
|
} else { delete document.onmousemove; } |
|
|
if (this.timer) { clearInterval(this.timer); } |
|
|
this.active=false; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Creates a new Navpopup. |
|
|
Gets a UID for the popup and |
|
|
@param init Contructor object. If <code>init.draggable</code> is true or absent, the popup becomes draggable. |
|
|
@constructor |
|
|
@class The Navpopup class. This generates popup hints, and does some management of them. |
|
|
*/ |
|
|
function Navpopup(init) { |
|
|
//alert('new Navpopup(init)'); |
|
|
/** UID for each Navpopup instance. |
|
|
Read-only. |
|
|
@type integer |
|
|
*/ |
|
|
this.uid=Navpopup.uid++; |
|
|
/** |
|
|
Read-only flag for current visibility of the popup. |
|
|
@type boolean |
|
|
@private |
|
|
*/ |
|
|
this.visible=false; |
|
|
/** Flag to be set when we want to cancel a previous request to |
|
|
show the popup in a little while. |
|
|
@private |
|
|
@type boolean |
|
|
*/ |
|
|
this.noshow=false; |
|
|
/** Categorised list of hooks. |
|
|
@see #runHooks |
|
|
@see #addHook |
|
|
@private |
|
|
@type Object |
|
|
*/ |
|
|
this.hooks={ |
|
|
'create': , |
|
|
'unhide': , |
|
|
'hide': |
|
|
}; |
|
|
/** List of downloads associated with the popup. |
|
|
@private |
|
|
@type Array |
|
|
*/ |
|
|
this.downloads=; |
|
|
/** Number of uncompleted downloads. |
|
|
@type integer |
|
|
*/ |
|
|
this.pending=null; |
|
|
/** Tolerance in pixels when detecting whether the mouse has left the popup. |
|
|
@type integer |
|
|
*/ |
|
|
this.fuzz=5; |
|
|
/** Flag to toggle running {@link #limitHorizontalPosition} to regulate the popup's position. |
|
|
@type boolean |
|
|
*/ |
|
|
this.constrained=true; |
|
|
/** The popup width in pixels. |
|
|
@private |
|
|
@type integer |
|
|
*/ |
|
|
this.width=0; |
|
|
/** The popup width in pixels. |
|
|
@private |
|
|
@type integer |
|
|
*/ |
|
|
this.height=0; |
|
|
/** The main content DIV element. |
|
|
@type HTMLDivElement |
|
|
*/ |
|
|
this.mainDiv=null; |
|
|
this.createMainDiv(); |
|
|
|
|
|
if (!init || typeof init.draggable=='undefined' || init.draggable) { |
|
|
this.makeDraggable(); |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
A UID for each Navpopup. This constructor property is just a counter. |
|
|
@type integer |
|
|
@private |
|
|
*/ |
|
|
Navpopup.uid=0; |
|
|
|
|
|
/** |
|
|
Retrieves the {@link #visible} attribute, indicating whether the popup is currently visible. |
|
|
@type boolean |
|
|
*/ |
|
|
Navpopup.prototype.isVisible=function() { |
|
|
return this.visible; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Repositions popup using CSS style. |
|
|
@private |
|
|
@param {integer} x x-coordinate (px) |
|
|
@param {integer} y y-coordinate (px) |
|
|
@param {boolean} noLimitHor Don't call {@link #limitHorizontalPosition} |
|
|
*/ |
|
|
Navpopup.prototype.reposition= function (x,y, noLimitHor) { |
|
|
log ('reposition('+x+','+y+','+noLimitHor+')'); |
|
|
if (typeof x != 'undefined' && x!==null) { this.left=x; } |
|
|
if (typeof y != 'undefined' && y!==null) { this.top=y; } |
|
|
if (typeof this.left != 'undefined' && typeof this.top != 'undefined') { |
|
|
this.mainDiv.style.left=this.left + 'px'; |
|
|
this.mainDiv.style.top=this.top + 'px'; |
|
|
} |
|
|
if (!noLimitHor) { this.limitHorizontalPosition(); } |
|
|
}; |
|
|
|
|
|
/** |
|
|
Prevents popups from being in silly locations. Hopefully. |
|
|
Should not be run if {@link #constrained} is true. |
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.limitHorizontalPosition=function() { |
|
|
if (!this.constrained || this.tooWide) { return; } |
|
|
this.updateDimensions(); |
|
|
var x=this.left; |
|
|
var w=this.width; |
|
|
var cWidth=document.body.clientWidth; |
|
|
|
|
|
|
|
|
log('limitHorizontalPosition: x='+x+ |
|
|
', this.left=' + this.left + |
|
|
', this.width=' + this.width + |
|
|
', cWidth=' + cWidth); |
|
|
|
|
|
|
|
|
if ( (x+w) >= cWidth || |
|
|
( x > 0 && this.maxWidth && this.width < this.maxWidth && this.height > this.width |
|
|
&& x > cWidth - this.maxWidth ) ) { |
|
|
// This is a very nasty hack. There has to be a better way! |
|
|
// We find the "natural" width of the div by positioning it at the far left |
|
|
// then reset it so that it should be flush right (well, nearly) |
|
|
this.mainDiv.style.left='-10000px'; |
|
|
this.mainDiv.style.width = this.maxWidth + 'px'; |
|
|
var naturalWidth=parseInt(this.mainDiv.offsetWidth, 10); |
|
|
var newLeft=cWidth - naturalWidth - 1; |
|
|
if (newLeft < 0) { newLeft = 0; this.tooWide=true; } // still unstable for really wide popups? |
|
|
log ('limitHorizontalPosition: moving to ('+newLeft + ','+ this.top+');' + ' naturalWidth=' + naturalWidth + ', clientWidth=' + cWidth); |
|
|
this.reposition(newLeft, null, true); |
|
|
} |
|
|
}; |
|
|
|
|
|
/** |
|
|
Counter indicating the z-order of the "highest" popup. |
|
|
We start the z-index at 1000 so that popups are above everything |
|
|
else on the screen. |
|
|
@private |
|
|
@type integer |
|
|
*/ |
|
|
Navpopup.highest=1000; |
|
|
|
|
|
/** |
|
|
Brings popup to the top of the z-order. |
|
|
We increment the {@link #highest} property of the contructor here. |
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.raise = function () { |
|
|
this.mainDiv.style.zIndex=Navpopup.highest + 1; |
|
|
++Navpopup.highest; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Shows the popup provided {@link #noshow} is not true. |
|
|
Updates the position, brings the popup to the top of the z-order and unhides it. |
|
|
*/ |
|
|
Navpopup.prototype.show = function () { |
|
|
//document.title+='s'; |
|
|
if (this.noshow) { return; } |
|
|
//document.title+='t'; |
|
|
this.reposition(); |
|
|
this.raise(); |
|
|
this.unhide(); |
|
|
}; |
|
|
|
|
|
|
|
|
/** |
|
|
Runs the {@link #show} method in a little while, unless we're |
|
|
already visible. |
|
|
@param {integer} time Delay in milliseconds |
|
|
@see #showSoonIfStable |
|
|
*/ |
|
|
Navpopup.prototype.showSoon = function (time) { |
|
|
if (this.visible) { return; } |
|
|
this.noshow=false; |
|
|
//~ We have to save the value of @tt{this} so that the closure below |
|
|
//~ works. |
|
|
var savedThis=this; |
|
|
//this.start_x = Navpopup.tracker.x; |
|
|
//this.start_y = Navpopup.tracker.y; |
|
|
setTimeout(function () { |
|
|
if (Navpopup.tracker.active) { |
|
|
savedThis.reposition.apply(savedThis, ); |
|
|
} |
|
|
//~ Have to use apply to invoke his member function here |
|
|
savedThis.show.apply(savedThis, ); |
|
|
}, time); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Checks to see if the mouse pointer has |
|
|
stabilised (checking every <code>time</code>/2 milliseconds) and runs the |
|
|
{@link #show} method if it has. This method makes {@link #showSoon} redundant. |
|
|
@param {integer} time The minimum time (ms) before the popup may be shown. |
|
|
*/ |
|
|
Navpopup.prototype.showSoonIfStable = function (time) { |
|
|
log ('showSoonIfStable, time='+time); |
|
|
if (this.visible) { return; } |
|
|
this.noshow = false; |
|
|
|
|
|
//~ initialize these variables so that we never run @tt{show} after |
|
|
//~ just half the time |
|
|
this.stable_x = -10000; this.stable_y = -10000; |
|
|
|
|
|
var stableShow = function() { |
|
|
log('stableShow called'); |
|
|
var new_x = Navpopup.tracker.x, new_y = Navpopup.tracker.y; |
|
|
var dx = savedThis.stable_x - new_x, dy = savedThis.stable_y - new_y; |
|
|
var fuzz2 = 0; // savedThis.fuzz * savedThis.fuzz; |
|
|
//document.title += '.join(',') + '] '; |
|
|
if ( dx * dx <= fuzz2 && dy * dy <= fuzz2 ) { |
|
|
log ('mouse is stable'); |
|
|
clearInterval(savedThis.showSoonStableTimer); |
|
|
savedThis.reposition.apply(savedThis, ); |
|
|
savedThis.show.apply(savedThis, ); |
|
|
return; |
|
|
} |
|
|
savedThis.stable_x = new_x; savedThis.stable_y = new_y; |
|
|
}; |
|
|
var savedThis = this; |
|
|
this.showSoonStableTimer = setInterval(stableShow, time/2); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Makes the popup unhidable until we call {@link #unstick}. |
|
|
*/ |
|
|
Navpopup.prototype.stick=function() { |
|
|
this.noshow=false; |
|
|
this.sticky=true; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Allows the popup to be hidden. |
|
|
*/ |
|
|
Navpopup.prototype.unstick=function() { |
|
|
this.sticky=false; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Sets the {@link #noshow} flag and hides the popup. This should be called |
|
|
when the mouse leaves the link before |
|
|
(or after) it's actually been displayed. |
|
|
*/ |
|
|
Navpopup.prototype.banish = function () { |
|
|
log ('banish called'); |
|
|
// hide and prevent showing with showSoon in the future |
|
|
this.noshow=true; |
|
|
if (this.showSoonStableTimer) { |
|
|
log('clearing showSoonStableTimer'); |
|
|
clearInterval(this.showSoonStableTimer); |
|
|
} |
|
|
this.hide(); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Runs hooks added with {@link #addHook}. |
|
|
@private |
|
|
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide' |
|
|
@param {String} when Controls exactly when the hook is run: either 'before' or 'after' |
|
|
*/ |
|
|
Navpopup.prototype.runHooks = function (key, when) { |
|
|
if (!this.hooks) { return; } |
|
|
var keyHooks=this.hooks; |
|
|
var len=keyHooks.length; |
|
|
for (var i=0; i< len; ++i) { |
|
|
if (keyHooks.when == when) { keyHooks.hook.apply(this, ); } |
|
|
} |
|
|
}; |
|
|
|
|
|
/** |
|
|
Adds a hook to the popup. Hook functions are run with <code>this</code> set to refer to the Navpopup instance, and no arguments. |
|
|
@param {Function} hook The hook function. |
|
|
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide' |
|
|
@param {String} when Controls exactly when the hook is run: either 'before' or 'after' |
|
|
*/ |
|
|
Navpopup.prototype.addHook = function ( hook, key, when ) { |
|
|
when = when || 'after'; |
|
|
if (!this.hooks) { return; } |
|
|
this.hooks.push( {hook: hook, when: when} ); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Creates the main DIV element, which contains all the actual popup content. |
|
|
Runs hooks with key 'create'. |
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.createMainDiv = function () { |
|
|
if (this.mainDiv) { return; } |
|
|
this.runHooks('create', 'before'); |
|
|
var mainDiv=document.createElement('div'); |
|
|
|
|
|
var savedThis=this; |
|
|
mainDiv.onclick=function(e) {savedThis.onclickHandler(e);}; |
|
|
mainDiv.className=(this.className) ? this.className : 'navpopup_maindiv'; |
|
|
mainDiv.id=mainDiv.className + this.uid; |
|
|
|
|
|
mainDiv.style.position='absolute'; |
|
|
mainDiv.style.display='none'; |
|
|
mainDiv.className='navpopup'; |
|
|
|
|
|
// easy access to javascript object through DOM functions |
|
|
mainDiv.navpopup=this; |
|
|
|
|
|
this.mainDiv=mainDiv; |
|
|
document.body.appendChild(mainDiv); |
|
|
this.runHooks('create', 'after'); |
|
|
}; |
|
|
/** |
|
|
Calls the {@link #raise} method. |
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.onclickHandler=function(e) { |
|
|
this.raise(); |
|
|
}; |
|
|
/** |
|
|
Makes the popup draggable, using a {@link Drag} object. |
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.makeDraggable=function(e) { |
|
|
if (!this.mainDiv) { this.createMainDiv(); } |
|
|
var drag=new Drag(); |
|
|
drag.startCondition=function(e) { |
|
|
try { if (!e.shiftKey) { return false; } } catch (err) { return false; } |
|
|
return true; |
|
|
}; |
|
|
var np=this; |
|
|
drag.endHook=function(x,y) { np.reposition(x,y); }; |
|
|
drag.init(this.mainDiv); |
|
|
}; |
|
|
|
|
|
/** Hides the popup using CSS. Runs hooks with key 'hide'. |
|
|
Sets {@link #visible} appropriately. {@link #banish} should be called externally instead of this method. |
|
|
|
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.hide = function () { |
|
|
this.runHooks('hide', 'before'); |
|
|
this.abortDownloads(); |
|
|
if (this.sticky) { return; } |
|
|
if (typeof this.visible != 'undefined' && this.visible) { |
|
|
this.mainDiv.style.display='none'; |
|
|
this.visible=false; |
|
|
} |
|
|
this.runHooks('hide', 'after'); |
|
|
}; |
|
|
|
|
|
/** Shows the popup using CSS. Runs hooks with key 'unhide'. |
|
|
Sets {@link #visible} appropriately. {@link #show} should be called externally instead of this method. |
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.unhide = function () { |
|
|
this.runHooks('unhide', 'before'); |
|
|
if (typeof this.visible != 'undefined' && !this.visible) { |
|
|
this.mainDiv.style.display='inline'; |
|
|
this.visible=true; |
|
|
} |
|
|
this.runHooks('unhide', 'after'); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Sets the <code>innerHTML</code> attribute of the main div containing the popup content. |
|
|
@param {String} html The HTML to set. |
|
|
*/ |
|
|
Navpopup.prototype.setInnerHTML = function (html) { |
|
|
this.mainDiv.innerHTML = html; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Updates the {@link #width} and {@link #height} attributes with the CSS properties. |
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.updateDimensions = function () { |
|
|
this.width=parseInt(this.mainDiv.offsetWidth, 10); |
|
|
this.height=parseInt(this.mainDiv.offsetHeight, 10); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Checks if the point (x,y) is within {@link #fuzz} of the |
|
|
{@link #mainDiv}. |
|
|
@param {integer} x x-coordinate (px) |
|
|
@param {integer} y y-coordinate (px) |
|
|
@type boolean |
|
|
*/ |
|
|
Navpopup.prototype.isWithin = function(x,y) { |
|
|
//~ If we're not even visible, no point should be considered as |
|
|
//~ being within the popup. |
|
|
if (!this.visible) { return false; } |
|
|
this.updateDimensions(); |
|
|
var fuzz=this.fuzz || 0; |
|
|
//~ Use a simple box metric here. |
|
|
return (x+fuzz >= this.left && x-fuzz <= this.left + this.width && |
|
|
y+fuzz >= this.top && y-fuzz <= this.top + this.height); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Adds a download to {@link #downloads}. |
|
|
@param {Downloader} download |
|
|
*/ |
|
|
Navpopup.prototype.addDownload=function(download) { |
|
|
if (!download) { return; } |
|
|
this.downloads.push(download); |
|
|
}; |
|
|
/** |
|
|
Aborts the downloads listed in {@link #downloads}. |
|
|
@see Downloader#abort |
|
|
*/ |
|
|
Navpopup.prototype.abortDownloads=function() { |
|
|
for(var i=0; i<this.downloads.length; ++i) { |
|
|
var d=this.downloads; |
|
|
if (d && d.abort) { d.abort(); } |
|
|
} |
|
|
this.downloads=; |
|
|
}; |
|
|
|
|
|
|
|
|
/** |
|
|
A {@link Mousetracker} instance which is a property of the constructor (pseudo-global). |
|
|
*/ |
|
|
Navpopup.tracker=new Mousetracker(); |
|
|
// ENDFILE: navpopup.js |
|
|
// STARTFILE: diff.js |
|
|
//<NOLITE> |
|
|
/* |
|
|
* Javascript Diff Algorithm |
|
|
* By John Resig (http://ejohn.org/) and ] |
|
|
* |
|
|
* More Info: |
|
|
* http://ejohn.org/projects/javascript-diff-algorithm/ |
|
|
*/ |
|
|
|
|
|
function diffEscape(n) { |
|
|
return n.entify(); |
|
|
} |
|
|
function delFmt(x) { |
|
|
if (!x.length) { return ''; } |
|
|
return "<del style='background:#FFE6E6;'>" + x.join('').split('\n').join('¶\n') +"</del>"; |
|
|
} |
|
|
function insFmt(x) { |
|
|
if (!x.length) { return ''; } |
|
|
return "<ins style='background:#AAFFEE;'>" + x.join('').split('\n').join('¶\n') +"</ins>"; |
|
|
} |
|
|
|
|
|
function copyDiffObj(x){ |
|
|
var ret=; |
|
|
for (var i=0; i<x.length; ++i) { |
|
|
if (typeof x == 'string') { ret=x; } |
|
|
else { |
|
|
ret={}; |
|
|
for (var prop in x) { ret=x; } |
|
|
} |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function countCrossings(a, b, i, eject) { |
|
|
// count the crossings on the edge starting at b |
|
|
if (!b.row && b.row !== 0) { return -1; } |
|
|
var count=0; |
|
|
for (var j=0; j<a.length; ++j) { |
|
|
if (!a.row && a.row !== 0) { continue; } |
|
|
if ( (j-b.row)*(i-a.row) > 0) { |
|
|
if(eject) { return true; } |
|
|
count++; |
|
|
} |
|
|
} |
|
|
return count; |
|
|
} |
|
|
|
|
|
function shortenDiffString(str, context) { |
|
|
var re=RegExp('(<del*?</del>|<ins*?</ins>)'); |
|
|
var splitted=str.parenSplit(re); |
|
|
var ret=; |
|
|
for (var i=0; i<splitted.length; i+=2) { |
|
|
if (splitted.length < 2*context) { |
|
|
ret += splitted; |
|
|
if (i+1<splitted.length) { ret += splitted; } |
|
|
continue; |
|
|
} |
|
|
else { |
|
|
if (i > 0) { ret += splitted.substring(0,context); } |
|
|
if (i+1 < splitted.length) { ret.push(splitted.substring(splitted.length-context) + splitted); } |
|
|
} |
|
|
} |
|
|
while (ret.length > 0 && !ret) { |
|
|
ret = ret.slice(1); |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
|
|
|
function diffString( o, n, simpleSplit, slow ) { |
|
|
var splitRe=RegExp('({2}|]{2}|{2,3}|{2,3}||=|+|\\b)'); |
|
|
|
|
|
o=diffEscape(o); n=diffEscape(n); |
|
|
var out, i; |
|
|
if (simpleSplit) { out = diff( o.split(/\b/), n.split(/\b/) ); } |
|
|
else { out = diff( o.parenSplit(splitRe), n.parenSplit(splitRe) ); } |
|
|
var str = ""; |
|
|
var acc=; // accumulator for prettier output |
|
|
|
|
|
// crossing pairings -- 'A B' vs 'B A' -- cause problems, so let's iron them out |
|
|
// this doesn't always do things optimally but it should be fast enough |
|
|
var maxOutputPair=0; |
|
|
for (i=0; i<out.n.length; ++i) { |
|
|
if ( out.n.row || out.n.row === 0 ) { |
|
|
if( maxOutputPair > out.n.row ) { |
|
|
// tangle - delete pairing |
|
|
out.o.row ]=out.o.row ].text; |
|
|
out.n=out.n.text; |
|
|
} |
|
|
if (maxOutputPair < out.n.row) { maxOutputPair = out.n.row; } |
|
|
} |
|
|
} |
|
|
|
|
|
// output the stuff preceding the first paired old line |
|
|
for (i=0; i<out.o.length && (!out.o.text && out.o !== 0); ++i) { acc.push( out.o ); } |
|
|
str += delFmt(acc); acc=; |
|
|
|
|
|
// main loop |
|
|
for ( i = 0; i < out.n.length; ++i ) { |
|
|
// output unpaired new "lines" |
|
|
while ( i < out.n.length && ( !out.n.text && out.n.text !== '' ) ) { acc.push( out.n ); } |
|
|
str += insFmt(acc); acc=; |
|
|
if ( i < out.n.length ) { // this new "line" is paired with the (out.n.row)th old "line" |
|
|
str += out.n.text; |
|
|
// output unpaired old rows starting after this new line's partner |
|
|
var m = out.n.row + 1; |
|
|
while ( m < out.o.length && !out.o.text && out.o.text !== '' ) { acc.push ( out.o ); } |
|
|
str += delFmt(acc); acc=; |
|
|
} |
|
|
} |
|
|
return str; |
|
|
} |
|
|
|
|
|
// see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Object |
|
|
window.jsReservedProperties=RegExp('^(constructor|prototype|__((define|lookup)etter)__|eval|hasOwnProperty|propertyIsEnumerable|to(Source|String|LocaleString)|(un)?watch|valueOf)$'); |
|
|
// FIXME: use obj.hasOwnProperty instead of this kludge! |
|
|
|
|
|
window.diffBugAlerts=''; |
|
|
|
|
|
|
|
|
function diffBugAlert(word) { |
|
|
if (diffBugAlerts.indexOf(word+'\n')==-1) { |
|
|
diffBugAlerts+=word+'\n'; |
|
|
alert('Bad word: '+word+'\n\nPlease report this bug.'); |
|
|
} |
|
|
} |
|
|
|
|
|
function diff( o, n ) { |
|
|
var ns = {}; |
|
|
var os = {}; |
|
|
var i; |
|
|
|
|
|
// pass 1: make hashtable ns with new rows as keys |
|
|
for ( i = 0; i < n.length; i++ ) { |
|
|
if ( jsReservedProperties.test(n) ) { n += '<!-- -->'; } |
|
|
if ( !ns ] ) { |
|
|
ns ] = { rows: , o: null }; |
|
|
} |
|
|
try { ns ].rows.push( i ); } catch (err) { diffBugAlert(n); } |
|
|
} |
|
|
|
|
|
// pass 2: make hashtable os with old rows as keys |
|
|
for ( i = 0; i < o.length; i++ ) { |
|
|
if ( jsReservedProperties.test(o) ) { o += '<!-- -->'; } |
|
|
if ( !os ] ) { |
|
|
os ] = { rows: , n: null }; |
|
|
} |
|
|
try {os ].rows.push( i ); } catch (err) { diffBugAlert(n); } |
|
|
} |
|
|
|
|
|
// pass 3: pair unique new rows and matching unique old rows |
|
|
for ( i in ns ) { |
|
|
if ( ns.rows.length == 1 && typeof(os) != "undefined" && os.rows.length == 1 ) { |
|
|
n.rows ] = { text: n.rows ], row: os.rows }; |
|
|
o.rows ] = { text: o.rows ], row: ns.rows }; |
|
|
} |
|
|
} |
|
|
|
|
|
// pass 4: pair matching rows immediately following paired rows (not necessarily unique) |
|
|
for ( i = 0; i < n.length - 1; i++ ) { |
|
|
if ( ( n.text || n.text==='' ) && ( ! n.text && n.text !== '' ) && |
|
|
n.row < o.length - 1 && ( ! o.row + 1 ].text || o.row + 1 ].text === '' ) && |
|
|
n == o.row + 1 ] ) { |
|
|
n = { text: n, row: n.row + 1 }; |
|
|
o.row+1] = { text: o.row+1], row: i + 1 }; |
|
|
} |
|
|
} |
|
|
|
|
|
// pass 5: pair matching rows immediately preceding paired rows (not necessarily unique) |
|
|
for ( i = n.length - 1; i > 0; i-- ) { |
|
|
if ( ( n.text || n.text==='' ) && ( ! n.text && n.text !== '' ) && |
|
|
n.row > 0 && ( ! o.row - 1 ].text || o.row - 1 ].text=== '' ) && |
|
|
n == o.row - 1 ] ) { |
|
|
n = { text: n, row: n.row - 1 }; |
|
|
o.row-1] = { text: o.row-1], row: i - 1 }; |
|
|
} |
|
|
} |
|
|
|
|
|
return { o: o, n: n }; |
|
|
} |
|
|
//</NOLITE> |
|
|
// ENDFILE: diff.js |
|
|
// STARTFILE: init.js |
|
|
function setSiteInfo() { |
|
|
if (window.popupLocalDebug) { |
|
|
pg.wiki.hostname = 'en.wikipedia.org'; |
|
|
} else { |
|
|
pg.wiki.hostname = location.hostname; // use in preference to location.hostname for flexibility (?) |
|
|
} |
|
|
pg.wiki.wikimedia=RegExp('(wiki(edia|source|books|news|quote)|wiktionary)org').test(pg.wiki.hostname); |
|
|
pg.wiki.wikia=RegExp('wikiacom$', 'i').test(pg.wiki.hostname); |
|
|
pg.wiki.isLocal=RegExp('^localhost').test(pg.wiki.hostname); |
|
|
pg.wiki.commons='commons.wikimedia.org'; |
|
|
pg.wiki.lang = pg.wiki.hostname.split('.'); |
|
|
// toolDbName needs pg.wiki.lang and pg.wiki.hostname |
|
|
window.toolDbName && (pg.wiki.userNameCookie=toolDbName(true) + 'UserName'); |
|
|
pg.wiki.prePath=''; |
|
|
if (pg.wiki.hostname == 'secure.wikimedia.org') { |
|
|
var s=document.location.href.split('/'); |
|
|
pg.wiki.prePath=s.slice(3,5).join('/'); |
|
|
pg.wiki.lang=s; |
|
|
} |
|
|
var port = location.port ? ':' + location.port : ''; |
|
|
pg.wiki.sitebase = joinPath(); |
|
|
} |
|
|
|
|
|
function getArticlePath() { |
|
|
if (isFunction(window.siteArticlePath)) { |
|
|
return siteArticlePath(); |
|
|
} |
|
|
return 'wiki'; |
|
|
} |
|
|
|
|
|
function getBotInterfacePath() { |
|
|
if (isFunction(window.siteBotInterfacePath)) { |
|
|
return siteBotInterfacePath(); |
|
|
} |
|
|
var botInterfacePath = pg.wiki.articlePath.replace(/\/indexphp$/, ''); |
|
|
if (pg.wiki.wikimedia) { botInterfacePath = 'w'; } // as in http://some.thing.com/search/?title=foo |
|
|
else if (pg.wiki.wikia) { botInterfacePath = ''; } |
|
|
return botInterfacePath; |
|
|
} |
|
|
|
|
|
function setTitleBase() { |
|
|
var protocol = ( window.popupLocalDebug ? 'http:' : location.protocol ); |
|
|
pg.wiki.articlePath = getArticlePath(); // as in http://some.thing.com/Article |
|
|
pg.wiki.botInterfacePath = getBotInterfacePath(); |
|
|
// default mediawiki setting is paths like http://some.thing.com/articlePath/index.php?title=foo |
|
|
|
|
|
var titletail = joinPath(); |
|
|
var titletail2 = joinPath(); |
|
|
|
|
|
// other sites may need to add code here to set titletail depending on how their urls work |
|
|
|
|
|
pg.wiki.titlebase = protocol + '//' + joinPath(); |
|
|
pg.wiki.titlebase2 = protocol + '//' + joinPath(); |
|
|
pg.wiki.wikibase = protocol + '//' + joinPath(); |
|
|
pg.wiki.articlebase = protocol + '//' + joinPath(); |
|
|
} |
|
|
|
|
|
|
|
|
////////////////////////////////////////////////// |
|
|
// Global regexps |
|
|
|
|
|
function setMainRegex() { |
|
|
var reStart='*://'; |
|
|
var preTitles = joinPath(php|wikiphtml)title=']); |
|
|
// slightly ugly hack when pg.wiki.articlePath is empty |
|
|
preTitles += '|' + literalizeRegex( ( pg.wiki.articlePath ? pg.wiki.articlePath + '/' : '')); |
|
|
|
|
|
var reEnd='/(' + preTitles + ')(*)*(?:#(.+))?'; |
|
|
pg.re.main = RegExp(reStart + literalizeRegex(pg.wiki.sitebase) + reEnd); |
|
|
} |
|
|
|
|
|
function setRegexps() { |
|
|
setMainRegex(); |
|
|
pg.re.urlNoPopup=RegExp('((title=|/)' + pg.ns.special + '(?:%3A|:)|section=)') ; |
|
|
pg.re.contribs =RegExp('(title=|/)' + pg.ns.special + '(?:%3A|:)Contributions' + '(&target=|/|/' + pg.ns.user+':)(.*)') ; |
|
|
pg.re.email =RegExp('(title=|/)' + pg.ns.special + '(?:%3A|:)Emailuser' + '(&target=|/|/(?:' + pg.ns.user+':)?)(.*)') ; |
|
|
|
|
|
//<NOLITE> |
|
|
// note: tries to get images in infobox templates too, e.g. movie pages, album pages etc |
|
|
// (^|\]* ]) * |
|
|
// (^|\]* ])(]*(+) *px)? |
|
|
// $4 = 120 as in 120px |
|
|
pg.re.image = RegExp('(^|\\]* ])(]*(+) *px)?|(?:\\n *?|) *(image|image_file|cover) *= *(*?) *? *\\n', 'img') ; |
|
|
pg.re.imageBracketCount = 6; |
|
|
|
|
|
pg.re.category = RegExp('\\]* ]) *', 'i'); |
|
|
pg.re.categoryBracketCount = 1; |
|
|
|
|
|
pg.re.ipUser=RegExp('('+pg.ns.user+':)?' + '((25|2|1||)\\.){3}' + |
|
|
'(25|2|1||)'); |
|
|
|
|
|
pg.re.stub= RegExp('stub|This .*-related article is a .*stub', 'im') ; |
|
|
pg.re.disambig=RegExp('(\\s*disambig|disambig\\s*|disamb\\s*|dab\\s*)' + |
|
|
'|\\s*(((geo|hn|road?)dis)|cc|shipindex)(\\s**)?\\s*' + // explicit, whole template names on this line |
|
|
'|is a .*disambiguation.*page', 'im') ; |
|
|
|
|
|
//</NOLITE> |
|
|
// FIXME replace with general parameter parsing function, this is daft |
|
|
pg.re.oldid=RegExp('oldid=(*)'); |
|
|
pg.re.diff=RegExp('diff=(*)'); |
|
|
} |
|
|
|
|
|
|
|
|
//<NOLITE> |
|
|
////////////////////////////////////////////////// |
|
|
// Image sources |
|
|
|
|
|
function setImageSources() { |
|
|
pg.wiki.imageSources=; |
|
|
|
|
|
// frequently seen thumbs |
|
|
pg.wiki.imageSources.push( |
|
|
{wiki: pg.wiki.hostname, thumb: true, width: 180}, // default |
|
|
{wiki: pg.wiki.hostname, thumb: true, width: 120} // gallery |
|
|
); |
|
|
|
|
|
// frequently seen thumbs on commons |
|
|
if (pg.wiki.wikimedia && pg.wiki.hostname!=pg.wiki.commons) { |
|
|
pg.wiki.imageSources.push( |
|
|
{wiki: pg.wiki.commons, thumb: true, width: 180}, |
|
|
{wiki: pg.wiki.commons, thumb: true, width: 120} |
|
|
); |
|
|
} |
|
|
|
|
|
// unusual thumb sizes and full-size |
|
|
pg.wiki.imageSources.push( |
|
|
{wiki: pg.wiki.hostname, thumb: true, width: 200}, // common? |
|
|
{wiki: pg.wiki.hostname, thumb: true, width: 250}, // common? |
|
|
{wiki: pg.wiki.hostname, thumb: true, width: 300}, |
|
|
{wiki: pg.wiki.hostname, thumb: true, width: 210}, |
|
|
{wiki: pg.wiki.hostname, thumb: true, width: 230}, |
|
|
{wiki: pg.wiki.hostname, thumb: false, width: 0} // no comma |
|
|
); |
|
|
|
|
|
// full-size on commons |
|
|
if (pg.wiki.wikimedia && pg.wiki.hostname!=pg.wiki.commons) { |
|
|
pg.wiki.imageSources.push({wiki: pg.wiki.commons, thumb: false, width: 0}); |
|
|
} |
|
|
} |
|
|
//</NOLITE> |
|
|
|
|
|
////////////////////////////////////////////////// |
|
|
// miscellany |
|
|
|
|
|
function setupCache() { |
|
|
// page caching |
|
|
pg.cache.pages = ; |
|
|
pg.cache.images = ; |
|
|
|
|
|
// global array for 404'ed image urls |
|
|
pg.cache.badImageUrls=; |
|
|
} |
|
|
|
|
|
function setMisc() { |
|
|
pg.current.link=null; |
|
|
pg.current.links=; |
|
|
|
|
|
// downloading images are put here |
|
|
pg.misc.imageArray=; |
|
|
|
|
|
setupCache(); |
|
|
// FIXME what is this for? |
|
|
pg.misc.gImage=null; // global for image |
|
|
|
|
|
// check to see if images are done with this timer |
|
|
pg.timer.image=null; |
|
|
|
|
|
// These are for checkImages() |
|
|
pg.counter.checkImages=0; |
|
|
pg.timer.checkImages=null; |
|
|
pg.timer.checkPopupPosition=null; |
|
|
pg.counter.loop=0; |
|
|
|
|
|
// ids change with each popup: popupImage0, popupImage1 etc |
|
|
pg.idNumber=0; |
|
|
|
|
|
// for myDecodeURI |
|
|
pg.misc.decodeExtras = [ |
|
|
{from: '%2C', to: ',' }, |
|
|
{from: '_', to: ' ' }, |
|
|
{from: '%24', to: '$'}, |
|
|
{from: '%26', to: '&' } // no , |
|
|
]; |
|
|
|
|
|
// for setPopupHTML - needed for timers and stuff |
|
|
pg.timer.popupHTML=; |
|
|
pg.misc.popupHTMLLoopFunctions = ; |
|
|
|
|
|
} |
|
|
|
|
|
function leadingInteger(s){ |
|
|
var n=s.match(/^(\d*)/); |
|
|
if (n) { return +n; } |
|
|
return null; |
|
|
} |
|
|
|
|
|
function setBrowserHacks() { |
|
|
var useOriginal=false; |
|
|
// browser-specific hacks |
|
|
if (typeof window.opera != 'undefined') { |
|
|
//if (leadingInteger(opera.version()) < 9) |
|
|
{ useOriginal=true; } // v9 beta still seems to have buggy css |
|
|
setDefault('popupNavLinkSeparator', ' · '); |
|
|
} else if (navigator.appName=='Konqueror') { |
|
|
setDefault('popupNavLinkSeparator', ' • '); |
|
|
pg.flag.isKonq=true; |
|
|
} else if ( navigator.vendor && navigator.vendor.toLowerCase().indexOf('apple computer')===0) { |
|
|
pg.flag.isSafari=true; |
|
|
var webkit=+navigator.userAgent.replace(RegExp('^.*AppleWebKit(\\d+).*', 'i'), '$1'); |
|
|
if (webkit < 420) { useOriginal=true; } |
|
|
} else if (navigator.appName.indexOf("Microsoft")!=-1) { |
|
|
setDefault('popupNavLinkSeparator', ' · '); |
|
|
useOriginal=true; |
|
|
pg.flag.isIE=true; |
|
|
} |
|
|
if (pg.flag.isIE || pg.flag.isKonq || pg.flag.isSafari) { |
|
|
pg.flag.linksLikeIE=true; |
|
|
} |
|
|
if (useOriginal && pg.structures.original) { |
|
|
setDefault('popupStructure','original'); |
|
|
} |
|
|
} |
|
|
|
|
|
function setupPopups() { |
|
|
// NB translatable strings should be set up first (strings.js) |
|
|
|
|
|
// basics |
|
|
setupDebugging(); |
|
|
setSiteInfo(); |
|
|
setTitleBase(); |
|
|
|
|
|
// namespaces etc |
|
|
setNamespaces(); |
|
|
setInterwiki(); |
|
|
|
|
|
// regexps |
|
|
setRegexps(); |
|
|
setRedirs(); |
|
|
|
|
|
// other stuff |
|
|
window.setImageSources && setImageSources(); |
|
|
setOptions(); // see options.js |
|
|
setBrowserHacks(); |
|
|
setMisc(); |
|
|
setupLivePreview(); |
|
|
|
|
|
// main deal here |
|
|
setupTooltips(); |
|
|
Navpopup.tracker.enable(); |
|
|
} |
|
|
// ENDFILE: init.js |
|
|
// STARTFILE: navlinks.js |
|
|
//<NOLITE> |
|
|
////////////////////////////////////////////////// |
|
|
// navlinks... let the fun begin |
|
|
// |
|
|
|
|
|
function defaultNavlinkSpec() { |
|
|
var str=''; |
|
|
str += '<b><<mainlink|shortcut= >></b>'; |
|
|
if (getValueOf('popupLastEditLink')) { |
|
|
str += '*<<lastEdit|shortcut=/>>|<<lastContrib>>|<<sinceMe>>if(oldid){|<<oldEdit>>|<<diffCur>>}'; |
|
|
} |
|
|
|
|
|
// user links |
|
|
// contribs - log - count - email - block |
|
|
// count only if applicable; block only if popupAdminLinks |
|
|
str += 'if(user){<br><<contribs|shortcut=c>>*<<userlog|shortcut=L|log>>'; |
|
|
str+='if(ipuser){*<<arin>>}if(wikimedia){*<<count|shortcut=#>>}'; |
|
|
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>|<<blocklog|log>>}}'; |
|
|
|
|
|
// editing links |
|
|
// talkpage -> edit|new - history - un|watch - article|edit |
|
|
// other page -> edit - history - un|watch - talk|edit|new |
|
|
var editstr='<<edit|shortcut=e>>'; |
|
|
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' + editstr + '}' |
|
|
var historystr='<<history|shortcut=h>>if(mainspace_en){|<<editors|shortcut=E|>>}'; |
|
|
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>'; |
|
|
|
|
|
str+='<br>if(talk){' + |
|
|
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' + |
|
|
'<b><<article|shortcut=a>></b>|<<editArticle|edit>>' + |
|
|
'}else{' + // not a talk page |
|
|
editOldidStr + '*' + historystr + '*' + watchstr + '*' + |
|
|
'<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' |
|
|
+ '}'; |
|
|
|
|
|
// misc links |
|
|
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>*<<move|shortcut=m>>'; |
|
|
|
|
|
// admin links |
|
|
str += 'if(admin){<br><<unprotect|unprotectShort>>|<<protect|shortcut=p>>|<<protectlog|log>>*' + |
|
|
'<<undelete|undeleteShort>>|<<delete|shortcut=d>>|<<deletelog|log>>}'; |
|
|
return str; |
|
|
} |
|
|
|
|
|
function navLinksHTML (article, hint, oldid) { |
|
|
var str = '<span class="popupNavLinks">' + defaultNavlinkSpec() + '</span>'; |
|
|
// BAM |
|
|
return navlinkStringToHTML(str, article, oldid); |
|
|
} |
|
|
|
|
|
function expandConditionalNavlinkString(s,article,oldid,recursionCount) { |
|
|
// nested conditionals (up to 10 deep) are ok, hopefully! (work from the inside out) |
|
|
if (typeof recursionCount!=typeof 0) { recursionCount=0; } |
|
|
var conditionalSplitRegex=RegExp( |
|
|
//(1 if \\( (2 2) \\) {(3 3)} (4 else {(5 5)} 4)1) |
|
|
'(;?\\s*if\\s*\\(\\s*(*)\\s*\\)\\s*\\{(*)\\}(\\s*else\\s*\\{(*?)\\}|))', 'i'); |
|
|
var splitted=s.parenSplit(conditionalSplitRegex); |
|
|
// $1: whole conditional |
|
|
// $2: test condition |
|
|
// $3: true expansion |
|
|
// $4: else clause (possibly empty) |
|
|
// $5: false expansion (possibly null) |
|
|
var numParens=5; |
|
|
var ret = splitted; |
|
|
for (var i=1; i<splitted.length; i=i+numParens+1) { |
|
|
|
|
|
var testString=splitted; |
|
|
var trueString=splitted; |
|
|
var falseString=splitted; |
|
|
if (typeof falseString=='undefined' || !falseString) { falseString=''; } |
|
|
var testResult=null; |
|
|
|
|
|
switch (testString) { |
|
|
case 'user': |
|
|
testResult=(article.userName())?true:false; |
|
|
break; |
|
|
case 'talk': |
|
|
testResult=(article.talkPage())?false:true; // talkPage converts _articles_ to talkPages |
|
|
break; |
|
|
case 'admin': |
|
|
testResult=getValueOf('popupAdminLinks')?true:false; |
|
|
break; |
|
|
case 'oldid': |
|
|
testResult=(typeof oldid != 'undefined' && oldid)?true:false; |
|
|
break; |
|
|
case 'ipuser': |
|
|
testResult=(article.isIpUser())?true:false; |
|
|
break; |
|
|
case 'mainspace_en': |
|
|
testResult=isInMainNamespace(article) && pg.wiki.hostname=='en.wikipedia.org'; |
|
|
break; |
|
|
case 'wikimedia': |
|
|
testResult=(pg.wiki.wikimedia) ? true : false; |
|
|
break; |
|
|
} |
|
|
|
|
|
switch(testResult) { |
|
|
case null: ret+=splitted; break; |
|
|
case true: ret+=trueString; break; |
|
|
case false: ret+=falseString; break; |
|
|
} |
|
|
|
|
|
// append non-conditional string |
|
|
ret += splitted; |
|
|
} |
|
|
if (conditionalSplitRegex.test(ret) && recursionCount < 10) { |
|
|
return expandConditionalNavlinkString(ret,article,oldid,recursionCount+1); |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function navlinkStringToArray(s, article, oldid) { |
|
|
s=expandConditionalNavlinkString(s,article,oldid); |
|
|
var splitted=s.parenSplit(RegExp('<<(.*?)>>')); |
|
|
var ret=; |
|
|
for (var i=0; i<splitted.length; ++i) { |
|
|
if (i%2) { // i odd, so s is a tag |
|
|
var t=new navlinkTag(); |
|
|
var ss=splitted.split('|'); |
|
|
t.id=ss; |
|
|
for (var j=1; j<ss.length; ++j) { |
|
|
var sss=ss.split('='); |
|
|
if (sss.length>1) { |
|
|
t]=sss; |
|
|
} |
|
|
else { // no assignment (no "="), so treat this as a title (overwriting the last one) |
|
|
t.text=popupString(sss); |
|
|
} |
|
|
} |
|
|
t.article=article; |
|
|
if (typeof oldid != 'undefined' && oldid != null) { t.oldid=oldid; } |
|
|
if (!t.text && t.id != 'mainlink') { t.text=popupString(t.id); } |
|
|
ret.push(t); |
|
|
} |
|
|
else { // plain HTML |
|
|
ret.push(splitted); |
|
|
} |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
|
|
|
function navlinkSubstituteHTML(s) { |
|
|
return s.split('*').join(getValueOf('popupNavLinkSeparator')) |
|
|
.split('<menurow>').join('<li class="popup_menu_row">') |
|
|
.split('</menurow>').join('</li>') |
|
|
.split('<menu>').join('<ul class="popup_menu">') |
|
|
.split('</menu>').join('</ul>'); |
|
|
|
|
|
} |
|
|
|
|
|
function navlinkDepth(magic,s) { |
|
|
return s.split('<' + magic + '>').length - s.split('</' + magic + '>').length; |
|
|
} |
|
|
|
|
|
|
|
|
// navlinkString: * becomes the separator |
|
|
// <<foo|bar=baz|fubar>> becomes a foo-link with attribute bar='baz' |
|
|
// and visible text 'fubar' |
|
|
// if(test){...} and if(test){...}else{...} work too (nested ok) |
|
|
|
|
|
function navlinkStringToHTML(s,article,oldid) { |
|
|
//limitAlert(navlinkStringToHTML, 5, 'navlinkStringToHTML\n' + article + '\n' + (typeof article)); |
|
|
var p=navlinkStringToArray(s,article,oldid); |
|
|
var html=''; |
|
|
var menudepth = 0; // nested menus not currently allowed, but doesn't do any harm to code for it |
|
|
var menurowdepth = 0; |
|
|
var wrapping = null; |
|
|
for (var i=0; i<p.length; ++i) { |
|
|
if (typeof p == typeof '') { |
|
|
html+=navlinkSubstituteHTML(p); |
|
|
menudepth += navlinkDepth('menu', p); |
|
|
menurowdepth += navlinkDepth('menurow', p); |
|
|
// if (menudepth === 0) { |
|
|
// tagType='span'; |
|
|
// } else if (menurowdepth === 0) { |
|
|
// tagType='li'; |
|
|
// } else { |
|
|
// tagType = null; |
|
|
// } |
|
|
} else if (typeof p.type != 'undefined' && p.type=='navlinkTag') { |
|
|
if (menudepth > 0 && menurowdepth === 0) { |
|
|
html += '<li class="popup_menu_item">' + p.html() + '</li>'; |
|
|
} else { |
|
|
html+=p.html(); |
|
|
} |
|
|
} |
|
|
} |
|
|
return html; |
|
|
} |
|
|
|
|
|
function navlinkTag() { |
|
|
this.type='navlinkTag'; |
|
|
} |
|
|
|
|
|
navlinkTag.prototype.html=function () { |
|
|
this.getNewWin(); |
|
|
this.getPrintFunction(); |
|
|
var html=''; |
|
|
var opening, closing; |
|
|
var tagType='span'; |
|
|
if (!tagType) { |
|
|
opening = ''; closing = ''; |
|
|
} else { |
|
|
opening = '<' + tagType + ' class="popup_' + this.id + '">'; |
|
|
closing = '</' + tagType + '>'; |
|
|
} |
|
|
if (typeof this.print!='function') { |
|
|
errlog ('Oh dear - invalid print function for a navlinkTag, id='+this.id); |
|
|
} else { |
|
|
html=this.print(this); |
|
|
if (typeof html != typeof '') {html='';} |
|
|
else if (typeof this.shortcut!='undefined') html=addPopupShortcut(html, this.shortcut); |
|
|
} |
|
|
return opening + html + closing; |
|
|
}; |
|
|
|
|
|
navlinkTag.prototype.getNewWin=function() { |
|
|
getValueOf('popupLinksNewWindow'); |
|
|
if (typeof pg.option.popupLinksNewWindow === 'undefined') { this.newWin=null; } |
|
|
this.newWin=pg.option.popupLinksNewWindow; |
|
|
} |
|
|
|
|
|
navlinkTag.prototype.getPrintFunction=function() { //think about this some more |
|
|
// this.id and this.article should already be defined |
|
|
if (typeof this.id!=typeof '' || typeof this.article!=typeof {} ) { return; } |
|
|
var html=''; |
|
|
var a,t; |
|
|
|
|
|
switch (this.id) { |
|
|
case 'email': case 'contribs': case 'block': case 'unblock': |
|
|
case 'userlog': case 'userSpace': |
|
|
this.article=this.article.userName(); |
|
|
} |
|
|
|
|
|
switch (this.id) { |
|
|
case 'userTalk': case 'newUserTalk': case 'editUserTalk': |
|
|
case 'userPage': case 'monobook': case 'editMonobook': case 'blocklog': |
|
|
this.article=this.article.userName(true); |
|
|
// fall through; no break |
|
|
case 'pagelog': case 'deletelog': case 'protectlog': |
|
|
delete this.oldid; |
|
|
} |
|
|
|
|
|
if (this.id=='editMonobook' || this.id=='monobook') { this.article.append('/monobook.js'); } |
|
|
|
|
|
if (this.id != 'mainlink') { |
|
|
// FIXME anchor handling should be done differently with Title object |
|
|
this.article=this.article.removeAnchor(); |
|
|
// if (typeof this.text=='undefined') this.text=popupString(this.id); |
|
|
} |
|
|
|
|
|
switch (this.id) { |
|
|
case 'undelete': this.print=specialLink; this.specialpage='Undelete'; this.sep='/'; break; |
|
|
case 'whatLinksHere': this.print=specialLink; this.specialpage='Whatlinkshere'; break; |
|
|
case 'relatedChanges': this.print=specialLink; this.specialpage='Recentchangeslinked'; break; |
|
|
case 'move': this.print=specialLink; this.specialpage='Movepage'; break; |
|
|
case 'contribs': this.print=specialLink; this.specialpage='Contributions'; break; |
|
|
case 'email': this.print=specialLink; this.specialpage='Emailuser'; break; |
|
|
case 'block': this.print=specialLink; this.specialpage='Blockip'; this.sep='&ip='; break; |
|
|
case 'unblock': this.print=specialLink; this.specialpage='Ipblocklist'; this.sep='&action=unblock&ip='; break; |
|
|
case 'userlog': this.print=specialLink; this.specialpage='Log'; this.sep='&user='; break; |
|
|
case 'blocklog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=block&page='; break; |
|
|
case 'pagelog': this.print=specialLink; this.specialpage='Log'; this.sep='&page='; break; |
|
|
case 'protectlog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=protect&page='; break; |
|
|
case 'deletelog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=delete&page='; break; |
|
|
case 'userSpace': this.print=specialLink; this.specialpage='Prefixindex'; this.sep='&namespace=2&from='; break; |
|
|
case 'search': this.print=specialLink; this.specialpage='Search'; this.sep='&fulltext=Search&search='; break; |
|
|
case 'history': case 'historyfeed': case 'unwatch': case 'watch': |
|
|
case 'unprotect': case 'protect': |
|
|
this.print=wikiLink; this.action=this.id; break; |
|
|
|
|
|
case 'delete': |
|
|
this.print=wikiLink; this.action='delete'; |
|
|
if (this.article.namespace()==pg.ns.image) { |
|
|
var img=this.article.stripNamespace(); |
|
|
this.action+='&image='+img; |
|
|
} |
|
|
break; |
|
|
|
|
|
case 'edit': // editOld should keep the oldid, but edit should not. |
|
|
delete this.oldid; // fall through |
|
|
case 'view': case 'purge': case 'render': |
|
|
this.print=wikiLink; |
|
|
this.action=this.id; break; |
|
|
case 'raw': |
|
|
this.print=wikiLink; this.action='raw&ctype=text/css'; break; |
|
|
case 'new': |
|
|
this.print=wikiLink; this.action='edit§ion=new'; break; |
|
|
case 'mainlink': |
|
|
if (typeof this.text=='undefined') { this.text=this.article.toString().entify(); } |
|
|
if (getValueOf('popupSimplifyMainLink') && isInStrippableNamespace(this.article)) { |
|
|
var s=this.text.split('/'); this.text=s; |
|
|
if (this.text=='' && s.length > 1) { this.text=s; } |
|
|
} |
|
|
this.print=titledWikiLink; |
|
|
if (typeof this.title=='undefined' && pg.current.link && typeof pg.current.link.href != 'undefined') { |
|
|
this.title=safeDecodeURI((pg.current.link.originalTitle)?pg.current.link.originalTitle:this.article); |
|
|
if (typeof this.oldid != 'undefined' && this.oldid) { |
|
|
this.title=tprintf('Revision %s of %s', ); |
|
|
} |
|
|
} |
|
|
this.action='view'; break; |
|
|
case 'userPage': |
|
|
case 'article': |
|
|
case 'monobook': |
|
|
case 'editMonobook': |
|
|
case 'editArticle': |
|
|
delete this.oldid; |
|
|
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article); |
|
|
this.article=this.article.articleFromTalkOrArticle(); |
|
|
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article); |
|
|
this.print=wikiLink; |
|
|
if (this.id.indexOf('edit')==0) { |
|
|
this.action='edit'; |
|
|
} else { this.action='view';} |
|
|
break; |
|
|
case 'userTalk': |
|
|
case 'talk': |
|
|
this.article=this.article.talkPage(); |
|
|
delete this.oldid; |
|
|
this.print=wikiLink; |
|
|
this.action='view'; break; |
|
|
case 'arin': |
|
|
this.print=arinLink; break; |
|
|
case 'count': |
|
|
this.print=kateLink; break; |
|
|
case 'google': |
|
|
this.print=googleLink; break; |
|
|
case 'contribsTree': |
|
|
this.print=contribsTreeLink; break; |
|
|
case 'editors': |
|
|
this.print=editorListLink; break; |
|
|
case 'globalsearch': |
|
|
this.print=globalSearchLink; break; |
|
|
case 'lastEdit': |
|
|
this.print=titledDiffLink; |
|
|
this.title=popupString('Show the last edit'); |
|
|
this.from='prev'; this.to='cur'; break; |
|
|
case 'oldEdit': |
|
|
this.print=titledDiffLink; |
|
|
this.title=popupString('Show the edit made to get revision') + ' ' + this.oldid; |
|
|
this.from='prev'; this.to=this.oldid; break; |
|
|
case 'editOld': |
|
|
this.print=wikiLink; this.action='edit'; break; |
|
|
case 'revert': |
|
|
this.print=wikiLink; this.action='revert'; break; |
|
|
case 'nullEdit': |
|
|
this.print=wikiLink; this.action='nullEdit'; break; |
|
|
case 'diffCur': |
|
|
this.print=titledDiffLink; |
|
|
this.title=tprintf('Show changes since revision %s', ); |
|
|
this.from=this.oldid; this.to='cur'; break; |
|
|
case 'editUserTalk': |
|
|
case 'editTalk': |
|
|
delete this.oldid; |
|
|
this.article=this.article.talkPage(); |
|
|
this.action='edit'; this.print=wikiLink; break; |
|
|
case 'newUserTalk': |
|
|
case 'newTalk': |
|
|
this.article=this.article.talkPage(); |
|
|
this.action='edit§ion=new'; this.print=wikiLink; break; |
|
|
case 'lastContrib': |
|
|
case 'sinceMe': |
|
|
this.print=magicHistoryLink; |
|
|
break; |
|
|
case 'togglePreviews': |
|
|
this.text=popupString(pg.option.simplePopups ? 'enable previews' : 'disable previews'); |
|
|
case 'disablePopups': case 'purgePopups': |
|
|
this.print=popupMenuLink; |
|
|
break; |
|
|
default: |
|
|
this.print=function () {return 'Unknown navlink type: '+this.id+''}; |
|
|
} |
|
|
}; |
|
|
// |
|
|
// end navlinks |
|
|
////////////////////////////////////////////////// |
|
|
//</NOLITE> |
|
|
// ENDFILE: navlinks.js |
|
|
// STARTFILE: shortcutkeys.js |
|
|
//<NOLITE> |
|
|
function popupHandleKeypress(evt) { |
|
|
var keyCode = window.event ? window.event.keyCode : ( evt.keyCode ? evt.keyCode : evt.which); |
|
|
if (!keyCode || !pg.current.link || !pg.current.link.navpopup) { return; } |
|
|
if (keyCode==27) { // escape |
|
|
killPopup(); |
|
|
return false; // swallow keypress |
|
|
} |
|
|
|
|
|
var letter=String.fromCharCode(keyCode); |
|
|
var links=pg.current.link.navpopup.mainDiv.getElementsByTagName('A'); |
|
|
var startLink=0; |
|
|
var i,j; |
|
|
|
|
|
if (popupHandleKeypress.lastPopupLinkSelected) { |
|
|
for (i=0; i<links.length; ++i) { |
|
|
if (links==popupHandleKeypress.lastPopupLinkSelected) { startLink=i; } |
|
|
} |
|
|
} |
|
|
for (j=0; j<links.length; ++j) { |
|
|
i=(startLink + j + 1) % links.length; |
|
|
if (links.getAttribute('popupkey')==letter) { |
|
|
if (evt && evt.preventDefault) evt.preventDefault(); |
|
|
links.focus(); |
|
|
popupHandleKeypress.lastPopupLinkSelected=links; |
|
|
return false; // swallow keypress |
|
|
} |
|
|
} |
|
|
|
|
|
// pass keypress on |
|
|
if (document.oldPopupOnkeypress) { return document.oldPopupOnkeypress(evt); } |
|
|
return true; |
|
|
} |
|
|
|
|
|
function addPopupShortcuts() { |
|
|
if (document.onkeypress && document.onkeypress.toString()==popupHandleKeypress.toString()) return; |
|
|
document.oldPopupOnkeypress=document.onkeypress; |
|
|
document.onkeypress=popupHandleKeypress; |
|
|
} |
|
|
|
|
|
function rmPopupShortcuts() { |
|
|
popupHandleKeypress.lastPopupLinkSelected=null; |
|
|
try { |
|
|
if (document.oldPopupOnkeypress && document.oldPopupOnkeypress.toString()==popupHandleKeypress.toString()) { |
|
|
// panic |
|
|
document.onkeypress=null; //function () {}; |
|
|
return; |
|
|
} |
|
|
document.onkeypress=document.oldPopupOnkeypress; |
|
|
} catch (nasties) { /* IE goes here */ } |
|
|
} |
|
|
|
|
|
|
|
|
function addLinkProperty(html, property) { |
|
|
// take "<a href=...>...</a> and add a property |
|
|
// not sophisticated at all, easily broken |
|
|
var i=html.indexOf('>'); |
|
|
if (i<0) { return html; } |
|
|
return html.substring(0,i) + ' ' + property + html.substring(i); |
|
|
} |
|
|
|
|
|
function addPopupShortcut(html, key) { |
|
|
if (!getValueOf('popupShortcutKeys')) { return html; } |
|
|
var ret= addLinkProperty(html, 'popupkey="'+key+'"'); |
|
|
if (key==' ') { key=popupString('spacebar'); } |
|
|
return ret.replace(RegExp('^(.*?)(title=")(.*?)(".*)$', 'i'),'$1$2$3 $4'); |
|
|
} |
|
|
//</NOLITE> |
|
|
// ENDFILE: shortcutkeys.js |
|
|
// STARTFILE: diffpreview.js |
|
|
//<NOLITE> |
|
|
function loadDiff(article, oldid, diff, navpop) { |
|
|
navpop.diffData={}; |
|
|
var oldRev, newRev; |
|
|
switch (diff) { |
|
|
case 'cur': |
|
|
switch ( oldid ) { |
|
|
case null: |
|
|
case '': |
|
|
case 'prev': |
|
|
// eg newmessages diff link |
|
|
oldRev='0&direction=prev'; |
|
|
newRev=0; |
|
|
break; |
|
|
default: |
|
|
oldRev = oldid; |
|
|
newRev = 0; |
|
|
} |
|
|
break; |
|
|
case 'prev': |
|
|
oldRev = ( oldid || 0 ) + '&direction=prev'; newRev = oldid; break; |
|
|
case 'next': |
|
|
oldRev = oldid; newRev = oldid + '&direction=next'; |
|
|
break; |
|
|
default: |
|
|
oldRev = oldid || 0; newRev = diff || 0; break; |
|
|
} |
|
|
oldRev = oldRev || 0; |
|
|
newRev = newRev || 0; |
|
|
|
|
|
pendingNavpopTask(navpop); |
|
|
getWiki(article, doneDiffNew, newRev, navpop); |
|
|
|
|
|
pendingNavpopTask(navpop); |
|
|
getWiki(article, doneDiffOld, oldRev, navpop); |
|
|
} |
|
|
|
|
|
function doneDiff(download, isOld) { |
|
|
if (!download.owner || !download.owner.diffData) { return; } |
|
|
var navpop=download.owner; |
|
|
var label= (isOld) ? 'Old' : 'New'; |
|
|
var otherLabel=(isOld) ? 'New' : 'Old'; |
|
|
navpop.diffData=download; |
|
|
completedNavpopTask(download.owner); |
|
|
if (navpop.diffData) { insertDiff(navpop); } |
|
|
} |
|
|
|
|
|
function doneDiffNew(download) { doneDiff(download, false); } |
|
|
function doneDiffOld(download) { doneDiff(download, true); } |
|
|
|
|
|
function rmBoringLines(a,b,context) { |
|
|
|
|
|
if (typeof context == 'undefined') { context=2; } |
|
|
// this is fairly slow... i think it's quicker than doing a word-based diff from the off, though |
|
|
var aa=, aaa=; |
|
|
var bb=, bbb=; |
|
|
var i, j; |
|
|
|
|
|
// first, gather all disconnected nodes in a and all crossing nodes in a and b |
|
|
for (i=0; i<a.length; ++i ) { |
|
|
if(!a.row || a.row===0) { aa=1; } |
|
|
else if (countCrossings(b,a,i, true)) { |
|
|
aa=1; |
|
|
bb.row ] = 1; |
|
|
} |
|
|
} |
|
|
|
|
|
// pick up remaining disconnected nodes in b |
|
|
for (i=0; i<b.length; ++i ) { |
|
|
if (bb==1) { continue; } |
|
|
if(!b.row || b.row===0) { bb=1; } |
|
|
} |
|
|
|
|
|
// another pass to gather context: we want the neighbours of included nodes which are not yet included |
|
|
// we have to add in partners of these nodes, but we don't want to add context for *those* nodes in the next pass |
|
|
for (i=0; i<b.length; ++i) { |
|
|
if ( bb == 1 ) { |
|
|
for (j=max(0,i-context); j < min(b.length, i+context); ++j) { |
|
|
if ( !bb ) { bb = 1; aa.row ] = 0.5; } |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
for (i=0; i<a.length; ++i) { |
|
|
if ( aa == 1 ) { |
|
|
for (j=max(0,i-context); j < min(a.length, i+context); ++j) { |
|
|
if ( !aa ) { aa = 1; bb.row ] = 0.5; } |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
for (i=0; i<bb.length; ++i) { |
|
|
if (bb > 0) { // it's a row we need |
|
|
if (b.row || b.row===0) { bbb.push(b.text); } // joined; partner should be in aa |
|
|
else { |
|
|
bbb.push(b); |
|
|
} |
|
|
} |
|
|
} |
|
|
for (i=0; i<aa.length; ++i) { |
|
|
if (aa > 0) { // it's a row we need |
|
|
if (a.row || a.row===0) { aaa.push(a.text); } // joined; partner should be in aa |
|
|
else { |
|
|
aaa.push(a); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return { a: aaa, b: bbb}; |
|
|
} |
|
|
|
|
|
function stripOuterCommonLines(a,b,context) { |
|
|
var i=0; |
|
|
while (i<a.length && i < b.length && a==b) { ++i; } |
|
|
var j=a.length-1; var k=b.length-1; |
|
|
while ( j>=0 && k>=0 && a==b ) { --j; --k; } |
|
|
|
|
|
return { a: a.slice(max(0,i - 1 - context), min(a.length+1, j + context+1)), |
|
|
b: b.slice(max(0,i - 1 - context), min(b.length+1, k + context+1)) }; |
|
|
} |
|
|
|
|
|
function insertDiff(navpop) { |
|
|
// for speed reasons, we first do a line-based diff, discard stuff that seems boring, then do a word-based diff |
|
|
// FIXME: sometimes this gives misleading diffs as distant chunks are squashed together |
|
|
var oldlines=navpop.diffData.Old.data.split('\n'); |
|
|
var newlines=navpop.diffData.New.data.split('\n'); |
|
|
getValueOf('popupDiffContextLines'); |
|
|
var inner=stripOuterCommonLines(oldlines,newlines,pg.option.popupDiffContextLines); |
|
|
oldlines=inner.a; newlines=inner.b; |
|
|
var truncated=false; |
|
|
getValueOf('popupDiffMaxLines'); |
|
|
if (oldlines.length > pg.option.popupDiffMaxLines || newlines.length > pg.option.popupDiffMaxLines) { |
|
|
// truncate |
|
|
truncated=true; |
|
|
inner=stripOuterCommonLines(oldlines.slice(0,pg.option.popupDiffMaxLines), |
|
|
newlines.slice(0,pg.option.popupDiffMaxLines), |
|
|
pg.option.popupDiffContextLines); |
|
|
oldlines=inner.a; newlines=inner.b; |
|
|
} |
|
|
|
|
|
var lineDiff=diff(oldlines, newlines); |
|
|
var lines2=rmBoringLines(lineDiff.o, lineDiff.n); |
|
|
var oldlines2=lines2.a; var newlines2=lines2.b; |
|
|
|
|
|
var simpleSplit = (String.prototype.parenSplit.toString().indexOf('native code')==-1); |
|
|
var html='<hr>'; |
|
|
if (getValueOf('popupDiffDates')) { |
|
|
html += diffDatesTable(navpop.diffData.Old, navpop.diffData.New); |
|
|
html += '<hr>'; |
|
|
} |
|
|
html += shortenDiffString( |
|
|
diffString(oldlines2.join('\n'), newlines2.join('\n'), simpleSplit), |
|
|
getValueOf('popupDiffContextCharacters') ).join('<hr>'); |
|
|
setPopupTipsAndHTML(html.split('\n').join('<br>') + |
|
|
(truncated ? '<hr><b>'+popupString('Diff truncated for performance reasons')+'</b>' : '') , |
|
|
'popupPreview'); |
|
|
} |
|
|
|
|
|
function diffDatesTable( oldDl, newDl ) { |
|
|
var html='<table class="popup_diff_dates">'; |
|
|
html += diffDatesTableRow( newDl, tprintf('New revision')); |
|
|
html += diffDatesTableRow( oldDl, tprintf('Old revision')); |
|
|
html += '</table>'; |
|
|
return html; |
|
|
} |
|
|
function diffDatesTableRow( dl, label ) { |
|
|
var txt=''; |
|
|
if (!dl) { |
|
|
txt=popupString('Something went wrong :-('); |
|
|
} else if (!dl.lastModified) { |
|
|
txt= (/^\s*$/.test(dl.data)) ? |
|
|
popupString('Empty revision, maybe non-existent') : popupString('Unknown date'); |
|
|
} else { |
|
|
var datePrint=getValueOf('popupDiffDatePrinter'); |
|
|
if (typeof dl.lastModified == 'function') { |
|
|
txt = dl.lastModified(); |
|
|
} else { |
|
|
txt = tprintf('Invalid %s %s', ); |
|
|
} |
|
|
} |
|
|
var revlink = generalLink({url: dl.url.replace(/&.*?(oldid=+).*/, '&$1'), |
|
|
text: label, title: label}); |
|
|
return simplePrintf('<tr><td>%s</td><td>%s</td></tr>', ); |
|
|
} |
|
|
//</NOLITE> |
|
|
// ENDFILE: diffpreview.js |
|
|
// STARTFILE: links.js |
|
|
//<NOLITE> |
|
|
///////////////////// |
|
|
// LINK GENERATION // |
|
|
///////////////////// |
|
|
|
|
|
// titledDiffLink --> titledWikiLink --> generalLink |
|
|
// wikiLink --> titledWikiLink --> generalLink |
|
|
// kateLink --> generalLink |
|
|
|
|
|
function titledDiffLink(l) { // article, text, title, from, to) { |
|
|
return titledWikiLink({article: l.article, action: l.to + '&oldid=' + l.from, |
|
|
newWin: l.newWin, |
|
|
text: l.text, title: l.title, |
|
|
/* hack: no oldid here */ |
|
|
actionName: 'diff'}); |
|
|
} |
|
|
|
|
|
|
|
|
function wikiLink(l) { |
|
|
//{article:article, action:action, text:text, oldid}) { |
|
|
if (! (typeof l.article == typeof {} |
|
|
&& typeof l.action == typeof '' && typeof l.text==typeof '')) return null; |
|
|
if (typeof l.oldid == 'undefined') l.oldid=null; |
|
|
if (!/^((edit|view|revert|render)$|(raw))/.test(l.action)) { l.oldid=null; } |
|
|
var hint=popupString(l.action + 'Hint'); // revertHint etc etc etc |
|
|
var oldidData=; |
|
|
var revisionString = tprintf('revision %s of %s', oldidData); |
|
|
log('revisionString='+revisionString); |
|
|
switch (l.action) { |
|
|
case 'edit§ion=new': hint = popupString('newSectionHint'); break; |
|
|
case 'raw&ctype=text/css': hint=popupString('rawHint'); break; |
|
|
case 'revert': |
|
|
var p=parseParams(pg.current.link.href); |
|
|
l.action='edit&autoclick=wpSave&autosummary=' + revertSummary(l.oldid, p.diff); |
|
|
if (p.diff=='prev') { |
|
|
l.action += '&direction=prev'; |
|
|
revisionString = tprintf('the revision prior to revision %s of %s', oldidData); |
|
|
} |
|
|
if (getValueOf('popupRevertSummaryPrompt')) { l.action += '&autosummaryprompt=true'; } |
|
|
log('revisionString is now '+revisionString); |
|
|
break; |
|
|
case 'nullEdit': |
|
|
l.action='edit&autoclick=wpSave&autosummary=null'; |
|
|
break; |
|
|
case 'historyfeed': |
|
|
l.action='history&feed=rss'; |
|
|
break; |
|
|
} |
|
|
|
|
|
if (hint) { |
|
|
if (l.oldid) { |
|
|
hint = simplePrintf(hint, ); |
|
|
} |
|
|
else { |
|
|
hint = simplePrintf(hint, ); |
|
|
} |
|
|
} |
|
|
else { |
|
|
hint = safeDecodeURI(l.article + '&action=' + l.action) + (l.oldid) ? '&oldid='+l.oldid : ''; |
|
|
} |
|
|
|
|
|
return titledWikiLink({article: l.article, action: l.action, text: l.text, newWin:l.newWin, |
|
|
title: hint, oldid: l.oldid}); |
|
|
} |
|
|
|
|
|
function revertSummary(oldid, diff) { |
|
|
if (getValueOf('popupUseQueryInterface')) { return revertSummaryQueried(oldid, diff); } |
|
|
if (typeof getValueOf('popupTimeOffset') == 'number' && /action=history/.test(document.location.href)) { |
|
|
var links=document.links; |
|
|
var numlinks=links.length; |
|
|
var date=null, editor=null; |
|
|
var dateRe = RegExp('^({2}:{2}),\s*(.*{3})$'); |
|
|
for (var i=0; i<numlinks-1; ++i) { |
|
|
if (RegExp('oldid='+oldid).test(links.href) && dateRe.test(links.innerHTML)) { |
|
|
// help konqueror out by putting the time at the end |
|
|
var ds = links.innerHTML.replace(dateRe, '$2 $1'); |
|
|
// d is parsed according to current locale, in FF at least |
|
|
// this is only OK if the user has set the time offset in prefs! |
|
|
var d=new Date(+(new Date(ds)) - 1000*3600*getValueOf('popupTimeOffset')); |
|
|
// strip out zero seconds and timezone in date, since they're prolly bogus |
|
|
date=d.toString().replace(/({2}:{2}):00.*/, '$1'); |
|
|
editor=Title.fromURL(links.href).userName(); |
|
|
break; |
|
|
} |
|
|
} |
|
|
if (date && editor && diff != 'prev') { |
|
|
return simplePrintf(getValueOf('popupExtendedRevertSummary'), ); |
|
|
} |
|
|
} |
|
|
if (diff != 'prev') { |
|
|
return simplePrintf(getValueOf('popupRevertSummary'), ); |
|
|
} |
|
|
return simplePrintf(getValueOf('popupRevertToPreviousSummary'), ); |
|
|
} |
|
|
|
|
|
function revertSummaryQueried(oldid, diff) { |
|
|
var ret=''; |
|
|
if (diff == 'prev') { |
|
|
ret=getValueOf('popupQueriedRevertToPreviousSummary'); |
|
|
} else { ret = getValueOf('popupQueriedRevertSummary'); } |
|
|
return ret + '&autorv=' + oldid; |
|
|
} |
|
|
|
|
|
function titledWikiLink(l) { |
|
|
// possible properties of argument: |
|
|
// article, action, text, title, oldid, actionName, className |
|
|
// oldid = null is fine here |
|
|
|
|
|
// article and action are mandatory args |
|
|
|
|
|
if (typeof l.article == 'undefined' || typeof l.action=='undefined') { |
|
|
errlog('got undefined article or actino in titledWikiLink'); |
|
|
return null; |
|
|
} |
|
|
|
|
|
var base = pg.wiki.titlebase + l.article.urlString(); |
|
|
var url=base; |
|
|
|
|
|
if (typeof l.actionName=='undefined' || !l.actionName) { l.actionName='action'; } |
|
|
|
|
|
// no need to add &action=view, and this confuses anchors |
|
|
if (l.action != 'view') { url = base + '&' + l.actionName + '=' + l.action; } |
|
|
|
|
|
if (typeof l.oldid!='undefined' && l.oldid) { url+='&oldid='+l.oldid; } |
|
|
|
|
|
var cssClass=pg.misc.defaultNavlinkClassname; |
|
|
if (typeof l.className!='undefined' && l.className) { cssClass=l.className; } |
|
|
|
|
|
return generalNavLink({url: url, newWin: l.newWin, |
|
|
title: (typeof l.title != 'undefined') ? l.title : null, |
|
|
text: (typeof l.text!='undefined')?l.text:null, |
|
|
className: cssClass}); |
|
|
} |
|
|
|
|
|
function getLastContrib(wikipage, newWin) { |
|
|
getHistoryInfo(wikipage, function(x){processLastContribInfo(x,{page: wikipage, newWin: newWin})}); |
|
|
} |
|
|
function processLastContribInfo(info, stuff) { |
|
|
if(!info.edits || !info.edits.length) { alert('Popups: an odd thing happened. Please retry.'); return; } |
|
|
if(!info.firstNewEditor) { |
|
|
alert(tprintf('Only found one editor: %s made %s edits', .editor,info.edits.length])); |
|
|
return; |
|
|
} |
|
|
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+info.firstNewEditor.oldid; |
|
|
displayUrl(newUrl, stuff.newWin); |
|
|
} |
|
|
function getDiffSinceMyEdit(wikipage, newWin) { |
|
|
getHistoryInfo(wikipage, function(x){processDiffSinceMyEdit(x,{page: wikipage, newWin: newWin})}); |
|
|
} |
|
|
function processDiffSinceMyEdit(info, stuff) { |
|
|
if(!info.edits || !info.edits.length) { alert('Popups: something fishy happened. Please try again.'); return; } |
|
|
var friendlyName=stuff.page.split('_').join(' '); |
|
|
if(!info.myLastEdit) { |
|
|
alert(tprintf('Couldn\'t find an edit by %s\nin the last %s edits to\n%s', |
|
|
)); |
|
|
return; |
|
|
} |
|
|
if(info.myLastEdit.index==0) { |
|
|
alert(tprintf("%s seems to be the last editor to the page %s", )); |
|
|
return; |
|
|
} |
|
|
var newUrl=pg.wiki.titlebase + new Title(stuff.page).urlString() + '&diff=cur&oldid='+ info.myLastEdit.oldid; |
|
|
displayUrl(newUrl, stuff.newWin); |
|
|
} |
|
|
function displayUrl(url, newWin){ |
|
|
if(newWin) { window.open(url); } |
|
|
else { document.location=url; } |
|
|
} |
|
|
|
|
|
function purgePopups() { |
|
|
processAllPopups(true); |
|
|
setupCache(); // deletes all cached items (not browser cached, though...) |
|
|
pg.option={}; |
|
|
abortAllDownloads(); |
|
|
} |
|
|
|
|
|
function processAllPopups(nullify, banish) { |
|
|
for (var i=0; i<pg.current.links.length; ++i) { |
|
|
if (!pg.current.links.navpopup) { continue; } |
|
|
(nullify || banish) && pg.current.links.navpopup.banish(); |
|
|
nullify && (pg.current.links.navpopup=null); |
|
|
} |
|
|
} |
|
|
|
|
|
function disablePopups(){ |
|
|
processAllPopups(false, true); |
|
|
setupTooltips(null, true); |
|
|
} |
|
|
|
|
|
function togglePreviews() { |
|
|
processAllPopups(true, true); |
|
|
pg.option.simplePopups=!pg.option.simplePopups; |
|
|
abortAllDownloads(); |
|
|
} |
|
|
|
|
|
function magicHistoryLink(l) { |
|
|
// FIXME use onclick change href trick to sort this out instead of window.open |
|
|
|
|
|
var jsUrl='', title=''; |
|
|
switch(l.id) { |
|
|
case 'lastContrib': |
|
|
jsUrl=simplePrintf('javascript:getLastContrib(\'%s\',%s)', |
|
|
); |
|
|
title=popupString('lastContribHint'); |
|
|
break; |
|
|
case 'sinceMe': |
|
|
jsUrl=simplePrintf('javascript:getDiffSinceMyEdit(\'%s\',%s)', |
|
|
); |
|
|
title=popupString('sinceMeHint'); |
|
|
break; |
|
|
} |
|
|
|
|
|
return generalNavLink({url: jsUrl, newWin: false, // can't have new windows with JS links, I think |
|
|
title: title, text: l.text}); |
|
|
} |
|
|
|
|
|
function popupMenuLink(l) { |
|
|
var jsUrl=simplePrintf('javascript:%s()', ); |
|
|
var title=popupString(simplePrintf('%sHint', )); |
|
|
return generalNavLink({url: jsUrl, newWin:false, title:title, text:l.text}); |
|
|
} |
|
|
|
|
|
function specialLink(l) { |
|
|
// properties: article, specialpage, text, sep |
|
|
if (typeof l.specialpage=='undefined'||!l.specialpage) return null; |
|
|
var base = pg.wiki.titlebase + pg.ns.special+':'+l.specialpage; |
|
|
if (typeof l.sep == 'undefined' || l.sep===null) l.sep='&target='; |
|
|
var article=l.article.urlString({keepSpaces: l.specialpage=='Search'}); |
|
|
var hint=popupString(l.specialpage+'Hint'); |
|
|
switch (l.specialpage) { |
|
|
case 'Log': |
|
|
switch (l.sep) { |
|
|
case '&user=': hint=popupString('userLogHint'); break; |
|
|
case '&type=block&page=': hint=popupString('blockLogHint'); break; |
|
|
case '&page=': hint=popupString('pageLogHint'); break; |
|
|
case '&type=protect&page=': hint=popupString('protectLogHint'); break; |
|
|
case '&type=delete&page=': hint=popupString('deleteLogHint'); break; |
|
|
default: log('Unknown log type, sep=' + l.sep); hint='Missing hint (FIXME)'; |
|
|
} |
|
|
break; |
|
|
case 'Prefixindex': article += '/'; break; |
|
|
} |
|
|
if (hint) hint = simplePrintf(hint, ); |
|
|
else hint = safeDecodeURI(l.specialpage+':'+l.article) ; |
|
|
|
|
|
var url = base + l.sep + article; |
|
|
return generalNavLink({url: url, title: hint, text: l.text, newWin:l.newWin}); |
|
|
} |
|
|
|
|
|
function generalLink(l) { |
|
|
// l.url, l.text, l.title, l.newWin, l.className |
|
|
|
|
|
if (typeof l.url=='undefined') return null; |
|
|
|
|
|
// only quotation marks in the url can screw us up now... I think |
|
|
var url=l.url.split('"').join('%22'); |
|
|
|
|
|
var ret='<a href="' + url + '"'; |
|
|
if (typeof l.title!='undefined' && l.title) ret += ' title="' + l.title + '"'; |
|
|
var newWin; |
|
|
if (typeof l.newWin=='undefined' || l.newWin===null) newWin=getValueOf('popupNewWindows'); |
|
|
else newWin=l.newWin; |
|
|
if (newWin) ret += ' target="_blank"'; |
|
|
if (typeof l.className!='undefined'&&l.className) ret+=' class="'+l.className+'"'; |
|
|
ret += '>'; |
|
|
if (typeof l.text==typeof '') ret+= l.text; |
|
|
ret +='</a>'; |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function appendParamsToLink(linkstr, params) { |
|
|
var sp=linkstr.parenSplit(RegExp('(href="+?)"', 'i')); |
|
|
if (sp.length<2) return null; |
|
|
var ret=sp.shift() + sp.shift(); |
|
|
ret += '&' + params + '"'; |
|
|
ret += sp.join(''); |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function changeLinkTargetLink(x) { // newTarget, text, hint, summary, clickButton, minor) { |
|
|
if (x.newTarget) { |
|
|
log ('changeLinkTargetLink: newTarget=' + x.newTarget); |
|
|
} |
|
|
// optional: oldTarget (in wikitext) |
|
|
// if x.newTarget omitted or null, remove the link |
|
|
|
|
|
// escape '&' and other nasties |
|
|
if(x.newTarget) { |
|
|
x.newTarget=encodeURI(x.newTarget); |
|
|
log('changeLinkTargetLink: newTarget encoded to ' + x.newTarget); |
|
|
} |
|
|
//x.text=encodeURI(x.text); // this buggers things up on zh.wikipedia.org and doesn't seem necessary |
|
|
x.clickButton=encodeURI(x.clickButton); |
|
|
|
|
|
// this'll break if charAt(0) is nasty |
|
|
if (typeof x.oldTarget != typeof '') x.oldTarget=safeDecodeURI(pg.current.article); |
|
|
var cA=literalizeRegex(x.oldTarget); |
|
|
var chs=cA.charAt(0).toUpperCase(); |
|
|
chs=''; |
|
|
var currentArticleRegexBit=chs+cA.substring(1); |
|
|
currentArticleRegexBit=currentArticleRegexBit |
|
|
.split(RegExp('+', 'g')).join('+') |
|
|
.split('\\(').join('(?:%2528|\\()') |
|
|
.split('\\)').join('(?:%2529|\\))'); |
|
|
currentArticleRegexBit = '\\s*(' + currentArticleRegexBit + ')\\s*'; |
|
|
// e.g. Computer (archaic) -> \s*(omputer(?:%2528|\()archaic(?:%2528|\)))\s* |
|
|
|
|
|
// autoedit=s~\ad)\]\]~]~g;s~\AD)~[[Computer-aided%20design|~g |
|
|
|
|
|
// get the page to edit from the title |
|
|
try { |
|
|
//var title=document.getElementsByTagName('h1').innerHTML.replace(RegExp(' ', 'g'), '_'); |
|
|
var title=document.title.split(' - '); |
|
|
title=''; |
|
|
title=title.join(' - ').replace(/ - $/, ''); |
|
|
} catch (err) { return; } |
|
|
|
|
|
var lk=titledWikiLink({article: new Title(title), newWin:x.newWin, |
|
|
action: 'edit', |
|
|
text: x.text, |
|
|
title: x.hint, |
|
|
className: 'popup_change_title_link' |
|
|
}); |
|
|
var cmd=''; |
|
|
if (x.newTarget) { |
|
|
cmd +='s~\\\\]~]~g;'; |
|
|
cmd += 's~\\~[['+x.newTarget+'|~g'; |
|
|
} else { |
|
|
cmd += 's~\\\\]~$1~g;'; |
|
|
cmd += 's~\\(.*?)\\]\\]~$2~g'; |
|
|
} |
|
|
cmd += '&autoclick='+x.clickButton; |
|
|
cmd += ( x.minor == null ) ? '' : '&autominor='+x.minor; |
|
|
cmd += ( x.watch == null ) ? '' : '&autowatch='+x.watch; |
|
|
cmd += '&autosummary='+x.summary; |
|
|
return appendParamsToLink(lk, 'autoedit='+cmd); |
|
|
} |
|
|
|
|
|
|
|
|
function redirLink(redirMatch) { |
|
|
// NB redirMatch is in wikiText |
|
|
var ret=''; |
|
|
|
|
|
if (getValueOf('popupAppendRedirNavLinks') && getValueOf('popupNavLinks')) { |
|
|
ret += '<hr>'; |
|
|
if (getValueOf('popupFixRedirs') && typeof autoEdit != 'undefined' && autoEdit) { |
|
|
log('redirLink: newTarget=' + redirMatch); |
|
|
ret += addPopupShortcut( |
|
|
changeLinkTargetLink( |
|
|
{newTarget: redirMatch, text: popupString('Redirects'), |
|
|
hint: popupString('Fix this redirect'), |
|
|
summary: simplePrintf(getValueOf('popupFixRedirsSummary'), |
|
|
), |
|
|
clickButton: getValueOf('popupRedirAutoClick'), minor: true, |
|
|
watch: getValueOf('popupWatchRedirredPages')}) |
|
|
, 'R'); |
|
|
ret += popupString(' to '); |
|
|
} |
|
|
else ret += popupString('Redirects') + popupString(' to '); |
|
|
return ret; |
|
|
} |
|
|
|
|
|
else return '<br> ' + popupString('Redirects') + popupString(' to ') + |
|
|
titledWikiLink({article: new Title().fromWikiText(redirMatch), action: 'view', /* FIXME: newWin */ |
|
|
text: safeDecodeURI(redirMatch), title: popupString('Bypass redirect')}); |
|
|
} |
|
|
|
|
|
function arinLink(l) { |
|
|
if (!saneLinkCheck(l)) { return null; } |
|
|
if ( ! l.article.isIpUser() || ! pg.wiki.wikimedia) return null; |
|
|
|
|
|
var uN=safeDecodeURI(l.article.userName()); |
|
|
|
|
|
return generalNavLink({url:'http://ws.arin.net/cgi-bin/whois.pl?queryinput=' + uN, newWin:l.newWin, |
|
|
title: tprintf('Look up %s in ARIN whois database', ), |
|
|
text: l.text}); |
|
|
} |
|
|
|
|
|
function toolDbName(cookieStyle) { |
|
|
var ret=null; |
|
|
var theWiki=pg.wiki.hostname.split('.'); |
|
|
if (pg.wiki.hostname==pg.wiki.commons) { |
|
|
ret = 'commonswiki'; |
|
|
} else { |
|
|
switch(theWiki) { |
|
|
case 'wikipedia': |
|
|
ret = pg.wiki.lang + 'wiki'; |
|
|
break; |
|
|
default: |
|
|
ret = pg.wiki.lang + theWiki; |
|
|
break; |
|
|
} |
|
|
} |
|
|
if (!cookieStyle) { ret+= '_p'; } |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function saneLinkCheck(l) { |
|
|
if (typeof l.article != typeof {} || typeof l.text != typeof '') { return false; } |
|
|
return true; |
|
|
} |
|
|
function kateLink(l) { |
|
|
if(!saneLinkCheck(l)) return null; |
|
|
if (! pg.wiki.wikimedia) return null; |
|
|
var uN=safeDecodeURI(l.article.userName()); |
|
|
var tool=getValueOf('popupEditCounterTool'); |
|
|
var url; |
|
|
var defaultToolUrl='http://tools.wikimedia.de/~$3/cgi-bin/count_edits?dbname=$2&user=$1'; |
|
|
|
|
|
switch(tool) { |
|
|
case 'custom': |
|
|
url=simplePrintf(getValueOf('popupEditCounterUrl'), ); |
|
|
break; |
|
|
default: |
|
|
url=simplePrintf(defaultToolUrl, ); |
|
|
} |
|
|
return generalNavLink({url:url, title: tprintf('katelinkHint', ), newWin:l.newWin, text: l.text}); |
|
|
} |
|
|
|
|
|
|
|
|
function contribsTreeLink(l) { |
|
|
if(!saneLinkCheck(l)) return null; |
|
|
if (! pg.wiki.wikimedia) return null; |
|
|
var uN=safeDecodeURI(l.article.userName()); |
|
|
|
|
|
var url='http://tools.wikimedia.de/~interiot/cgi-bin/contribution_tree?dbname='; |
|
|
url += toolDbName() + '&user='+ uN; |
|
|
|
|
|
return generalNavLink({url:url, title: tprintf('contribsTreeHint', ), newWin:l.newWin, text: l.text}); |
|
|
} |
|
|
|
|
|
function globalSearchLink(l) { |
|
|
if(!saneLinkCheck(l)) return null; |
|
|
|
|
|
var base='http://vs.aka-online.de/cgi-bin/globalwpsearch.pl?timeout=120&search='; |
|
|
var article=l.article.urlString({keepSpaces:true}); |
|
|
|
|
|
return generalNavLink({url:base + article, newWin:l.newWin, |
|
|
title: tprintf('globalSearchHint', ), |
|
|
text: l.text}); |
|
|
} |
|
|
|
|
|
function googleLink(l) { |
|
|
if(!saneLinkCheck(l)) return null; |
|
|
|
|
|
var base='http://www.google.com/search?q='; |
|
|
var article=l.article.urlString({keepSpaces:true}); |
|
|
|
|
|
return generalNavLink({url:base + '%22' + article + '%22', newWin:l.newWin, |
|
|
title: tprintf('googleSearchHint', ), |
|
|
text: l.text}); |
|
|
} |
|
|
|
|
|
function editorListLink(l) { |
|
|
if(!saneLinkCheck(l)) return null; |
|
|
var article= l.article.articleFromTalkPage() || l.article; |
|
|
var base='http://tools.wikimedia.de/~tim/counter/?page='; |
|
|
return generalNavLink({url:base+article.urlString(), |
|
|
title: tprintf('editorListHint', ), newWin:l.newWin, text: l.text}); |
|
|
} |
|
|
|
|
|
function generalNavLink(l) { |
|
|
l.className = (l.className==null) ? 'popupNavLink' : l.className; |
|
|
return generalLink(l); |
|
|
} |
|
|
|
|
|
////////////////////////////////////////////////// |
|
|
// magic history links |
|
|
// |
|
|
|
|
|
function getHistoryInfo(wikipage, whatNext) { |
|
|
log('getHistoryInfo'); |
|
|
getHistory(wikipage, whatNext ? function(d){whatNext(processHistory(d));} : processHistory); |
|
|
} |
|
|
|
|
|
function getHistory(wikipage, onComplete) { |
|
|
log('getHistory'); |
|
|
var url; |
|
|
if (getValueOf('popupUseQueryInterface')) { |
|
|
url = pg.wiki.wikibase + '/query.php?format=json&what=revisions&titles=' + |
|
|
new Title(wikipage).urlString() + '&rvlimit=' + getValueOf('popupHistoryLimit'); |
|
|
} else { |
|
|
url = pg.wiki.titlebase + new Title(wikipage).urlString() + |
|
|
'&action=history' + '&limit=' + getValueOf('popupHistoryLimit'); |
|
|
} |
|
|
log('getHistory: url='+url); |
|
|
return startDownload(url, pg.idNumber+'history', onComplete); |
|
|
} |
|
|
|
|
|
function processHistory(download) { |
|
|
if (getValueOf('popupUseQueryInterface')) { |
|
|
return processHistoryQuery(download); |
|
|
} |
|
|
return processHistoryScreenScrape(download); |
|
|
} |
|
|
|
|
|
function processHistoryScreenScrape(download) { |
|
|
// screen scrape alert |
|
|
|
|
|
var d=download.data; |
|
|
// pg.misc.data=d; // for debugging |
|
|
|
|
|
var edits=; |
|
|
var split=d.split('<li>'); |
|
|
|
|
|
for (var i=0; i<split.length; ++i) { |
|
|
var histRe=RegExp( |
|
|
'^.*?type="radio" value="(*)".*?class=\'history-user\'>' + |
|
|
'<a href="(/User:|/search/title=(Special:Contributions&target=|User:))(*)'); |
|
|
var match=histRe.exec(split); |
|
|
if (match) { |
|
|
edits.push({ oldid: match, editor: match }); |
|
|
} |
|
|
} |
|
|
return finishProcessHistory(edits); |
|
|
} |
|
|
|
|
|
function processHistoryQuery(download) { |
|
|
log ('processHistoryQuery'); |
|
|
var d=download.data; |
|
|
log('d='+d); |
|
|
var jsobj; |
|
|
try { |
|
|
log('trying to parse JSON'); |
|
|
log('jsobj=' + d); |
|
|
eval('jsobj=' + d); |
|
|
log('done eval; jsobj='+jsobj); |
|
|
window.x=jsobj; |
|
|
var p=jsobj; |
|
|
log('got p'); |
|
|
for (var pageid in p) { |
|
|
var revisions=p; |
|
|
// we only get the first one |
|
|
break; |
|
|
} |
|
|
log('got revisions'); |
|
|
log(revisions.length +' of them'); |
|
|
} catch (someError) { |
|
|
log('Something went wrong with JSON business'); |
|
|
return finishProcessHistory(); |
|
|
} |
|
|
var edits=; |
|
|
for (var i=0; i<revisions.length; ++i) { |
|
|
edits.push({ oldid: revisions, editor: revisions }); |
|
|
} |
|
|
log('processed ' + edits.length + ' edits'); |
|
|
return finishProcessHistory(edits); |
|
|
} |
|
|
|
|
|
|
|
|
function finishProcessHistory(edits) { |
|
|
var histInfo={}; |
|
|
|
|
|
histInfo.edits=edits; |
|
|
var userName=getValueOf('popupUserName') || Cookie.read(pg.wiki.userNameCookie).split('+').join('_'); |
|
|
histInfo.userName=userName; |
|
|
|
|
|
for (var i=0; i<edits.length; ++i) { |
|
|
if (typeof histInfo.myLastEdit == 'undefined' && userName && edits.editor==userName) { |
|
|
histInfo.myLastEdit={index: i, oldid: edits.oldid, previd: (i==0 ? null : edits.oldid)}; |
|
|
} |
|
|
if (typeof histInfo.firstNewEditor == 'undefined' && edits.editor != edits.editor) { |
|
|
histInfo.firstNewEditor={index:i, oldid:edits.oldid, previd: (i==0 ? null : edits.oldid)}; |
|
|
} |
|
|
} |
|
|
//pg.misc.historyInfo=histInfo; |
|
|
return histInfo; |
|
|
} |
|
|
//</NOLITE> |
|
|
// ENDFILE: links.js |
|
|
// STARTFILE: options.js |
|
|
////////////////////////////////////////////////// |
|
|
// options |
|
|
|
|
|
// check for cookies and existing value, else use default |
|
|
function defaultize(x) { |
|
|
var val=null; |
|
|
if (x!='popupCookies') { |
|
|
defaultize('popupCookies'); |
|
|
if (pg.option.popupCookies && (val=Cookie.read(x))) { |
|
|
pg.option=val; |
|
|
return; |
|
|
} |
|
|
} |
|
|
if (pg.option===null || typeof pg.option=='undefined') { |
|
|
if (typeof window != 'undefined' ) pg.option=window; |
|
|
else pg.option=pg.optionDefault; |
|
|
} |
|
|
} |
|
|
|
|
|
function newOption(x, def) { |
|
|
pg.optionDefault=def; |
|
|
} |
|
|
|
|
|
function setDefault(x, def) { |
|
|
return newOption(x, def); |
|
|
} |
|
|
|
|
|
function getValueOf(varName) { |
|
|
defaultize(varName); |
|
|
return pg.option; |
|
|
} |
|
|
|
|
|
function useDefaultOptions() { // for testing |
|
|
for (var p in pg.optionDefault) { |
|
|
pg.option=pg.optionDefault; |
|
|
if (typeof window!='undefined') { delete window; } |
|
|
} |
|
|
} |
|
|
|
|
|
function setOptions() { |
|
|
// user-settable parameters and defaults |
|
|
|
|
|
// Basic options |
|
|
newOption('popupDelay', 0.5); |
|
|
newOption('popupHideDelay', 0.5); |
|
|
newOption('simplePopups', false); |
|
|
newOption('popupStructure', 'shortmenus'); // see later - default for popupStructure is 'original' if simplePopups is true |
|
|
newOption('popupActionsMenu', true); |
|
|
newOption('popupSetupMenu', true); |
|
|
newOption('popupAdminLinks', false); |
|
|
newOption('popupShortcutKeys', false); |
|
|
newOption('popupHistoricalLinks', true); |
|
|
newOption('popupOnlyArticleLinks', true); |
|
|
newOption('removeTitles', true); |
|
|
newOption('popupMaxWidth', 350); |
|
|
newOption('popupInitialWidth', false); // integer or false |
|
|
newOption('popupSimplifyMainLink', true); |
|
|
newOption('popupAppendRedirNavLinks', true); |
|
|
newOption('popupTocLinks', false); |
|
|
newOption('popupSubpopups', true); |
|
|
newOption('popupUserName', ''); // should be magically detected with cookies if this isn't set |
|
|
|
|
|
//<NOLITE> |
|
|
// images |
|
|
newOption('popupImages', true); |
|
|
newOption('imagePopupsForImages', true); |
|
|
newOption('popupNeverGetThumbs', false); |
|
|
newOption('popupImagesFromThisWikiOnly', false); |
|
|
newOption('popupMinImageWidth', 50); |
|
|
newOption('popupLoadImagesSequentially', false); |
|
|
newOption('popupImagesToggleSize', true); |
|
|
newOption('popupImageSize', 60); |
|
|
|
|
|
// redirs, dabs, reversion |
|
|
newOption('popupFixRedirs', false); |
|
|
newOption('popupRedirAutoClick', 'wpDiff'); |
|
|
newOption('popupFixDabs', false); |
|
|
newOption('popupRevertSummaryPrompt', false); |
|
|
newOption('popupRedlinkRemoval', false); |
|
|
newOption('popupWatchDisambiggedPages', null); |
|
|
newOption('popupWatchRedirredPages', null); |
|
|
|
|
|
// navlinks |
|
|
newOption('popupNavLinks', true); |
|
|
newOption('popupNavLinkSeparator', ' ⋅ '); |
|
|
newOption('popupLastEditLink', true); |
|
|
newOption('popupEditCounterTool', 'interiot'); |
|
|
newOption('popupEditCounterUrl', ''); |
|
|
newOption('popupExtraUserMenu', ''); |
|
|
//</NOLITE> |
|
|
|
|
|
// previews etc |
|
|
newOption('popupPreviews', true); |
|
|
newOption('popupSummaryData', true); |
|
|
newOption('popupMaxPreviewSentences', 4); |
|
|
newOption('popupMaxPreviewCharacters', 600); |
|
|
newOption('popupLastModified', true); |
|
|
newOption('popupPreviewKillTemplates', true); |
|
|
newOption('popupPreviewRawTemplates', false); |
|
|
newOption('popupPreviewFirstParOnly', true); |
|
|
newOption('popupPreviewCutHeadings', true); |
|
|
|
|
|
//<NOLITE> |
|
|
// diffs |
|
|
newOption('popupPreviewDiffs', true); |
|
|
newOption('popupDiffMaxLines', 100); |
|
|
newOption('popupDiffContextLines', 2); |
|
|
newOption('popupDiffContextCharacters', 40); |
|
|
newOption('popupDiffDates', true); |
|
|
newOption('popupDiffDatePrinter', 'toLocaleString'); |
|
|
|
|
|
// edit summaries. God, these are ugly. |
|
|
newOption('popupFixDabsSummary', popupString('defaultpopupFixDabsSummary') ); |
|
|
newOption('popupExtendedRevertSummary', popupString('defaultpopupExtendedRevertSummary') ); |
|
|
newOption('popupTimeOffset', null); |
|
|
newOption('popupRevertSummary', popupString('defaultpopupRevertSummary') ); |
|
|
newOption('popupRevertToPreviousSummary', popupString('defaultpopupRevertToPreviousSummary') ); |
|
|
newOption('popupQueriedRevertSummary', popupString('defaultpopupQueriedRevertSummary') ); |
|
|
newOption('popupQueriedRevertToPreviousSummary', popupString('defaultpopupQueriedRevertToPreviousSummary') ); |
|
|
newOption('popupFixRedirsSummary', popupString('defaultpopupFixRedirsSummary') ); |
|
|
newOption('popupRedlinkSummary', popupString('defaultpopupRedlinkSummary') ); |
|
|
newOption('popupRmDabLinkSummary', popupString('defaultpopupRmDabLinkSummary') ); |
|
|
//</NOLITE> |
|
|
// misc |
|
|
newOption('popupLiveOptions', false); |
|
|
newOption('popupLiveOptionsExpanded', false); |
|
|
newOption('popupCookies', false); |
|
|
newOption('popupHistoryLimit', 50); |
|
|
//<NOLITE> |
|
|
newOption('popupFilters', [popupFilterStubDetect, popupFilterDisambigDetect, |
|
|
popupFilterPageSize, popupFilterCountLinks, |
|
|
popupFilterCountImages, popupFilterCountCategories, |
|
|
popupFilterLastModified]); |
|
|
newOption('extraPopupFilters', ); |
|
|
newOption('popupOnEditSelection', true); |
|
|
newOption('popupUseQueryInterface', true); |
|
|
newOption('popupPreviewHistory', true); |
|
|
newOption('popupImageLinks', true); |
|
|
newOption('popupCategoryMembers', true); |
|
|
newOption('popupHistoryPreviewLimit', 25); |
|
|
newOption('popupContribsPreviewLimit',25); |
|
|
//</NOLITE> |
|
|
|
|
|
// new windows |
|
|
newOption('popupNewWindows', false); |
|
|
newOption('popupLinksNewWindow', {'lastContrib': true, 'sinceMe': true}); |
|
|
} |
|
|
// ENDFILE: options.js |
|
|
// STARTFILE: strings.js |
|
|
//<NOLITE> |
|
|
////////////////////////////////////////////////// |
|
|
// Translatable strings |
|
|
////////////////////////////////////////////////// |
|
|
// |
|
|
// See instructions at |
|
|
// http://en.wikipedia.org/Wikipedia:Tools/Navigation_popups/Translation |
|
|
|
|
|
pg.string = { |
|
|
///////////////////////////////////// |
|
|
// summary data, searching etc. |
|
|
///////////////////////////////////// |
|
|
'article': 'article', |
|
|
'category': 'category', |
|
|
'categories': 'categories', |
|
|
'image': 'image', |
|
|
'images': 'images', |
|
|
'stub': 'stub', |
|
|
'Empty page': 'Empty page', |
|
|
'kB': 'kB', |
|
|
'bytes': 'bytes', |
|
|
'day': 'day', |
|
|
'days': 'days', |
|
|
'hour': 'hour', |
|
|
'hours': 'hours', |
|
|
'minute': 'minute', |
|
|
'minutes': 'minutes', |
|
|
'second': 'second', |
|
|
'seconds': 'seconds', |
|
|
'week': 'week', |
|
|
'weeks': 'weeks', |
|
|
'search': 'search', |
|
|
'SearchHint': 'Find English Misplaced Pages articles containing %s', |
|
|
'web': 'web', |
|
|
'global': 'global', |
|
|
'globalSearchHint': 'Search across Wikipedias in different languages for %s', |
|
|
'googleSearchHint': 'Google for %s', |
|
|
///////////////////////////////////// |
|
|
// article-related actions and info |
|
|
// (some actions also apply to user pages) |
|
|
///////////////////////////////////// |
|
|
'actions': 'actions', ///// view articles and view talk |
|
|
'popupsMenu': 'popups', |
|
|
'togglePreviewsHint': 'Toggle preview generation in popups on this page', |
|
|
'toggle previews': 'toggle previews', |
|
|
'show previews': 'enable previews', |
|
|
'disable previews': 'disable previews', |
|
|
'reset': 'reset', |
|
|
'disable': 'disable popups', |
|
|
'disablePopupsHint': 'Disable popups on this page. Reload page to re-enable.', |
|
|
'purgePopupsHint': 'Reset popups, clearing all cached popup data.', |
|
|
'PopupsHint': 'Reset popups, clearing all cached popup data.', |
|
|
'spacebar': 'space', |
|
|
'view': 'view', |
|
|
'view article': 'view article', |
|
|
'viewHint': 'Go to %s', |
|
|
'talk': 'talk', |
|
|
'talk page': 'talk page', |
|
|
'this revision': 'this revision', |
|
|
'revision %s of %s': 'revision %s of %s', |
|
|
'Revision %s of %s': 'Revision %s of %s', |
|
|
'the revision prior to revision %s of %s': 'the revision prior to revision %s of %s', |
|
|
'Toggle image size': 'Click to toggle image size', |
|
|
'del': 'del', ///// delete, protect, move |
|
|
'delete': 'delete', |
|
|
'deleteHint': 'Delete %s', |
|
|
'undeleteShort': 'un', |
|
|
'UndeleteHint': 'Show the deletion history for %s', |
|
|
'protect': 'protect', |
|
|
'protectHint': 'Restrict editing rights to %s', |
|
|
'unprotectShort': 'un', |
|
|
'unprotectHint': 'Allow %s to be edited by anyone again', |
|
|
'move': 'move', |
|
|
'move page': 'move page', |
|
|
'MovepageHint': 'Change the title of %s', |
|
|
'edit': 'edit', ///// edit articles and talk |
|
|
'edit article': 'edit article', |
|
|
'editHint': 'Change the content of %s', |
|
|
'edit talk': 'edit talk', |
|
|
'new': 'new', |
|
|
'new topic': 'new topic', |
|
|
'newSectionHint': 'Start a new section on %s', |
|
|
'null edit': 'null edit', |
|
|
'nullEditHint': 'Submit an edit to %s, making no changes ', |
|
|
'hist': 'hist', ///// history, diffs, editors, related |
|
|
'history': 'history', |
|
|
'historyHint': 'List the changes made to %s', |
|
|
'last': 'last', |
|
|
'lastEdit': 'lastEdit', |
|
|
'show last edit': 'most recent edit', |
|
|
'Show the last edit': 'Show the effects of the most recent change', |
|
|
'lastContrib': 'lastContrib', |
|
|
'last set of edits': 'latest edits', |
|
|
'lastContribHint': 'Show the net effect of changes made by the last editor', |
|
|
'cur': 'cur', |
|
|
'diffCur': 'diffCur', |
|
|
'Show changes since revision %s': 'Show changes since revision %s', |
|
|
'%s old': '%s old', // as in 4 weeks old |
|
|
'oldEdit': 'oldEdit', |
|
|
'purge': 'purge', |
|
|
'purgeHint': 'Demand a fresh copy of %s', |
|
|
'raw': 'source', |
|
|
'rawHint': 'Download the source of %s', |
|
|
'render': 'simple', |
|
|
'renderHint': 'Show a plain HTML version of %s', |
|
|
'Show the edit made to get revision': 'Show the edit made to get revision', |
|
|
'sinceMe': 'sinceMe', |
|
|
'changes since mine': 'diff my edit', |
|
|
'sinceMeHint': 'Show changes since my last edit', |
|
|
'Couldn\'t find an edit by %s\nin the last %s edits to\n%s': 'Couldn\'t find an edit by %s\nin the last %s edits to\n%s', |
|
|
'eds': 'eds', |
|
|
'editors': 'editors', |
|
|
'editorListHint': 'List the users who have edited %s', |
|
|
'related': 'related', |
|
|
'relatedChanges': 'relatedChanges', |
|
|
'related changes': 'related changes', |
|
|
'RecentchangeslinkedHint': 'Show changes in articles related to %s', |
|
|
'editOld': 'editOld', ///// edit old version, or revert |
|
|
'rv': 'rv', |
|
|
'revert': 'revert', |
|
|
'revertHint': 'Revert to %s', |
|
|
'defaultpopupRedlinkSummary': 'Removing link to empty page ] using ]', |
|
|
'defaultpopupFixDabsSummary': 'Disambiguate ] to ] using ]', |
|
|
'defaultpopupFixRedirsSummary': 'Redirect bypass from ] to ] using ]', |
|
|
'defaultpopupExtendedRevertSummary': 'Revert to revision dated %s by %s, oldid %s using ]', |
|
|
'defaultpopupRevertToPreviousSummary': 'Revert to the revision prior to revision %s using ]', |
|
|
'defaultpopupRevertSummary': 'Revert to revision %s using ]', |
|
|
'defaultpopupQueriedRevertToPreviousSummary': 'Revert to the revision prior to revision $1 dated $2 by $3 using ]', |
|
|
'defaultpopupQueriedRevertSummary': 'Revert to revision $1 dated $2 by $3 using ]', |
|
|
'defaultpopupRmDabLinkSummary': 'Remove link to dab page ] using ]', |
|
|
'Redirects': 'Redirects', // as in Redirects to ... |
|
|
' to ': ' to ', // as in Redirects to ... |
|
|
'Bypass redirect': 'Bypass redirect', |
|
|
'Fix this redirect': 'Fix this redirect', |
|
|
'disambig': 'disambig', ///// add or remove dab etc. |
|
|
'disambigHint': 'Disambiguate this link to ]', |
|
|
'Click to disambiguate this link to:': 'Click to disambiguate this link to:', |
|
|
'remove this link': 'remove this link', |
|
|
'remove all links to this page from this article': 'remove all links to this page from this article', |
|
|
'remove all links to this disambig page from this article': 'remove all links to this disambig page from this article', |
|
|
'mainlink': 'mainlink', ///// links, watch, unwatch |
|
|
'wikiLink': 'wikiLink', |
|
|
'wikiLinks': 'wikiLinks', |
|
|
'links here': 'links here', |
|
|
'whatLinksHere': 'whatLinksHere', |
|
|
'what links here': 'what links here', |
|
|
'WhatlinkshereHint': 'List the pages that are hyperlinked to %s', |
|
|
'unwatchShort': 'un', |
|
|
'watchThingy': 'watch', // called watchThingy because {}.watch is a function |
|
|
'watchHint': 'Add %s to my watchlist', |
|
|
'unwatchHint': 'Remove %s from my watchlist', |
|
|
'Only found one editor: %s made %s edits': 'Only found one editor: %s made %s edits', |
|
|
'%s seems to be the last editor to the page %s': '%s seems to be the last editor to the page %s', |
|
|
'rss': 'rss', |
|
|
///////////////////////////////////// |
|
|
// diff previews |
|
|
///////////////////////////////////// |
|
|
'Diff truncated for performance reasons': 'Diff truncated for performance reasons', |
|
|
'Old revision': 'Old revision', |
|
|
'New revision': 'New revision', |
|
|
'Something went wrong :-(': 'Something went wrong :-(', |
|
|
'Empty revision, maybe non-existent': 'Empty revision, maybe non-existent', |
|
|
'Unknown date': 'Unknown date', |
|
|
///////////////////////////////////// |
|
|
// other special previews |
|
|
///////////////////////////////////// |
|
|
'Empty category': 'Empty category', |
|
|
'Category members (%s shown)': 'Category members (%s shown)', |
|
|
'No image links found': 'No image links found', |
|
|
'File links': 'File links', |
|
|
///////////////////////////////////// |
|
|
// user-related actions and info |
|
|
///////////////////////////////////// |
|
|
'user': 'user', ///// user page, talk, email, space |
|
|
'user page': 'user page', |
|
|
'user talk': 'user talk', |
|
|
'edit user talk': 'edit user talk', |
|
|
'leave comment': 'leave comment', |
|
|
'email': 'email', |
|
|
'email user': 'email user', |
|
|
'EmailuserHint': 'Send an email to %s', |
|
|
'space': 'space', // short form for userSpace link |
|
|
'PrefixindexHint': 'Show pages in the userspace of %s', |
|
|
'count': 'count', ///// contributions, tree, log |
|
|
'edit counter': 'edit counter', |
|
|
'katelinkHint': 'Count the countributions made by %s', |
|
|
'contribs': 'contribs', |
|
|
'contributions': 'contributions', |
|
|
'ContributionsHint': 'List the contributions made by %s', |
|
|
'tree': 'tree', |
|
|
'contribsTreeHint': 'Explore %s\'s contributions by namespace and by article', |
|
|
'log': 'log', |
|
|
'user log': 'user log', |
|
|
'userLogHint': 'Show %s\'s user log', |
|
|
'arin': 'ARIN lookup', ///// ARIN lookup, block user or IP |
|
|
'Look up %s in ARIN whois database': 'Look up %s in the ARIN whois database', |
|
|
'unblockShort': 'un', |
|
|
'block': 'block', |
|
|
'block user': 'block user', |
|
|
'IpblocklistHint': 'Unblock %s', |
|
|
'BlockipHint': 'Prevent %s from editing', |
|
|
'block log': 'block log', |
|
|
'blockLogHint': 'Show the block log for %s', |
|
|
'protectLogHint': 'Show the protection log for %s', |
|
|
'pageLogHint': 'Show the page log for %s', |
|
|
'deleteLogHint': 'Show the deletion log for %s', |
|
|
'Invalid %s %s': 'The option %s is invalid: %s', |
|
|
///////////////////////////////////// |
|
|
// Autoediting |
|
|
///////////////////////////////////// |
|
|
'Enter a non-empty edit summary or press cancel to abort': 'Enter a non-empty edit summary or press cancel to abort', |
|
|
'Failed to get revision information, please edit manually.\n\n': 'Failed to get revision information, please edit manually.\n\n', |
|
|
'The %s button has been automatically clicked. Please wait for the next page to load.': 'The %s button has been automatically clicked. Please wait for the next page to load.', |
|
|
'Could not find button %s. Please check the settings in your javascript file.': 'Could not find button %s. Please check the settings in your javascript file.', |
|
|
///////////////////////////////////// |
|
|
// Popups setup |
|
|
///////////////////////////////////// |
|
|
'Display navigation links at the top of the popup': 'Display navigation links at the top of the popup', |
|
|
'Load images': 'Load images', |
|
|
'Never download extra stuff for images/previews': 'Never download extra stuff for images/previews', |
|
|
'Open full-size image': 'Open full-size image', |
|
|
'Show/hide options': 'Show/hide options', |
|
|
'Show image previews': 'Show image previews', |
|
|
'Show navigation links': 'Show navigation links', |
|
|
'Show page summary data': 'Show page summary data', |
|
|
'Show previews': 'Show previews', |
|
|
'Show summary data': 'Show summary data', |
|
|
'Show text previews': 'Show text previews', |
|
|
'Simple popups': 'Simple popups', |
|
|
'Toggle this option': 'Toggle this option', |
|
|
'zxy': 'zxy' |
|
|
}; |
|
|
|
|
|
|
|
|
function popupString(str) { |
|
|
if (typeof popupStrings != 'undefined' && popupStrings && popupStrings) { return popupStrings; } |
|
|
if (pg.string) { return pg.string; } |
|
|
return str; |
|
|
} |
|
|
|
|
|
|
|
|
function tprintf(str,subs) { |
|
|
if (typeof subs != typeof ) { subs = ; } |
|
|
return simplePrintf(popupString(str), subs); |
|
|
} |
|
|
|
|
|
//</NOLITE> |
|
|
// ENDFILE: strings.js |
|