User:Vector Potential/popups.js

var popupVersion="Sun Jul 2 18:23:28 EDT 2006";

// STARTFILE: main.js

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

// ** **

// ** 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: مدیا מיוחד Мэдыя **

// ** **

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

////////////////////////////////////////////////////////////////////

// Import stylesheet(s)

//

if ( window.localCSS ) {

document.write('');

} else {

document.write('');

}

//////////////////////////////////////////////////

// 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

////////////////////////////////////////////////////////////////////

$(setupPopups);

/// Local Variables: ///

/// mode:c ///

/// End: ///

// ENDFILE: main.js

// STARTFILE: actions.js

function setupTooltips(container, remove) {

log('setupTooltips, container='+container+', remove='+remove);

if (!container) {

//

// the main initial call

if (getValueOf('popupOnEditSelection') && window.doSelectionPopup ) {

try {

document.editform.wpTextbox1.onmouseup=function() { doSelectionPopup(); };

} catch (neverMind) {}

}

//

// 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);

}

function defaultPopupsContainer() {

if (getValueOf('popupOnlyArticleLinks')) {

return document.getElementById('article') ||

document.getElementById('content') || document;

}

return document;

}

function setupTooltipsLoop(anchors,begin,howmany,sleep, remove) {

log(simplePrintf('setupTooltipsLoop(%s,%s,%s,%s,%s)', arguments));

var finish=begin+howmany;

var loopend = min(finish, anchors.length);

var j=loopend - begin;

log ('setupTooltips: anchors.length=' + anchors.length + ', begin=' + begin + ', howmany=' + howmany + ', loopend=' + loopend + ', remove=' + remove);

var doTooltip= remove ? removeTooltip : addTooltip;

// try a faster (?) loop construct

if (j > 0) {

do {

var a=anchors[loopend - j];

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

log ('killing popup for toclinks[' + j + ']');

doTooltip(tocLinks[j], 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;

}

}

function addTooltip(a) {

if ( !isPopupLink(a) ) { return; }

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

}

}

//

function removeTooltip(a) {

if ( !a.hasPopup ) { return; }

a.onmouseover = null;

a.onmouseout = null;

if (a.originalTitle) { a.title = a.originalTitle; }

a.hasPopup=false;

}

//

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');

}

//

if (window.addPopupShortcuts && window.rmPopupShortcuts) {

np.addHook(addPopupShortcuts, 'unhide', 'after');

np.addHook(rmPopupShortcuts, 'hide', 'before');

}

//

}

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


' + 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);

//

if(getValueOf('popupPreviewDiffs') && window.loadDiff) {

diff=params.diff; // alert(params.diff);

}

if(getValueOf('popupPreviewHistory') && getValueOf('popupUseQueryInterface')) {

history=(params.action=='history');

}

//

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 {

log ('using existing popup content - just showing');

}

a.navpopup.showSoonIfStable(a.navpopup.delay);

getValueOf('popupInitialWidth');

if (typeof pg.timer.checkPopupPosition==typeof 1) { clearInterval(pg.timer.checkPopupPosition); }

pg.timer.checkPopupPosition=setInterval(checkPopupPosition, 600);

//

if (getValueOf('popupLiveOptions')) {

setPopupHTML(popupLiveOptionsHTML(), 'popupLiveOptions', pg.idNumber,

function () { popupToggleShowOptions(true); } );

}

if (getValueOf('popupRedlinkRemoval') && a.className=='new') {

setPopupHTML('
'+popupRedlinkHTML(), 'popupRedlink', pg.idNumber);

}

//

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);

//

} else if ( diff || diff === 0 ) {

//alert([article,oldid,diff]);

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);

//

} 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[2]);

var trailingRubbish=redirMatch[4];

navpop.redir++;

navpop.redirTarget=target;

//

if (window.redirLink) {

var warnRedir = redirLink(target);

setPopupHTML(warnRedir, 'popupWarnRedir');

}

//

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;

}

//

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));

}

}

//

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


' + download.data.entify().split('\\n').join('
\\n') + '
';

setPopupHTML(h, 'popupPreview');

}

else {

// deal with tricksy anchors

var anch=art.anchorString();

var d=anchorize(download.data, anch);

var urlBase=joinPath([pg.wiki.articlebase, art.urlString()]);

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[0]) { return d.substring(d.indexOf(match[0])); }

// now try to deal with == foo baz boom == -> #foo_baz_boom

var lines=d.split('\n');

