// CONTENTS
// Utility functions
// Popup stuff
// global variables
// html generation
// downloading
// link generation
// manipulation functions
// tests
// actions
// thingies
////////////////////////////////////////////////////////////////////
// Utility functions
////////////////////////////////////////////////////////////////////
function time() {
var d=new Date();
return d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds() +
'.' + (d.getTime() % 1000);
};
var gMsg='';
function log(x) { if(gMsg!='')gMsg += '\n'; gMsg+=time() + ' ' + x; };
function myalert(x) { return alert(time()+'\n'+ x); };
// eg sourceJS('http://www.bosrup.com/web/overlib/overlib.js');
function sourceJS(url) {
var str='<script type="text/javascript" src="';
str += url;
str += '"></script>';
return document.write(str);
};
// eg sourceWikipediaJS('en.wiki.x.io', 'User:Lupin/overlib.js');
function sourceWikipediaJS(wiki, name) {
var url='http://' + wiki + '/w/index.php?title=';
url += name;
url += '&action=raw&ctype=text/javascript&dontcountme=s';
return sourceJS(url);
};
// eg sourceLupinJS('overlib');
function sourceLupinJS(name) {
return sourceWikipediaJS('en.wiki.x.io', 'User:Lupin/'+name + '.js');
};
////////////////////////////////////////////////////////////////////
// Popup stuff
////////////////////////////////////////////////////////////////////
sourceLupinJS('livepreview');
sourceLupinJS('overlib');
sourceLupinJS('md5-2.2alpha');
// this shouldn't be needed. maybe my cache needs purging...
function md5_hex(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); };
//////////////////////
// GLOBAL VARIABLES //
//////////////////////
// regexes
var exceptions=/((title=|\/)Special:|section=[0-9])/ ;
var contributions=/(title=|\/)Special:Contributions(&target=|\/|\/User:)(.*)/ ;
var emailuser=/(title=|\/)Special:Emailuser(&target=|\/|\/User:)(.*)/ ;
var talk=/Talk:/i ;
var imageRegex= /(^|\[\[)image: *([^|\]]*[^|\] ]) */img ;
var imageRegexBracketCount = 2;
var categoryRegex= /\[\[category: *([^|\]]*[^|\] ]) */i ;
var categoryRegexBracketCount = 1;
var stubRegex= /stub[}][}]|This .*-related article is a .*stub/im ;
var disambigRegex= /[{][{]disambig|is a .*disambiguation.*page/im ;
var re;
var splitLoc=window.location.href.split('/');
var thisWiki=splitLoc[2];
var protocol=splitLoc[0].split(':')[0];
var titletail='/w/index.php?title=';
// we're not set up for interwiki stuff yet - only affect en, commons
// and wiktionary links
if (thisWiki=='commons.wikimedia.org') {
re=/[^:]*:\/\/commons\.wikimedia\.org\/w(iki\/|\/index\.php\?title=)([^&]*)/ ;
} else if (thisWiki=='en.wiktionary.org') {
re=
/[^:]*:\/\/en\.wiktionary\.org\/w(iki\/|\/index\.php\?title=)([^&]*)/ ;
} else {
re=/[^:]*:\/\/en\.wikipedia\.org\/w(iki\/|\/index\.php\?title=)([^&]*)/ ;
}
var titlebase=protocol+'://'+thisWiki+titletail;
var wikibase=protocol+'://'+thisWiki+'/wiki/';
var imageSources=new Array ();
imageSources.push(
{active: false, wiki: thisWiki, thumb: true, width: popupImageSize},
{active: false, wiki: thisWiki, thumb: true, width: 180}, // default
{active: false, wiki: thisWiki, thumb: true, width: 120}, // gallery
{active: false, wiki: thisWiki, thumb: true, width: 200}, // common?
{active: false, wiki: thisWiki, thumb: true, width: 210},
{active: false, wiki: thisWiki, thumb: true, width: 230},
{active: false, wiki: thisWiki, thumb: true, width: 250}, // common?
{active: false, wiki: thisWiki, thumb: true, width: 300},
{active: false, wiki: thisWiki, thumb: false, width: 0} // no comma
);
if (thisWiki!='commons.wikimedia.org') {
imageSources.push(
{active: false, wiki: 'commons.wikimedia.org',
thumb: true, width: popupImageSize},
{active: false, wiki: 'commons.wikimedia.org',
thumb: true, width: 180},
{active: false, wiki: 'commons.wikimedia.org',
thumb: true, width: 120},
{active: false, wiki: 'commons.wikimedia.org',
thumb: false, width: 0} // no trailing comma
);
}
// downloading images are put here
var imageArray=new Array();
// page caching
var gCachedPages = new Array ();
var gImageCache = new Array();
// FIXME what is this for?
var gImage=null; // global for image
// check to see if images are done with this timer
var popupImageTimer=null;
// misc debug messages
var popupDebug=null;
// These are for checkImages()
var counter=0;
var checkImagesTimer=null;
var loopcounter=0;
// ids change with each popup: popupImage0, popupImage1 etc
var popupImageId=0;
var kateBase='http://kohl.wikimedia.org/~kate/cgi-bin/count_edits'
+ '?dbname=enwiki&user='
// for myDecodeURI
var decodeExtras = new Array ();
decodeExtras.push (
{from: '%2C', to: ',' },
{from: '_', to: ' ' },
{from: '%26', to: '&' } // no ,
);
// for setPopupHTML - needed for timers and stuff
var popupHTMLTimers=new Array();
var popupHTMLLoopFunctions = new Array();
// FIXME - eliminate this
var redirCount=0;
var popupImagesToggleSize=true;
var popupImageSize=60;
// user-settable parameters and defaults
if (typeof popupDelay == 'undefined') { var popupDelay=null; }
var dpopupDelay=0.5;
if (typeof popupFgColor == 'undefined') { var popupFgColor=null; }
var dpopupFgColor='#CCCCFF';
if (typeof popupBgColor == 'undefined') { var popupBgColor=null; }
var dpopupBgColor='#333399';
if (typeof removeTitles == 'undefined') { var removeTitles=null; }
var dremoveTitles=true;
if (typeof imagePopupsForImages == 'undefined') { var imagePopupsForImages=null; }
var dimagePopupsForImages=true;
if (typeof extraPageInfo == 'undefined') { var extraPageInfo = null; }
var dextraPageInfo=true;
if (typeof simplePopups == 'undefined') { var simplePopups=null; }
var dsimplePopups = false;
if (typeof downloadImages == 'undefined') { var downloadImages=null; }
var ddownloadImages=true;
if (typeof popupPreviews == 'undefined') { var popupPreviews=null; }
var dpopupPreviews=true;
/////////////////////
// HTML GENERATION //
/////////////////////
// generate html for popup image
// <a id="popupImageLinkn"><img id="popupImagen">
// where n=popupImageId
function imageHTML(article) {
var ret='';
popupImageId++;
ret+='<a id="popupImageLink' + popupImageId + '">';
ret += '<img ' + // src="' + imgurl + '" ' +
'width=' + popupImageSize +
' align="right" valign="top" + id="popupImage' + popupImageId
+ '" style="display: none;"></img>';
ret+='</a>';
return ret;
};
function isInToc(a) {
var obj = a;
var i=0;
do {obj = obj.parentNode; ++i; }
while (obj.id != 'toc' && obj.nodeName != 'HTML');
/*
log('traversed '+i+' elements,' +
'arriving at obj.nodeName=' +obj.nodeName+
', obj.id=' + obj.id);
*/
if (obj.nodeName == 'HTML') return false;
return true;
}
function articleFromAnchor(a) {
log('articleFromAnchor');
var h=a.href;
var article=null;
log('h='+h);
var contribs=contributions.exec(h);
if (contribs != null) {
article='User:'+contribs[3];
return article;
}
var email=emailuser.exec(h);
if (email != null) {
article='User:'+email[3];
return article;
}
// no more special cases to check --
// hopefully it's not a disguised user-related page
var m=re.exec(h);
if(m===null) return null;
article=m[2];
return article;
};
// Generate html for whole popup
// this is ugly
function popupHTML (a) {
var c=a.className;
// if (c=='new') alert('new!');
var article = articleFromAnchor(a);
var hint=a.originalTitle;
if (hint == '' || hint == null)
hint = myDecodeURI(article);
var html='';
html +=imageHTML(article);
var simplifyMainLink = true;
var visibleMainLinkText=myDecodeURI(article);
if ( simplifyMainLink ) {
var s= visibleMainLinkText.split('/');
visibleMainLinkText = s[s.length-1];
if (visibleMainLinkText == '' && s.length > 1)
{
// shouldn't happen...
visibleMainLinkText=s[s.length-2];
}
}
html+='<b>';
html+=titledWikiLink(article,
'view',
visibleMainLinkText,
hint);
html+='</b>';
html+='<span id="popupImageStatus'+popupImageId+'"></span>';
// Get rid of anchor now
article=removeAnchor(article);
if (userName(article) != null) {
html += '<br>' + contribsLink(article, 'contribs');
html += ' ⋅ ' + kateLink(article, 'count');
html += ' ⋅ ' + emailLink(article, 'email');
}
html += '<br>' + wikiLink(article, 'edit', 'edit');
var ta=articleFromTalkPage(article);
if (ta != null) html +='|' +
wikiLink(article, 'edit§ion=new', 'new');
html += ' ⋅ ' + wikiLink(article, 'history', 'history');
html += ' ⋅ ' + wikiLink(article, 'unwatch', 'un') + '|';
html += wikiLink(article, 'watch', 'watch');
var t=talkPage(article);
if (t != null) html += ' ⋅ ' +
'<b>' + wikiLink(t, 'view', 'talk') + '</b>' +
'|' + wikiLink(t, 'edit', 'edit') +
'|' + wikiLink(t, 'edit§ion=new', 'new');
if (ta != null) html +=' ⋅ ' +
'<b>' + wikiLink(ta, 'view', 'article') + '</b>' +
'|' + wikiLink(ta, 'edit', 'edit');
html += '<br>' + specialLink(article, 'Whatlinkshere', 'whatLinksHere');
html += ' ⋅ ' + specialLink(article, 'Recentchangeslinked', 'relatedChanges');
html += '<span id="popupWarnRedir' + popupImageId + '"></span>';
html += '<span id="popupGubbins' + popupImageId + '"></span>';
html += '<span id="popupPreview' + popupImageId + '"></span>';
return html;
};
/////////////////
// DOWNLOADING //
/////////////////
//////////////
//
// downloader
//
//
function downloader(url) {
// Source: http://jibbering.com/2002/4/httprequest.html
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();
}
this.url = url;
this.id=null;
this.callbackFunction = null;
if (this.http) {
// public
this.send = this.http.send;
this.abort = this.http.abort;
}
else this.http=false;
};
new downloader();
downloader.prototype.setCallback = function (f) {
if(!this.http) return;
this.http.onreadystatechange = f;
this.callbackFunction = f;
};
downloader.prototype.runCallback = function () {
this.callbackFunction(this);
};
downloader.prototype.getData = function () {
if(!this.http) return;
return this.http.responseText;
};
downloader.prototype.setTarget = function () {
if(!this.http) return;
this.http.open("GET", this.url, true);
};
downloader.prototype.start=function () {
// alert('downloader instance got told to start()');
if(!this.http) return;
return this.http.send(null);
};
downloader.prototype.getReadyState=function () {
if(!this.http) return;
return this.http.readyState;
};
function newDownload(url, id, callback) {
var d=new downloader(url);
d.id=id;
d.setTarget();
var f = function () {
if (d.getReadyState() == 4)
{ d.data=d.getData(); callback(d);}
};
d.setCallback(f);
return d;//d.start();
};
function fakeDownload(url,id,callback,data) {
var d=newDownload(url,callback);
d.id=id;
d.data=data;
return callback(d);
};
function startDownload(url, id, callback) {
var d=newDownload(url, id, callback);
d.start();
};
//
//
// downloader
//
//////////////
// Schematic for a getWiki call
//
// getWiki->-getPageWithCaching
// |
// false | true
// getPage<-[findPictureInCache]->-onComplete(a fake download)
// \.
// (async)->addPageToCache(download)->-onComplete(download)
function getWiki(wikipage, onComplete) {
log('getWiki, wikipage='+wikipage);
var url = titlebase + removeAnchor(wikipage) + '&action=raw';
return getPageWithCaching(url, onComplete);
};
// check cache to see if page exists
function getPageWithCaching(url, onComplete) {
log ('getPageWithCaching, url='+url);
var i=findInPageCache(url);
if (i > -1) {
return fakeDownload(url, popupImageId, onComplete, gCachedPages[i].data);
}
return getPage(url, onComplete);
};
function getPage(url, onComplete) {
log ('getPage, url='+url);
var callback= function (d) {
log('callback from getPage activated');
addPageToCache(d); onComplete(d) } ;
return startDownload(url, popupImageId, callback);
};
function findInPageCache(url) {
for (var i=0; i<gCachedPages.length; ++i) {
if (url==gCachedPages[i].url) {
log('found url at index '+i);
return i;
}
}
log('did not find url='+url);
return -1;
};
function cachedPage (url,data) {
this.url=url;
this.data=data;
};
function addPageToCache(download) {
log ('addPageToCache, page.url='+download.url);
/*
log ('addPageToCache now calling findInPageCache');
if (findInPageCache(download) > -1) {
log ('not adding - already there'); return;
}
log ('new page - adding');
*/
var page = new cachedPage(download.url, download.data);
return gCachedPages.push(page);
};
/*
var gCurrentDownload = null;
function abortCurrentDownload(download) {
if (gCurrentDownload) {
try { gCurrentDownload.abort(); }
catch (anerror) {return 'could not abort download object';}
}
return true;
}
*/
/////////////////////
// LINK GENERATION //
/////////////////////
function wikiLink(article, action, text) {
var prehint=null;
switch (action) {
case 'edit': prehint = 'Edit '; break;
case 'history': prehint = 'Show history for '; break;
case 'unwatch': prehint = 'Stop watching '; break;
case 'watch': prehint = 'Watch '; break;
case 'view': prehint = 'Go to '; break;
case 'edit§ion=new': prehint = 'Start a new topic on '; break;
default: true;
}
var hint;
if (prehint != null) hint=prehint + myDecodeURI(article);
else prehint = myDecodeURI(article + '&action=' + action);
return titledWikiLink(article, action, text, hint);
};
function titledWikiLink(article, action, text, title) {
var base = titlebase + article;
var url=base;
// no need to add action&view, and this confuses anchors
if (action != 'view') url = base + '&action=' + action;
var hint;
if (title == null || title == '')
hint = ''
else
hint = 'title="' + title + '"';
return '<a href="' + url + '" ' + hint + '>' + text + '</a>';
};
function specialLink(article, specialpage, text) {
var base = titlebase + 'Special:'+specialpage;
var url = base + '&target=' + article;
var prehint=null;
switch (specialpage) {
case 'Whatlinkshere':
prehint='Show the articles which link to '; break;
case 'Recentchangeslinked':
prehint='Show recent changes in articles related to '; break;
case 'Contributions':
prehint='Show the contributions made by '; break;
case 'Emailuser':
prehint='Email '; break;
}
var hint;
if (prehint != null) hint = prehint + myDecodeURI(article);
else hint = myDecodeURI(specialpage+':'+article) ;
return '<a href="' + url + '" title="' + hint + '">' + text + '</a>';
};
function redirLink(redirMatch) { /* NB redirMatch is in wikiText */
log ('making redirLink for page [['+redirMatch+']]');
var ret=titledWikiLink(myEncodeURI(redirMatch),
'view',
myDecodeURI(redirMatch),
'Bypass redirect');
return ret;
};
function doNotRedirLink(redirPage, linkText, hintText) { /* NB redirPage is in wikiText */
log('making doNotRedirLink for page(?) [['+redirPage+']]');
var ret=titledWikiLink(myEncodeURI(redirPage),
'edit',
linkText,
hintText);
return ret;
};
function contribsLink(article, text) {
return specialLink(userName(article), 'Contributions', text);
};
function emailLink(article, text) {
return specialLink(userName(article), 'Emailuser', text);
};
function kateLink(article, text) {
var uN=myDecodeURI(userName(article));
return '<a href="' + kateBase + uN + '" title="'
+ 'Count the contributions made by ' + uN + '">' + text + '</a>';
};
////////////////////////////
// MANIPULATION FUNCTIONS //
////////////////////////////
function upcaseFirst(str) {
return str[0].toUpperCase() + str.substring(1);
};
function formatBytes(num) {
ret = (num > 949) ? (Math.round(num/100)/10+'kB') :
(num +' bytes' ) ;
return ret;
}
function getPageInfo(data) {
var numImages = countImages(data);
var numLinks = countLinks(data);
var numCategories = countCategories(data);
var stats='c. ';
stats += formatBytes(data.length);
stats += ', ';
stats +=numLinks + ' wikiLink' + ((numLinks!=1)?'s, ':', ');
stats +=numImages + ' image' + ((numImages!=1)?'s, ':', ');
stats +=numCategories + ' categor' + ((numCategories!=1)?'ies':'y');
var pageInfo='';
if (isStub(data)) pageInfo+='stub, ';
if (isDisambig(data)) pageInfo += 'disambig, ';
if (pageInfo != '' )
pageInfo = upcaseFirst(pageInfo);
return pageInfo + stats;
};
function getValidImageFromWikiText(wikiText) {
var imagePage=null;
// nb in imageRegex we're interested in the second bracketed expression
// this may change if the regex changes :-(
//var match=imageRegex.exec(wikiText);
var matched=null;
var match;
while ( match = imageRegex.exec(wikiText)) {
/* now find a sane image name - exclude templates by seeking { */
var m = match[2];
log('is '+m+' a valid name for an image?');
if ( isValidImageName(m) ) { matched=m;
log('yes!');
break;}
log('no...');
}
imageRegex.lastIndex=0;
if (!matched) return null;
if (matched[0] >= 'a' && matched[0] <= 'z') {
// upcase first character if ascii
matched = upcaseFirst(matched);
}
imagePage='Image:'+matched;
return imagePage;
};
function countLinks(wikiText) {
// this could be improved!
return wikiText.split('[[').length - 1;
};
// if N = # matches, n = # brackets, then
// String.split(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.split(imageRegex).length - 1) /
(imageRegexBracketCount + 1);
};
function countCategories(wikiText) {
return (wikiText.split(categoryRegex).length - 1) /
(categoryRegexBracketCount + 1);
};
function talkPage(article) {
if (article.indexOf('Talk:') > -1 || article.indexOf('talk:') > -1 )
return null;
var i=article.indexOf(':');
if (i == -1) return 'Talk:'+article;
else return article.substring(0,i)+'_talk:' + article.substring(i+1);
};
function articleFromTalkPage(talkpage) {
var i=talkpage.indexOf('Talk:');
var j=talkpage.indexOf('_talk:');
if ( i == -1 && j == -1 )
return null;
if ( i > -1 ) return talkpage.substring(i+5);
return talkpage.split('_talk:').join(':');
};
function userName(article) {
var i=article.indexOf('User');
var j=article.indexOf(':');
if (i != 0 || j < -1) return null;
var k=article.indexOf('/');
if (k==-1) return article.substring(j+1);
else return article.substring(j+1,k);
};
function stripNamespace(article) {
// this isn't very sophisticated
// it just removes everything up to the final :
var list = article.split(':');
return list[list.length-1];
};
function imagePathComponent(article) {
if (isImage(article)) {
var stripped=stripNamespace(article);
var forhash=myDecodeURI(stripped).split(' ').join('_');
var hash=md5_hex(forhash);
var pathcpt=hash.substring(0,1) + '/' + hash.substring(0,2) + '/';
return pathcpt;
}
else return null;
};
function getImageUrlStart(wiki) { // this returns a trailing slash
switch (wiki) {
case 'en.wiki.x.io':
return 'http://up.wiki.x.io/wikipedia/en/';
case 'commons.wikimedia.org':
return 'http://up.wiki.x.io/wikipedia/commons/';
case 'en.wiktionary.org':
return 'http://en.wiktionary.org/upload/en/';
default: // unsupported - take a guess
var lang=wiki.split('.')[0];
return 'http://' + wiki + '/upload/' + lang +'/';
}
}
function imageURL(img, wiki) {
if (popupDebug > 10) alert ('imageURL\n\nimg=' + img + '\nwiki='+wiki);
var imgurl=null;
if (isImage(img)) {
var pathcpt = imagePathComponent(img);
var stripped=stripNamespace(img);
imgurl=getImageUrlStart(wiki) + pathcpt + stripped;
}
return imgurl;
};
function imageThumbURL(img, wiki, width) {
//
// eg http://up.wiki.x.io/wikipedia/en/thumb/6/61/
// Rubiks_cube_solved.jpg/120px-Rubiks_cube_solved.jpg
var imgurl=null;
if (isImage(img)) {
var pathcpt = imagePathComponent(img);
var stripped=stripNamespace(img);
imgurl=getImageUrlStart(wiki) + "thumb/"
+ pathcpt + stripped + '/' + width +"px-" + stripped;
}
return imgurl;
};
// (a) myDecodeURI (first standard decodeURI, then exceptions)
// (b) change spaces to underscores
// (c) encodeURI (just the straight one, no exceptions)
function wikiMarkupToAddressFragment (str) { // for images
var ret = myDecodeURI(str);
ret = ret.split(' ').join('_');
ret = encodeURI(ret);
return ret;
};
function addressFragmentToWikiMarkup (str) {
// seemingly, not :( the inverse of wikiMarkupToAddressFragment
log ('addressFragmentToWikiMarkup\nstr='+str);
var ret = myDecodeURI(str);
/* ret = ret.split('_').join(' '); */
/* ret = myEncodeURI(str); */
log('addressFragmentToWikiMarkup\nret='+ret);
return ret;
};
function myDecodeURI (str) {
var ret=decodeURI(str);
for (var i=0; i<decodeExtras.length; ++i) {
var from=decodeExtras[i].from;
var to=decodeExtras[i].to;
ret=ret.split(from).join(to);
}
log ('myDecodeURI: ' +str+ ' to ' +ret);
return ret;
};
function myEncodeURI (str) {
log ('myEncodeURI: str='+str);
var ret=str;
ret=encodeURI(ret);
log (' : after encodeURI, ret=' +ret);
for (var i=0; i<decodeExtras.length; ++i) {
var from=decodeExtras[i].from;
var to=decodeExtras[i].to;
ret=ret.split(to).join(from);
}
log (' : after decodeExtras, ret='+ret);
return ret;
};
function removeAnchor(article) {
// is there a #? if not, we're done
var i=article.indexOf('#');
if (i == -1) return article;
// head#tail
var head = article.substring(0,i);
var tail = article.substring(i+1);
return head;
};
///////////
// TESTS //
///////////
function isStub(data) { return stubRegex.test(data); }
function isDisambig(data) { return disambigRegex.test(data); }
function isValidImageName(str){ // extend as needed...
return ( str.split('{').length == 1 );
};
function isInNamespace(article, namespace) {
var i=article.indexOf(namespace+':');
var j=article.indexOf(namespace+'_talk:');
if (i == -1 && j == -1) return false;
return true;
};
function isImage(thing) {
return isInNamespace(thing, 'Image');
};
function isImageOk(img)
{
// IE test
if (!img.complete)
return false;
// gecko test
if (typeof img.naturalWidth != "undefined" && img.naturalWidth == 0)
return false;
// No other way of checking: assume it's ok.
return true;
};
function anchorContainsImage(a) {
// iterate over children of anchor a
// see if any are images
if (a===null) return false;
kids=a.childNodes;
for (var i=0; i<kids.length; ++i) {
if (kids[i].nodeName=='IMG') return true;
}
return false;
};
/////////////
// ACTIONS //
/////////////
var imageCache = new Array ();
function loadThisImage (image) {
if (!downloadImages) return;
var msg = '';
msg += 'loadThisImage; image=' + image;
msg += '\nimagePathComponent(image) = ' + imagePathComponent;
var stripped=stripNamespace(image);
var forhash=myDecodeURI(stripped).split(' ').join('_');
var hash=md5_hex(forhash);
var pathcpt=hash.substring(0,1) + '/' + hash.substring(0,2) + '/';
msg +='\n\nbreakdown:\n stripped==stripNamespace(image)='+stripped;
msg +='\nforhash=myDecodeURI(stripped).split(" ").join("_")='+forhash;
msg +='\nhash=md5_hex(forhash)-' +hash;
msg +='\npathcpt='+pathcpt;
if (!isValidImageName(image)) return false;
if(popupDebug != null)
alert(msg);
msg='List of urls:\n';
var imageUrls=new Array();
for (var i=0; i<imageSources.length; ++i) {
var url;
if (imageSources[i].thumb)
url=imageThumbURL(image, imageSources[i].wiki, imageSources[i].width);
else
url=imageURL(image, imageSources[i].wiki);
for (var j=0; j<gImageCache.length; ++j) {
if (url == gImageCache[j]) return loadThisImageAtThisUrl(image, url);
}
imageUrls.push(url);
}
msg='imageUrls:\n';
for (var i=0; i<imageUrls.length; ++i) {
var url = imageUrls[i];
imageSources[i].active=false;
msg += '\n'+url;
imageArray[i]=new Image();
imageArray[i].src=url;
}
//myalert(msg);
if (popupDebug) alert (msg);
if (popupImageTimer != null) {
clearInterval(popupImageTimer);
counter=0;
}
gImage=image;
popupImageTimer=setInterval("checkImages()", 250);
return;
};
function loadThisImageAtThisUrl(image, url) {
//myalert('loading "best" image:\n'+url);
gImage=image;
imageArray = new Array();
imageArray[0] = new Image();
imageArray[0].src=url;
if (popupImageTimer != null) {
clearInterval(popupImageTimer);
counter=0;
}
popupImageTimer=setInterval("checkImages()", 250);
return;
}
function loadImages(article) {
if(! isImage(article) ) return;
if (popupDebug) alert('loadImages, article='+article);
return loadThisImage(article);
};
function setPopupHTML (str, elementId, popupId) {
if (typeof popupId === 'undefined') popupId = popupImageId;
var popupElement=
document.getElementById(elementId+popupId);
var timer;
if (typeof popupHTMLTimers[elementId] == 'undefined') {
timer=null;
}
else {
timer=popupHTMLTimers[elementId];
}
if (popupElement != null) {
if(timer) clearInterval(timer);
popupHTMLTimers[elementId]=null;
popupElement.innerHTML=str;
log('setPopupElement found the '+elementId+popupId+ ' element' +
'\nstr='+str+
'\npopupElement.innerHTML=' + popupElement.innerHTML);
return true;
} else {
log('setPopupElement did not find the '+elementId+popupId+ ' element' +
'\nstr='+str);
var loopFunction=function() { setPopupHTML(str,elementId,popupId);}
popupHTMLLoopFunctions[elementId] = loopFunction;
if (!timer) {
var doThis = 'popupHTMLLoopFunctions["'+elementId+'"]()';
popupHTMLTimers[elementId] = setInterval(doThis, 600);
}
}
};
function setImageStatus(str, id) {
return setPopupHTML(str, 'popupImageStatus', id);
};
function setPopupTrailer(str,id) {
return setPopupHTML(str, 'popupGubbins', id);}
function checkImages() {
if (checkImagesTimer!=null) {
clearInterval(checkImagesTimer);
checkImagesTimer=null;
if (loopcounter > 10); {loopcounter=0; return;}
loopcounter++;
} else counter++;
var status = ( counter % 2 ) ? ':' : '.' ;
setImageStatus(status);
if (counter > 100) {counter = 0; clearInterval(popupImageTimer);}
var popupImage=null;
popupImage=document.getElementById("popupImage"+popupImageId);
if (popupImage == null) {
// this doesn't seem to happen any more in practise for some reason
// still, I'll leave it in
checkImagesTimer=setInterval("checkImages()",333);
return;
}
// get the first image to successfully load
// and put it in the popupImage
for(var i = 0; i < imageArray.length; ++i) {
if(isImageOk(imageArray[i])) {
// stop all the gubbins, assign the image and return
clearInterval(popupImageTimer);
if(isImage(gImage)) {
popupImage.src=imageArray[i].src;
popupImage.style.display='inline';
imageSources[i].active=true;
// should we check to see if it's already there? maybe...
gImageCache.push(imageArray[i].src);
//myalert('gImageCache='+gImageCache.join('\n'));
setPopupImageLink(gImage, imageSources[i].wiki);
stopImagesDownloading();
}
setImageStatus('');
// reset evil nonconstant globals
delete imageArray; imageArray=new Array();
popupImageTimer=null;
counter=0;
loopcounter=0;
return popupImage.src;
}
}
};
function stopImagesDownloading() {
gImage=null;
if (imageArray == null) return null;
var i;
for (i=0; i<imageArray.length; ++i) {
//imageArray[i].src=''; // this is a REALLY BAD IDEA
delete imageArray[i];
//imageArray[i] = new Image();
}
imageArray = new Array ();
return i;
};
function toggleSize() {
var imgContainer=this;
if (!imgContainer) { alert('imgContainer is null :/'); return;}
img=imgContainer.firstChild;
if (!img) { alert('img is null :/'); return;}
var msg='';
for (var i=0; i<imgContainer.childNodes.length; ++i)
msg += '\nimgContainer.childNodes['+i+'].width=' +
imgContainer.childNodes[i].width;
if (!img.style.width || img.style.width=='')
img.style.width='100%';
else img.style.width=''; // popupImageSize+'px';
};
function setPopupImageLink (img, wiki) {
if( wiki === null || img === null ) return null;
var a=document.getElementById("popupImageLink"+popupImageId);
if (a === null) return null;
var linkURL = imageURL(img, wiki);
if (linkURL != null) {
if (popupImagesToggleSize) {
a.onclick=toggleSize;
a.title='Toggle image size';
} else {
a.href=linkURL;
a.title='Open full-size image';
}
}
return linkURL;
};
function setupTooltips() {
debugsimple('top of setupTooltips');
if (typeof window.opera != 'undefined')
dsimplePopups=true;
// debugsimple('couple of lines later, after opera hack');
var anchors=document.getElementsByTagName('A');
// alert(anchors.length + 'anchors');
var s='';
if (removeTitles==null) removeTitles=dremoveTitles;
for (var i=0; i<anchors.length; ++i)
{
var a=anchors[i];
var h=a.href;
var contribs=contributions.exec(h);
var email=emailuser.exec(h);
var exc=exceptions.exec(h);
var m=re.exec(h);
if (
(! isInToc(a)) &&
(contribs != null || (exc == null && m != null) )
) {
a.onmouseover=mouseOverWikiLink;
a.onmouseout= mouseOutWikiLink;
a.onclick= killPopup;
if (removeTitles) {
a.originalTitle=a.title;
a.title='';
}
}
}
};
//////////////
// THINGIES //
//////////////
// How the URLs for images in the popup come about
// loadPreviewImage
// |
// getWiki
// |<----------------see other schematic for details
// insertPreviewImage (insertPreviewImage = onComplete)
// |
// | insertPreviewImage 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
var currentLink=null;
function mouseOverWikiLink() {
// FIXME: should not generate the HTML until the delay has elapsed,
// and then popup immediately. Can be a CPU hog otherwise.
/* // good idea? dunno
if ( typeof o3_showingsticky != "undefined" && o3_showingsticky != 0 ) {
return;
}
*/
var a=this;
if (a==currentLink) return;
else currentLink=a;
var html = popupHTML(a);
var article=articleFromAnchor(a);
if (popupImageTimer != null) {
clearInterval(popupImageTimer);
counter=0;
}
if (popupDelay==null) popupDelay=dpopupDelay;
if (popupFgColor==null) popupFgColor=dpopupFgColor;
if (popupBgColor==null) popupBgColor=dpopupBgColor;
if (downloadImages==null) downloadImages=ddownloadImages;
log('running overlib now');
var setmaxwidth = function () { over.style.maxWidth = '300px'; }
registerHook("createPopup", setmaxwidth, FAFTER);
overlib(html,
STICKY,
/* MOUSEOFF, */
WRAP,
CELLPAD, 5,
OFFSETX, 2,
OFFSETY, 2,
DELAY, popupDelay*1000,
FGCOLOR, popupFgColor,
BGCOLOR, popupBgColor);
debugsimple('just after overlib call');
if (simplePopups===null)
simplePopups = dsimplePopups;
debugsimple('after default assignment');
if(simplePopups) return;
debugsimple('simplePopups was false, so we continue...');
if (imagePopupsForImages===null)
imagePopupsForImages = dimagePopupsForImages;
var previewImage=true;
gImage=null;
if (
isImage(article) &&
( imagePopupsForImages || ! anchorContainsImage(a) )
)
{
loadImages(article);
}
else if (!isImage(article) && previewImage) {
redirCount=0;
loadPreviewImage(article);
}
};
function loadPreviewImage(article) {
/* var imgStatus;
imgStatus='';
for (var i=0; i<redirCount+2; ++i) imgStatus += '.';
setImageStatus(imgStatus); */
var ret=getWiki(article, insertPreviewImage);
return ret;
};
function loadPreviewImageFromRedir(redirPage, redirMatch) {
/* redirMatch is a regex match */
var target = redirMatch[1];
var trailingRubbish=redirMatch[2];
log ('found a first redirect to ' + target);
++redirCount;
var warnRedir='<br>Redirects to ';
warnRedir += redirLink(target);
if (trailingRubbish.length > 0) {
log ('found trailing rubbish: in redirect to\n'
+ target + '\n\n' + trailingRubbish);
warnRedir += ', '+formatBytes(trailingRubbish.length);
warnRedir += ' ' +
doNotRedirLink(redirPage, 'hidden', 'Show text hidden after #redirect[[...]]');
}
setPopupHTML(warnRedir, 'popupWarnRedir');
return loadPreviewImage(myEncodeURI(target));
};
function extractChunk(str, header, breakChar) {
if (str.indexOf(header) != 0) return null;
if (!breakChar) return str.substring(header.length);
var findChar=str.indexOf(breakChar);
if (findChar==-1) return str.substring(header.length);
return str.substring(header.length, findChar);
}
function urlToWikiPage (url) {
log ('urlToWikiPage\nurl='+url);
var urlFragment=null;
if (!urlFragment) urlFragment=extractChunk(url, titlebase, '&');
if (!urlFragment) urlFragment=extractChunk(url, wikibase, '?');
if (!urlFragment) return null;
return addressFragmentToWikiMarkup(urlFragment);
}
function insertPreviewImage(download) {
log('starting insertPreviewImage');
if (download.id != popupImageId) {
log('download.id='+download.id
+' does not match popupImageId='+popupImageId);
return;
}
var wikiText=download.data;
log ('considering '+wikiText.length+' bytes of data, starting\n'
+ wikiText.substring(0,50) );
var redirectRegex= /^[ \n]*[#]redirect[: \n]*\[\[([^\]]*)\]\]\s*(.*)/i ;
var redirMatch = redirectRegex.exec(wikiText);
if (redirMatch && redirCount==0) {
return loadPreviewImageFromRedir(urlToWikiPage(download.url), redirMatch);
} else redirCount=0;
log('not a redirect');
// setImageStatus(''); // this seems redundant
if (extraPageInfo===null) extraPageInfo=dextraPageInfo;
if (extraPageInfo) {
log ('getting extraPageInfo');
var pgInfo=getPageInfo(wikiText);
log ('got '+pgInfo);
setPopupTrailer('<br>' + pgInfo);
}
var imagePage=getValidImageFromWikiText(wikiText);
if(imagePage) {
log ('found imagePage='+imagePage);
// loadThisImage expects an "address fragment"
imagePage = wikiMarkupToAddressFragment(imagePage);
loadThisImage(imagePage);
}
else log('no image found');
if (popupPreviews===null) popupPreviews=dpopupPreviews;
if (popupPreviews) {popupPreview(download);}
log ('done insertPreviewImage');
// myalert(gMsg); gMsg='';
};
var localTest=false;
function popupPreview(download) {
// FIXME: horizonal rules should be skipped
// FIXME: get rid of html tagsx
if (localTest)
var data = download.substring(0,8000); // FOR TESTING
else
var data=download.data.substring(0,10000); // huge pages be gone
var datum=new Array();
datum.push(data);
// we strip in order:
//
// * html comments <!-- ... -->
// * contents of <div...> ... </div> and <gallery> ... </gallery>
// * templates {{ }}
// * wikitext tables
// * wikitext images
// * wikitext rules ----
// * lines starting with a :
// * html tables
// * a mop-up: delete all lines starting with <
// * all html tags
// * "chunks" of italic text (heuristics alert)
// * __TOC__, __NOTOC__
// hasta la vista, comments
data=data.replace(RegExp('<!--(\\n|[^-]|-[^-]|--[^>])*-->', 'g'), '');
// say goodbye, divs
data=data.replace(RegExp('< *div[^>]* *>(.|\\n)*< */ *div *>',
'gi'),
'');
// and galleries
data=data.replace(RegExp('< *gallery[^>]* *>(.|\\n)*< */ *gallery *>',
'gi'),
'');
// try to remove templates
data=data.replace(RegExp('[{][{]([{][{][^}]*[}][}]|[^{}])*[}][}]', 'g'), '');
datum.push(data);
// tables are bad, too
data=data.replace
(RegExp('[{]\\|([{][|]([^\\|]|\\|[^}])*[|][}]|[^\\|]|\\|[^}])*\\|[}]', 'g')
, '');
datum.push(data);
// images are a nono
// who says regexes aren't fun?
// this ain't so good, try it on [[User:dbenbenn]]
// i think we should match:
// [[image: ...... ]] where ....... is consists of repetitions of any of:
// 1. not [ or ]
// 2. [[ (not ])* ]]
// 3. [ (not ])* ]
var imagedetector
='[[][[]\\s*image\\s*:([^\\[\\]]|\\[\\[[^\\]]*\\]\\]|\\[[^\\]]*\\])*\\]\\]';
var crudeImageRegex = RegExp(imagedetector, 'gi');
// alert(data.match(crudeImageRegex).join('\n-\n'));
data=data.replace(crudeImageRegex, '');
// we simply *can't* be doing with horizontal rules right now
data=data.replace(RegExp('^-{4,}','mg'),'');
// no indented lines
data=data.replace(RegExp('(^|\\n) *:[^\\n]*','g'), '\n');
// or html tables // this doesn't cope with embedded tables
// for example, [[Kingdom of Ireland]] (yucky source)
//data=data.replace(RegExp('<table[^>]*>([\\n\\s]|[^<]|<[^/]|</[^t]|'+
//'</t[^a]|</ta[^b]|'+
//'</tab[^l]|</tabl[^e]|</table[^>])*</table>','gi'),
//'');
// may this is good enough?
data=data.replace(RegExp('< *table[^>]* *>([^\\n]|\\n[^\\n])*< */ *table *>\\n\\n', 'gi'),
'');
// let's delete lines starting with <. it's worth a try.
data=data.replace(RegExp('(^|\\n) *<[^\\n]*', 'g'), '\n');
// or those pesky html tags
data=data.replace(RegExp('<[^>]*>','g'),'');
// chunks of italic text? you crazy, man?
// Fails on [[United Kingdom]]
// now works on [[Ireland]]
//var italicChunkRegex =
// new RegExp
// ("((^|\\n)\\s*:*\\s*''([^']|'''|'[^']){30,}(.|\\n[^\\n])*''[.!?\\s]*\\n)*"
// , 'g');
var italicChunkRegex=new RegExp
("((^|\\n)\\s*:*\\s*''[^']([^']|'''|'[^']){20}(.|\\n[^\\n])*''[.!?\\s]*\\n)*"
, 'g');
data=data.replace(italicChunkRegex, '');
// return data; //TESTING
// replace __TOC__, __NOTOC__ and whatever else there is
// this'll probably do
data=data.replace(RegExp('^__[A-Z_]*__ *$', 'gm'),'');
// dont't be givin' me no subsequent paragraphs, either, you hear me?
stuff=(RegExp('^\\s*([^\\n]|\\n[^\\n])*')).exec(data);
if (stuff)
data = stuff[0];
// superfluous sentences are RIGHT OUT.
// note: 1 set of parens here needed to make the slice work
data = data.split(RegExp('([!?.]+["'+"'"+']*\\s)','g'));
// leading space is bad, mmkay?
data[0]=data[0].replace(RegExp('^\\s*'), '');
var notSentenceEnds=RegExp('([^.][a-z][.][a-z]|etc|sic)$', 'i');
data = fixSentenceEnds(data, notSentenceEnds);
var maxPreviewSentences=3;
var maxPreviewCharacters=800;
var n=maxPreviewSentences;
var d;
do {d=firstSentences(data,n); --n; }
while ( d.length > maxPreviewCharacters && n > 0 );
data = d;
// myalert(datum.join('\n===\n'));
if (localTest)
return data; // FOR TESTING
var newhtml=wiki2html(data); // needs livepreview
//newhtml = '<table width="260"><tr><td>' + newhtml +
//'</td></tr></table>';
setPopupHTML('<hr>'+newhtml, 'popupPreview');
};
function fixSentenceEnds(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<strs.length-2; ++i) {
if (reg.test(strs[i])) {
a=new Array ();
for (var j=0; j<strs.length; ++j) {
if (j<i) a[j]=strs[j];
if (j==i) a[i]=strs[i]+strs[i+1]+strs[i+2];
if (j>i+2) a[j-2]=strs[j];
}
return fixSentenceEnds(a,reg);
}
}
//alert('returning '+strs.join('\n\n'));
return strs;
}
function firstSentences(strs, howmany) {
var t=strs.slice(0, 2*howmany);
return t.join('');
}
var stopPopupTimer=null;
function fuzzyCursorOff(fuzz) {
if (!over) return null;
var left = parseInt(over.style.left);
var top = parseInt(over.style.top);
var right = left + (over.offsetWidth >= parseInt(o3_width) ? over.offsetWidth : parseInt(o3_width));
var bottom = top + (over.offsetHeight >= o3_aboveheight ? over.offsetHeight : o3_aboveheight);
if (o3_x < left-fuzz || o3_x > right+fuzz || o3_y < top-fuzz || o3_y > bottom + fuzz) return true;
return false;
}
// seems that fuzzyCursorOff should precede mouseOutWikiLink in the source
// or sometimes during page loads, errors are generated
function mouseOutWikiLink () {
// document.title += '!';
if (fuzzyCursorOff(5)) {
if (stopPopupTimer) {
clearInterval(stopPopupTimer);
stopPopupTimer=null;
}
killPopup();
return;
}
if (!stopPopupTimer)
stopPopupTimer=setInterval("mouseOutWikiLink()", 500);
};
function killPopup() {
// o3_showingsticky should be defined in overlib.js
//if ( typeof o3_showingsticky != "undefined" && o3_showingsticky == 0 ) {
cClick();
currentLink=null;
// abortCurrentDownload();
stopImagesDownloading();
//}
return true; // preserve default action (eg from onclick)
};
////////////////////////////////////////////////////////////////////
// Run things
////////////////////////////////////////////////////////////////////
if (window.addEventListener)
window.addEventListener("load",setupTooltips,false);
else if (window.attachEvent)
window.attachEvent("onload",setupTooltips);
else {
window._old_ABCD_onload = window.onload;
window.onload = function() {
window._old_ABCD_onload();
setupTooltips();
}
}
function debugsimple(pos) {
alert(pos+
'\n\n'+
'simplePopups='+
simplePopups+
'\nthis is '+
((simplePopups===null)? '':'**not** ') +
'null' +
'\n\ndsimplePopups='+dsimplePopups
);
}
debugsimple('first pass');
//simplePopups=true;
//debugsimple('just set the bugger to true');