Misplaced Pages

User:Lupin/popups.js

Article snapshot taken from Wikipedia with creative commons attribution-sharealike license. Give it a read and then ask your questions in the chat. We can research this topic together.
< User:Lupin

This is an old revision of this page, as edited by Lupin (talk | contribs) at 16:09, 11 September 2005 (fix block links, hopefully). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

Revision as of 16:09, 11 September 2005 by Lupin (talk | contribs) (fix block links, hopefully)(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff)
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump.
This code will be executed when previewing this page.
This user script seems to have a documentation page at User:Lupin/popups.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Misplaced Pages:Bypass your cache.
var popupVersion="Sat Sep 10 10:18:44 EDT 2005 bugfix(1)";
// 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.wikipedia.org', 'User:Lupin/overlib.js');

function sourceWikipediaJS(wiki, name) {
  var url='http://' + wiki + '/search/?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');

// livepreview uses a broken hex_md5 function, so we avoid it
function md5_hex(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); };

//////////////////////
// GLOBAL VARIABLES //
//////////////////////

// regexes

var exceptions=RegExp('((title=|/)Special:|section=)') ;
var contributions=RegExp('(title=|/)Special:Contributions(&target=|/|/User:)(.*)') ;
var emailuser=    RegExp('(title=|/)Special:Emailuser' +'(&target=|/|/User:)(.*)') ;
var talk=RegExp('Talk:', 'i') ;

//                      (^|\]* ]) * 
//                      (^|\]* ])(]*(+) *px)?
//                                                        $4 = 120 as in 120px
var imageRegex= RegExp('(^|\\]* ])(]*(+) *px)?',
                       'img') ;
var imageRegexBracketCount = 4;

var categoryRegex= RegExp('\\]* ]) *', 'i') ;
var categoryRegexBracketCount = 1;

var stubRegex= RegExp('stub|This .*-related article is a .*stub', 'im') ;
var disambigRegex= 
  RegExp('(disambig|disambig|dab)' +
         '|is a .*disambiguation.*page', 'im') ;

var ipUserRegex=RegExp('(User:)?' + 
                       '((25|2|1||)\\.){3}' + 
                       '(25|2|1||)');

var oldidRegex=RegExp('oldid=(*)');

var splitLoc=window.location.href.split('/');
var thisWiki=splitLoc;
var wikimediaWiki=RegExp('wikiedia\.org').test(thisWiki);
var protocol=splitLoc.split(':');

var titletail='/index.php?title=';
if (wikimediaWiki) titletail = '/w' + titletail;


// /REGEX

// we're not set up for interwiki stuff yet - only affect en, commons 
// and wiktionary links

var reStart='*://';
var preTitles='wiki/|w/index\\.php\\?title=';
if (!wikimediaWiki) preTitles += '|index\\.php\\?title=';
var reEnd='/(' + preTitles + ')(*)';
var re = RegExp(reStart + thisWiki.split('.').join('\\.') + reEnd);

var titlebase=protocol+'://'+thisWiki+titletail;     
var wikibase=protocol+'://'+thisWiki+'/';