for (var i=0; i

lines[i]=lines[i].replace(RegExp('\\*?[|])?(.*?)[\\]]{2}', 'g'), '$2');

if (lines[i].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.

*************************************************

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 User:Lupin

Copyright Aaron Boodman.

Saying stupid things daily since March 2001.

  • /

/**

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 null.

@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 null.

@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 oRoot is dragged.

@param {DOMElement} oRoot The object which moves when o is dragged, or o if omitted.

  • /

Drag.prototype.init = function(o, oRoot) {

var dragObj = this;

this.obj = o;

o.onmousedown = function(e) { dragObj.start.apply( dragObj, [e]); };

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, [e] ); };

document.onmouseup = function(e) { dragObj.end.apply( dragObj, [e] ); };

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[o.hmode ? "left" : "right"] = nx + "px";

this.obj.root.style[o.vmode ? "top" : "bottom"] = 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[this.obj.hmode ? "left" : "right"], 10),

parseInt(this.obj.root.style[this.obj.vmode ? "top" : "bottom"], 10));

}

};

// ENDFILE: domdrag.js

// STARTFILE: liveoptions.js

//

//////////////////////////////////////////////////

// live options nonsense

//

function popupToggleVar(varstring) {

pg.option[varstring] = ! getValueOf(varstring);

if (getValueOf('popupCookies')) {

Cookie.create(pg.option[varstring], String(pg.option[varstring]));

}

}

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

html += '';

html += '

html += (window[varstring]) ? 'checked="checked" ' : '';

html += 'onClick="javascript:popupToggleVar(' + "'" + varstring + "'" +

')">' + label + '';

return html;

}

function popupLiveOptionsHTML() {

var html = '';

html += '
';

html += '

html += 'style="border: thin dotted black; cursor: pointer" ';

html += 'onClick="javascript:popupToggleShowOptions()">';

html += 'Options ';

html += '';

html += '

';

return html;

}

//

// ENDFILE: liveoptions.js

// STARTFILE: structures.js

//

pg.structures.original={};

pg.structures.original.popupLayout=function () {

return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle',

'popupData', 'popupOtherLinks',

'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks',

'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],

'popupMiscTools', ['popupRedlink', 'popupLiveOptions'],

'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('<>',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[newStructure]={};

for (var prop in pg.structures[oldStructure]) {

pg.structures[newStructure][prop]=pg.structures[oldStructure][prop];

}

}

copyStructure('original', 'nostalgia');

pg.structures.nostalgia.popupTopLinks=function(x) {

var str='';

str += '<>';

// user links

// contribs - log - count - email - block

// count only if applicable; block only if popupAdminLinks

str += 'if(user){
<>';

str+='if(wikimedia){*<>}';

str+='if(ipuser){}else{*<>}if(admin){*<>}}';

// editing links

// talkpage -> edit|new - history - un|watch - article|edit

// other page -> edit - history - un|watch - talk|edit|new

var editstr='<>';

var editOldidStr='if(oldid){<>|<>|<>}else{'

+ editstr + '}'

var historystr='<>';

var watchstr='<>|<>';

str+='
if(talk){' +

editOldidStr+'|<>' + '*' + historystr+'*'+watchstr + '*' +

'<>|<>' +

'}else{' + // not a talk page

editOldidStr + '*' + historystr + '*' + watchstr + '*' +

'<>|<>|<>'

+ '}';

// misc links

str += '
<>*<>';

str += 'if(admin){
}else{*}<>';

// admin links

str += 'if(admin){*<>|<>*' +

'<>|<>}';

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('<>',x.article,x.oldid);

};

pg.structures.fancy.popupTopLinks=function(x) {

var hist='<>|<>if(mainspace_en)

<>}';

var watch='<>|<>';

var move='<>';

return navlinkStringToHTML('if(talk){' +

'<>|<>*' + hist + '*' +

'<>|<>' + '*' + watch + '*' + move +

'}else{<>*' + hist +

'*<>|<>|<>' +

'*' + watch + '*' + move+'}
', x.article, x.oldid);

};

pg.structures.fancy.popupOtherLinks=function(x) {

var admin='<>|<>*<>|<>';

var user='<>if(wikimedia){|<>}';

user+='if(ipuser){|<>}else{*<

popupString('email')+'>>}if(admin){*<>}';

var normal='<>*<>';

return navlinkStringToHTML('
if(user){' + user + '*}if(admin){'+admin+'if(user){
}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 User:MacGyverMagic

copyStructure('fancy', 'fancy2');

pg.structures.fancy2.popupTopLinks=function(x) { // hack out the
at the end and put one at the beginning

return '
'+pg.structures.fancy.popupTopLinks(x).replace(RegExp('
$','i'),'');

};

pg.structures.fancy2.popupLayout=function () { // move toplinks to after the title

return ['popupError', 'popupImage', 'popupTitle', 'popupData', 'popupTopLinks', 'popupOtherLinks',

'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],

'popupMiscTools', ['popupRedlink', 'popupLiveOptions'],

'popupPrePreviewSep', 'popupPreview', 'popupPostPreview', 'popupFixDab'];

};

/** -- menus -- **/

copyStructure('original', 'menus');

pg.structures.menus.popupLayout=function () {

return ['popupError', 'popupImage', 'popupTopLinks', 'popupTitle', 'popupOtherLinks',

'popupRedir', ['popupWarnRedir', 'popupRedirTopLinks', 'popupRedirTitle', 'popupRedirData', 'popupRedirOtherLinks'],

'popupData', 'popupMiscTools', ['popupRedlink', 'popupLiveOptions'],

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

';

var endspan='';

var hist='<>';

if (!shorter) { hist = '' + hist +

'|<>if(mainspace_en){|<>}'; }

var lastedit='<>';

var jsHistory='<><>';

var linkshere='<>';

var related='<>';

var search='<>if(wikimedia){|<>}' +

'|<>';

var watch='<>|<>';

var protect='<>|<>|<>';

var del='<>|<>|<>';

var move='<>';

var nullPurge='<>|<>';

var viewOptions='<>|<>|<>';

var editRow='if(oldid){<>|<>' +

'<>' + '}else{<>}';

var newTopic='if(talk){<>}';

var protectDelete='if(admin){' + protect + '' + del + '}';

if (getValueOf('popupActionsMenu')) {

s.push( '<>*' + dropdiv + ''+popupString('actions') + '');

} else {

s.push( dropdiv + '<>');

}

s.push( '

')

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


' + watch + protectDelete);

s.push('


' +

'if(talk){<><>}' +

'else{<><>' +

'<>}

' + enddiv);

// user menu starts here

var email='<>';

var contribs= 'if(wikimedia){}<>' +

'if(wikimedia){|<>}';

s.push('if(user){*' + dropdiv + ''+popupString('user')+'');

s.push('

'); +

s.push('<>|<>');

s.push('<><>' +

'<>');

if(!shorter) { s.push( 'if(ipuser){<>}else{' + email + '}') }

else { s.push( 'if(ipuser){}else{' + email + '}') }

s.push('


' + contribs + '<>');

s.push('if(wikimedia){<>}');

s.push('if(admin){<>|<>}');

s.push('<>' + getValueOf('popupExtraUserMenu'));

s.push('

' + enddiv + '}');

// popups menu starts here

if (getValueOf('popupSetupMenu')) {

s.push('*' + dropdiv + '' + popupString('popupsMenu') + '

');

s.push('<>');

s.push('<>');

s.push('<>');

s.push('

'+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;

//

pg.structures.lite={};

pg.structures.lite.popupLayout=function () {

return ['popupTitle', 'popupPreview' ];

};

pg.structures.lite.popupTitle=function (x) {

log (x.article + ': structures.lite.popupTitle');

//return navlinkStringToHTML('<>',x.article,x.oldid);

return '

' + x.article.toString() + '
';

};

// ENDFILE: structures.js

// STARTFILE: autoedit.js

//

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[1]);

} 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

data=cmdList[i].action(data, cmdList[i]);

}

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 [p].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

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, [getParamValue('autorv'), '(unknown)', '(unknown)']);

} 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[btn]) {

var headings=document.getElementsByTagName('h1');

if (headings) {

var div=document.createElement('div');

var button=document.editform[btn];

div.innerHTML='' +

tprintf('The %s button has been automatically clicked. Please wait for the next page to load.',

[ button.value ]) + '';

document.title='('+document.title+')';

headings[0].parentNode.insertBefore(div, headings[0]);

button.click();

}

} else {

alert(tprintf('Could not find button %s. Please check the settings in your javascript file.', [ btn ]));

}

}

}

function getRvSummary(template, json) {

var o=getJsObj(json);

try {

var edit = anyChild(o.pages).revisions[0];

} catch (badness) {return false;}

var timestamp = edit.timestamp.split(/[A-Z]/g).join(' ').replace(/^ *| *$/g, '');

return simplePrintf(template, [edit.revid, timestamp, edit.user]);

}

$(autoEdit);

//

// 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 abort

@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 aborted 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.id] = this;

this.http.send(null);

};

/** Gets the 'Last-Modified' date from the download headers.

Should be run after the download completes.

Returns null 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 f(this) 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[this.id];

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 callback(d),

where d 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[x].aborted=true;

pg.misc.downloadsInProgress[x].abort();

delete pg.misc.downloadsInProgress[x];

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

* - 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 :Image:...

* - Updated MD5 function (hopefully it will work with UTF-8)

* - Fixed bug in handling of links inside images

*

* To do:

* - Better support for

* - Full support for

* - 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([pg.wiki.prePath, pg.wiki.articlePath]) + '/',

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: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']

}

}

// options with default values or backreferences

with (Insta.conf) {

user.name = user.name

'Wikipedian'

user.signature = ''+user.name+''

//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[0],o='',c,p

for (;i

// allow character escaping

i -= c=f.charAt(p+1)=='?'?1:0

o += f.substring(0,p)+(c?'?':a[i])

f=f.substr(p+1+c)

} else break;

return o+f

}

function html_entities(s) { return s.replace(/&/g,"&").replace(//g,">") }

function max(a,b) { return (a>b)?a:b }

function min(a,b) { return (a

// 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

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[0].substr(0,c.length)==c) : ($r = ll[0].match(c)) }

function $$(c) { return ll[0]==c } // compare current line against a string

function _(p) { return ll[0].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[1])

// close uncontinued lists

for (var i=prev.length-1; i >= ipos; i--) {

var pi = prev.charAt(i)

if (pi=='*') ps('')

else if (pi=='#') ps('')

// close a dl only if the new item is not a dl item (:, ; or empty)

else switch (l_match[1].charAt(i)) { case'':case'*':case'#': ps('') }

}

// open new lists

for (var i=ipos; i

var li = l_match[1].charAt(i)

if (li=='*') ps('

    ')

    else if (li=='#') ps('

      ')

      // 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('

      ') }

      }

      switch (l_match[1].charAt(l_match[1].length-1)) {

      case '*': case '#':

      ps('

    1. ' + parse_inline_nowiki(l_match[2])); break

      case ';':

      ps('

      ')

      var dt_match

      // handle ;dt :dd format

      if (dt_match = l_match[2].match(/(.*?)(:.*?)$/)) {

      ps(parse_inline_nowiki(dt_match[1]))

      ll.unshift(dt_match[2])

      } else ps(parse_inline_nowiki(l_match[2]))

      break

      case ':':

      ps('

      ' + parse_inline_nowiki(l_match[2]))

      }

      prev=l_match[1]

      }

      // 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('', $(/^\{\|( .*)$/)? $r[1]: ''))

      for (;remain();) if ($('|')) switch (_(1)) {

      case '}': endl('

'); return

case '-': endl(f('', $(/\*(.*)/)[1])); 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[1] == '|+') ps('

else ps('

if (typeof td_match[3] != 'undefined') {

ps(' ' + td_match[3])

match_i = 4

} else match_i = 2

ps('>')

if (td_match[1] != '|+') {

// useor !! 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[match_i].split((td_match[1] == '|')? '': /(?:\|\!!)/)

ps(parse_inline_nowiki(td_line.shift()))

while (td_line.length) ll.unshift(td_match[1] + td_line.pop())

} else ps(td_match[match_i])

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

')

do endl(parse_inline_nowiki(ll[0].substring(1)) + "\n"); while (remain() && $(' '))

ps('

')

}

function parse_block_image()

{

ps(parse_image(sh()))

}

function parse_image(str)

{

//

// get what's in between "Image:" and ""

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[0].match(/^(\d*)px$/)) width = w_match[1]

else switch(attr[0]) {

case 'thumb':

case 'thumbnail':

thumb=true;

case 'frame':

frame=true;

break;

case 'none':

case 'right':

case 'left':

center=false;

align=attr[0];

break;

case 'center':

center=true;

align='none';

break;

default:

if (attr.length == 1) caption = attr[0];

}

} else filename = tag;

var o='';

if (frame) {

if (align=='') align = 'right';

o += f("

", align);

if (thumb) {

if (!width) width = Insta.conf.wiki.default_thumb_width;

o += f("

?", 2+width*1, make_image(filename, caption, width)) +

f("

?
",

Insta.conf.paths.articles + Insta.conf.locale.image + ':' + filename,

Insta.conf.paths.magnify_icon,

parse_inline_nowiki(caption)

)

} else {

o += '

' + make_image(filename, caption) + f("
?
", parse_inline_nowiki(caption))

}

o += '

';

} else if (align != '') {

o += f("

?
", align, make_image(filename, caption, width));

} else {

return make_image(filename, caption, width);

}

return center? f("

?
", o): o;

//

}

function parse_inline_nowiki(str)

{

var start, lastend=0

var substart=0, nestlev=0, open, close, subloop;

var html='';

while (-1 != (start = str.indexOf('', substart))) {

html += parse_inline_wiki(str.substring(lastend, start));

start += 8;

substart = start;

subloop = true;

do {

open = str.indexOf('', substart);

close = str.indexOf('', substart);

if (close<=openopen==-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)

{

//

// 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("", Insta.conf.paths.images_fallback + source, Insta.conf.paths.images + source, (caption!=)? "alt='" + caption + "'" : , width);

return f("?", (caption!=)? "title='" + caption + "'" : , Insta.conf.paths.articles + Insta.conf.locale.image + ':' + filename, img);

//

}

function parse_inline_images(str)

{

//

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<=openopen==-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;

}

//

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?'':'';

} else {

em=!em;

o+=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[1]);

str = str.replace(aux_match[0], f("", 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.getUTCMonth()], 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).

// :Category:..., :Image:..., etc...

replace(RegExp('\\[\\[:((?:'+Insta.conf.locale.category+'|'+Insta.conf.locale.image+'|'+Insta.conf.wiki.interwiki+'):.*?)\\]\\]','gi'), "$1").

replace(RegExp('\\[\\[(?:'+Insta.conf.locale.category+'|'+Insta.conf.wiki.interwiki+'):.*?\\]\\]','gi'),'').

// /Relative links

replace(/\[\[(\/[^|]*?)\]\]/g, f("$1", Insta.conf.baseUrl)).

// Relative links

replace(/\[\[(\/.*?)\|(.+?)\]\]/g, f("$2", Insta.conf.baseUrl)).

// Common links

replace(/\[\[([^|]*?)\]\](\w*)/g, f("$1$2", Insta.conf.paths.articles)).

// Links

replace(/\[\[(.*?)\|([^\]]+?)\]\](\w*)/g, f("$2$3", Insta.conf.paths.articles)).

// Namespace

replace(/\[\[([^\]]*?:)?(.*?)( *\(.*?\))?\|\]\]/g, f("$2", Insta.conf.paths.articles)).

// External links

replace(/\[(https?|news|ftp|mailto|gopher|irc):(\/*)([^\]]*?) (.*?)\]/g, "$4").

replace(/\[http:\/\/(.*?)\]/g, "[#]").

replace(/\[(news|ftp|mailto|gopher|irc):(\/*)(.*?)\]/g, "$1:$2$3").

replace(/(^| )(https?|news|ftp|mailto|gopher|irc):(\/*)([^ $]*[^.,!?;: $])/g, "$1$2:$3$4").

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('??', $r[1].length, parse_inline_nowiki($r[2]), $r[1].length, $r[3]))

} else if ($(/^[*#:;]/)) {

p=0

parse_list()

} else if ($(' ')) {

p=0

parse_pre()

} else if ($('{|')) {

p=0

parse_table()

} else if ($(/^----+$/)) {

p=0

endl('


')

} else if ($(Insta.BLOCK_IMAGE)) {

p=0

parse_block_image()

} else {

// handle paragraphs

if ($$('')) {

if (p = (remain()>1 && ll[1]==(''))) endl('


')

} else {

if(!p) {

ps('

')

p=1

}

ps(parse_inline_nowiki(ll[0]) + ' ')

}

sh();

}

return o

};

window.wiki2html=function(txt,baseurl) {

Insta.conf.baseUrl=baseurl;

return Insta.convert(txt);

};

// ENDFILE: livepreview.js

// STARTFILE: pageinfo.js

//

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', [formatAge(age)])).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

if (list[i] && typeof list[i] == 'function') {

var s=list[i](data, download);

if (s) { ret.push(s); }

}

}

return ret;

}

function getPageInfo(data, download) {

if (!datadata.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')) ;

}

//

// ENDFILE: pageinfo.js

// STARTFILE: titles.js

/**

@fileoverview Defines the {@link Title} class, and associated crufty functions.

Title deals with article titles and their various

forms. {@link Stringwrapper} is the parent class of

Title, 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 Title.

@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(/((?:[.][0-9A-F]{2})+)/);

var len=split.length;

for (var j=1; j

// FIXME s/decodeURI/decodeURIComponent/g ?

split[j]=decodeURIComponent(split[j].split('.').join('%')).split('_').join(' ');

}

return split.join('');

};

Title.prototype.urlAnchor=function() {

var split=this.anchor.parenSplit('/((?:[%][0-9A-F]{2})+)/');

var len=split.length;

for (var j=1; j

split[j]=split[j].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[0]=splitted[0].split('&').join('%26');

if (pg.flag.linksLikeIE) {

splitted[0]=encodeURI(decode_utf8(splitted[0]));

}

h=splitted.join('?');

var contribs=pg.re.contribs.exec(h);

if (contribs !== null) {

if (contribs[1]=='title=') { contribs[3]=contribs[3].split('+').join(' '); }

this.setUtf(this.decodeNasties(pg.ns.user + ':' + contribs[3]));

return this;

}

var email=pg.re.email.exec(h);

if (email !== null) {

this.setUtf(this.decodeNasties(pg.ns.user + ':' + email[3]));

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[2]=m[2].split('+').join('_');

}

var extracted = m[2] + (m[3] ? '#' + m[3] : '');

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(/((?:[%][0-9A-Fa-f]{2})+)/);

var len=split.length;

for (var i=1; i

// FIXME is decodeURIComponent better?

split[i]=unescape(split[i]);

}

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);

};

//

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 !== 0j == -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[0]+':'+this.value).split(' ').join('_');

return this.value;

}

for (var i=0; i< pg.ns.withTalkList.length; ++i) {

if (splitted[1]==pg.ns.withTalkList[i]) {

splitted[1]=pg.ns.talkList[i];

this.value=splitted.join(':').substring(1).split(' ').join('_');

return this.value;

}

}

this.value=null;

return null;

};

//

Title.prototype.namespace=function() {

var n=this.value.indexOf(':');

if (n<0) { return ''; }

var list=pg.ns.list;

for (var i=0; i

if (upcaseFirst(list[i]) == this.value.substring(0,n)) { return list[i]; }

}

return '';

};

//

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 < 2splitted[0].length > 0) { this.value=null; return null; }

if (splitted[1]==pg.ns.talkList[0]) {

splitted[1]='';

this.value=splitted.join(':').substring(2).split(' ').join('_');

return this.value;

}

for (var i=1; i< pg.ns.talkList.length; ++i) {

if (splitted[1]==pg.ns.talkList[i]splitted[1]==pg.ns.talkList[i].split(' ').join('_')) {

splitted[1]=pg.ns.withTalkList[i];

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());

};

//

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[list.length-1];

};

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[1]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

var z=t[i].split('=');

z.push(null);

ret[z[0]]=z[1];

}

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

var from=pg.misc.decodeExtras[i].from;

var to=pg.misc.decodeExtras[i].to;

ret=ret.split(from).join(to);

}

return ret;

}

function safeDecodeURI(str) { var ret=myDecodeURI(str); return retstr; }

///////////

// TESTS //

///////////

//

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

return false;

}

//

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) === 0h.indexOf(pg.wiki.articlebase) === 0h.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

//

//////////////////////////////////////////////////

// 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[i];

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);

}

};

//

// ENDFILE: cookies.js

// STARTFILE: getpage.js

//////////////////////////////////////////////////

// Wiki-specific downloading

//

// Schematic for a getWiki call

//

// getWiki->-getPageWithCaching

// |

// false | true

// getPage<-[findPictureInCache]->-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 (oldidoldid===0oldid==='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[i].data, pg.cache.pages[i].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

if (url==pg.cache.pages[i].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

//

/*

* 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[i] = bkey[i] ^ 0x36363636;

opad[i] = bkey[i] ^ 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[i] = (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[i];

q = Math.floor(x / divisor);

x -= q * divisor;

if(quotient.length > 0q > 0)

quotient[quotient.length] = q;

}

remainders[remainders.length] = x;

dividend = quotient;

}

/* Convert the remainders to the output string */

var output = "";

for(i = remainders.length - 1; i >= 0; i--)

output += encoding.charAt(remainders[i]);

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[i] = 0;

for(var i = 0; i < input.length * 8; i += 8)

output[i>>5] |= (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>>5] >>> (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[len >> 5] |= 0x80 << ((len) % 32);

x[(((len + 64) >>> 9) << 4) + 14] = 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[i+ 0], 7 , -680876936);

d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);

