User:Lupin/popupstest.js

// 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=' '; return document.write(str); };

// eg sourceWikipediaJS('en.wikipedia.org', '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.wikipedia.org', '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= /(^|\) */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 //  // where n=popupImageId function imageHTML(article) { var ret=''; popupImageId++; ret+=''; ret += ' '; ret+=''; 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+=''; html+=titledWikiLink(article,                      'view',                        visibleMainLinkText,                       hint); html+='';

html+=' '; // Get rid of anchor now article=removeAnchor(article);

if (userName(article) != null) { html += ' ' + contribsLink(article, 'contribs'); html += ' &sdot; ' + kateLink(article, 'count'); html += ' &sdot; ' + emailLink(article, 'email'); }

html += ' ' + wikiLink(article, 'edit', 'edit'); var ta=articleFromTalkPage(article); if (ta != null) html +='|' + wikiLink(article, 'edit&section=new', 'new'); html += ' &sdot; ' + wikiLink(article, 'history', 'history'); html += ' &sdot; ' + wikiLink(article, 'unwatch', 'un') + '|'; html += wikiLink(article, 'watch', 'watch'); var t=talkPage(article); if (t != null) html += ' &sdot; ' +  + wikiLink(t, 'view', 'talk') +  + '|' + wikiLink(t, 'edit', 'edit') + '|' + wikiLink(t, 'edit&section=new', 'new'); if (ta != null) html +=' &sdot; ' +  + wikiLink(ta, 'view', 'article') +  + '|' + wikiLink(ta, 'edit', 'edit'); html += ' ' + specialLink(article, 'Whatlinkshere', 'whatLinksHere'); html += ' &sdot; ' + specialLink(article, 'Recentchangeslinked', 'relatedChanges');

html += ' '; html += ' '; html += ' '; 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 -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&section=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 '' + text + ''; };

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 '' + 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.wikipedia.org': return 'http://upload.wikimedia.org/wikipedia/en/'; case 'commons.wikimedia.org': return 'http://upload.wikimedia.org/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://upload.wikimedia.org/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=' 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(' ' + 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...> ... and //   * 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) //   * ,

// hasta la vista, comments data=data.replace(RegExp(, '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: // 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(' ','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, 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 = ' '; setPopupHTML(' '+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');