var popupImagesToggleSize=true;
var popupImageSize=60;

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 (wikimediaWiki && 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='

switch(thisWiki) {
 case 'commons.wikimedia.org': kateBase += 'commonswiki';
   break;
 case  'en.wiktionary.org': kateBase += 'enwiktionary';
   break;
 default: kateBase += 'enwiki';
}

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


// options

// this should expand to something like
// if (window.foo===null) window.foo=window.dfoo;
function defaultize(x) {
  var a='window.'+x;
  var b='window.d'+x;
  eval('if ('+a+'===null) '+a+'=' + b + ';');
};

// this should expand to something like
// if (typeof window.foo=='undefined') { window.foo=null; }; window.dfoo = 0.5;
function newOption(x, def) {
  var a='window.'  + x;
  var b='window.d' + x;
  eval('if (typeof '+ a + '==\'undefined\') {' + a + '=null; }; ' + 
       b + '=' + def + ';');
};

// user-settable parameters and defaults
newOption('popupDelay', 0.5);
newOption('popupFgColor', "'#CCCCFF'");
newOption('popupBgColor', "'#333399'");
newOption('removeTitles', true);
newOption('imagePopupsForImages', true);
newOption('popupSummaryData', true);
newOption('simplePopups',  false);
newOption('popupAdminLinks',  false);
newOption('popupImages', true);
newOption('popupPreviews', true);
newOption('maxPreviewSentences', 4);
newOption('maxPreviewCharacters', 600);
newOption('popupNavLinks', true);
newOption('popupNavLinkSeparator', "' &sdot; '");
newOption('popupOnlyArticleLinks', true);
newOption('popupNeverGetThumbs', false);
newOption('popupImagesFromThisWikiOnly', false);
newOption('popupAppendRedirNavLinks', true);
newOption('popupMaxWidth', 300);
newOption('popupSimplifyMainLink', true);
newOption('popupMinImageWidth', 50);


// browser-specific hacks

// if (typeof window.opera != 'undefined') {}

if ((self.navigator.appName)=='Konqueror') {
  dpopupNavLinkSeparator=' &bull; ';
} else if ((self.navigator.appName).indexOf("Microsoft")!=-1) {
  dpopupNavLinkSeparator=' &#183; ';
}

// 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) {
    var m=re.exec(this);
    if (!m) return ;
    // without this, we have 
    // 'ab'.parenSplit(/a|(b)/) != 'ab'.split(/a|(b)/)
    for(var i=0; i<m.length; ++i) {
      if (typeof m=='undefined') m='';
    }
    return 
      .concat(m.slice(1))
      .concat(this.substring(m.index+m.length).parenSplit(re));
  };
} else {
  String.prototype.parenSplit=function (re) {return this.split(re);};
}

/////////////////////
// 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 align="right" valign="top" id="popupImage' + popupImageId + '" ' 
    + 'style="display: none;"></img>';
  ret += '</a>';
  return ret;
};

function isInToc(a) {
  return isSubElementOf(a,'toc');
};

function isInArticle(a) { 
  // content is for monobook.. damn. was hoping to avoid skin references
  if (document.getElementById('article')) return isSubElementOf(a, 'article');
  else return isSubElementOf(a, 'content');
};

function isSubElementOf(a, tag) {
  var obj = a;
  var i=0;
  // this may well slow things down when this function is iterated
  // would perhaps be more efficient to cache this result before the 
  // function is called. or maybe the browser does that magically anyway
  var tagged=document.getElementById(tag);
  if (!tagged) return false;

  do {obj = obj.parentNode; ++i; }
  while (obj != tagged && obj.nodeName != 'HTML');

  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;
    return article;
  } 
  
  var email=emailuser.exec(h);
  if (email != null) {
    article='User:'+email;
    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;
  return article;
};

function oldidFromAnchor(a) {
  // log('oldidFromAnchor');
  var h=a.href;
  var m=oldidRegex.exec(h);
  if (m) return m;
  return null;
};

// Generate html for whole popup
function popupHTML (a) {

  var c=a.className;
  // if (c=='new') alert('new!');

  var article = articleFromAnchor(a);

  var html='';
  
  html +=imageHTML(article);

  var hint=a.originalTitle;
  if (hint == '' || hint == null)
    hint = safeDecodeURI(article);

  var oldid=oldidFromAnchor(a);
  
  defaultize('popupNavLinks');
  if (popupNavLinks)
    html += navLinksHTML(article, hint, oldid);

  html += emptySpanHTML('popupWarnRedir', popupImageId);
  html += emptySpanHTML('popupGubbins'  , popupImageId);
  html += emptySpanHTML('popupPreview'  , popupImageId);
  return html;
}; 


function isIpUser(user) {
  return ipUserRegex.test(user);
};

function mainLinkHTML(article, hint, oldid) {
  var html='';
  var visibleMainLinkText=safeDecodeURI(article);

  defaultize('popupSimplifyMainLink');
  if ( popupSimplifyMainLink ) { 
    var s= visibleMainLinkText.split('/');
    visibleMainLinkText = s;
    if (visibleMainLinkText == '' && s.length > 1) {
      // shouldn't happen...
      visibleMainLinkText=s;
    }
  }
  
  if (typeof hint =='undefined') hint=safeDecodeURI(article);
  if (oldid) hint += ' oldid='+oldid;
  html+='<b>';
  html+=titledWikiLink(article, 'view', visibleMainLinkText, hint, oldid);
  html+='</b>';
  return html;
};