c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);

b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);

a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);

d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);

c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);

b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);

a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);

d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);

c = md5_ff(c, d, a, b, x[i+10], 17, -42063);

b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);

a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);

d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);

c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);

b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);

a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);

d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);

c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);

b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);

a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);

d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);

c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);

b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);

a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);

d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);

c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);

b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);

a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);

d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);

c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);

b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);

d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);

c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);

b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);

a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);

d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);

c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);

b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);

a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);

d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);

c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);

b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);

a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);

d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);

c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);

b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);

d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);

c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);

b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);

a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);

d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);

c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);

b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);

a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);

d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);

c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);

b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);

a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);

d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);

c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);

b = md5_ii(b, c, d, a, x[i+ 9], 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));

}

//

// 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

if (typeof m[i]=='undefined') m[i]='';

}

ret.push(s.substring(0,m.index));

ret = ret.concat(m.slice(1));

s=s.substring(m.index + m[0].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/wiki/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

//

function encode_utf8(rohtext) {

// dient der Normalisierung des Zeilenumbruchs

rohtext = rohtext.replace(/\r\n/g,"\n");

var utftext = "";

for(var n=0; 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[p];

}

return null;

}

//

function decode_utf8(utftext) {

var plaintext = ""; var i=0; var c=c1=c2=0;

// while-Schleife, weil einige Zeichen uebersprungen werden

while(i

{

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

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[i+1];

}

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

if (array[i]==value) { return i; }

}

return null;

}

function removeNulls(list) {

var ret=[];

for (var i=0; i

if (list[i]) {

ret.push(list[i]);

}

}

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|\$[0-9]+)/);

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[i]); } else { ret.push(cmd); }

