Wikipedista:Che/popups.js

Z Wikipedie, otevřené encyklopedie

Poznámka: Po uložení musíte vyprázdnit mezipaměť vašeho prohlížeče, jinak změny neuvidíte.

// [[User:Lupin/popups.js]] - an attemp to local mod 

var popupVersion="01:18, 13. 9. 2005 (UTC) cs:che";
// 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 + '/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');

// 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=|/)Speci(%C3%A1ln%C3%AD|ální|al):|section=[0-9])') ;
var contributions=RegExp('(title=|/)Speci(%C3%A1ln%C3%AD|ální|al):Contributions(&target=|/|/Wikipedista:)(.*)') ;
var emailuser= RegExp('(title=|/)Speci(%C3%A1ln%C3%AD|ální|al):Emailuser' +'(&target=|/|/Wikipedista:)(.*)') ;
var talk=RegExp('Diskuse:', 'i') ;

// (^|\[\[)image: *([^|\]]*[^|\] ]) * 
// (^|\[\[)image: *([^|\]]*[^|\] ])([^0-9\]]*([0-9]+) *px)?
// $4 = 120 as in 120px
var imageRegex= RegExp('(^|\\[\\[)soubor: *([^|\\]]*[^|\\] ])([^0-9\\]]*([0-9]+) *px)?',
                       'img') ;
var imageRegexBracketCount = 4;

var categoryRegex= RegExp('\\[\\[kategorie: *([^|\\]]*[^|\\] ]) *', 'i') ;
var categoryRegexBracketCount = 1;

var stubRegex= RegExp('pahýl[}][}]|Tento .*článek je .*pahýl', 'im') ;
var disambigRegex= 
  RegExp('([{][{]rozcestník|rozcestník[}][}])' +
         '|je .*rozcestník', 'im') ;

var ipUserRegex=RegExp('(Wikipedista:)?' + 
                       '((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}' + 
                       '(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])');

var oldidRegex=RegExp('[?&]oldid=([0-9]*)');

var splitLoc=window.location.href.split('/');
var thisWiki=splitLoc[2];
var wikimediaWiki=RegExp('wiki[pm]edia\.org').test(thisWiki);
var protocol=splitLoc[0].split(':')[0];

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

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://tools.wikimedia.de/~kate/cgi-bin/count_edits' 
             + '?dbname='

switch(thisWiki) {
 case 'commons.wikimedia.org': kateBase += 'commonswiki_p';
   break;
 case 'cs.wikipedia.org': kateBase += 'cswiki_p';
   break;
 default: kateBase += 'enwiki_p';
}

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', true);
newOption('popupAdminLinks', false);
newOption('popupImages', true);
newOption('popupPreviews', true);
newOption('maxPreviewSentences', 4);
newOption('maxPreviewCharacters', 600);
newOption('popupNavLinks', true);
newOption('popupNavLinkSeparator', "' ⋅ '");
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=' • ';
} else if ((self.navigator.appName).indexOf("Microsoft")!=-1) {
  dpopupNavLinkSeparator=' · ';
}

// 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 [this];
    // without this, we have 
    // 'ab'.parenSplit(/a|(b)/) != 'ab'.split(/a|(b)/)
    for(var i=0; i<m.length; ++i) {
      if (typeof m[i]=='undefined') m[i]='';
    }
    return [this.substring(0,m.index)]
      .concat(m.slice(1))
      .concat(this.substring(m.index+m[0].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='Wikipedista:'+contribs[4];
    return article;
  } 
  
  var email=emailuser.exec(h);
  if (email != null) {
    article='Wikipedista:'+email[4];
    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;
};

function oldidFromAnchor(a) {
  // log('oldidFromAnchor');
  var h=a.href;
  var m=oldidRegex.exec(h);
  if (m) return m[1];
  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[s.length-1];
    if (visibleMainLinkText == '' && s.length > 1) {
      // shouldn't happen...
      visibleMainLinkText=s[s.length-2];
    }
  }
  
  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, 'příspěvky');
  var k = kateLink(article, 'počet'); 
  if (k) html += popupNavLinkSeparator + kateLink(article, 'počet');
  html += popupNavLinkSeparator + emailLink(article, 'email');
  if (popupAdminLinks) html += popupNavLinkSeparator + blockLink(article, 'blokovat');
  return html;
};

function reportErrorHTML(err) {
  return 'Chyba: '+err+ '. <a href="' + 
    'http://cs.wikipedia.org/wiki/User_talk:Che?action=edit&section=new' + 
    '">Udat!</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', 'editovat', oldid);
    html += popupNavLinkSeparator + wikiLink(article, 'history', 'historie');
    html += popupNavLinkSeparator + wikiLink(article, 'unwatch', 'ne') 
      + '|' + wikiLink(article, 'watch', 'sleduj');
    html += popupNavLinkSeparator + 
      '<b>' + wikiLink(t, 'view', 'diskuse') + '</b>' +
      '|' + wikiLink(t, 'edit', 'editovat') +
      '|' + wikiLink(t, 'edit&section=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', 'editovat', oldid)
      +'|' + wikiLink(article, 'edit&section=new', '+');
    html += popupNavLinkSeparator + wikiLink(article, 'history', 'historie');
    html += popupNavLinkSeparator + wikiLink(article, 'unwatch', 'ne') 
      + '|' + wikiLink(article, 'watch', 'sleduj');
    html += popupNavLinkSeparator + 
      '<b>' + wikiLink(ta, 'view', 'článek') + '</b>' +
      '|' + wikiLink(ta, 'edit', 'editovat');
  }
  return html;
};