function emptySpanHTML(name, id) {
  return '<span id="' + name + id + '"></span>';
};

function userNavLinksHTML(article) {
  // contribs - count - email - block
  // count only if applicable; block only if popupAdminLinks
  var html = '';
  html += contribsLink(article, 'contribs');
  var k = kateLink(article, 'count'); 
  if (k) html += popupNavLinkSeparator + kateLink(article, 'count');
  html += popupNavLinkSeparator + emailLink(article, 'email');
  if (popupAdminLinks) html += popupNavLinkSeparator + blockLink(article, 'block');
  return html;
};

function reportErrorHTML(err) {
  return 'Error: '+err+ ' Please <a href="' + 
    'http://en.wikipedia.org/User_talk:Lupin?action=edit&section=new' + 
    '">report this</a>!';
};

// this function, rather ugly
function editingNavLinksHTML(article, oldid) {
  // edit - history - un|watch - talk|edit|new, OR (if a talk page)

  // edit|new - history - un|watch - article|edit
  
  var html = '';

  var t=talkPage(article);
  if (t) {
    // it's not a talk page
    html += wikiLink(article, 'edit', 'edit', oldid);
    html += popupNavLinkSeparator + wikiLink(article, 'history', 'history');
    html += popupNavLinkSeparator + wikiLink(article, 'unwatch', 'un') 
      + '|' + wikiLink(article, 'watch', 'watch');
    html += popupNavLinkSeparator + 
      '<b>' + wikiLink(t, 'view', 'talk') + '</b>' +
      '|' + wikiLink(t, 'edit', 'edit') +
      '|' + wikiLink(t, 'edit&section=new', 'new');
  } else {
    var ta=articleFromTalkPage(article);
    if (!ta) return reportErrorHTML('Not a talk page and not an article.');
    // it's a talk page
    html += wikiLink(article, 'edit', 'edit', oldid)
      +'|' + wikiLink(article, 'edit&section=new', 'new');
    html += popupNavLinkSeparator + wikiLink(article, 'history', 'history');
    html += popupNavLinkSeparator + wikiLink(article, 'unwatch', 'un') 
      + '|' + wikiLink(article, 'watch', 'watch');
    html += popupNavLinkSeparator + 
      '<b>' + wikiLink(ta, 'view', 'article') + '</b>' +
      '|' + wikiLink(ta, 'edit', 'edit');
  }
  return html;
};

function miscNavLinksHTML(article) {
  // whatLinksHere - relatedChanges - move
  var html='';
  html += specialLink(article, 'Whatlinkshere', 'whatLinksHere');
  html += popupNavLinkSeparator + specialLink(article, 'Recentchangeslinked', 
                                              'relatedChanges');
  html += popupNavLinkSeparator + specialLink(article, 'Movepage', 'move');
  return html;
};

function adminNavLinksHTML(article) {
  var html = '';
  html += wikiLink(article, 'unprotect', 'un') + '|';
  html += wikiLink(article, 'protect', 'protect');
  html += popupNavLinkSeparator + undeleteLink(article, 'un') + '|';
  html += wikiLink(article, 'delete', 'delete');
  return html;
};

function navLinksHTML (article, hint, oldid) {
  var html='';

  html += mainLinkHTML(article, hint, oldid);

  // Get rid of anchor (or whatever the bit after the # is called) now
  article=removeAnchor(article);

  // span for blinkenlights
  html+=emptySpanHTML('popupImageStatus', popupImageId);

  defaultize('popupNavLinkSeparator');
  defaultize('popupAdminLinks');

  if (userName(article) != null) 
    html += '<br>' + userNavLinksHTML(article);

  html += '<br>' + editingNavLinksHTML(article, oldid);

  html += '<br>' + miscNavLinksHTML(article);

  if (popupAdminLinks) 
    html += '<br>' + adminNavLinksHTML(article);
  
  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;
};

new downloader();

downloader.prototype.send = function (x) {
  if (!this.http) return;
  return this.http.send(x);
};

downloader.prototype.abort = function () {
  if (!this.http) return;
  return this.http.abort();
};

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<-->-onComplete(a fake download)
//   \.
//     (async)->addPageToCache(download)->-onComplete(download)