++i;

} else {

var j=parseInt( cmd.replace('$', ''), 10 ) - 1;

if ( j > -1 && j < subs.length ) { ret.push(subs[j]); } else { ret.push(cmd); }

}

} while (s.length > 0);

return ret.join('');

}

function max(a,b){return 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

//

//////////////////////////////////////////////////

// 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', [newTarget]),

summary: simplePrintf(

getValueOf('popupFixDabsSummary'), [friendlyCurrentArticleName, newTarget ]),

clickButton: 'wpDiff', minor: true, oldTarget: oldTarget,

watch: getValueOf('popupWatchDisambiggedPages')

});

}

function listLinks(wikitext, oldTarget) {

var reg=RegExp('\\[\\[([^|]*?)(\\\\]\\])', 'gi');

var ret=[];

var splitted=wikitext.parenSplit(reg);

// ^[a-z]+ should match interwiki links, hopefully (case-insensitive)

// and ^[a-z]* should match those and :Category... style links too

var omitRegex=RegExp('^[a-z]*:|^[Ss]pecial:|^[Ii]mage|^[Cc]ategory');

var friendlyCurrentArticleName=pg.current.article.split('_').join(' ');

for (var i=1; i

if (typeof splitted[i] == typeof 'string' && splitted[i].length>0 && !omitRegex.test(splitted[i])) {

ret.push( retargetDab(splitted[i], 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'), [friendlyCurrentArticleName]),

watch: getValueOf('popupWatchDisambiggedPages')}));

return ret;

}

function rmDupesFromSortedList(list) {

var ret=[];

for (var i=0; i

if (ret.length===0list[i]!=ret[ret.length-1]) { ret.push(list[i]); }

}

return ret;

}

function makeFixDab(data, oldTarget) {

var list=listLinks(data, oldTarget);

if (list.length===0) { log('listLinks returned empty list'); return null; }

var html='


' + popupString('Click to disambiguate this link to:') + '
';

html+=list[0];

for (var i=1; i

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'), [friendlyCurrentArticleName])});

}

//

// 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[elementId] == 'undefined') { timer=null; }

else { timer=pg.timer.popupHTML[elementId]; }

var popupElement=document.getElementById(elementId+popupId);

if (popupElement) {

if(timer) { clearInterval(timer); }

pg.timer.popupHTML[elementId]=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[elementId] = loopFunction;

if (!timer) {

var doThis = 'pg.misc.popupHTMLLoopFunctions["'+elementId+'"]()';

pg.timer.popupHTML[elementId] = setInterval(doThis, 600);

}

}

return null;

}

//

function setImageStatus(str, id) {return; } // setPopupHTML(str, 'popupImageStatus', id);}

function setPopupTrailer(str,id) {return setPopupHTML(str, 'popupData', id);}

//

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.originalTitlearticle.hintValue();

oldid=(getValueOf('popupHistoricalLinks')) ? oldidFromAnchor(a) : null;

}