function miscNavLinksHTML(article) {
  // whatLinksHere - relatedChanges - move
  var html='';
  html += specialLink(article, 'Whatlinkshere', 'odkazuje-sem');
  html += popupNavLinkSeparator + specialLink(article, 'Recentchangeslinked', 
                                              'související');
  html += popupNavLinkSeparator + specialLink(article, 'Movepage', 'přesunout');
  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<-[findPictureInCache]->-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[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) 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 = 'Upravit ';              break;
  case 'history':          prehint = 'Historie ';             break;
  case 'unwatch':          prehint = 'Nesledovat ';           break;
  case 'watch':            prehint = 'Sledovat ';             break;
  case 'view':             prehint = 'Jít na ';               break;
  case 'unprotect':        prehint = 'Odemknout ';            break;
  case 'protect':          prehint = 'Zamknout ';             break;
  case 'delete':           prehint = 'Smazat ';               break;
  case 'edit&section=new': prehint = 'Začít nové téma na ';   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='Stránky odkazující na '; break;
  case 'Recentchangeslinked': prehint='Změny související s '; break;
  case 'Contributions':       prehint='Příspěvky '; break;
  case 'Emailuser':           prehint='Email '; break;
  case 'Blockip':             prehint='Zablokovat '; break;
  case 'Movepage':            prehint='Přesunout '; break;
  case 'Undelete':            prehint='Historie smazaných editací '; 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>' + 'Přesměrování na ' +
      navLinksHTML(wikiMarkupToAddressFragment(redirMatch));
    
  else return '<br /> Přesměrování na ' +
         titledWikiLink(myEncodeURI(redirMatch), 'view', 
                        safeDecodeURI(redirMatch), 'Hup tam');
};

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="'
    + 'Spočítej příspěvky ' + 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;B') ;
};

function popupFilterStubDetect(data) {
  return (isStub(data)) ? 'Pahýl' : '';
};

function popupFilterDisambigDetect(data) {
  return (isDisambig(data)) ? 'Rozcestník' : '';
};

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

function popupFilterCountLinks(data) {
  var num=countLinks(data);
  if (!num) return 'bez&nbsp;odkazů';
  return String(num) + '&nbsp;odkaz' + ((num!=1)?((num>4)?'ů':'y'):'');
};

function popupFilterCountImages(data) {
  var num=countImages(data);
  if (!num) return 'bez&nbsp;obrázků';
  return String(num) + '&nbsp;' + ((num!=1)?((num>4)?'obrázků':'obrázky'):'obrázek');
};

function popupFilterCountCategories(data) {
  var num=countCategories(data);
  if (!num) return 'bez&nbsp;kategorií';
  return String(num) + '&nbsp;kategorie';
}

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[i](data);
    if (s!='') pageInfoArray.push(s);
  }

  for (var i=0; i<extraPopupFilters.length; ++i) {
    var s=extraPopupFilters[i](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[2];
    var pxWidth=match[4];
    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('Diskuse:') > -1 || article.indexOf('diskuse:') > -1 ) 
    return null;

  var i=article.indexOf(':');
  if (i == -1) return 'Diskuse:'+article;
  else return article.substring(0,i)+'_diskuse:' + article.substring(i+1);
};

function articleFromTalkPage(talkpage) {
  var i=talkpage.indexOf('Diskuse:');
  var j=talkpage.indexOf('_diskuse:');
  if ( i == -1 && j  == -1 ) return null;
  if ( i > -1 ) return talkpage.substring(i+8);
  return talkpage.split('_diskuse:').join(':');
};

function userName(article) {
  var i=article.indexOf('Wikipedista');
  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) {
  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 'cs.wikipedia.org':
      return 'http://upload.wikimedia.org/wikipedia/cs/';
    default: // unsupported - take a guess
      if (wikimediaWiki) {
        var lang=wiki.split('.')[0];
        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[i].from;
    var to=decodeExtras[i].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[i].from;
    var to=decodeExtras[i].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+'_diskuse:');
  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[i].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[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);
    }
    if (url!=null) 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;
    return true;
  } else {
    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.width=popupImageSize;
        popupImage.style.display='inline';
        imageSources[i].active=true;
        // should we check to see if it's already there? maybe...
        gImageCache.push(imageArray[i].src); 

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

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[i];
    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
// | 
// [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); 
  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://tiny__out__url.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[1];
  var trailingRubbish=redirMatch[2];
  ++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('^[ \\n]*[#]redirect[: \\n]*\\[\\[([^\\]]*)\\]\\]\\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 '''{{Šablona}}''' 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('[{][{][^}\\s|]*box_begin(.|\\n)*[^{\\s|]*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 [[User:dbenbenn]]
  // i think we should match:
  // [[Soubor: ...... ]] where ....... is consists of repetitions of any of:
  // 1. not [ or ]
  // 2. [[ (not ])* ]] 
  // 3. [ (not ])* ]
  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) *:[^\\n]*','g'), '\n');

  // or html tables // this doesn't cope with embedded tables
  //data=data.replace(RegExp('<table[^>]*>([\\n\\s]|[^<]|<[^/]|</[^t]|'+
  // 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) *<[^\\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])*''[.!?\\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_]*__ *$', '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]|\\n[^\\n])*')).exec(data);
  if (stuff) 
    data = stuff[0];  
  
  /// 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[0]=data[0].replace(RegExp('^\\s*'), '');

  var notSentenceEnds=RegExp('([^.][a-z][.][a-z]|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[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 () {
  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: ///