function getWiki(wikipage, onComplete, oldid) {
  // log('getWiki, wikipage='+wikipage); 
  // set ctype=text/css to get around opera bug
  var url = titlebase + removeAnchor(wikipage) + '&action=raw&ctype=text/css';
  if (oldid!=null && oldid!='')
    url += '&oldid='+oldid;

  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.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.url) return i;
  }
  return -1; 
};

function cachedPage (url,data) {
  this.url=url;
  this.data=data;
};

function addPageToCache(download) {
  log ('addPageToCache, page.url='+download.url);
  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, oldid) {
  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 'unprotect':        prehint = 'Unprotect ';            break;
  case 'protect':          prehint = 'Protect ';              break;
  case 'delete':           prehint = 'Delete ';               break;
  case 'edit&section=new': prehint = 'Start a new topic on '; break;
  default: true;
  }

  if (action!='edit' && action!='view') oldid=null;
  
  var hint;
  if (prehint != null) {
    hint=prehint + safeDecodeURI(article);
    hint += (oldid) ? ' oldid='+oldid : '';
  }
  else hint = safeDecodeURI(article + '&action=' + action) 
         + (oldid) ? '&oldid='+oldid : '';
  return titledWikiLink(article, action, text, hint, oldid);
};

function titledWikiLink(article, action, text, title, oldid) {
  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 = 'title="' + title + '"'; 

  if (oldid != null && oldid != '')
    url+='&oldid='+oldid;

  return '<a href="' + url + '" ' + hint + '>' + text + '</a>';
};

function specialLink(article, specialpage, text, sep) {
  var base = titlebase +  'Special:'+specialpage;
  if (typeof sep == 'undefined' || sep===null) sep='&target=';
  var url = base + sep + article;
  var prehint=null;
  switch (specialpage) {
  case 'Whatlinkshere':       prehint='Show articles which link to '; break;
  case 'Recentchangeslinked': prehint='Show changes in articles related to '; break;
  case 'Contributions':       prehint='Show contributions made by '; break;
  case 'Emailuser':           prehint='Email '; break;
  case 'Blockip':             prehint='Block '; break;
  case 'Movepage':            prehint='Move '; break;
  case 'Undelete':            prehint='Show deletion history for '; break;
  }
  var hint;
  if (prehint != null) hint = prehint + safeDecodeURI(article);
  else hint = safeDecodeURI(specialpage+':'+article) ;
  return '<a href="' + url + '" title="' + hint + '">' + text + '</a>';
};

function redirLink(redirMatch) { 
  /* NB redirMatch is in wikiText */

  defaultize('popupAppendRedirNavLinks');
  defaultize('popupNavLinks');

  if (popupAppendRedirNavLinks && popupNavLinks)
    return '<hr>' + 'Redirects to ' +
      navLinksHTML(wikiMarkupToAddressFragment(redirMatch));
    
  else return '<br> Redirects to ' +
         titledWikiLink(myEncodeURI(redirMatch), 'view', 
                        safeDecodeURI(redirMatch), 'Bypass redirect');
};

function doNotRedirLink(redirPage, linkText, hintText) { 
  /* NB redirPage is in wikiText */
  return titledWikiLink(myEncodeURI(redirPage), 'edit', linkText, hintText);
};

function contribsLink(article, text) {
  return specialLink(userName(article), 'Contributions', text);
};

function emailLink(article, text) {
  return specialLink(userName(article), 'Emailuser', text);
};

function blockLink(article, text) {
  return specialLink(userName(article), 'Blockip', text, '&ip=');
};

function kateLink(article, text) {
  if (isIpUser(userName(article)) || ! wikimediaWiki) return null;

  var uN=safeDecodeURI(userName(article));
  return '<a href="' + kateBase + uN + '" title="'
    + 'Count the contributions made by ' + uN + '">' + text + '</a>';
};

function undeleteLink(article, text) {
  return specialLink(article, 'Undelete', text, '/');
};

////////////////////////////
// MANIPULATION FUNCTIONS //
////////////////////////////

function upcaseFirst(str) {
  return str.charAt(0).toUpperCase() + str.substring(1);
};