var x={ article:article, hint: hint, oldid: oldid };

var structure=pg.structures[getValueOf('popupStructure')];

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

var f=findThis(redirs, spans[i]);

//log('redir='+redir+', f='+f+', spans[i]='+spans[i]);

if ( (f!==null && !redir)(f===null && redir) ) {

//log('skipping this set of the loop');

continue;

}

var structurefn=structure[spans[i]];

switch (typeof structurefn) {

case 'function':

//log('running '+spans[i]+'({article:'+x.article+', hint:'+x.hint+', oldid: '+x.oldid+'})');

setPopupHTML(structurefn(x), spans[i]);

break;

case 'string':

setPopupHTML(structurefn, spans[i]);

break;

default:

errlog('unknown thing with label '+spans[i]);

break;

}

}

}

// flatten an array

function flatten(list, start) {

var ret=[];

if (typeof start == 'undefined') { start=0; }

for (var i=start; i

if (typeof list[i] == typeof []) {

return ret.concat(flatten(list[i])).concat(flatten(list, i+1));

}

else { ret.push(list[i]); }

}

return ret;

}

// Generate html for whole popup

function popupHTML (a) {

getValueOf('popupStructure');

var structure=pg.structures[pg.option.popupStructure];

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

if (typeof list[i] == typeof '') {

ret += emptySpanHTML(list[i], pg.idNumber, 'div');

} else if (typeof list[i] == typeof [] && list[i].length > 0 ) {

ret = ret.parenSplit(RegExp('(]*?>$)')).join(makeEmptySpans(list[i]));

} else if (typeof list[i] == typeof {} && list[i].nodeType ) {

ret += emptySpanHTML(list[i].name, pg.idNumber, list[i].nodeType);

}

}

return ret;

}

function emptySpanHTML(name, id, tag, classname) {

tag = tag'span';

classname = classnamename;

return simplePrintf('<%s id="%s" class="%s">', [tag, name + id, classname, tag]);

}

// generate html for popup image

//

// where n=pg.idNumber

function imageHTML(article) {

return simplePrintf('',

[ pg.idNumber, pg.idNumber ]);

}

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

if (uls[i].className=='popup_menu') {

if (uls[i].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 diamyo

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[^>]* *>[\\s\\S]*?< */ *div *>',

'gi'), '');

};

/**

@private

  • /

Previewmaker.prototype.killGalleries = function () {

this.data=this.data.replace(RegExp('< *gallery[^>]* *>[\\s\\S]*?< */ *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[0].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)[0].length;

} else if (sb && sb.test(txt)) {

depth++;

removal=sb.exec(txt)[0].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.length-1];

sp[sp.length-1]='';

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('[{][{][^{}\\s|]*?(float|box)[_ ](begin|start)', 'i'), /[}][}]\s*/, '{{');

// infoboxes etc

// from User:Zyxw/popups.js: kill frames too

this.kill(RegExp('[{][{][^{}\\s|]*?(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: Comparison_of_Intel_Central_Processing_Units

this.kill('{|', '', '{|');

this.kill(//i, /<\/table.*?>/i, //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 ...

this.kill(//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

var splitted=this.data.parenSplit(/(<.*?>)/);

var len=splitted.length;

for (var i=1; i

switch (splitted[i]) {

case '':

case '':

break;

default:

splitted[i]='';

}

}

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])*''[.!?\\s]*\\n)+", 'g');

// keep stuff separated, though, so stick in \n (fixes Union Jack?

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) *:[^\\n]*','g'), '\n');

// replace __TOC__, __NOTOC__ and whatever else there is

// this'll probably do

this.data=this.data.replace(RegExp('^__[A-Z_]*__ *$', '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('^[\\s\\n]*'), '');

stuff=(RegExp('^([^\\n]|\\n[^\\n\\s])*')).exec(this.data);

if (stuff) { d = stuff[0]; }

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[0]=d[0].replace(RegExp('^\\s*'), '');

var notSentenceEnds=RegExp('([^.][a-z][.][a-z]|etc|sic|Dr|Mr|Mrs|Ms|St|\\^\\*|\\s[A-Zvclm])$', '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[i] to strs[i+1] & strs[i+2] if strs[i] matches regex reg

for (var i=0; i

if (reg.test(strs[i])) {

a=[];

for (var j=0; j

if (j

if (j==i) a[i]=strs[i]+strs[i+1]+strs[i+2];

if (j>i+2) a[j-2]=strs[j];

}

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 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();

};

//

/** 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[pg.cache.pages.length-1].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);

}

//

/**

Works around a quoting bug in livepreview.

wiki2html('Foo\'s "bar"') gives @literal{}

which doesn't do very well. We change this into @literal{}

@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

if(i%2==0) { ret += splitted[i]; continue; }

if(i%2==1) { ret += 'href="' + splitted[i].split('"').join('%22') + '"'; }

}

// fix question marks in wiki links

// maybe this'll break some stuff :-(

ret=ret.replace(RegExp('\(

// 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('


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

 templates

this.html=this.html.replace(RegExp('[{][{][^}]*

[^}]*[}][}]','gi'), '');

};

/**

@private

  • /

Previewmaker.prototype.killMultilineTemplates = function() {

this.kill(RegExp('\\s*[{][{][^{}]*\\n'), '}}', '{{');

};

// ENDFILE: previewmaker.js

// STARTFILE: querypreview.js

//

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['pages'])['imagelinks'];

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

if (ret.length === 0) { return popupString('No image links found'); }

return '

' + popupString('File links') + '

' + linkList(ret);

}

function categoryPreviewHTML(article, download) {

var jsobj=getJsObj(download.data);

try{ var list=jsobj['pages']; }

catch(someError) { return 'Category preview failed :( Is the query.php extension installed?'; }

var ret=[];

for (var p in list) { ret.push(list[p]['title']); }

if (ret.length === 0) { return popupString('Empty category'); }

return '

' + tprintf('Category members (%s shown)', [ret.length]) + '

' +linkList(ret);

}

function linkList(list) {

list.sort(function(x,y) { return (x==y ? 0 : (x

var buf=[];

for (var i=0; i

buf.push(wikiLink({article: new Title(list[i]),

text: list[i].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['pages'];

var edits = anyChild(p)[reallyContribs ? 'contributions' : 'revisions'];

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

';

var day=null;

var curart=article;

for (var i=0; i

if (reallyContribs) { var page=h[i]['*']; curart = new Title(page); }

var minor=typeof h[i]['minor']=='undefined' ? '' : 'm ';

var d=h[i]['timestamp'].split(/[A-Z]/g);

var thisDay=d[0];

if (thisDay==day) { d[0]=''; }

day=thisDay;

if (d[0]) {

ret += '

';

}

ret +='

';

ret +='

';

ret +='

';

var col3url=, col3txt=;

if (!reallyContribs) {

var user=h[i]['user'];

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

';

var comment='';

if (typeof h[i]['comment'] != 'undefined') { comment=h[i]['comment'].entify();}

else { comment=h[i]['*']; }

ret +='

';

ret +='

';

}

ret+='

' +

d[0]+'

';

return ret;

}

//

// ENDFILE: querypreview.js

// STARTFILE: debug.js

////////////////////////////////////////////////////////////////////

// Debugging functions

////////////////////////////////////////////////////////////////////

function setupDebugging() {

//

// 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 {

//

window.log = function(x) {};

window.errlog = function(x) {};

//

}

//

}

// ENDFILE: debug.js

// STARTFILE: images.js

//

// 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

// |

// [wikiMarkupToAddressFragment]

// |

// | mouseOverWikiLink (gets an "address fragment",

// | | no processing needed)

// \->-*loadThisImage---<----loadImages

// |

// [image(Thumb)URL]-->--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[i].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[0];

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[0] = new Image();

pg.misc.imageArray[0].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[i])) {

// stop all the gubbins, assign the image and return

log('checkImages: got at pos '+i+', src='+pg.misc.imageArray[i].src);

clearInterval(pg.timer.image);

if(pg.misc.gImage && pg.misc.gImage.namespace() == pg.ns.image) {

popupImage.src=pg.misc.imageArray[i].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[i].src);

setPopupImageLink(pg.misc.gImage, pg.wiki.imageSources[i].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[i].src=''; // this is a REALLY BAD IDEA

delete pg.misc.imageArray[i];

//pg.misc.imageArray[i] = 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(['http://upload.wikimedia.org', pg.wiki.prePath]) + '/'; 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

var url;

if (pg.wiki.imageSources[i].thumb) {

url=imageThumbURL(image, pg.wiki.imageSources[i].wiki, pg.wiki.imageSources[i].width);

} else { url=imageURL(image, pg.wiki.imageSources[i].wiki); }

for (var j=0; j

if (url == pg.cache.images[j]) {

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

var url = imageUrls[i];

pg.misc.imageArray[i]=new Image();

pg.misc.imageArray[i].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, /^'; }

if ( !ns[ n[i] ] ) {

ns[ n[i] ] = { rows: [], o: null };

}

try { ns[ n[i] ].rows.push( i ); } catch (err) { diffBugAlert(n[i]); }

}

// pass 2: make hashtable os with old rows as keys

for ( i = 0; i < o.length; i++ ) {

if ( jsReservedProperties.test(o[i]) ) { o[i] += ''; }

if ( !os[ o[i] ] ) {

os[ o[i] ] = { rows: [], n: null };

}

try {os[ o[i] ].rows.push( i ); } catch (err) { diffBugAlert(n[i]); }

}

// pass 3: pair unique new rows and matching unique old rows

for ( i in ns ) {

if ( ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1 ) {

n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] };

o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] };

}

}

// pass 4: pair matching rows immediately following paired rows (not necessarily unique)

for ( i = 0; i < n.length - 1; i++ ) {

if ( ( n[i].text || n[i].text=== ) && ( ! n[i+1].text && n[i+1].text !== ) &&

n[i].row < o.length - 1 && ( ! o[ n[i].row + 1 ].text || o[ n[i].row + 1 ].text === '' ) &&

n[i+1] == o[ n[i].row + 1 ] ) {

n[i+1] = { text: n[i+1], row: n[i].row + 1 };

o[n[i].row+1] = { text: o[n[i].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[i].text || n[i].text=== ) && ( ! n[i-1].text && n[i-1].text !== ) &&

n[i].row > 0 && ( ! o[ n[i].row - 1 ].text || o[ n[i].row - 1 ].text=== '' ) &&

n[i-1] == o[ n[i].row - 1 ] ) {

n[i-1] = { text: n[i-1], row: n[i].row - 1 };

o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1 };

}

}

return { o: o, n: n };

}

//

// 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([pm]edia|source|books|news|quote)|wiktionary)[.]org').test(pg.wiki.hostname);

pg.wiki.wikia=RegExp('[.]wikia[.]com$', '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('.')[0];

// 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[4];

}

var port = location.port ? ':' + location.port : '';

pg.wiki.sitebase = joinPath([pg.wiki.hostname + port, pg.wiki.prePath]);

}

function getArticlePath() {

if (isFunction(window.siteArticlePath)) {

return siteArticlePath();

}

return 'wiki';

}

function getBotInterfacePath() {

if (isFunction(window.siteBotInterfacePath)) {

return siteBotInterfacePath();

}

var botInterfacePath = pg.wiki.articlePath.replace(/\/index[.]php$/, '');

if (pg.wiki.wikimedia) { botInterfacePath = 'w'; } // as in http://some.thing.com/w/index.php?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/wiki/Article

pg.wiki.botInterfacePath = getBotInterfacePath();

// default mediawiki setting is paths like http://some.thing.com/articlePath/index.php?title=foo

var titletail = joinPath([pg.wiki.botInterfacePath, 'index.php?title=']);

var titletail2 = joinPath([pg.wiki.botInterfacePath, 'wiki.phtml?title=']);

// other sites may need to add code here to set titletail depending on how their urls work

pg.wiki.titlebase = protocol + '//' + joinPath([pg.wiki.sitebase, titletail]);

pg.wiki.titlebase2 = protocol + '//' + joinPath([pg.wiki.sitebase, titletail2]);

pg.wiki.wikibase = protocol + '//' + joinPath([pg.wiki.sitebase, pg.wiki.botInterfacePath]);

pg.wiki.articlebase = protocol + '//' + joinPath([pg.wiki.sitebase, pg.wiki.articlePath]);

}

//////////////////////////////////////////////////

// Global regexps

function setMainRegex() {

var reStart='[^:]*://';

var preTitles = joinPath([literalizeRegex(pg.wiki.botInterfacePath), '(?:index[.]php|wiki[.]phtml)[?]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=[0-9])') ;

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+':)?)(.*)') ;

//

// note: tries to get images in infobox templates too, e.g. movie pages, album pages etc

// (^|\[\[)image: *([^|\]]*[^|\] ]) *

// (^|\[\[)image: *([^|\]]*[^|\] ])([^0-9\]]*([0-9]+) *px)?

// $4 = 120 as in 120px

pg.re.image = RegExp('(^|\\[\\[)' + pg.ns.image + ': *([^|\\]]*[^|\\] ])([^0-9\\]]*([0-9]+) *px)?|(?:\\n *[|]?|[|]) *(image|image_file|cover) *= *([^|]*?) *[|]? *\\n', 'img') ;

pg.re.imageBracketCount = 6;

pg.re.category = RegExp('\\[\\[' +pg.ns.category + ': *([^|\\]]*[^|\\] ]) *', 'i');

pg.re.categoryBracketCount = 1;

pg.re.ipUser=RegExp('('+pg.ns.user+':)?' + '((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}' +

'(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])');

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)|[234]cc|shipindex)(\\s*[|][^}]*)?\\s*[}][}]' + // explicit, whole template names on this line

'|is a .*disambiguation.*page', 'im') ;

//

// FIXME replace with general parameter parsing function, this is daft

pg.re.oldid=RegExp('[?&]oldid=([^&]*)');

pg.re.diff=RegExp('[?&]diff=([^&]*)');

}

//

//////////////////////////////////////////////////

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

}

}

//

//////////////////////////////////////////////////

// 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*)/)[1];

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

//

//////////////////////////////////////////////////

// navlinks... let the fun begin

//

function defaultNavlinkSpec() {

var str='';

str += '<>';

if (getValueOf('popupLastEditLink')) {

str += '*<>|<>|<>if(oldid){|<>|<>}';

}

// user links

// contribs - log - count - email - block

// count only if applicable; block only if popupAdminLinks

str += 'if(user){
<>*<>';

str+='if(ipuser){*<>}if(wikimedia){*<>}';

str+='if(ipuser){}else{*<>}if(admin){*<>|<>}}';

// editing links

// talkpage -> edit|new - history - un|watch - article|edit

// other page -> edit - history - un|watch - talk|edit|new

var editstr='<>';

var editOldidStr='if(oldid){<>|<>|<>}else{' + editstr + '}'

var historystr='<>if(mainspace_en){|<>}';

var watchstr='<>|<>';

str+='
if(talk){' +

editOldidStr+'|<>' + '*' + historystr+'*'+watchstr + '*' +

'<>|<>' +

'}else{' + // not a talk page

editOldidStr + '*' + historystr + '*' + watchstr + '*' +

'<>|<>|<>'

+ '}';

// misc links

str += '
<>*<>*<>';

// admin links

str += 'if(admin){
<>|<>|<>*' +

'<>|<>|<>}';

return str;

}

function navLinksHTML (article, hint, oldid) {

var str = '' + defaultNavlinkSpec() + '';

// 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*([\\w]*)\\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[0];

for (var i=1; i

var testString=splitted[i+2-1];

var trueString=splitted[i+3-1];

var falseString=splitted[i+5-1];

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[i]; break;

case true: ret+=trueString; break;

case false: ret+=falseString; break;

}

// append non-conditional string

ret += splitted[i+numParens];

}

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

if (i%2) { // i odd, so s is a tag

var t=new navlinkTag();

var ss=splitted[i].split('|');

t.id=ss[0];

for (var j=1; j

var sss=ss[j].split('=');

if (sss.length>1) {

t[sss[0]]=sss[1];

}

else { // no assignment (no "="), so treat this as a title (overwriting the last one)

t.text=popupString(sss[0]);

}

}

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[i]);

}

}

return ret;

}

function navlinkSubstituteHTML(s) {

return s.split('*').join(getValueOf('popupNavLinkSeparator'))

.split('').join('

')

.split('

').join('').join('');

}

function navlinkDepth(magic,s) {

return s.split('<' + magic + '>').length - s.split('').length;

}

// navlinkString: * becomes the separator

// <> 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

if (typeof p[i] == typeof '') {

html+=navlinkSubstituteHTML(p[i]);

menudepth += navlinkDepth('menu', p[i]);

menurowdepth += navlinkDepth('menurow', p[i]);

// if (menudepth === 0) {

// tagType='span';

// } else if (menurowdepth === 0) {

// tagType='li';

// } else {

// tagType = null;

// }

} else if (typeof p[i].type != 'undefined' && p[i].type=='navlinkTag') {

if (menudepth > 0 && menurowdepth === 0) {

html += '

';

} else {

html+=p[i].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 = '';

}

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[this.id] === 'undefined') { this.newWin=null; }

this.newWin=pg.option.popupLinksNewWindow[this.id];

}

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[s.length-1];

if (this.text=='' && s.length > 1) { this.text=s[s.length-2]; }

}

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.oldid, this.title]);

}

}

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.oldid]);

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

//////////////////////////////////////////////////

//

// ENDFILE: navlinks.js

// STARTFILE: shortcutkeys.js

//

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

if (links[i]==popupHandleKeypress.lastPopupLinkSelected) { startLink=i; }

}

}

for (j=0; j

i=(startLink + j + 1) % links.length;

if (links[i].getAttribute('popupkey')==letter) {

if (evt && evt.preventDefault) evt.preventDefault();

links[i].focus();

popupHandleKeypress.lastPopupLinkSelected=links[i];

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 "... 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 ['+key+']$4');

}

//

// ENDFILE: shortcutkeys.js

// STARTFILE: diffpreview.js

//

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[label]=download;

completedNavpopTask(download.owner);

if (navpop.diffData[otherLabel]) { 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

if(!a[i].row || a[i].row===0) { aa[i]=1; }

else if (countCrossings(b,a,i, true)) {

aa[i]=1;

bb[ a[i].row ] = 1;

}

}

// pick up remaining disconnected nodes in b

for (i=0; i

if (bb[i]==1) { continue; }

if(!b[i].row || b[i].row===0) { bb[i]=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

if ( bb[i] == 1 ) {

for (j=max(0,i-context); j < min(b.length, i+context); ++j) {

if ( !bb[j] ) { bb[j] = 1; aa[ b[j].row ] = 0.5; }

}

}

}

for (i=0; i

if ( aa[i] == 1 ) {

for (j=max(0,i-context); j < min(a.length, i+context); ++j) {

if ( !aa[j] ) { aa[j] = 1; bb[ a[j].row ] = 0.5; }

}

}

}

for (i=0; i

if (bb[i] > 0) { // it's a row we need

if (b[i].row || b[i].row===0) { bbb.push(b[i].text); } // joined; partner should be in aa

else {

bbb.push(b[i]);

}

}

}

for (i=0; i

if (aa[i] > 0) { // it's a row we need

if (a[i].row || a[i].row===0) { aaa.push(a[i].text); } // joined; partner should be in aa

else {

aaa.push(a[i]);

}

}

}

return { a: aaa, b: bbb};

}

function stripOuterCommonLines(a,b,context) {

var i=0;

while (i

var j=a.length-1; var k=b.length-1;

while ( j>=0 && k>=0 && a[j]==b[k] ) { --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='


';

if (getValueOf('popupDiffDates')) {

html += diffDatesTable(navpop.diffData.Old, navpop.diffData.New);

html += '


';

}

html += shortenDiffString(

diffString(oldlines2.join('\n'), newlines2.join('\n'), simpleSplit),

getValueOf('popupDiffContextCharacters') ).join('


');

setPopupTipsAndHTML(html.split('\n').join('
') +

(truncated ? '


'+popupString('Diff truncated for performance reasons')+'' : '') ,

'popupPreview');

}

function diffDatesTable( oldDl, newDl ) {

var html='

';

html += diffDatesTableRow( newDl, tprintf('New revision'));

html += diffDatesTableRow( oldDl, tprintf('Old revision'));

html += '

';

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[datePrint] == 'function') {

txt = dl.lastModified[datePrint]();

} else {

txt = tprintf('Invalid %s %s', ['popupDiffDatePrinter', datePrint]);

}

}

var revlink = generalLink({url: dl.url.replace(/&.*?(oldid=[0-9]+).*/, '&$1'),

text: label, title: label});

return simplePrintf('%s%s', [ revlink, txt ]);

}

//

// ENDFILE: diffpreview.js

// STARTFILE: links.js

//

/////////////////////

// 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=[l.oldid, safeDecodeURI(l.article)];

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, [revisionString]);

}