function formatBytes(num) {
  return (num > 949) ? (Math.round(num/100)/10+'kB') : (num +'&nbsp;bytes') ;
};

function popupFilterStubDetect(data) {
  return (isStub(data)) ? 'stub' : '';
};

function popupFilterDisambigDetect(data) {
  return (isDisambig(data)) ? 'disambig' : '';
};

function popupFilterPageSize(data) {
  return formatBytes(data.length);
};

function popupFilterCountLinks(data) {
  var num=countLinks(data);
  return String(num) + '&nbsp;wikiLink' + ((num!=1)?'s':'');
};

function popupFilterCountImages(data) {
  var num=countImages(data);
  return String(num) + '&nbsp;image' + ((num!=1)?'s':'');
};

function popupFilterCountCategories(data) {
  var num=countCategories(data);
  return String(num) + '&nbsp;categor' + ((num!=1)?'ies':'y');
}

var popupFilters=new Array();

popupFilters.push(popupFilterStubDetect);
popupFilters.push(popupFilterDisambigDetect);
popupFilters.push(popupFilterPageSize);
popupFilters.push(popupFilterCountLinks);
popupFilters.push(popupFilterCountImages);
popupFilters.push(popupFilterCountCategories);

if (typeof extraPopupFilters=='undefined') { var extraPopupFilters=new Array(); }

function getPageInfo(data) {
  if (!data || data.length == 0) return 'Empty page';

  var pageInfoArray=new Array();
  for (var i=0; i<popupFilters.length; ++i) {
    var s=popupFilters(data);
    if (s!='') pageInfoArray.push(s);
  }

  for (var i=0; i<extraPopupFilters.length; ++i) {
    var s=extraPopupFilters(data);
    if (s!='') pageInfoArray.push(s);
  }

  var pageInfo=pageInfoArray.join(', ');
  if (pageInfo != '' ) 
    pageInfo = upcaseFirst(pageInfo);
  return pageInfo;
};     

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;
  defaultize('popupMinImageWidth');
  while ( match = imageRegex.exec(wikiText)) {
    /* now find a sane image name - exclude templates by seeking { */
    var m = match;
    var pxWidth=match;
    if ( isValidImageName(m) && 
         (!pxWidth || parseInt(pxWidth) >= popupMinImageWidth) ) { 
      matched=m; 
      break;
    }
  } 
  imageRegex.lastIndex=0;
  if (!matched) return null;
  if (matched.charAt(0) >= 'a' && matched.charAt(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.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(imageRegex).length - 1) / 
    (imageRegexBracketCount + 1);
};

function countCategories(wikiText) {
  return (wikiText.parenSplit(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;
};

function imagePathComponent(article) {
  var stripped=stripNamespace(article);
  var forhash=safeDecodeURI(stripped).split(' ').join('_');
  var hash=md5_hex(forhash);
  var pathcpt=hash.substring(0,1) + '/' + hash.substring(0,2) + '/';
  return pathcpt;
};

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
      if (wikimediaWiki) {
        var lang=wiki.split('.');
        return 'http://' + wiki + '/upload/' + lang +'/';
      }
      else /* this should work for wikicities */
        return 'http://' + wiki + '/images/';
  }
};