else {

hint = simplePrintf(hint, [safeDecodeURI(l.article)]);

}

}

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('^([0-9]{2}:[0-9]{2}),\s*(.*[1-3][0-9]{3})$');

for (var i=0; i

if (RegExp('oldid='+oldid).test(links[i].href) && dateRe.test(links[i].innerHTML)) {

// help konqueror out by putting the time at the end

var ds = links[i].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(/([0-9]{2}:[0-9]{2}):00.*/, '$1');

editor=Title.fromURL(links[i+1].href).userName();

break;

}

}

if (date && editor && diff != 'prev') {

return simplePrintf(getValueOf('popupExtendedRevertSummary'), [date, editor, oldid]);

}

}

if (diff != 'prev') {

return simplePrintf(getValueOf('popupRevertSummary'), [ oldid ]);

}

return simplePrintf(getValueOf('popupRevertToPreviousSummary'), [ oldid ]);

}

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', [info.edits[0].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',

[info.userName, getValueOf('popupHistoryLimit'), friendlyName]));

return;

}

if(info.myLastEdit.index==0) {

alert(tprintf("%s seems to be the last editor to the page %s", [info.userName, friendlyName]));

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

if (!pg.current.links[i].navpopup) { continue; }

(nullify || banish) && pg.current.links[i].navpopup.banish();

nullify && (pg.current.links[i].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)',

[l.article.toString(true).split("'").join("\\'"), l.newWin]);