function imageURL(img, wiki) {
  defaultize('popupImagesFromThisWikiOnly');
  if (popupImagesFromThisWikiOnly && wiki != thisWiki) return null;
  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
  //           ^^^^^^^^^^^^^^^^^^^^^^^ 
  //          wikicities omits this bit
  //  AND wikicities needs a new pathcpt for each filename including thumbs

  defaultize('popupImagesFromThisWikiOnly');
  if (popupImagesFromThisWikiOnly && wiki != thisWiki) return null;

  defaultize('popupNeverGetThumbs');
  if (popupNeverGetThumbs) return null;

  var imgurl=null;
  if (isImage(img)) {
    var stripped=stripNamespace(img);
    var pathcpt;
    if (wikimediaWiki) pathcpt = imagePathComponent(stripped);
    else pathcpt = imagePathComponent(width+'px-'+stripped);
    imgurl=getImageUrlStart(wiki) +  "thumb/" + pathcpt;
    if (wikimediaWiki) imgurl += stripped + '/';
    imgurl += 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 = safeDecodeURI(str);
  ret = ret.split(' ').join('_');
  ret = encodeURI(ret);
  return ret;
};

function addressFragmentToWikiMarkup (str) { 
  // seemingly, not :( the inverse of wikiMarkupToAddressFragment
  return safeDecodeURI(str);
};

function myDecodeURI (str) {
  log('myDecodeURI, str=' + str);
  var ret;
  try { ret=decodeURI(str); }
  catch (summat) { return null; }
  for (var i=0; i<decodeExtras.length; ++i) {
    var from=decodeExtras.from;
    var to=decodeExtras.to;
    ret=ret.split(from).join(to);
  }
  return ret;
};

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

function myEncodeURI (str) {
  log ('myEncodeURI: str='+str);
  var ret=str;
  ret=encodeURI(ret);
  for (var i=0; i<decodeExtras.length; ++i) {
    var from=decodeExtras.from;
    var to=decodeExtras.to;
    ret=ret.split(to).join(from);
  }
  return ret;
};

function removeAnchor(article) {
  // is there a #? if not, we're done
  var i=article.indexOf('#');
  if (i == -1) return article;

  return article.substring(0,i);
};

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

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

function anchorContainsImage(a) {
  // iterate over children of anchor a
  // see if any are images
  if (a===null) return false;
  kids=a.childNodes;
  for (var i=0; i<kids.length; ++i) {
    if (kids.nodeName=='IMG') return true;
  }
  return false;
};

/////////////
// ACTIONS //
/////////////

var imageCache = new Array ();

function loadThisImage (image) {
  if (!popupImages) return;
  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.thumb) 
      url=imageThumbURL(image, imageSources.wiki, imageSources.width);
    else 
      url=imageURL(image, imageSources.wiki);

    for (var j=0; j<gImageCache.length; ++j) {
      if (url == gImageCache) return loadThisImageAtThisUrl(image, url);
    }
    if (url!=null) imageUrls.push(url);
  }
 
  msg='imageUrls:\n';
  for (var i=0; i<imageUrls.length; ++i) {
    var url = imageUrls;
    imageSources.active=false;
 
    msg += '\n'+url; 

    imageArray=new Image();
    imageArray.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 = new Image();
  imageArray.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 == 'undefined') {
    timer=null;
  }
  else {
    timer=popupHTMLTimers;
  }

  if (popupElement != null) {
    if(timer) clearInterval(timer);
    popupHTMLTimers=null;
    popupElement.innerHTML=str;
    return true;
  } else {
    var loopFunction=function() { setPopupHTML(str,elementId,popupId);}
    popupHTMLLoopFunctions = loopFunction;
    if (!timer) {
      var doThis = 'popupHTMLLoopFunctions()';
      popupHTMLTimers = 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)) {
      // stop all the gubbins, assign the image and return

      clearInterval(popupImageTimer);

      if(isImage(gImage)) {
        popupImage.src=imageArray.src;
        popupImage.width=popupImageSize;
        popupImage.style.display='inline';
        imageSources.active=true;
        // should we check to see if it's already there? maybe...
        gImageCache.push(imageArray.src); 

        setPopupImageLink(gImage, imageSources.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.src=''; // this is a REALLY BAD IDEA
    delete imageArray;
    //imageArray = 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.width=' + 
      imgContainer.childNodes.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;
};

var ranSetupTooltipsAlready=false;

function setupTooltips() {
  if (ranSetupTooltipsAlready) return;
  ranSetupTooltipsAlready=true;

  var anchors;
  defaultize('popupOnlyArticleLinks');

  if (popupOnlyArticleLinks)
    anchors=( document.getElementById('article') || 
              document.getElementById('content')   ).getElementsByTagName('A');
  else
    anchors=document.getElementsByTagName('A');
  // alert(anchors.length + 'anchors');
  var s='';
 
  defaultize('removeTitles');

  for (var i=0; i<anchors.length; ++i) {
    var a=anchors;
    var h=a.href;
    // var contribs=contributions.test(h);
    // var email=emailuser.test(h);
    // var exc=exceptions.test(h);
    // var m=re.test(h);
    if (
        ( (re.test(h)  && !exceptions.test(h)) 
          || 
          (contributions.test(h) && h.indexOf('&limit=') == -1 ) 
        )
        && (! isInToc(a)) 
       ) {
      a.onmouseover=mouseOverWikiLink;
      a.onmouseout= mouseOutWikiLink;
      a.onclick= killPopup;
      if (removeTitles && typeof a.originalTitle=='undefined') {
        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
//          |                                   
//           
//       |                                      
//       |                     mouseOverWikiLink  (gets an "address fragment",       
//       |                            |            no processing needed)
//       \->-loadThisImage---<----loadImages    
//                 |                            
//           -->--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); 
  var oldid=oldidFromAnchor(a);

  if (popupImageTimer != null) {
    clearInterval(popupImageTimer);
    counter=0;
  }

  defaultize('popupDelay');
  defaultize('popupFgColor');
  defaultize('popupBgColor');
  defaultize('popupImages');
  defaultize('popupMaxWidth');

  log('running overlib now');
  if (typeof popupMaxWidth == 'number') {
    window.setmaxwidth = function () {
      over.style.maxWidth = popupMaxWidth+'px'; 
      
      // hack for IE
      // see http://www.svendtofte.com/code/max_width_in_ie/
      // who knows if this will work? not me.
      // use setExpression as documented here on msdn: http://tinyurl.com/dqljn 
      
      if (over.style.setExpression) {
        over.style.setExpression('width', 
                                 'document.body.clientWidth > '
                                 + popupMaxWidth + ' ? "' 
                                 + popupMaxWidth + 'px": "auto" );');
      }
    };
    registerHook("createPopup", window.setmaxwidth, FAFTER);
      
  }

  overlib(html, STICKY, WRAP, CELLPAD, 5, OFFSETX, 2, OFFSETY, 2, 
          DELAY, popupDelay*1000, FGCOLOR, popupFgColor, BGCOLOR, popupBgColor);
  
  defaultize('simplePopups');
  if(simplePopups) return;

  defaultize('imagePopupsForImages');

  var previewImage=true;

  gImage=null;

  if (isImage(article) && ( imagePopupsForImages || ! anchorContainsImage(a) )) {
    loadImages(article);
  }
  else if (!isImage(article) && previewImage) {
    redirCount=0;
    loadPreviewImage(article, oldid);
  }
};

function loadPreviewImage(article, oldid) {
  return getWiki(article, insertPreviewImage, oldid);
};

function loadPreviewImageFromRedir(redirPage, redirMatch) {
  /* redirMatch is a regex match */
  var target = redirMatch;
  var trailingRubbish=redirMatch;
  ++redirCount;
  var warnRedir = redirLink(target);
/*if (trailingRubbish.length > 0) {
    warnRedir += ', '+formatBytes(trailingRubbish.length);
    warnRedir += '&nbsp;' + 
      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) {
  if (download.id != popupImageId) return;

  var wikiText=download.data;

  var redirectRegex= RegExp('^*redirect*\\]*)\\]\\]\\s*(.*)',
                            'i') ;
  var redirMatch = redirectRegex.exec(wikiText);

  if (redirMatch && redirCount==0) { 
    return loadPreviewImageFromRedir(urlToWikiPage(download.url), redirMatch);
  } else redirCount=0;

  defaultize('popupSummaryData');

  if (popupSummaryData) {
    var pgInfo=getPageInfo(wikiText);
    setPopupTrailer('<br>' + pgInfo);
  }

  var imagePage=getValidImageFromWikiText(wikiText);
  if(imagePage) {
    // loadThisImage expects an "address fragment"
    imagePage = wikiMarkupToAddressFragment(imagePage); 
    loadThisImage(imagePage);
  }
   
  defaultize('popupPreviews');
  if (popupPreviews) {popupPreview(download);}

};

var localTest=false;

function popupPreview(download) {
  // FIXME: horizonal rules should be skipped
  // FIXME: get rid of html tagsx
  if (!download || typeof download.data=='undefined' 
      || typeof download.data.length=='undefined') return;
  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.substring(0,1000));
  
  //   we strip in order:
  // 
  //    * html comments <!-- ... -->
  //    * contents of <div...> ... </div> and <gallery> ... </gallery>
  //    * HACK: {{taxobox_start ... taxobox_end}}
  //    * templates {{ .... }} (replace with spaces so that '''{{template}}''' works)
  //    * wikitext tables 
  //    * wikitext images and category tags
  //    * 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
  // NB *? is non-greedy (minimal) version of * - see 
  // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:RegExp
  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'),
                      '');

  // taxobox hack... in fact, there's a saudiprincebox_begin, so let's be more general
  data=data.replace(RegExp('*box_begin(.|\\n)**box_end *',
                           '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.substring(0,1000));

  // images are a nono
  // who says regexes aren't fun?
  // this ain't so good, try it on ]
  // i think we should match:
  // ] where ....... is consists of repetitions of any of:
  // 1. not 
  // 2. )* ]] 
  // 3. )* ]
  var imagedetector
    ='\\s*(image|category)\\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) *:*','g'), '\n');

  // or html tables // this doesn't cope with embedded tables
  //data=data.replace(RegExp('<table*>(||<|</|'+
  // may this is good enough?
  data=data.replace(RegExp('<table*>(.|\\n.)*\\n?</ *table *>\\n+', 'gi'),
                    '');
  
  // let's delete lines starting with <. it's worth a try.
  data=data.replace(RegExp('(^|\\n) *<*', 'g'), '\n');

  // and those pesky html tags
  data=data.replace(RegExp('<*>','g'),'');

  // chunks of italic text? you crazy, man?
  var italicChunkRegex=new RegExp
    ("((^|\\n)\\s*:*\\s*''(|'''|'){20}(.|\\n)*''*\\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('^__*__ *$', 'gmi'),'');

  // dont't be givin' me no subsequent paragraphs, either, you hear me?
  /// first we "normalize" section headings, removing whitespace after, adding before
  
  data=data.replace(RegExp('\\s*(==+*==+)\\s*', 'g'), '\n\n$1 ');

  /// then we want to get rid of paragraph breaks whose text ends badly
  data=data.replace(RegExp('() *\\n{2,}', 'g'), '$1\n');

  stuff=(RegExp('^\\s*(|\\n)*')).exec(data);
  if (stuff) 
    data = stuff;  
  
  /// now put \n\n after sections so that bullets and numbered lists work
  data=data.replace(RegExp('(==+*==+)\\s*', 'g'), '$1\n\n');

  // superfluous sentences are RIGHT OUT.
  // note: 1 set of parens here needed to make the slice work
  data = data.parenSplit(RegExp('(+*\\s)','g'));
  // leading space is bad, mmkay?
  data=data.replace(RegExp('^\\s*'), '');

  var notSentenceEnds=RegExp('(|etc|sic|Dr|Mr|Mrs|Ms)$'
                             + '|' +
                             '\\]*$'
                             , 'i');
  
  data = fixSentenceEnds(data, notSentenceEnds);

  defaultize('maxPreviewSentences');
  defaultize('maxPreviewCharacters');
  
  var n=maxPreviewSentences; 
  var d;
  
  do {d=firstSentences(data,n); --n; } 
  while ( d.length > maxPreviewCharacters && n > 0 );
  
  data = d;
  
  datum.push('\n\nfinal one:');
  datum.push(data.substring(0,1000));

  if (localTest) return data; // FOR TESTING

  var newhtml=wiki2html(data); // needs livepreview
    
  if (!RegExp('^\\s*$').test(newhtml) && 
      (popupNavLinks || popupSummaryData)) 
    {
      newhtml = '<hr>' + newhtml;
      setPopupHTML(newhtml, 'popupPreview');
    }
};

function fixSentenceEnds(strs, reg) { 
  // take an array of strings, strs
  // join strs to strs & strs if strs matches regex reg
  
  for (var i=0; i<strs.length-2; ++i) {
    if (reg.test(strs)) {
      a=new Array ();
      for (var j=0; j<strs.length; ++j) {
        if (j<i)   a=strs;
        if (j==i)  a=strs+strs+strs;
        if (j>i+2) a=strs;
      }
      return 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 () {
  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();
  if (checkImagesTimer != null) {
    clearInterval(checkImagesTimer); checkImagesTimer=null;
  }
  if (popupImageTimer != null) {
    clearInterval(popupImageTimer); popupImageTimer=null;
  }
  //}
  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();
  }
}

/// Local Variables: ///
/// mode:c ///
/// End: ///