title=popupString('lastContribHint');

break;

case 'sinceMe':

jsUrl=simplePrintf('javascript:getDiffSinceMyEdit(\'%s\',%s)',

[l.article.toString(true).split("'").join("\\'"), l.newWin]);

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()', [l.id]);

var title=popupString(simplePrintf('%sHint', [l.id]));

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, [safeDecodeURI(l.article)]);

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

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

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='['+chs + chs.toLowerCase()+']';

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*([Cc]omputer[_ ](?:%2528|\()archaic(?:%2528|\)))\s*

// autoedit=s~\[\[([Cc]ad)\]\]~$1~g;s~\[\[([Cc]AD)[|]~[[Computer-aided%20design|~g

// get the page to edit from the title

try {

//var title=document.getElementsByTagName('h1')[0].innerHTML.replace(RegExp(' ', 'g'), '_');

var title=document.title.split(' - ');

title[title.length-1]='';

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~\\[\\['+currentArticleRegexBit+'\\]\\]~$1~g;';

cmd += 's~\\[\\['+currentArticleRegexBit+'[|]~[['+x.newTarget+'|~g';

} else {

cmd += 's~\\[\\['+currentArticleRegexBit+'\\]\\]~$1~g;';

cmd += 's~\\[\\['+currentArticleRegexBit+'[|](.*?)\\]\\]~$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 += '


';

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'),

[pg.current.article.split('_').join(' '), redirMatch ]),

clickButton: getValueOf('popupRedirAutoClick'), minor: true,

watch: getValueOf('popupWatchRedirredPages')})

, 'R');

ret += popupString(' to ');

}

else ret += popupString('Redirects') + popupString(' to ');

return ret;

}

else return '
' + 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', [uN]),

text: l.text});

}

function toolDbName(cookieStyle) {

var ret=null;

var theWiki=pg.wiki.hostname.split('.')[1];

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'), [ uN, toolDbName() ]);

break;

default:

url=simplePrintf(defaultToolUrl, [ uN, toolDbName(), tool ]);

}

return generalNavLink({url:url, title: tprintf('katelinkHint', [uN]), 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', [uN]), 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', [article]),

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', [article]),

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', [article]), 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('

  • ');

    for (var i=0; i

    var histRe=RegExp(

    '^[(].*?type="radio" value="([0-9]*)".*?class=\'history-user\'>' +

    '

    var match=histRe.exec(split[i]);

    if (match) {

    edits.push({ oldid: match[1], editor: match[4] });

    }

    }

    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['pages'];

    log('got p');

    for (var pageid in p) {

    var revisions=p[pageid]['revisions'];

    // 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

    edits.push({ oldid: revisions[i]['revid'], editor: revisions[i]['user'] });

    }

    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

    if (typeof histInfo.myLastEdit == 'undefined' && userName && edits[i].editor==userName) {

    histInfo.myLastEdit={index: i, oldid: edits[i].oldid, previd: (i==0 ? null : edits[i-1].oldid)};

    }

    if (typeof histInfo.firstNewEditor == 'undefined' && edits[i].editor != edits[0].editor) {

    histInfo.firstNewEditor={index:i, oldid:edits[i].oldid, previd: (i==0 ? null : edits[i-1].oldid)};

    }

    }

    //pg.misc.historyInfo=histInfo;

    return histInfo;

    }

    //

    // 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[x]=val;

    return;

    }

    }

    if (pg.option[x]===null || typeof pg.option[x]=='undefined') {

    if (typeof window[x] != 'undefined' ) pg.option[x]=window[x];

    else pg.option[x]=pg.optionDefault[x];

    }

    }

    function newOption(x, def) {

    pg.optionDefault[x]=def;

    }

    function setDefault(x, def) {

    return newOption(x, def);

    }

    function getValueOf(varName) {

    defaultize(varName);

    return pg.option[varName];

    }

    function useDefaultOptions() { // for testing

    for (var p in pg.optionDefault) {

    pg.option[p]=pg.optionDefault[p];

    if (typeof window[p]!='undefined') { delete window[p]; }

    }

    }

    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

    //

    // 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', '');

    //

    // 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);

    //

    // 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') );

    //

    // misc

    newOption('popupLiveOptions', false);

    newOption('popupLiveOptionsExpanded', false);

    newOption('popupCookies', false);

    newOption('popupHistoryLimit', 50);

    //

    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);

    //

    // new windows

    newOption('popupNewWindows', false);

    newOption('popupLinksNewWindow', {'lastContrib': true, 'sinceMe': true});

    }

    // ENDFILE: options.js

    // STARTFILE: strings.js

    //

    //////////////////////////////////////////////////

    // Translatable strings

    //////////////////////////////////////////////////

    //

    // See instructions at

    // http://en.wikipedia.org/wiki/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 Wikipedia 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 %s using javascript',

    'defaultpopupFixDabsSummary': 'Disambiguate %s to %s using javascript',

    'defaultpopupFixRedirsSummary': 'Redirect bypass from %s to %s using javascript',

    'defaultpopupExtendedRevertSummary': 'Revert to revision dated %s by %s, oldid %s using javascript',

    'defaultpopupRevertToPreviousSummary': 'Revert to the revision prior to revision %s using javascript',

    'defaultpopupRevertSummary': 'Revert to revision %s using javascript',

    'defaultpopupQueriedRevertToPreviousSummary': 'Revert to the revision prior to revision dated $2 by $3 (talk) using popups',

    'defaultpopupQueriedRevertSummary': 'Revert to revision dated $2 by $3 (talk) using javascript',

    'defaultpopupRmDabLinkSummary': 'Remove link to dab page %s using javascript',

    '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 %s',

    '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[str]) { return popupStrings[str]; }

    if (pg.string[str]) { return pg.string[str]; }

    return str;

    }

    function tprintf(str,subs) {

    if (typeof subs != typeof []) { subs = [subs]; }

    return simplePrintf(popupString(str), subs);

    }

    //

    // ENDFILE: strings.js