Revision as of 04:33, 25 March 2006 view sourceFreakofnurture (talk | contribs)36,981 editsm this should keep the {{hndis|Surname, Given}} sort-key invocations from breaking teh popups← Previous edit |
Latest revision as of 14:34, 9 February 2021 view source Xaosflux (talk | contribs)Edit filter managers, Autopatrolled, Bureaucrats, Importers, Interface administrators, Oversighters, Administrators83,950 edits expand directions |
(32 intermediate revisions by 15 users not shown) |
Line 1: |
Line 1: |
|
|
var popScript = '//en.wikipedia.org/search/?action=raw&ctype=text/javascript&title=MediaWiki:Gadget-popups.js'; |
|
var popupVersion="Thu Mar 2 19:40:47 EST 2006"; |
|
|
|
var popStyleSheet = '//en.wikipedia.org/search/?action=raw&ctype=text/css&title=MediaWiki:Gadget-navpop.css'; |
|
// STARTFILE: main.js |
|
|
|
if ( window.localCSS ) { popStyleSheet = 'http://localhost:8080/js/navpop.css'; } |
|
// ********************************************************************** |
|
|
// ** Warning ** |
|
|
// ********************************************************************** |
|
|
// ** if you edit this file, be sure that your editor recognizes it as ** |
|
|
// ** utf8, or the weird and wonderful characters in the namespaces ** |
|
|
// ** below will be completely broken. You can check with the show ** |
|
|
// ** changes button before submitting the edit. ** |
|
|
// ** test: مدیا מיוחד Мэдыя ** |
|
|
// ********************************************************************** |
|
|
|
|
|
|
|
function popups_importScriptURI(url) { |
|
//////////////////////////////////////////////////////////////////// |
|
|
|
var s = document.createElement('script'); |
|
// Import stylesheet(s) |
|
|
|
s.setAttribute('src',url); |
|
// |
|
|
|
s.setAttribute('type','text/javascript'); |
|
|
|
|
|
document.getElementsByTagName('head').appendChild(s); |
|
|
|
|
|
return s; |
|
if (1) { |
|
|
document.write('<link rel="stylesheet" type="text/css" href="' + |
|
|
'http://en.wikipedia.org/search/?title=User:Lupin/navpop.css' + |
|
|
'&action=raw&ctype=text/css&dontcountme=s">'); |
|
|
} else { |
|
|
document.write('<link rel="stylesheet" type="text/css" href="http://localhost:8080/js/navpop.css">'); |
|
|
} |
|
} |
|
|
|
|
|
|
function popups_importStylesheetURI(url) { |
|
////////////////////////////////////////////////// |
|
|
|
return document.createStyleSheet ? document.createStyleSheet(url) : popups_appendCSS('@import "' + url + '";'); |
|
// Globals |
|
|
// |
|
|
|
|
|
// Trying to shove as many of these as possible into the pg (popup globals) object |
|
|
window.pg = { |
|
|
re: {}, // regexps |
|
|
ns: {}, // namespaces |
|
|
string: {}, // translatable strings |
|
|
wiki: {}, // local site info |
|
|
misc: {}, // YUCK PHOOEY |
|
|
option: {}, // options, see newOption etc |
|
|
optionDefault: {}, // default option values |
|
|
flag: {}, // misc flags |
|
|
cache: {}, // page and image cache |
|
|
diffData: {}, // support for diff previews |
|
|
structures: {}, // navlink structures |
|
|
timer: {}, // all sorts of timers (too damn many) |
|
|
counter: {}, // .. and all sorts of counters |
|
|
current: {}, // state info |
|
|
endoflist: null |
|
|
}; |
|
|
|
|
|
window.pop = { // wrap various functions in here |
|
|
init: {}, |
|
|
util: {}, |
|
|
endoflist: null |
|
|
}; |
|
|
|
|
|
//////////////////////////////////////////////////////////////////// |
|
|
// Run things |
|
|
//////////////////////////////////////////////////////////////////// |
|
|
|
|
|
addOnloadHook(setupPopups); |
|
|
|
|
|
/// Local Variables: /// |
|
|
/// mode:c /// |
|
|
/// End: /// |
|
|
|
|
|
// ENDFILE: main.js |
|
|
// STARTFILE: actions.js |
|
|
function setupTooltips(container) { |
|
|
if (!container) { |
|
|
// the main initial call |
|
|
if (getValueOf('popupOnEditSelection')) { |
|
|
try { |
|
|
document.editform.wpTextbox1.onmouseup=function() { doSelectionPopup(); }; |
|
|
} catch (neverMind) {} |
|
|
} |
|
|
// article/content is a structure-dependent thing |
|
|
if (getValueOf('popupOnlyArticleLinks')) { |
|
|
container = document.getElementById('article') || document.getElementById('content') || document; |
|
|
} else { |
|
|
container = document; |
|
|
} |
|
|
} |
|
|
|
|
|
if (container.ranSetupTooltipsAlready) { return; } |
|
|
container.ranSetupTooltipsAlready=true; |
|
|
|
|
|
var anchors; |
|
|
anchors=container.getElementsByTagName('A'); |
|
|
setupTooltipsLoop(anchors, 0, 250, 100); |
|
|
} |
|
} |
|
|
|
|
|
function setupTooltipsLoop(anchors,begin,howmany,sleep) { |
|
function popups_appendCSS(text) { |
|
|
var s = document.createElement('style'); |
|
var finish=begin+howmany; |
|
|
|
s.type = 'text/css'; |
|
var loopend = min(finish, anchors.length); |
|
|
|
s.rel = 'stylesheet'; |
|
var remTitles = getValueOf('removeTitles'); |
|
|
|
if (s.styleSheet) s.styleSheet.cssText = text //IE |
|
var j=loopend - begin; |
|
|
|
else s.appendChild(document.createTextNode(text + '')) //Safari sometimes borks on null |
|
log ('setupTooltips: anchors.length=' + anchors.length + ', begin=' + begin + |
|
|
|
document.getElementsByTagName('head').appendChild(s); |
|
', howmany=' + howmany + ', loopend=' + loopend); |
|
|
|
return s; |
|
// try a faster (?) loop construct |
|
|
if (j > 0) { |
|
|
do { |
|
|
var a=anchors; |
|
|
if (!a || !a.href) { |
|
|
log('got null anchor at index ' + loopend - j); |
|
|
continue; |
|
|
} |
|
|
if ( isPopupLink(a) ) { |
|
|
a.onmouseover=mouseOverWikiLink; |
|
|
a.onmouseout= mouseOutWikiLink; |
|
|
a.onclick= killPopup; |
|
|
if (remTitles && typeof a.originalTitle=='undefined') { |
|
|
a.originalTitle=a.title; |
|
|
a.title=''; |
|
|
} |
|
|
} |
|
|
} while (--j); |
|
|
} |
|
|
if (finish < anchors.length) { |
|
|
pop.runOnce(function() {setupTooltipsLoop(anchors,finish,howmany,sleep);}, sleep); |
|
|
} |
|
|
else { |
|
|
// now eliminate popups from the TOC |
|
|
if (! getValueOf('popupTocLinks')) { |
|
|
var toc=document.getElementById('toc'); |
|
|
if (toc) { |
|
|
var tocLinks=toc.getElementsByTagName('A'); |
|
|
var tocLen = tocLinks.length; |
|
|
for (var j=0; j<tocLen; ++j) { |
|
|
log ('killing popup for toclinks'); |
|
|
a=tocLinks; |
|
|
a.onmouseover=null; a.onmouseout=null; a.onclick=null; |
|
|
if (a.originalTitle) { a.title=a.originalTitle; } |
|
|
} |
|
|
// looks like we just killed any onclick stuff that used to be going on in the toc |
|
|
// life is hard |
|
|
} |
|
|
} |
|
|
|
|
|
pg.flag.finishedLoading=true; |
|
|
} |
|
|
} |
|
} |
|
|
|
|
|
|
popups_importStylesheetURI(popStyleSheet); |
|
// add CSS class to table |
|
|
|
popups_importScriptURI(popScript); |
|
|
|
|
|
|
if ( typeof mw !== 'undefined' ) { |
|
function addPopupStylesheetClasses () { |
|
|
|
mw.loader.using( , function() { |
|
var tables=over.getElementsByTagName('table'); |
|
|
|
var k = 'User:Lupin/popups.js', |
|
tables.className='popupBorderTable'; |
|
|
|
t = 'Information: You are importing User:Lupin/popups.js' + |
|
tables.className='popupTable'; |
|
|
|
' into your User:USERNAME/common.js or User:USERNAME/<skin>.js!\n' + |
|
var fonts=over.getElementsByTagName('font'); |
|
|
|
'This script is unmaintained. Please remove this inclusion and enable the Navigation popups Gadget in the preferences of your account instead.', |
|
fonts.className='popupFont'; |
|
|
|
x = mw.storage.get( k ); |
|
fonts.id='overFontWrapper'; |
|
|
} |
|
|
|
|
|
|
|
if ( !x ) { |
|
function registerHooks(np) { |
|
|
|
mw.storage.set( k, 1 ); |
|
var popupMaxWidth=getValueOf('popupMaxWidth'); |
|
|
|
alert( t ); |
|
|
|
|
|
} else { |
|
if (typeof popupMaxWidth == 'number') { |
|
|
|
x++; |
|
var setMaxWidth = function () { |
|
|
|
mw.storage.set( k, x ); |
|
np.mainDiv.style.maxWidth = popupMaxWidth + 'px'; |
|
|
|
if ( x % 25 === 0 ) { |
|
np.maxWidth = popupMaxWidth; |
|
|
|
mw.notify( t ); |
|
|
|
|
|
} |
|
// hack for IE |
|
|
// see http://www.svendtofte.com/code/max_width_in_ie/ |
|
|
// use setExpression as documented here on msdn: http://tinyurl dot com/dqljn |
|
|
|
|
|
if (np.mainDiv.style.setExpression) { |
|
|
np.mainDiv.style.setExpression('width', 'document.body.clientWidth > ' + |
|
|
popupMaxWidth + ' ? "' +popupMaxWidth + 'px": "auto"'); |
|
|
} |
|
|
}; |
|
|
np.addHook(setMaxWidth, 'unhide', 'before'); |
|
|
} |
|
|
|
|
|
np.addHook(addPopupShortcuts, 'unhide', 'after'); |
|
|
np.addHook(rmPopupShortcuts, 'hide', 'before'); |
|
|
|
|
|
|
|
|
//don't do this here... |
|
|
//np.addHook(window.fillEmptySpans, 'create', 'after'); |
|
|
|
|
|
//registerHook("createPopup", window.addPopupStylesheetClasses, FAFTER); |
|
|
} |
|
|
|
|
|
|
|
|
function mouseOverWikiLink() { |
|
|
if (!pg.flag.finishedLoading) { return; } |
|
|
return mouseOverWikiLink2(this); |
|
|
} |
|
|
|
|
|
function mouseOverWikiLink2(a) { |
|
|
// FIXME: should not generate the HTML until the delay has elapsed, |
|
|
// and then popup immediately. Can be a CPU hog otherwise. |
|
|
|
|
|
//log('mouseOverWikiLink: a='+a+', pg.current.link='+pg.current.link); |
|
|
|
|
|
// try not to duplicate effort |
|
|
if ( a==pg.current.link && a.navpopup && a.navpopup.isVisible() ) { return; } |
|
|
pg.current.link=a; |
|
|
|
|
|
if (getValueOf('simplePopups') && pg.option.popupStructure===null) { |
|
|
// reset *default value* of popupStructure |
|
|
//log ('simplePopups is true and no popupStructure selected. Defaulting to "original"'); |
|
|
setDefault('popupStructure', 'original'); |
|
|
} |
|
|
|
|
|
var article=(new Title()).fromAnchor(a); |
|
|
// set global variable (ugh) to hold article (wikipage) |
|
|
pg.current.article = article; |
|
|
var diff=null; |
|
|
var oldid=oldidFromAnchor(a); |
|
|
if (getValueOf('popupPreviewDiffs')) { |
|
|
diff=diffFromAnchor(a); |
|
|
} |
|
|
|
|
|
if (pg.timer.image !== null) { |
|
|
clearInterval(pg.timer.image); |
|
|
pg.timer.image=null; |
|
|
pg.counter.checkImages=0; |
|
|
} |
|
|
|
|
|
if (!a.navpopup) { |
|
|
log ('mouseoverwikilink2: creating new Navpopup'); |
|
|
a.navpopup = new Navpopup(); |
|
|
a.navpopup.fuzz=5; |
|
|
a.navpopup.delay=getValueOf('popupDelay')*1000; |
|
|
// a.navpopup.append($t(a.navpopup.uid)); |
|
|
// increment global counter now |
|
|
a.navpopup.popupIdNumber = ++pg.idNumber; |
|
|
registerHooks(a.navpopup); |
|
|
} |
|
|
if (a.navpopup.pending===null || a.navpopup.pending!==0) { |
|
|
// either fresh popups or those with unfinshed business are redone from scratch |
|
|
log ('doing popup content from scratch; a.navpopup.pending='+a.navpopup.pending); |
|
|
a.navpopup.setInnerHTML(popupHTML(a)); |
|
|
fillEmptySpans(); |
|
|
/* if (bug) { |
|
|
setPopupHTML('Cannot generate diff until ' + |
|
|
'<a href="http://bugzilla.wikimedia.org/show_bug.cgi?id=4838">this patch</a> ' + |
|
|
'is applied to Mediawiki. Sorry!<hr>', 'popupError'); |
|
|
} |
|
|
*/ |
|
|
|
|
|
} else { |
|
|
log ('using existing popup content - just showing'); |
|
|
} |
|
|
a.navpopup.showSoonIfStable(a.navpopup.delay); |
|
|
|
|
|
getValueOf('popupInitialWidth'); |
|
|
|
|
|
if (typeof pg.timer.checkPopupPosition==typeof 1) { clearInterval(pg.timer.checkPopupPosition); } |
|
|
pg.timer.checkPopupPosition=setInterval(checkPopupPosition, 600); |
|
|
|
|
|
if (getValueOf('popupLiveOptions')) { |
|
|
setPopupHTML(popupLiveOptionsHTML(), 'popupLiveOptions', pg.idNumber, |
|
|
function () { popupToggleShowOptions(true); } ); |
|
|
} |
|
|
if (getValueOf('popupRedlinkRemoval') && a.className=='new') { |
|
|
setPopupHTML('<br>'+popupRedlinkHTML(), 'popupRedlink', pg.idNumber); |
|
|
} |
|
|
|
|
|
if(getValueOf('simplePopups')) { return; } |
|
|
|
|
|
// We're creating a closure with all our data in it because... we're lazy |
|
|
// is this a bad idea? |
|
|
pop.unsimplify = function () { |
|
|
log('pop.unsimplify'); |
|
|
a.navpopup.unsimplified=true; |
|
|
var previewImage=true; |
|
|
|
|
|
pg.misc.gImage=null; |
|
|
//alert(diff+'\n'+oldid); |
|
|
if ( diff===null ) { |
|
|
if (isImage(article) && ( getValueOf('imagePopupsForImages') || ! anchorContainsImage(a) )) { |
|
|
loadImages(article); |
|
|
} |
|
|
else if (!isImage(article) && previewImage ) { |
|
|
pg.counter.redir=0; |
|
|
loadPreview(article, oldid, diff, a.navpopup); |
|
|
} |
|
|
} |
|
|
else { |
|
|
loadDiff(article, oldid, diff); |
|
|
} |
|
|
|
|
|
var s=document.getElementById('popupUnsimplify' + pg.idNumber); |
|
|
if (s && s.style) { |
|
|
s.style.display='none'; |
|
|
} |
|
|
}; |
|
|
|
|
|
if (getValueOf('popupUnsimplifyLink')) { return; } |
|
|
if (!a.navpopup.unsimplified || a.navpopup.pending!==0 ) { |
|
|
log ('running pop.unsimplify(), unsimplified='+a.navpopup.unsimplified + |
|
|
', pending='+a.navpopup.pending); |
|
|
a.navpopup.pending=0; |
|
|
pop.unsimplify(); |
|
|
} |
|
|
} |
|
|
|
|
|
function loadPreview(article, oldid, diff, navpop) { |
|
|
log('loadPreview(' + article + ', ' + oldid + ', ' + navpop + ')'); |
|
|
if (navpop && navpop.pending===null) { navpop.pending=0; } |
|
|
++navpop.pending; |
|
|
getWiki(article, insertPreview, oldid, navpop); |
|
|
} |
|
|
|
|
|
function loadPreviewFromRedir(redirMatch, navpop) { |
|
|
// redirMatch is a regex match |
|
|
//log('loadPreviewFromRedir, pg.counter.redir='+pg.counter.redir); |
|
|
var target = new Title().fromWikiText(redirMatch); |
|
|
var trailingRubbish=redirMatch; |
|
|
pg.counter.redir++; |
|
|
var warnRedir = redirLink(target); |
|
|
|
|
|
setPopupHTML(warnRedir, 'popupWarnRedir'); |
|
|
fillEmptySpans({redir: true, redirTarget: target}); |
|
|
|
|
|
return loadPreview(target, null, null, navpop); |
|
|
} |
|
|
|
|
|
function insertPreview(download) { |
|
|
//log('insertPreview, pg.counter.redir='+pg.counter.redir); |
|
|
if (download.id != pg.idNumber) { |
|
|
//log ('insertPreview: download.id='+download.id+' but pg.idNumber='+pg.idNumber+'. Bailing...'); |
|
|
return; |
|
|
} |
|
|
var wikiText=download.data; |
|
|
var navpop=download.owner; |
|
|
if (navpop && navpop.pending) { --navpop.pending; } |
|
|
var redirMatch = pg.re.redirect.exec(wikiText); |
|
|
|
|
|
var art=pg.current.article; |
|
|
|
|
|
if (pg.counter.redir===0 && redirMatch) { |
|
|
pg.current.redirSource=pg.current.article; |
|
|
loadPreviewFromRedir(redirMatch, navpop); |
|
|
return; |
|
|
} |
|
|
|
|
|
var redirSource=pg.current.redirSource||''; |
|
|
|
|
|
if (pg.counter.redir===0) { // not a redir, so we don't have to specify an oldTarget |
|
|
makeFixDabs(wikiText); |
|
|
} else { |
|
|
makeFixDabs(wikiText, redirSource); |
|
|
} |
|
|
|
|
|
pg.current.redirSource=null; |
|
|
pg.counter.redir=0; |
|
|
|
|
|
if (getValueOf('popupSummaryData')) { |
|
|
var pgInfo=getPageInfo(wikiText, download); |
|
|
setPopupTrailer('<br>' + pgInfo); |
|
|
} |
|
|
|
|
|
|
|
|
var imagePage=getValidImageFromWikiText(wikiText); |
|
|
if(imagePage) { |
|
|
// loadThisImage expects an "address fragment" |
|
|
imagePage = wikiMarkupToAddressFragment(imagePage); |
|
|
loadThisImage(imagePage); |
|
|
} |
|
|
|
|
|
if (getValueOf('popupPreviews')) { |
|
|
if (download && typeof download.data == typeof ''){ |
|
|
if (isInNamespace(pg.current.article, 'Template') && getValueOf('popupPreviewRawTemplates')) { |
|
|
// FIXME compare/consolidate with diff escaping code for wikitext |
|
|
var h='<hr><tt>' + |
|
|
download.data.split("&").join("&").split("<").join("<").split(">").join(">").split('\\n').join('<br>\\n') + |
|
|
'</tt>'; |
|
|
setPopupHTML(h, 'popupPreview'); |
|
|
} |
|
|
else { |
|
|
// deal with tricksy anchors |
|
|
var anch=decodeAnchor(pg.current.article); |
|
|
var d=download.data; |
|
|
if (anch) { |
|
|
var anchRe=RegExp('=+\\s*' + literalizeRegex(anch).replace(//g, '') + '\\s*=+'); |
|
|
var match=d.match(anchRe); |
|
|
if(match && match.length > 0 && match) { d=d.substring(d.indexOf(match)); } |
|
|
else { // try to deal with == foo ] boom == -> #foo_baz_boom |
|
|
var lines=d.split('\n'); |
|
|
for (var i=0; i<lines.length; ++i) { |
|
|
lines=lines.replace(RegExp('{2}(]*?)?(.*?)]{2}', 'g'), '$2'); |
|
|
if (lines.match(anchRe)) { |
|
|
d=d.split('\n').slice(i).join('\n').replace(RegExp('^*'), ''); |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
} |
|
|
}); |
|
var p=new Previewmaker(d.substring(0,10000)); |
|
|
p.showPreview(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
} |
|
|
|
|
function killPopup() { |
|
|
if (getValueOf('popupShortcutKeys')) { rmPopupShortcuts(); } |
|
|
pg.current.link.navpopup.banish(); |
|
|
pg.current.link=null; |
|
|
abortAllDownloads(); |
|
|
stopImagesDownloading(); |
|
|
if (pg.timer.checkPopupPosition !== null) { |
|
|
clearInterval(pg.timer.checkPopupPosition); |
|
|
pg.timer.checkPopupPosition=null; |
|
|
} |
|
|
if (pg.timer.checkImages !== null) { clearInterval(pg.timer.checkImages); pg.timer.checkImages=null; } |
|
|
if (pg.timer.image !== null) { clearInterval(pg.timer.image); pg.timer.image=null; } |
|
|
return true; // preserve default action (eg from onclick) |
|
|
} |
|
|
|
|
|
// ENDFILE: actions.js |
|
|
// STARTFILE: domdrag.js |
|
|
/** |
|
|
@fileoverview |
|
|
The {@link Drag} object, which enables objects to be dragged around. |
|
|
|
|
|
<pre> |
|
|
************************************************* |
|
|
dom-drag.js |
|
|
09.25.2001 |
|
|
www.youngpup.net |
|
|
************************************************** |
|
|
10.28.2001 - fixed minor bug where events |
|
|
sometimes fired off the handle, not the root. |
|
|
************************************************* |
|
|
Pared down, some hooks added by ] |
|
|
|
|
|
Copyright Aaron Boodman. |
|
|
Saying stupid things daily since March 2001. |
|
|
</pre> |
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
/** |
|
|
Creates a new Drag object. This is used to make various DOM elements draggable. |
|
|
@constructor |
|
|
*/ |
|
|
function Drag () { |
|
|
/** |
|
|
Condition to determine whether or not to drag. This function should take one parameter, an Event. |
|
|
To disable this, set it to <code>null</code>. |
|
|
@type Function |
|
|
*/ |
|
|
this.startCondition = null; |
|
|
/** |
|
|
Hook to be run when the drag finishes. This is passed the final coordinates of the dragged object (two integers, x and y). |
|
|
To disables this, set it to <code>null</code>. |
|
|
@type Function |
|
|
*/ |
|
|
this.endHook = null; |
|
|
} |
|
|
|
|
|
/** |
|
|
Gets an event in a cross-browser manner. |
|
|
@param {Event} e |
|
|
@private |
|
|
*/ |
|
|
Drag.prototype.fixE = function(e) { |
|
|
if (typeof e == 'undefined') { e = window.event; } |
|
|
if (typeof e.layerX == 'undefined') { e.layerX = e.offsetX; } |
|
|
if (typeof e.layerY == 'undefined') { e.layerY = e.offsetY; } |
|
|
return e; |
|
|
}; |
|
|
/** |
|
|
Initialises the Drag instance by telling it which object you want to be draggable, and what you want to drag it by. |
|
|
@param {DOMElement} o The "handle" by which <code>oRoot</code> is dragged. |
|
|
@param {DOMElement} oRoot The object which moves when <code>o</code> is dragged, or <code>o</code> if omitted. |
|
|
*/ |
|
|
Drag.prototype.init = function(o, oRoot) { |
|
|
var dragObj = this; |
|
|
this.obj = o; |
|
|
o.onmousedown = function(e) { dragObj.start.apply( dragObj, ); }; |
|
|
o.dragging = false; |
|
|
o.draggable = true; |
|
|
o.hmode = true ; |
|
|
o.vmode = true ; |
|
|
|
|
|
o.root = oRoot && oRoot !== null ? oRoot : o ; |
|
|
|
|
|
if (isNaN(parseInt(o.root.style.left, 10))) { o.root.style.left = "0px"; } |
|
|
if (isNaN(parseInt(o.root.style.top, 10))) { o.root.style.top = "0px"; } |
|
|
|
|
|
o.root.onthisStart = function(){}; |
|
|
o.root.onthisEnd = function(){}; |
|
|
o.root.onthis = function(){}; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Starts the drag. |
|
|
@private |
|
|
@param {Event} e |
|
|
*/ |
|
|
Drag.prototype.start = function(e) { |
|
|
var o = this.obj; // = this; |
|
|
e = this.fixE(e); |
|
|
if (this.startCondition && !this.startCondition(e)) { return; } |
|
|
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10); |
|
|
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10); |
|
|
o.root.onthisStart(x, y); |
|
|
|
|
|
o.lastMouseX = e.clientX; |
|
|
o.lastMouseY = e.clientY; |
|
|
|
|
|
var dragObj = this; |
|
|
o.onmousemoveDefault = document.onmousemove; |
|
|
o.dragging = true; |
|
|
document.onmousemove = function(e) { dragObj.drag.apply( dragObj, ); }; |
|
|
document.onmouseup = function(e) { dragObj.end.apply( dragObj, ); }; |
|
|
// document.onclick = document.onmouseup; |
|
|
// document.onclick = document.onmouseup; |
|
|
|
|
|
return false; |
|
|
}; |
|
|
/** |
|
|
Does the drag. |
|
|
@param {Event} e |
|
|
@private |
|
|
*/ |
|
|
Drag.prototype.drag = function(e) { |
|
|
e = this.fixE(e); |
|
|
var o = this.obj; |
|
|
|
|
|
var ey = e.clientY; |
|
|
var ex = e.clientX; |
|
|
var y = parseInt(o.vmode ? o.root.style.top : o.root.style.bottom, 10); |
|
|
var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10 ); |
|
|
var nx, ny; |
|
|
|
|
|
nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1)); |
|
|
ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1)); |
|
|
|
|
|
if (o.xMapper) { nx = o.xMapper(y); } |
|
|
else if (o.yMapper) { ny = o.yMapper(x); } |
|
|
|
|
|
this.obj.root.style = nx + "px"; |
|
|
this.obj.root.style = ny + "px"; |
|
|
this.obj.lastMouseX = ex; |
|
|
this.obj.lastMouseY = ey; |
|
|
|
|
|
this.obj.root.onthis(nx, ny); |
|
|
return false; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Ends the drag. |
|
|
@private |
|
|
*/ |
|
|
Drag.prototype.end = function() { |
|
|
//Document.onmousemove = null; |
|
|
document.onmousemove=this.obj.onmousemoveDefault; |
|
|
document.onmouseup = null; |
|
|
// document.onclick = null; |
|
|
// document.onmousedown = null; |
|
|
this.obj.dragging = false; |
|
|
if (this.endHook) { |
|
|
this.endHook( parseInt(this.obj.root.style, 10), |
|
|
parseInt(this.obj.root.style, 10)); |
|
|
} |
|
|
}; |
|
|
|
|
|
// ENDFILE: domdrag.js |
|
|
// STARTFILE: liveoptions.js |
|
|
////////////////////////////////////////////////// |
|
|
// live options nonsense |
|
|
// |
|
|
|
|
|
function popupToggleVar(varstring) { |
|
|
pg.option = ! getValueOf(varstring); |
|
|
if (getValueOf('popupCookies')) { |
|
|
Cookie.create(pg.option, String(pg.option)); |
|
|
} |
|
|
} |
|
|
|
|
|
function popupToggleShowOptions (dummy) { |
|
|
// just update state if dummy is true |
|
|
getValueOf('popupLiveOptionsExpanded'); |
|
|
if (!dummy) { pg.option.popupLiveOptionsExpanded=!pg.option.popupLiveOptionsExpanded; } |
|
|
setPopupHTML((pg.option.popupLiveOptionsExpanded) ? '<<' : '>>', |
|
|
'optionPopped'); |
|
|
var s=document.getElementById('popupOptionsDiv'); |
|
|
// if (!s) return; |
|
|
if (pg.option.popupLiveOptionsExpanded) { s.style.display='inline'; } |
|
|
else { s.style.display='none'; } |
|
|
} |
|
|
|
|
|
function popupOptionsCheckboxHTML(varstring, label, title) { |
|
|
var html='<br>'; |
|
|
html += '<span title="'+title+'">'; |
|
|
html += '<input type="checkbox" id="'+varstring+'Checkbox" '; |
|
|
html += (window) ? 'checked="checked" ' : ''; |
|
|
html += 'onClick="javascript:popupToggleVar(' + "'" + varstring + "'" + |
|
|
')">' + label + '</input></span>'; |
|
|
return html; |
|
|
} |
|
|
|
|
|
function popupLiveOptionsHTML() { |
|
|
var html = ''; |
|
|
html += '<br>'; |
|
|
html += '<span title="' + popupString('Show/hide options') + '" '; |
|
|
html += 'style="border: thin dotted black; cursor: pointer" '; |
|
|
html += 'onClick="javascript:popupToggleShowOptions()">'; |
|
|
html += 'Options <span id="optionPopped' + pg.idNumber + '"></span>'; |
|
|
html += '</span>'; |
|
|
html += '<div style="display: none" id="popupOptionsDiv">'; |
|
|
html += popupOptionsCheckboxHTML('simplePopups', popupString('Simple popups'), |
|
|
popupString('Never download extra stuff for images/previews')); |
|
|
html += popupOptionsCheckboxHTML('popupUnsimplifyLink', popupString('Preview only on click'), |
|
|
popupString('Only start downloading when told to do so')); |
|
|
//html += popupOptionsCheckboxHTML('popupCookies', popupString('cookies'), |
|
|
// popupString('Use cookies to store popups options')); |
|
|
html += popupOptionsCheckboxHTML('popupNavLinks', popupString('Show navigation links'), |
|
|
popupString('Display navigation links at the top of the popup')); |
|
|
html += popupOptionsCheckboxHTML('popupImages', popupString('Show image previews'), |
|
|
popupString('Load images')); |
|
|
html += popupOptionsCheckboxHTML('popupSummaryData', popupString('Show summary data'), |
|
|
popupString('Show page summary data')); |
|
|
html += popupOptionsCheckboxHTML('popupPreviews', popupString('Show text previews'), |
|
|
popupString('Show previews')); |
|
|
|
|
|
var extraOptions=[ |
|
|
'imagePopupsForImages', |
|
|
'popupAdminLinks', |
|
|
'popupAppendRedirNavLinks', |
|
|
'popupCookies', |
|
|
'popupFixDabs', |
|
|
'popupFixRedirs', |
|
|
'popupHistoricalLinks', |
|
|
'popupImagesFromThisWikiOnly', |
|
|
'popupImagesToggleSize', |
|
|
'popupLastEditLink', |
|
|
'popupLiveOptions', |
|
|
'popupLoadImagesSequentially', |
|
|
'popupNeverGetThumbs', |
|
|
'popupOnlyArticleLinks', |
|
|
'popupPreviewKillTemplates', |
|
|
'popupPreviewFirstParOnly', |
|
|
'popupShortcutKeys', |
|
|
'popupSimplifyMainLink', |
|
|
'removeTitles' // no , |
|
|
]; |
|
|
for (var i=0; i<extraOptions.length; ++i) { |
|
|
html += popupOptionsCheckboxHTML(extraOptions, extraOptions, popupString('Toggle this option')); |
|
|
} |
|
|
|
|
|
html += '</div>'; |
|
|
return html; |
|
|
} |
|
|
|
|
|
// ENDFILE: liveoptions.js |
|
|
// STARTFILE: structures.js |
|
|
pg.structures.original={}; |
|
|
pg.structures.original.popupLayout=function () { |
|
|
return ['popupBar', 'popupError', 'popupImage', 'popupTopLinks', 'popupTitle', 'popupData', 'popupOtherLinks', |
|
|
'popupRedir', , |
|
|
'popupMiscTools', , |
|
|
'popupPreview', 'popupFixDab']; |
|
|
}; |
|
|
pg.structures.original.popupRedirSpans=function () { |
|
|
return ; |
|
|
}; |
|
|
pg.structures.original.popupTitle=function (x) { |
|
|
log ('defaultstructure.popupTitle'); |
|
|
if (!getValueOf('popupNavLinks')) { return navlinkStringToHTML('<b><<mainlink>></b>',x.article,x.oldid); } |
|
|
return ''; |
|
|
}; |
|
|
pg.structures.original.popupTopLinks=function (x) { |
|
|
log ('defaultstructure.popupTopLinks'); |
|
|
if (getValueOf('popupNavLinks')) { return navLinksHTML(x.article, x.hint, x.oldid); } |
|
|
return ''; |
|
|
}; |
|
|
pg.structures.original.popupImage=function(x) { |
|
|
log ('original.popupImage, x.article='+x.article+', pg.idNumber='+pg.idNumber); |
|
|
return imageHTML(x.article); |
|
|
}; |
|
|
pg.structures.original.popupUnsimplify=function(x) { |
|
|
if (getValueOf('popupUnsimplifyLink')) { |
|
|
return '<br><span onClick="javascript:pop.unsimplify()" ' + |
|
|
'style="cursor:pointer; border: thin dotted black" ' + |
|
|
'title="' +popupString('Download preview data') + '">' + |
|
|
'Get preview data' + '</span>'; |
|
|
} |
|
|
return ''; |
|
|
}; |
|
|
pg.structures.original.popupRedirTitle=pg.structures.original.popupTitle; |
|
|
pg.structures.original.popupRedirTopLinks=pg.structures.original.popupTopLinks; |
|
|
|
|
|
|
|
|
function copyStructure(oldStructure, newStructure) { |
|
|
pg.structures={}; |
|
|
for (var prop in pg.structures) { |
|
|
pg.structures=pg.structures; |
|
|
} |
|
|
} |
|
|
|
|
|
/** -- fancy -- **/ |
|
|
copyStructure('original', 'fancy'); |
|
|
pg.structures.fancy.popupTitle=function (x) { |
|
|
return navlinkStringToHTML('<font size=+0><<mainlink>></font>',x.article,x.oldid); |
|
|
}; |
|
|
pg.structures.fancy.popupTopLinks=function(x) { |
|
|
var hist='<<history|shortcut=h|hist>>|<<lastEdit|shortcut=/|last>>if(mainspace){|<<editors|shortcut=E|eds>>}'; |
|
|
var watch='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>'; |
|
|
var move='<<move|shortcut=m|move>>'; |
|
|
return navlinkStringToHTML('if(talk){' + |
|
|
'<<edit|shortcut=e>>|<<new|shortcut=+|+>>*' + hist + '*' + |
|
|
'<<article|shortcut=a>>|<<editArticle|edit>>' + '*' + watch + '*' + move + |
|
|
'}else{<<edit|shortcut=e>>*' + hist + |
|
|
'*<<talk|shortcut=t|>>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' + |
|
|
'*' + watch + '*' + move+'}<br>', x.article, x.oldid); |
|
|
}; |
|
|
pg.structures.fancy.popupOtherLinks=function(x) { |
|
|
var admin='<<unprotect|unprotectShort>>|<<protect|shortcut=p>>*<<undelete|undeleteShort>>|<<delete|shortcut=d|del>>'; |
|
|
var user='<<contribs|shortcut=c>>if(wikimedia){|<<count|shortcut=#|#>>}'; |
|
|
user+='if(ipuser){|<<arin>>}else{*<<email|shortcut=E|'+ |
|
|
popupString('email')+'>>}if(admin){*<<block|shortcut=b>>}'; |
|
|
|
|
|
var normal='<<whatLinksHere|shortcut=l|links here>>*<<relatedChanges|shortcut=r|related>>'; |
|
|
return navlinkStringToHTML('<br>if(user){' + user + '*}if(admin){'+admin+'if(user){<br>}else{*}}' + normal, |
|
|
x.article, x.oldid); |
|
|
}; |
|
|
pg.structures.fancy.popupRedirTitle=pg.structures.fancy.popupTitle; |
|
|
pg.structures.fancy.popupRedirTopLinks=pg.structures.fancy.popupTopLinks; |
|
|
pg.structures.fancy.popupRedirOtherLinks=pg.structures.fancy.popupOtherLinks; |
|
|
|
|
|
|
|
|
/** -- fancy2 -- **/ |
|
|
// hack for ] |
|
|
copyStructure('fancy', 'fancy2'); |
|
|
pg.structures.fancy2.popupTopLinks=function(x) { // hack out the <br> at the end and put one at the beginning |
|
|
return '<br>'+pg.structures.fancy.popupTopLinks(x).replace(RegExp('<br>$','i'),''); |
|
|
}; |
|
|
pg.structures.fancy2.popupLayout=function () { // move toplinks to after the title |
|
|
return ['popupBar', 'popupError', 'popupImage', 'popupTitle', 'popupData', 'popupTopLinks', 'popupOtherLinks', |
|
|
'popupRedir', , |
|
|
'popupMiscTools', , |
|
|
'popupPreview', 'popupFixDab']; |
|
|
}; |
|
|
|
|
|
/** -- menus -- **/ |
|
|
copyStructure('original', 'menus'); |
|
|
pg.structures.menus.popupLayout=function () { |
|
|
return ['popupBar', 'popupError', 'popupImage', 'popupTopLinks', 'popupTitle', 'popupOtherLinks', |
|
|
'popupRedir', , |
|
|
'popupData', 'popupMiscTools', , |
|
|
'popupPreview', 'popupFixDab']; |
|
|
}; |
|
|
pg.structures.menus.popupBar = function (x) { |
|
|
// return <a href="javascript:toggleSticky(' + pg.current.link.navpopup.uid + ')">(un)stick</a>'; |
|
|
return ''; |
|
|
}; |
|
|
function toggleSticky(uid) { |
|
|
var popDiv=document.getElementById('navpopup_maindiv'+uid); |
|
|
if (!popDiv) { return; } |
|
|
if (!popDiv.navpopup.sticky) { popDiv.navpopup.stick(); } |
|
|
else { |
|
|
popDiv.navpopup.unstick(); |
|
|
popDiv.navpopup.hide(); |
|
|
} |
|
|
} |
|
|
pg.structures.menus.popupTopLinks = function (x) { |
|
|
var s=''; |
|
|
var dropdiv='<div class="popup_drop">'; |
|
|
var menuspan='<span class="popup_menu">'; |
|
|
var enddiv='</div>'; |
|
|
var endspan='</span>'; |
|
|
var hist='if(mainspace){<line>}<<history|shortcut=h>>if(mainspace){|<<editors|shortcut=E>></line>}'; |
|
|
var lastedit='<<lastEdit|shortcut=/|show last edit>><<lastContrib|last set of edits>><<sinceMe|changes since mine>>'; |
|
|
var linkshere='<<whatLinksHere|shortcut=l|what links here>>'; |
|
|
var related='<<relatedChanges|shortcut=r|related changes>>'; |
|
|
var search='<<search|shortcut=s>>if(wikimedia){|<<globalsearch|shortcut=g|global>>}'; |
|
|
search += '|<<google|shortcut=G|web>>'; |
|
|
var watch='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>'; |
|
|
var protect='<<unprotect|unprotectShort>>|<<protect|shortcut=p>>'; |
|
|
var del='<<undelete|undeleteShort>>|<<delete|shortcut=d>>'; |
|
|
var move='<<move|shortcut=m|move page>>'; |
|
|
if (getValueOf('popupActionsMenu')) { |
|
|
s += '<<mainlink>>*' + dropdiv + '<a href="#">'+popupString('actions') + '</a>'; |
|
|
} else { s+= dropdiv + '<<mainlink>>'; } |
|
|
s+= menuspan + |
|
|
'if(oldid){<line><<edit|shortcut=e>>|<<editOld|shortcut=e|this revision>></line>' + |
|
|
'<<revert|shortcut=v>>' + |
|
|
'}else{<<edit|shortcut=e>>}' + |
|
|
'if(talk){<<new|shortcut=+|new topic>>}' + |
|
|
hist + lastedit + move + linkshere + related + |
|
|
'<<nullEdit|shortcut=n|null edit>>' + |
|
|
'<line>' + search + '</line>' + |
|
|
'<hr>' + |
|
|
'<line>' + watch + '</line>' + |
|
|
'if(admin){<line>' + protect + '</line><line>' + del + '</line>}' + |
|
|
'if(talk){' + |
|
|
'<hr>' + |
|
|
'<<article|shortcut=a|view article>>' + |
|
|
'<<editArticle|edit article>>' + |
|
|
'}else{' + |
|
|
'<hr>' + |
|
|
'<<talk|shortcut=t|talk page>>' + |
|
|
'<<editTalk|edit talk>>' + |
|
|
'<<newTalk|shortcut=+|new topic>>' + |
|
|
'}' + |
|
|
endspan + enddiv; |
|
|
s+='if(user){*' + dropdiv + '<a href="#">'+popupString('user')+'</a>' + menuspan + |
|
|
'<line><<userPage|shortcut=u|user page>>|<<userSpace|space>></line>' + |
|
|
'<<userTalk|shortcut=t|user talk>>' + |
|
|
'<<editUserTalk|edit user talk>>' + |
|
|
'<<newUserTalk|shortcut=+|leave comment>>' + |
|
|
'if(ipuser){<<arin>>}else{<<email|shortcut=E|email user>>}' + |
|
|
'<hr>' + |
|
|
'if(wikimedia){<line>}' + |
|
|
'<<contribs|shortcut=c|contributions>>' + |
|
|
'if(wikimedia){|<<contribsTree|tree>></line>}' + |
|
|
'<<userlog|shortcut=L|user log>>' + |
|
|
'if(wikimedia){<<count|shortcut=#|edit counter>>}' + |
|
|
'if(admin){<line><<unblock|unblockShort>>|<<block|shortcut=b|block user>></line>}' + |
|
|
'<<blocklog|shortcut=B|block log>>' + |
|
|
getValueOf('popupExtraUserMenu') + |
|
|
endspan + enddiv + '}'; |
|
|
return navlinkStringToHTML(s, x.article, x.oldid); |
|
|
}; |
|
|
|
|
|
pg.structures.menus.popupRedirTitle=pg.structures.menus.popupTitle; |
|
|
pg.structures.menus.popupRedirTopLinks=pg.structures.menus.popupTopLinks; |
|
|
|
|
|
// ENDFILE: structures.js |
|
|
// STARTFILE: autoedit.js |
|
|
function getParamValue(paramName) { |
|
|
var cmdRe=RegExp(''+paramName+'=(*)'); |
|
|
var h=document.location; |
|
|
var m=cmdRe.exec(h); |
|
|
if (m) { |
|
|
try { |
|
|
return decodeURI(m); |
|
|
} catch (someError) {} |
|
|
} |
|
|
return null; |
|
|
} |
|
|
|
|
|
function substitute(data,cmdBody) { |
|
|
// alert('sub\nfrom: '+cmdBody.from+'\nto: '+cmdBody.to+'\nflags: '+cmdBody.flags); |
|
|
var fromRe=RegExp(cmdBody.from, cmdBody.flags); |
|
|
return data.replace(fromRe, cmdBody.to); |
|
|
} |
|
|
|
|
|
function execCmds(data, cmdList) { |
|
|
for (var i=0; i<cmdList.length; ++i) { |
|
|
data=cmdList.action(data, cmdList); |
|
|
} |
|
|
return data; |
|
|
} |
|
|
|
|
|
function parseCmd(str) { |
|
|
// returns a list of commands |
|
|
if (!str.length) { return ; } |
|
|
var p=false; |
|
|
switch (str.charAt(0)) { |
|
|
case 's': |
|
|
p=parseSubstitute(str); |
|
|
break; |
|
|
case 'j': |
|
|
p=parseJavascript(str); |
|
|
break; |
|
|
default: |
|
|
return false; |
|
|
} |
|
|
if (p) { return .concat(parseCmd(p.remainder)); } |
|
|
return false; |
|
|
} |
|
|
|
|
|
function unEscape(str, sep) { |
|
|
return str.split('\\\\').join('\\').split('\\'+sep).join(sep).split('\\n').join('\n'); |
|
|
} |
|
|
|
|
|
|
|
|
function runJavascript(data, argWrapper) { |
|
|
// flags aren't used (yet) |
|
|
|
|
|
// from the user's viewpoint, |
|
|
// data is a special variable may appear inside code |
|
|
// and gets assigned the text in the edit box |
|
|
|
|
|
// alert('eval-ing '+argWrapper.code); |
|
|
|
|
|
// commenting this out - it's something of a security nightmare and it's not actually used |
|
|
|
|
|
//return eval(argWrapper.code); |
|
|
} |
|
|
|
|
|
function parseJavascript(str) { |
|
|
// takes a string like j/code/;othercmds and parses it |
|
|
|
|
|
var tmp,code,flags; |
|
|
|
|
|
if (str.length<3) { return false; } |
|
|
var sep=str.charAt(1); |
|
|
str=str.substring(2); |
|
|
|
|
|
tmp=skipOver(str,sep); |
|
|
if (tmp) { code=tmp.segment.split('\n').join('\\n'); str=tmp.remainder; } |
|
|
else { return false; } |
|
|
|
|
|
flags=''; |
|
|
if (str.length) { |
|
|
tmp=skipOver(str,';') || skipToEnd(str, ';'); |
|
|
if (tmp) {flags=tmp.segment; str=tmp.remainder; } |
|
|
} |
|
|
|
|
|
return { action: runJavascript, code: code, flags: flags, remainder: str }; |
|
|
} |
|
|
|
|
|
function parseSubstitute(str) { |
|
|
// takes a string like s/a/b/flags;othercmds and parses it |
|
|
|
|
|
var from,to,flags,tmp; |
|
|
|
|
|
if (str.length<4) { return false; } |
|
|
var sep=str.charAt(1); |
|
|
str=str.substring(2); |
|
|
|
|
|
tmp=skipOver(str,sep); |
|
|
if (tmp) { from=tmp.segment; str=tmp.remainder; } |
|
|
else { return false; } |
|
|
|
|
|
tmp=skipOver(str,sep); |
|
|
if (tmp) { to=tmp.segment; str=tmp.remainder; } |
|
|
else { return false; } |
|
|
|
|
|
flags=''; |
|
|
if (str.length) { |
|
|
tmp=skipOver(str,';') || skipToEnd(str, ';'); |
|
|
if (tmp) {flags=tmp.segment; str=tmp.remainder; } |
|
|
} |
|
|
|
|
|
return {action: substitute, from: from, to: to, flags: flags, remainder: str}; |
|
|
|
|
|
} |
|
|
|
|
|
function skipOver(str,sep) { |
|
|
var endSegment=findNext(str,sep); |
|
|
if (endSegment<0) { return false; } |
|
|
var segment=unEscape(str.substring(0,endSegment), sep); |
|
|
return {segment: segment, remainder: str.substring(endSegment+1)}; |
|
|
} |
|
|
|
|
|
function skipToEnd(str,sep) { |
|
|
return {segment: str, remainder: ''}; |
|
|
} |
|
|
|
|
|
function findNext(str, ch) { |
|
|
for (var i=0; i<str.length; ++i) { |
|
|
if (str.charAt(i)=='\\') { i+=2; } |
|
|
if (str.charAt(i)==ch) { return i; } |
|
|
} |
|
|
return -1; |
|
|
} |
|
|
|
|
|
function setCheckbox(param, box) { |
|
|
var val=getParamValue(param); |
|
|
if (val!==null) { |
|
|
switch (val) { |
|
|
case '1': case 'yes': case 'true': |
|
|
box.checked=true; |
|
|
break; |
|
|
case '0': case 'no': case 'false': |
|
|
box.checked=false; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
function autoEdit() { |
|
|
if (!document.editform) { return false; } |
|
|
if (window.autoEdit.alreadyRan) { return false; } |
|
|
window.autoEdit.alreadyRan=true; |
|
|
var cmdString=getParamValue('autoedit'); |
|
|
if (cmdString) { |
|
|
try { |
|
|
var editbox=document.editform.wpTextbox1; |
|
|
} catch (dang) { return; } |
|
|
var cmdList=parseCmd(cmdString); |
|
|
var input=editbox.value; |
|
|
var output=execCmds(input, cmdList); |
|
|
editbox.value=output; |
|
|
} |
|
|
|
|
|
var summary=getParamValue('autosummary'); |
|
|
var summaryprompt=getParamValue('autosummaryprompt'); |
|
|
if (summaryprompt) { |
|
|
var txt='Enter a non-empty edit summary or press cancel to abort'; |
|
|
if (popupString) { txt=popupString(txt); } |
|
|
var response=prompt(txt, summary); |
|
|
if (response) { summary=response; } |
|
|
else { return; } |
|
|
} |
|
|
if (summary) { document.editform.wpSummary.value=summary; } |
|
|
|
|
|
setCheckbox('autominor', document.editform.wpMinoredit); |
|
|
setCheckbox('autowatch', document.editform.wpWatchthis); |
|
|
|
|
|
var btn=getParamValue('autoclick'); |
|
|
if (btn) { |
|
|
if (document.editform && document.editform) { |
|
|
var headings=document.getElementsByTagName('h1'); |
|
|
if (headings) { |
|
|
var div=document.createElement('div'); |
|
|
var button=document.editform; |
|
|
div.innerHTML='<font size=+1><b>The "' + button.value + |
|
|
'" button has been automatically clicked.' + |
|
|
' Please wait for the next page to load.</b></font>'; |
|
|
document.title='('+document.title+')'; |
|
|
headings.parentNode.insertBefore(div, headings); |
|
|
button.click(); |
|
|
} |
|
|
} else { |
|
|
alert('autoedit.js\n\nautoclick: could not find button "'+ btn +'".'); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
addOnloadHook(autoEdit); |
|
|
|
|
|
// ENDFILE: autoedit.js |
|
|
// STARTFILE: downloader.js |
|
|
/** |
|
|
@fileoverview |
|
|
{@link Downloader}, a xmlhttprequest wrapper, and helper functions. |
|
|
*/ |
|
|
|
|
|
/** |
|
|
Creates a new Downloader |
|
|
@constructor |
|
|
@class The Downloader class. Create a new instance of this class to download stuff. |
|
|
@param {String} url The url to download. This can be omitted and supplied later. |
|
|
*/ |
|
|
function Downloader(url) { |
|
|
// Source: http://jibbering.com/2002/4/httprequest.html |
|
|
/** xmlhttprequest object which we're wrapping */ |
|
|
this.http = false; |
|
|
|
|
|
/*@cc_on @*/ |
|
|
/*@if (@_jscript_version >= 5) |
|
|
// JScript gives us Conditional compilation, |
|
|
// we can cope with old IE versions. |
|
|
// and security blocked creation of the objects. |
|
|
try { |
|
|
this.http = new ActiveXObject("Msxml2.XMLHTTP"); |
|
|
} catch (e) { |
|
|
try { |
|
|
this.http = new ActiveXObject("Microsoft.XMLHTTP"); |
|
|
} catch (E) { |
|
|
// this.http = false; |
|
|
} |
|
|
} |
|
|
@end @*/ |
|
|
|
|
|
if (! this.http && typeof XMLHttpRequest!='undefined') { this.http = new XMLHttpRequest(); } |
|
|
/** The url to download */ |
|
|
this.url = url; |
|
|
/** A universally unique ID number */ |
|
|
this.id=null; |
|
|
/** Modification date, to be culled from the incoming headers */ |
|
|
this.lastModified = null; |
|
|
/** What to do when the download completes successfully */ |
|
|
this.callbackFunction = null; |
|
|
/** Flag set on <code>abort</code> */ |
|
|
this.aborted = false; |
|
|
} |
|
|
|
|
|
new Downloader(); |
|
|
|
|
|
/** Submits the http request. */ |
|
|
Downloader.prototype.send = function (x) { |
|
|
if (!this.http) { return null; } |
|
|
return this.http.send(x); |
|
|
}; |
|
|
/** Aborts the download, setting the <code>aborted</code> field to true. */ |
|
|
Downloader.prototype.abort = function () { |
|
|
if (!this.http) { return null; } |
|
|
this.aborted=true; |
|
|
return this.http.abort(); |
|
|
}; |
|
|
/** Returns the downloaded data. */ |
|
|
Downloader.prototype.getData = function () {if (!this.http) { return null; } return this.http.responseText;}; |
|
|
/** Prepares the download. */ |
|
|
Downloader.prototype.setTarget = function () {if (!this.http) { return null; } this.http.open("GET", this.url, true);}; |
|
|
/** Gets the state of the download. */ |
|
|
Downloader.prototype.getReadyState=function () {if (!this.http) { return null; } return this.http.readyState;}; |
|
|
|
|
|
pg.misc.downloadsInProgress = { }; |
|
|
|
|
|
/** Starts the download. |
|
|
Note that setTarget {@link Downloader#setTarget} must be run first |
|
|
*/ |
|
|
Downloader.prototype.start=function () { |
|
|
if (!this.http) { return; } |
|
|
pg.misc.downloadsInProgress = this; |
|
|
this.http.send(null); |
|
|
}; |
|
|
|
|
|
/** Gets the 'Last-Modified' date from the download headers. |
|
|
Should be run after the download completes. |
|
|
Returns <code>null</code> on failure. |
|
|
@return {Date} |
|
|
*/ |
|
|
Downloader.prototype.getLastModifiedDate=function () { |
|
|
if(!this.http) { return null; } |
|
|
var lastmod=null; |
|
|
try { |
|
|
lastmod=this.http.getResponseHeader('Last-Modified'); |
|
|
} catch (err) {} |
|
|
if (lastmod) { return new Date(lastmod); } |
|
|
return null; |
|
|
}; |
|
|
|
|
|
/** Sets the callback function. |
|
|
@param {Function} f callback function, called as <code>f(this)</code> on success |
|
|
*/ |
|
|
Downloader.prototype.setCallback = function (f) { |
|
|
if(!this.http) { return; } |
|
|
this.http.onreadystatechange = f; |
|
|
}; |
|
|
|
|
|
////////////////////////////////////////////////// |
|
|
// helper functions |
|
|
|
|
|
/** Creates a new {@link Downloader} and prepares it for action. |
|
|
@param {String} url The url to download |
|
|
@param {integer} id The ID of the {@link Downloader} object |
|
|
@param {Function} callback The callback function invoked on success |
|
|
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser |
|
|
*/ |
|
|
function newDownload(url, id, callback) { |
|
|
var d=new Downloader(url); |
|
|
if (!d.http) { return 'ohdear'; } |
|
|
d.id=id; |
|
|
d.setTarget(); |
|
|
var f = function () { |
|
|
if (d.getReadyState() == 4) { |
|
|
delete pg.misc.downloadsInProgress; |
|
|
d.data=d.getData(); |
|
|
d.lastModified=d.getLastModifiedDate(); |
|
|
callback(d); |
|
|
} |
|
|
}; |
|
|
d.setCallback(f); |
|
|
return d; |
|
|
} |
|
|
/** Simulates a download from cached data. |
|
|
The supplied data is put into a {@link Downloader} as if it had downloaded it. |
|
|
@param {String} url The url. |
|
|
@param {integer} id The ID. |
|
|
@param {Function} callback The callback, which is invoked immediately as <code>callback(d)</code>, |
|
|
where <code>d</code> is the new {@link Downloader}. |
|
|
@param {String} data The (cached) data. |
|
|
@param {Date} lastModified The (cached) last modified date. |
|
|
*/ |
|
|
function fakeDownload(url, id, callback, data, lastModified) { |
|
|
var d=newDownload(url,callback); |
|
|
d.id=id; d.data=data; |
|
|
d.lastModified=lastModified; |
|
|
return callback(d); |
|
|
} |
|
|
|
|
|
/** |
|
|
Starts a download. |
|
|
@param {String} url The url to download |
|
|
@param {integer} id The ID of the {@link Downloader} object |
|
|
@param {Function} callback The callback function invoked on success |
|
|
@return {String/Downloader} the {@link Downloader} object created, or 'ohdear' if an unsupported browser |
|
|
*/ |
|
|
function startDownload(url, id, callback) { |
|
|
var d=newDownload(url, id, callback); |
|
|
if (typeof d == typeof '' ) { return d; } |
|
|
d.start(); |
|
|
return d; |
|
|
} |
|
|
|
|
|
/** |
|
|
Aborts all downloads which have been started. |
|
|
*/ |
|
|
function abortAllDownloads() { |
|
|
for ( var x in pg.misc.downloadsInProgress ) { |
|
|
try { |
|
|
pg.misc.downloadsInProgress.aborted=true; |
|
|
pg.misc.downloadsInProgress.abort(); |
|
|
delete pg.misc.downloadsInProgress; |
|
|
} catch (e) { } |
|
|
} |
|
|
} |
|
|
|
|
|
// ENDFILE: downloader.js |
|
|
// STARTFILE: livepreview.js |
|
|
// Last update: 21:51, 15 Feb 2005 (UTC) |
|
|
|
|
|
var lp={}; |
|
|
|
|
|
function setupLivePreview() { |
|
|
// User options |
|
|
window.lp.userName=window.lp.userName||'Wikipedian'; |
|
|
window.lp.userSignature=window.lp.userSignature||window.lp.userName; |
|
|
window.lp.showImages=false; //window.lp.showImages||true; |
|
|
|
|
|
// System options |
|
|
window.lp.languageCode=window.lp.languageCode||'en'; |
|
|
window.lp.interwikiCodes=pg.wiki.interwiki; |
|
|
window.lp.baseArticlePath=pg.wiki.articlePath; |
|
|
window.lp.userNamespace=pg.ns.user; |
|
|
window.lp.imageNamespace=pg.ns.image; |
|
|
window.lp.categoryNamespace=pg.ns.category; |
|
|
window.lp.imageBasePath=getImageUrlStart(pg.wiki.hostname); |
|
|
|
|
|
window.lp.mathBasePath=window.lp.mathBasePath||'/math/'; |
|
|
window.lp.imageFallbackPath=window.lp.imageFallbackPath||'http://upload.wikimedia.org/wikipedia/commons/'; |
|
|
window.lp.defaultThumbWidth=getValueOf('popupImageSize'); |
|
|
window.lp.skinMagnifyClip=window.lp.skinMagnifyClip||'/skins/common/images/magnify-clip.png'; |
|
|
|
|
|
} |
|
|
|
|
|
function wiki2html(str){ |
|
|
str=strip_cr(str); |
|
|
var w=new WikiCode(); |
|
|
w.lines=str.split(/\n/); |
|
|
w.parse(); |
|
|
return w.html; |
|
|
} |
|
|
|
|
|
function WikiCode() { |
|
|
this.lines=; |
|
|
this.html=''; |
|
|
window.lp.signature=']'; |
|
|
window.lp.blockImage=new RegExp('^\\[\\['+window.lp.imageNamespace+':.*?\\|.*?(?:frame|thumbnail|thumb|none|right|left|center)','i'); |
|
|
} |
|
|
WikiCode.prototype._parse_table_data=function(){ |
|
|
var td_match,td_line; |
|
|
td_match=this.lines.shift().match(RegExp(/^(\|\+|\||!)((?:(*?)\|(?!\|))?(.*))$/)); |
|
|
if (td_match=='|+'){ this.html+='<caption'; } |
|
|
else { this.html+='<t'+((td_match=='|')?'d':'h'); } |
|
|
if (typeof td_match!='undefined') { this.html+=' '+td_match;td_line=td_match.split('||'); } |
|
|
else { td_line=td_match.split('||'); } |
|
|
this.html+='>'; |
|
|
while(td_line.length>1){ this.lines.unshift(td_match+td_line.pop()); } |
|
|
this.html+=_parse_inline_nowiki(td_line); |
|
|
var td=new WikiCode(); |
|
|
var table_count=0; |
|
|
while(this.lines.length){ |
|
|
if(this.lines.charAt(0)=='|') { |
|
|
if(table_count===0) {break;} |
|
|
else if(this.lines.charAt(1)=='}'){--table_count;} |
|
|
} |
|
|
else if(this.lines.charAt(0)=='!'&&table_count===0) {break;} |
|
|
else if(this.lines.substr(0,2)=='{|') {table_count++;} |
|
|
td.lines.push(this.lines.shift()); |
|
|
} |
|
|
if(td.lines.length) {td.parse();} |
|
|
this.html+=td.html; |
|
|
}; |
|
|
WikiCode.prototype._parse_pre=function(){ |
|
|
this.html+='<pre>'; |
|
|
do{ |
|
|
this._endline(_parse_inline_nowiki(this.lines.substring(1,this.lines.length))+"\n"); |
|
|
} while(this.lines.length&&this.lines.charAt(0)==' '); |
|
|
this.html+='</pre>'; |
|
|
}; |
|
|
WikiCode.prototype._parse_block_image=function(){ |
|
|
this.html+=_parse_image(this.lines.shift()); |
|
|
}; |
|
|
WikiCode.prototype._endline=function(str){ |
|
|
this.html+=str; |
|
|
this.lines.shift(); |
|
|
}; |
|
|
WikiCode.prototype.parse=function() { |
|
|
var p=false; |
|
|
do{ |
|
|
var h_match=this.lines.match(/^(={1,6})(.*)\1(.*)$/); |
|
|
if(h_match){ |
|
|
p=false; |
|
|
this._endline('<h'+h_match.length+'>' + _parse_inline_nowiki(h_match)+ |
|
|
'</h'+h_match.length+'>' + h_match); |
|
|
}else if(this.lines.match(/^/)){ |
|
|
p=false; |
|
|
this._parse_list(); |
|
|
}else if(this.lines.charAt(0)==' '){ |
|
|
p=false; |
|
|
this._parse_pre(); |
|
|
}else if(this.lines.substr(0,2)=='{|'){ |
|
|
p=false; |
|
|
this._parse_table(); |
|
|
}else if(this.lines.match(/^----+$/)){ |
|
|
p=false; |
|
|
this._endline('<hr/>'); |
|
|
}else if(this.lines.match(window.lp.blockImage)){ |
|
|
p=false; |
|
|
this._parse_block_image(); |
|
|
}else{ |
|
|
if(this.lines===''){ |
|
|
p=(this.lines.length>1&&this.lines===''); |
|
|
if(p){this._endline('<p><br />');} |
|
|
} else { |
|
|
if(!p){this.html+='<p>'; p=true;} |
|
|
this.html+=_parse_inline_nowiki(this.lines)+' '; |
|
|
} |
|
|
this.lines.shift(); |
|
|
} |
|
|
}while(this.lines.length); |
|
|
}; |
|
|
WikiCode.prototype._parse_list=function(){ |
|
|
var prev=''; |
|
|
var l_match,imatch,dt_match; |
|
|
while(this.lines.length&&(l_match=this.lines.match(/^(+)(.*)$/))){ |
|
|
this.lines.shift(); |
|
|
imatch=str_imatch(prev,l_match); |
|
|
for(var i=prev.length-1;i>=imatch;i--){ |
|
|
if(prev.charAt(i)=='*'){this.html+='</ul>';} |
|
|
else if(prev.charAt(i)=='#'){this.html+='</ol>';} |
|
|
else{ |
|
|
this.html+='</d'+((prev.charAt(i)==';')?'t':'d')+'>'; |
|
|
switch(l_match.charAt(i)){ |
|
|
case'': case'*': case'#': |
|
|
this.html+='</dl>'; |
|
|
} |
|
|
} |
|
|
} |
|
|
for (i=imatch;i<l_match.length;i++){ |
|
|
if(l_match.charAt(i)=='*'){this.html+='<ul>';} |
|
|
else if(l_match.charAt(i)=='#'){this.html+='<ol>';} |
|
|
else{ |
|
|
switch(prev.charAt(i)){ |
|
|
case'':case'*':case'#': |
|
|
this.html+='<dl>'; |
|
|
} |
|
|
this.html+='<d'+((l_match.charAt(i)==';')?'t':'d')+'>'; |
|
|
} |
|
|
} |
|
|
switch(l_match.charAt(l_match.length-1)) { |
|
|
case'*': case'#': this.html+='<li>'+_parse_inline_nowiki(l_match); break; |
|
|
case';': |
|
|
dt_match=l_match.match(/(.*?) (:.*?)$/); |
|
|
if(dt_match) { |
|
|
this.html+=_parse_inline_nowiki(dt_match); |
|
|
this.lines.unshift(dt_match);} |
|
|
break; |
|
|
case':': |
|
|
this.html+=_parse_inline_nowiki(l_match); |
|
|
} |
|
|
prev=l_match; |
|
|
} |
|
|
for(i=prev.length-1;i>=0;i--){ |
|
|
if(prev.charAt(i)=='*'){this.html+='</ul>';} |
|
|
else if(prev.charAt(i)=='#'){this.html+='</ol>';} |
|
|
else{this.html+='</d'+((prev.charAt(i)==';')?'t':'d')+'></dl>';} |
|
|
} |
|
|
}; |
|
|
WikiCode.prototype._parse_table=function(){var table_match=this.lines.match(/^\{\|( .*)$/);if(table_match){this._endline('<table'+table_match+'>');}else{this._endline('<table>');}do{if(this.lines.charAt(0)=='|'){switch(this.lines.charAt(1)){case'}':this._endline('</table>');return;case'-':this._endline('<tr '+this.lines.match(/\|-*(.*)/)+'>');break;default:this._parse_table_data();}}else if(this.lines.charAt(0)=='!'){this._parse_table_data();}else{this.lines.shift();}}while(this.lines.length);}; |
|
|
function _parse_image(str){var attr=str.substring(window.lp.imageNamespace.length+3,str.length-2).split(/\s*\|\s*/);var filename=attr;var caption=attr;var width,w_match;var thumb=false;var frame=false;var center=false;var align='';var html='';do{w_match=attr.match(/^(\d*)px$/);if(w_match){width=w_match;}else switch(attr){case'thumb':case'thumbnail':thumb=true;case'frame':frame=true;break;case'none':case'right':case'left':center=false;align=attr;break;case'center':center=true;align='none';}attr.shift();}while(attr.length);if(frame){if(align===''){align='right';}html+="<div class='thumb t"+align+"'>";if(thumb){if(!width){width=window.lp.defaultThumbWidth;}html+="<div style='width:"+(2+parseInt(width,10))+"px;'>";html+=_make_image(filename,caption,width);html+="<div class='thumbcaption'><div class='magnify' style='float:right'><a href='"+window.lp.baseArticlePath+window.lp.imageNamespace+':'+filename+"' class='internal' title='Enlarge'><img src='"+window.lp.skinMagnifyClip+"' /></a></div>"+_parse_inline_nowiki(caption)+"</div>";}else{html+='<div>';html+=_make_image(filename,caption);html+="<div class='thumbcaption'>"+_parse_inline_nowiki(caption)+"</div>";}html+='</div></div>';}else if(align){html+="<div class='float"+align+"'><span>"+_make_image(filename,caption,width)+"</span></div>";}else{return _make_image(filename,caption,width);}if(center){return"<div class='center'>"+html+'</div>';}else{return html;}} |
|
|
function _parse_inline_nowiki(str){var start,lastend=0;var substart=0,nestlev=0,open,close,subloop;var html='';while(-1!=(start=str.indexOf('<'+'nowiki>',substart))){html+=_parse_inline_wiki(str.substring(lastend,start));start+=8;substart=start;subloop=true;do{open=str.indexOf('<'+'nowiki>',substart);close=str.indexOf('</nowiki>',substart);if(close<=open||open==-1){if(close==-1){return html+html_entities(str.substr(start));}substart=close+9;if(nestlev){nestlev--;}else{lastend=substart;html+=html_entities(str.substring(start,lastend-9));subloop=false;}}else{substart=open+8;nestlev++;}}while(subloop);}return html+_parse_inline_wiki(str.substr(lastend));}; |
|
|
function _make_image(filename,caption,width){filename=filename.charAt(0).toUpperCase()+filename.substr(1);filename=filename.replace(/ /g,'_');var md5=hex_md5(filename);var source=md5.charAt(0)+'/'+md5.substr(0,2)+'/'+filename;var img;if(window.lp.showImages){if(width){width="width='"+width+"px'";}img="<img onerror='this.onerror=null;this.src=\""+window.lp.imageFallbackPath+source+"\";' src='"+window.lp.imageBasePath+source+"' alt='"+caption+"' "+width+"/>";}else{img=window.lp.imageNamespace+':'+filename+" <em style='color:red;'>(images disabled)</em>";}caption=_strip_inline_wiki(caption);return"<a class='image' title='"+caption+"' href='"+window.lp.baseArticlePath+window.lp.imageNamespace+':'+filename+"'>"+img+"</a>";} |
|
|
function _parse_inline_images(str){var start,substart=0,nestlev=0;var loop,close,open,wiki,html;while(-1!=(start=str.indexOf(']',substart);open=str.indexOf('[[',substart);if(close<=open||open==-1){if(close==-1){return str;}substart=close;if(nestlev){nestlev--;}else{wiki=str.substring(start,close+2);html=_parse_image(wiki);str=str.replace(wiki,html);substart=start+html.length;loop=false;}}else{substart=open;nestlev++;}}while(loop);}else{break;}}return str;} |
|
|
|
|
|
function _parse_inline_wiki(str){ |
|
|
var aux_match,math_md5; |
|
|
str=_parse_inline_images(str); |
|
|
while (aux_match=str.match(/<(?:)math>(.*?)<\/math>/i)) { |
|
|
math_md5=hex_md5(aux_match); |
|
|
str=str.replace(aux_match,"<img src='"+window.lp.mathBasePath+math_md5+'.png'+"' />"); |
|
|
} |
|
|
// log('livepreview: str='+str); |
|
|
return str.replace(/'''''(.*?)''(.*?)'''/g,'<strong><em>$1</em>$2</strong>') |
|
|
.replace(/'''''(.*?)'''(.*?)''/g,'<em><strong>$1</strong>$2</em>') |
|
|
.replace(/'''(.*?)''(.*?)'''''/g,'<strong>$1<em>$2</em></strong>') |
|
|
.replace(/'''(.*?)'''/g,'<strong>$1</strong>') |
|
|
.replace(/''(.*?)''/g,'<em>$1</em>') |
|
|
.replace(/~{5}(?!~)/g,Date()) |
|
|
.replace(/~{4}(?!~)/g,window.lp.signature+' '+Date()) |
|
|
.replace(/~{3}(?!~)/g,window.lp.signature) |
|
|
.replace(RegExp('\\\\]','gi'), |
|
|
"<a href='"+window.lp.baseArticlePath+"$1'>$1</a>") |
|
|
.replace(RegExp('\\\\]','gi'),'') |
|
|
.replace(/\*?)\]\](\w*)/g,"<a href='"+window.lp.baseArticlePath+"$1'>$1$2</a>") |
|
|
.replace(/\]+?)\]\](\w*)/g,"<a href='"+window.lp.baseArticlePath+"$1'>$2$3</a>") |
|
|
.replace(/\]*?:)?(.*?)( *\(.*?\))?\|\]\]/g,"<a href='"+window.lp.baseArticlePath+"$1$2$3'>$2</a>") |
|
|
.replace(/\]*?)) (.*?)\]/g,"<a class='external' title='$1' href='$1'>$5</a>") |
|
|
.replace(/\/g,"<a class='external' title='$1' href='$1'></a>") |
|
|
.replace(/\/g,"<a class='external' title='$1' href='$1'>$1</a>") |
|
|
.replace(/(^| )((https?|news|ftp|mailto|gopher|irc):(\/*)(*))/g,"$1<a class='external' title='$2' href='$2'>$2</a>") |
|
|
.replace('__NOTOC__','') |
|
|
.replace('__NOEDITSECTION__',''); |
|
|
} /* comment for enscript */ |
|
|
function _strip_inline_wiki(str){return str.replace(/\]*\|(.*?)\]\]/g,'$1').replace(/\\]/g,'$1').replace(/''(.*?)''/g,'$1');} |
|
|
function max(a,b){if(a>b){return a;}return b;} |
|
|
function min(a,b){if(a<b){return a;}return b;} |
|
|
function str_imatch(str_a,str_b){var lim=min(str_a.length,str_b.length);for(var i=0;i<lim;i++){if(str_a.charAt(i)!=str_b.charAt(i)){return i;}}return i;} |
|
|
function strip_cr(str){return str.replace(/\n\r/g,"\n").replace(/\r/g,'');} |
|
|
function html_entities(str){return str.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">");} |
|
|
//</math> |
|
|
|
|
|
// ENDFILE: livepreview.js |
|
|
// STARTFILE: pageinfo.js |
|
|
function popupFilterPageSize(data) {return formatBytes(data.length);} |
|
|
function popupFilterCountLinks(data) { |
|
|
var num=countLinks(data);return String(num) + ' ' + ((num!=1)?popupString('wikiLinks'):popupString('wikiLink')); |
|
|
} |
|
|
|
|
|
function popupFilterCountImages(data) { |
|
|
var num=countImages(data);return String(num) + ' ' + ((num!=1)?popupString('images'):popupString('image')); |
|
|
} |
|
|
|
|
|
function popupFilterCountCategories(data) { |
|
|
var num=countCategories(data); return String(num) + ' ' + ((num!=1)?popupString('categories'):popupString('category')); |
|
|
} |
|
|
|
|
|
|
|
|
function popupFilterLastModified(data,download) { |
|
|
var lastmod=download.lastModified; |
|
|
var now=new Date(); |
|
|
var age=now-lastmod; |
|
|
if (lastmod && getValueOf('popupLastModified')) { |
|
|
return (formatAge(age) + ' ' + popupString('old')).replace(RegExp(' ','g'), ' '); |
|
|
} |
|
|
return ''; |
|
|
} |
|
|
|
|
|
function formatAge(age) { |
|
|
// coerce into a number |
|
|
var a=0+age, aa=a; |
|
|
|
|
|
var seclen = 1000; |
|
|
var minlen = 60*seclen; |
|
|
var hourlen = 60*minlen; |
|
|
var daylen = 24*hourlen; |
|
|
var weeklen = 7*daylen; |
|
|
|
|
|
var numweeks = (a-a%weeklen)/weeklen; a = a-numweeks*weeklen; var sweeks = addunit(numweeks, 'week'); |
|
|
var numdays = (a-a%daylen)/daylen; a = a-numdays*daylen; var sdays = addunit(numdays, 'day'); |
|
|
var numhours = (a-a%hourlen)/hourlen; a = a-numhours*hourlen; var shours = addunit(numhours,'hour'); |
|
|
var nummins = (a-a%minlen)/minlen; a = a-nummins*minlen; var smins = addunit(nummins, 'minute'); |
|
|
var numsecs = (a-a%seclen)/seclen; a = a-numsecs*seclen; var ssecs = addunit(numsecs, 'second'); |
|
|
|
|
|
//alert( .join(':') ); |
|
|
|
|
|
if (aa > 4*weeklen) { return sweeks; } |
|
|
if (aa > weeklen) { return sweeks + ' ' + sdays; } |
|
|
if (aa > daylen) { return sdays + ' ' + shours; } |
|
|
if (aa > 6*hourlen) { return shours; } |
|
|
if (aa > hourlen) { return shours + ' ' + smins; } |
|
|
if (aa > 10*minlen) { return smins; } |
|
|
if (aa > minlen) { return smins + ' ' + ssecs; } |
|
|
return ssecs; |
|
|
} |
|
|
|
|
|
function addunit(num,str) { return '' + num + ' ' + ((num!=1) ? popupString(str+'s') : popupString(str)) ;} |
|
|
|
|
|
function runPopupFilters(list, data, download) { |
|
|
var ret=; |
|
|
for (var i=0; i<list.length; ++i) { |
|
|
if (list && typeof list == 'function') { |
|
|
var s=list(data, download); |
|
|
if (s) { ret.push(s); } |
|
|
} |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function getPageInfo(data, download) { |
|
|
if (!data || data.length === 0) { return popupString('Empty page'); } |
|
|
|
|
|
var popupFilters=getValueOf('popupFilters') || ; |
|
|
var extraPopupFilters = getValueOf('extraPopupFilters') || ; |
|
|
var pageInfoArray = runPopupFilters(popupFilters.concat(extraPopupFilters), data, download); |
|
|
|
|
|
var pageInfo=pageInfoArray.join(', '); |
|
|
if (pageInfo !== '' ) { pageInfo = upcaseFirst(pageInfo); } |
|
|
return pageInfo; |
|
|
} |
|
|
|
|
|
|
|
|
// this could be improved! |
|
|
function countLinks(wikiText) { return wikiText.split('[[').length - 1; } |
|
|
|
|
|
// if N = # matches, n = # brackets, then |
|
|
// String.parenSplit(regex) intersperses the N+1 split elements |
|
|
// with Nn other elements. So total length is |
|
|
// L= N+1 + Nn = N(n+1)+1. So N=(L-1)/(n+1). |
|
|
|
|
|
function countImages(wikiText) {return (wikiText.parenSplit(pg.re.image).length - 1) / (pg.re.imageBracketCount + 1);} |
|
|
|
|
|
function countCategories(wikiText) {return (wikiText.parenSplit(pg.re.category).length - 1) / (pg.re.categoryBracketCount + 1); } |
|
|
|
|
|
function popupFilterStubDetect(data) { return (isStub(data)) ? popupString('stub') : ''; } |
|
|
function popupFilterDisambigDetect(data) { return (isDisambig(data)) ? popupString('disambig') : ''; } |
|
|
|
|
|
function formatBytes(num) {return (num > 949) ? (Math.round(num/100)/10+popupString('kB')) : (num +' ' + popupString('bytes')) ;} |
|
|
|
|
|
// ENDFILE: pageinfo.js |
|
|
// STARTFILE: titles.js |
|
|
/** |
|
|
@fileoverview Defines the {@link Title} class, and associated crufty functions. |
|
|
|
|
|
<code>Title</code> deals with article titles and their various |
|
|
forms. {@link Stringwrapper} is the parent class of |
|
|
<code>Title</code>, which exists simply to make things a little |
|
|
neater. |
|
|
|
|
|
*/ |
|
|
|
|
|
/** |
|
|
Creates a new Stringwrapper. |
|
|
@constructor |
|
|
|
|
|
@class the Stringwrapper class. This base class is not really |
|
|
useful on its own; it just wraps various common string operations. |
|
|
*/ |
|
|
function Stringwrapper() { |
|
|
/** |
|
|
Wrapper for this.toString().indexOf() |
|
|
@param {String} x |
|
|
@type integer |
|
|
*/ |
|
|
this.indexOf=function(x){return this.toString().indexOf(x);}; |
|
|
/** |
|
|
Returns this.value. |
|
|
@type String |
|
|
*/ |
|
|
this.toString=function(){return this.value;}; |
|
|
/** |
|
|
Wrapper for {@link String#parenSplit} applied to this.toString() |
|
|
@param {RegExp} x |
|
|
@type Array |
|
|
*/ |
|
|
this.parenSplit=function(x){return this.toString().parenSplit(x);}; |
|
|
/** |
|
|
Wrapper for this.toString().substring() |
|
|
@param {String} x |
|
|
@param {String} y (optional) |
|
|
@type String |
|
|
*/ |
|
|
this.substring=function(x,y){ |
|
|
if (typeof y=='undefined') { return this.toString().substring(x); } |
|
|
return this.toString().substring(x,y); |
|
|
}; |
|
|
/** |
|
|
Wrapper for this.toString().split() |
|
|
@param {String} x |
|
|
@type Array |
|
|
*/ |
|
|
this.split=function(x){return this.toString().split(x);}; |
|
|
/** |
|
|
Wrapper for this.toString().replace() |
|
|
@param {String} x |
|
|
@param {String} y |
|
|
@type String |
|
|
*/ |
|
|
this.replace=function(x,y){ return this.toString().replace(x,y); }; |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
Creates a new <code>Title</code>. |
|
|
@constructor |
|
|
|
|
|
@class The Title class. Holds article titles and converts them into |
|
|
various forms. Also deals with anchors, by which we mean the bits |
|
|
of the article URL after a # character, representing locations |
|
|
within an article. |
|
|
|
|
|
@param {String} value The initial value to assign to the |
|
|
article. This must be the canonical title (see {@link |
|
|
Title#value}. Omit this in the constructor and use another function |
|
|
to set the title if this is unavailable. |
|
|
*/ |
|
|
function Title(val) { |
|
|
/** |
|
|
The canonical article title. This must be in UTF-8 with no |
|
|
entities, escaping or nasties. Also, underscores should be |
|
|
replaced with spaces. |
|
|
@type String |
|
|
@private |
|
|
*/ |
|
|
this.value=null; |
|
|
this.setUtf(val); |
|
|
/** |
|
|
The canonical form of the anchor. This should by decoded utf-8 text. |
|
|
@type String |
|
|
*/ |
|
|
this.anchor=''; |
|
|
} |
|
|
Title.prototype=new Stringwrapper(); |
|
|
/** |
|
|
Returns the canonical representation of the article title with anchor. |
|
|
@fixme Decide specs for anchor |
|
|
@return String The article title and the anchor. |
|
|
*/ |
|
|
Title.prototype.toString=function() { |
|
|
return this.value + ( this.anchor ? '#' + this.anchor : '' ); |
|
|
}; |
|
|
Title.urlAnchor=function() { |
|
|
// FIXME FIXME FIXME |
|
|
return this.anchor; |
|
|
} |
|
|
Title.fromURL=function(h) { |
|
|
return new Title().fromURL(h); |
|
|
}; |
|
|
Title.prototype.fromURL=function(h) { |
|
|
if (typeof h != 'string') { |
|
|
this.value=null; |
|
|
return this; |
|
|
} |
|
|
|
|
|
// NOTE : playing with decodeURI, encodeURI, escape, unescape, |
|
|
// we seem to be able to replicate the IE borked encoding |
|
|
|
|
|
// IE doesn't do this new-fangled utf-8 thing. |
|
|
// and it's worse than that. |
|
|
// IE seems to treat the query string differently to the rest of the url |
|
|
// the query is treated as bona-fide utf8, but the first bit of the url is pissed around with |
|
|
|
|
|
// we fix up & for all browsers, just in case. |
|
|
var splitted=h.split('?'); |
|
|
splitted=splitted.split('&').join('%26'); |
|
|
|
|
|
if (pg.flag.linksLikeIE) { |
|
|
splitted=encodeURI(decode_utf8(splitted)); |
|
|
} |
|
|
|
|
|
h=splitted.join('?'); |
|
|
|
|
|
var contribs=pg.re.contribs.exec(h); |
|
|
if (contribs !== null) { |
|
|
this.value = this.decodeNasties(pg.ns.user + ':' + contribs); |
|
|
return this; |
|
|
} |
|
|
|
|
|
var email=pg.re.email.exec(h); |
|
|
if (email !== null) { |
|
|
this.value=this.decodeNasties(pg.ns.user + ':' + email); |
|
|
return this; |
|
|
} |
|
|
|
|
|
// no more special cases to check -- |
|
|
// hopefully it's not a disguised user-related page |
|
|
var m=pg.re.main.exec(h); |
|
|
if(m===null) { this.value=null; } |
|
|
else { this.value=this.decodeNasties(m); } |
|
|
return this; |
|
|
}; |
|
|
Title.prototype.decodeNasties=function(txt) { |
|
|
return this.decodeEscapes(decodeURI(txt)); |
|
|
} |
|
|
Title.prototype.decodeEscapes=function(txt) { |
|
|
var split=txt.parenSplit(/({2})/); |
|
|
var len=split.length; |
|
|
for (var i=1; i<len; i=i+2) { |
|
|
split=unescape(split); |
|
|
} |
|
|
return split.join(''); |
|
|
}; |
|
|
Title.fromAnchor=function(a) { |
|
|
return new Title().fromAnchor(a); |
|
|
}; |
|
|
Title.prototype.fromAnchor=function(a) { |
|
|
if (!a) { this.value=null; return this; } |
|
|
return this.fromURL(a.href); |
|
|
}; |
|
|
Title.prototype.fromWikiText=function(txt) { |
|
|
// FIXME!!! |
|
|
if (!pg.flag.linksLikeIE) { txt=myDecodeURI(txt); } |
|
|
this.value=txt; |
|
|
return this; |
|
|
}; |
|
|
Title.prototype.hintValue=function(){ |
|
|
if(!this.value) { return ''; } |
|
|
return safeDecodeURI(this.value); |
|
|
}; |
|
|
Title.prototype.toUserName=function(withNs) { |
|
|
//limitAlert(Title.prototype.toUserName, 4, 'toUserName'+'\nwithNs='+withNs+'\nthis.value='+this.value); |
|
|
if (!this.value) { return null; } |
|
|
var i=this.value.indexOf(pg.ns.user); |
|
|
var j=this.value.indexOf(':'); |
|
|
if (i !== 0 || j == -1) { |
|
|
this.value=null; |
|
|
return null; |
|
|
} |
|
|
var k=this.value.indexOf('/'); |
|
|
if (k==-1) { this.value=this.value.substring(j+1); } |
|
|
else { this.value=this.value.substring(j+1,k); } |
|
|
if (withNs) { this.value = pg.ns.user + ':' + this.value; } |
|
|
//limitAlert(Title.prototype.toUserName, 4,'end of toUserName'+'\nwithNs='+withNs+'\nthis.value='+this.value); |
|
|
return this.value; |
|
|
}; |
|
|
Title.prototype.userName=function(withNs) { |
|
|
var t=(new Title(this.value)); |
|
|
t.toUserName(withNs); |
|
|
if (t.value) { return t; } |
|
|
return null; |
|
|
}; |
|
|
Title.prototype.toTalkPage=function() { |
|
|
// convert article to a talk page, or if we can't return null |
|
|
// or, in other words, return null if this ALREADY IS a talk page |
|
|
// and return the corresponding talk page otherwise |
|
|
if (this.value===null) { return null; } |
|
|
var talkRegex=namespaceListToRegex(pg.ns.talkList); |
|
|
if (talkRegex.exec(this.value)) { this.value=null; return null;} |
|
|
|
|
|
var nsRegex=namespaceListToRegex(pg.ns.withTalkList); |
|
|
var splitted=this.value.parenSplit(nsRegex); |
|
|
if (splitted.length<2) { |
|
|
this.value= (pg.ns.talkList+':'+this.value).split(' ').join('_'); |
|
|
return this.value; |
|
|
} |
|
|
for (var i=0; i< pg.ns.withTalkList.length; ++i) { |
|
|
if (splitted==pg.ns.withTalkList) { |
|
|
splitted=pg.ns.talkList; |
|
|
this.value=splitted.join(':').substring(1).split(' ').join('_'); |
|
|
return this.value; |
|
|
} |
|
|
} |
|
|
this.value=null; |
|
|
return null; |
|
|
}; |
|
|
Title.prototype.talkPage=function() { |
|
|
var t=new Title(this.value); |
|
|
t.toTalkPage(); |
|
|
if (t.value) { return t; } |
|
|
return null; |
|
|
}; |
|
|
Title.prototype.isTalkPage=function() { |
|
|
if (this.talkPage()===null) { return true; } |
|
|
return false; |
|
|
}; |
|
|
Title.prototype.toArticleFromTalkPage=function() { |
|
|
var talkRegex=namespaceListToRegex(pg.ns.talkList); |
|
|
var splitted=this.value.parenSplit(talkRegex); |
|
|
if (splitted.length < 2 || splitted.length > 0) { this.value=null; return null; } |
|
|
if (splitted==pg.ns.talkList) { |
|
|
splitted=''; |
|
|
this.value=splitted.join(':').substring(2).split(' ').join('_'); |
|
|
return this.value; |
|
|
} |
|
|
for (var i=1; i< pg.ns.talkList.length; ++i) { |
|
|
if (splitted==pg.ns.talkList |
|
|
|| splitted==pg.ns.talkList.split(' ').join('_')) { |
|
|
splitted=pg.ns.withTalkList; |
|
|
this.value= splitted.join(':').substring(1).split(' ').join('_'); |
|
|
return this.value; |
|
|
} |
|
|
} |
|
|
this.value=null; |
|
|
return this.value; |
|
|
}; |
|
|
Title.prototype.articleFromTalkPage=function() { |
|
|
var t=new Title(this.value); |
|
|
t.toArticleFromTalkPage(); |
|
|
if (t.value) { return t; } |
|
|
return null; |
|
|
}; |
|
|
Title.prototype.articleFromTalkOrArticle=function() { |
|
|
var t=new Title(this.value); |
|
|
if ( t.toArticleFromTalkPage() ) { return t; } |
|
|
return this; |
|
|
}; |
|
|
Title.prototype.stripNamespace=function(){ // returns a string, not a Title |
|
|
// this isn't very sophisticated |
|
|
// it just removes everything up to the final : |
|
|
// BUG: probably does silly things for images with colons in the name - check it out |
|
|
var list = this.value.split(':'); |
|
|
return list; |
|
|
}; |
|
|
Title.prototype.setUtf=function(value){ |
|
|
if (!value) { this.value=''; return; } |
|
|
var anch=value.indexOf('#'); |
|
|
if(anch < 0) { this.value=value; this.anchor=''; return; } |
|
|
this.value=value.substring(0,anch); |
|
|
this.anchor=value.substring(anch+1); |
|
|
this.ns=null; // wait until namespace() is called |
|
|
}; |
|
|
Title.prototype.namespace=function() { |
|
|
// FIXME |
|
|
}; |
|
|
Title.prototype.setUrl=function(urlfrag) { |
|
|
var anch=urlfrag.indexOf('#'); |
|
|
this.value=safeDecodeURI(urlfrag.substring(0,anch)); |
|
|
this.anchor=value.substring(anch+1); |
|
|
}; |
|
|
Title.prototype.append=function(x){ |
|
|
this.setUtf(this.value + x); |
|
|
}; |
|
|
Title.prototype.isIpUser=function() { |
|
|
return pg.re.ipUser.test(this.userName()); |
|
|
}; |
|
|
Title.prototype.urlString=function() { |
|
|
return encodeURI(this.value).split('&').join('%26').split('?').join('%3F').split('+').join('%2B'); |
|
|
} |
|
|
|
|
|
|
|
|
function paramValue(param, url) { |
|
|
var s=url.parenSplit(RegExp('' + literalizeRegex(param) + '=(*)')); |
|
|
if (!url) { return null; } |
|
|
return s || null; |
|
|
} |
|
|
|
|
|
// all sorts of stuff here |
|
|
// almost everything needs to be rewritten |
|
|
|
|
|
function oldidFromAnchor(a) { return paramValue('oldid', a.href); } |
|
|
function diffFromAnchor(a) { return paramValue('diff', a.href); } |
|
|
|
|
|
function stripNamespace(article) { |
|
|
// FIXME used in images.js.... extend Title class to Image class? |
|
|
|
|
|
// this isn't very sophisticated |
|
|
// it just removes everything up to the final : |
|
|
// BUG: probably does silly things for images with colons in the name - check it out |
|
|
var list = article.split(':'); |
|
|
return list; |
|
|
} |
|
|
|
|
|
// (a) myDecodeURI (first standard decodeURI, then pg.re.urlNoPopup) |
|
|
// (b) change spaces to underscores |
|
|
// (c) encodeURI (just the straight one, no pg.re.urlNoPopup) |
|
|
|
|
|
function wikiMarkupToAddressFragment (str) { // for images |
|
|
var ret = safeDecodeURI(str); |
|
|
ret = ret.split(' ').join('_'); |
|
|
ret = encodeURI(ret); |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function myDecodeURI (str) { |
|
|
var ret; |
|
|
try { ret=decodeURI(str.toString()); } |
|
|
catch (summat) { return str; } |
|
|
for (var i=0; i<pg.misc.decodeExtras.length; ++i) { |
|
|
var from=pg.misc.decodeExtras.from; |
|
|
var to=pg.misc.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<pg.misc.decodeExtras.length; ++i) { |
|
|
var from=pg.misc.decodeExtras.from; |
|
|
var to=pg.misc.decodeExtras.to; |
|
|
ret=ret.split(to).join(from); |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function Titleanchor() { |
|
|
// FIXME |
|
|
this.fromEncoded=function(str) { |
|
|
// FIXME probably incorrect! |
|
|
this.encoded=str; |
|
|
this.value=unesape(str.replace(RegExp('()', 'g'), '%$1')); |
|
|
}; |
|
|
this.getEncoded=function(str) { |
|
|
if (this.encoded) { |
|
|
return this.encoded; |
|
|
} |
|
|
// FIXME |
|
|
return 'Not yet implemented'; |
|
|
}; |
|
|
this.setUTF=function(str) { |
|
|
this.value=str; |
|
|
this.encoded=this.getEncoded(); |
|
|
}; |
|
|
} |
|
|
Titleanchor.prototype=new Stringwrapper(); |
|
|
|
|
|
function removeAnchor(article) { |
|
|
if (!article) { return null; } |
|
|
// is there a #? if not, we're done |
|
|
var i=article.indexOf('#'); |
|
|
if (i == -1) { return article; } |
|
|
return article.substring(0,i); |
|
|
} |
|
|
function getAnchor(article) { |
|
|
if(!article) { return null; } |
|
|
var i=article.indexOf('#'); |
|
|
if (i == -1) { return ''; } |
|
|
return article.substring(i+1); |
|
|
} |
|
|
function decodeAnchor(article) { |
|
|
var anch=getAnchor(article); |
|
|
if(!anch) { return ''; } |
|
|
anch=anch.replace(RegExp('()', 'g'), '%$1'); |
|
|
return unescape(anch); |
|
|
} |
|
|
|
|
|
/////////// |
|
|
// TESTS // |
|
|
/////////// |
|
|
|
|
|
function isIpUser(user) {return pg.re.ipUser.test(user);} |
|
|
|
|
|
function isStub(data) { return pg.re.stub.test(data); } |
|
|
function isDisambig(data) { |
|
|
return ! pg.current.article.isTalkPage() && pg.re.disambig.test(data); |
|
|
} |
|
|
|
|
|
function isValidImageName(str){ // extend as needed... |
|
|
return ( str.indexOf('{') == -1 ); |
|
|
} |
|
|
|
|
|
function isInNamespaceOrTalk(article, namespace) { |
|
|
if (!article) { return false; } |
|
|
if (isInNamespace(article, namespace)) { return true; } |
|
|
// FIXME: internationalize this |
|
|
if (article.indexOf(namespace+'_talk:') === 0) { return true; } |
|
|
return false; |
|
|
}; |
|
|
|
|
|
function isInNamespace(article, namespace) { |
|
|
if (!article) { return false; } |
|
|
var list=namespace; |
|
|
if (typeof list == typeof '') list=; |
|
|
for (var i=0; i<list.length; ++i) { |
|
|
if (article.indexOf(list+':')===0) { return true; } |
|
|
// should also respect namespaces encoded with wikiMarkupToAddressFragment |
|
|
if (article.indexOf(wikiMarkupToAddressFragment(list+':'))===0) { return true; } |
|
|
} |
|
|
return false; |
|
|
}; |
|
|
|
|
|
function isInStrippableNamespace(article) { |
|
|
return isInNamespace(article, pg.ns.nonArticleList); |
|
|
}; |
|
|
function isInMainNamespace(article) { |
|
|
return !isInStrippableNamespace(article); |
|
|
} |
|
|
|
|
|
function isImage(thing) {return isInNamespaceOrTalk(thing, pg.ns.image);}; |
|
|
function isImagePage(thing) {return isInNamespace (thing, pg.ns.image);}; |
|
|
|
|
|
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; |
|
|
}; |
|
|
|
|
|
function isPopupLink(a) { |
|
|
// NB for performance reasons, TOC links generally return true |
|
|
// they should be stripped out later |
|
|
|
|
|
// FIXME is this faster inline? |
|
|
if (a.onclick) { return false; } |
|
|
var h=a.href; |
|
|
return ( |
|
|
( ( |
|
|
( h.indexOf(pg.wiki.titlebase) === 0 || h.indexOf(pg.wiki.wikibase) === 0 ) |
|
|
&& !pg.re.urlNoPopup.test(h)) |
|
|
|| |
|
|
(pg.re.contribs.test(h) && h.indexOf('&limit=') == -1 ) |
|
|
) |
|
|
); |
|
|
} |
|
|
|
|
|
// ENDFILE: titles.js |
|
|
// STARTFILE: cookies.js |
|
|
////////////////////////////////////////////////// |
|
|
// Cookie handling |
|
|
// from http://www.quirksmode.org/js/cookies.html |
|
|
|
|
|
var Cookie= { |
|
|
create: function(name,value,days) |
|
|
{ |
|
|
var expires; |
|
|
if (days) |
|
|
{ |
|
|
var date = new Date(); |
|
|
date.setTime(date.getTime()+(days*24*60*60*1000)); |
|
|
expires = "; expires="+date.toGMTString(); |
|
|
} |
|
|
else { expires = ""; } |
|
|
document.cookie = name+"="+value+expires+"; path=/"; |
|
|
}, |
|
|
|
|
|
read: function(name) |
|
|
{ |
|
|
var nameEQ = name + "="; |
|
|
var ca = document.cookie.split(';'); |
|
|
for(var i=0;i < ca.length;i++) |
|
|
{ |
|
|
var c = ca; |
|
|
while (c.charAt(0)==' ') { c = c.substring(1,c.length); } |
|
|
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length,c.length); } |
|
|
} |
|
|
return null; |
|
|
}, |
|
|
|
|
|
erase: function(name) |
|
|
{ |
|
|
Cookie.create(name,"",-1); |
|
|
} |
|
|
}; |
|
|
|
|
|
// ENDFILE: cookies.js |
|
|
// STARTFILE: getpage.js |
|
|
////////////////////////////////////////////////// |
|
|
// Wiki-specific downloading |
|
|
// |
|
|
|
|
|
// Schematic for a getWiki call |
|
|
// |
|
|
// getWiki->-getPageWithCaching |
|
|
// | |
|
|
// false | true |
|
|
// getPage<-->-onComplete(a fake download) |
|
|
// \. |
|
|
// (async)->addPageToCache(download)->-onComplete(download) |
|
|
|
|
|
function getWiki(article, onComplete, oldid, owner) { |
|
|
// NB wikipage is a Title object |
|
|
log('getWiki, article='+article); |
|
|
var wikipage=article.urlString(); |
|
|
// set ctype=text/css to get around opera bug |
|
|
var url = pg.wiki.titlebase + removeAnchor(wikipage) + '&action=raw&ctype=text/css'; |
|
|
if (oldid || oldid===0 || oldid==='0') { url += '&oldid='+oldid; } |
|
|
else { url += '&maxage=0&smaxage=0'; } |
|
|
|
|
|
getPageWithCaching(url, onComplete, owner); |
|
|
}; |
|
|
|
|
|
// check cache to see if page exists |
|
|
|
|
|
function getPageWithCaching(url, onComplete, owner) { |
|
|
log('getPageWithCaching, url='+url); |
|
|
var i=findInPageCache(url); |
|
|
if (i > -1) return fakeDownload(url, pg.idNumber, onComplete, pg.cache.pages.data, pg.cache.pages.lastModified); |
|
|
var d=getPage(url, onComplete, owner); |
|
|
if (d && owner && owner.addDownload) { |
|
|
owner.addDownload(d); |
|
|
d.owner=owner; |
|
|
} |
|
|
}; |
|
|
|
|
|
function getPage(url, onComplete, owner) { |
|
|
log('getPage'); |
|
|
var callback= function (d) { if (!d.aborted) {addPageToCache(d); onComplete(d)} }; |
|
|
return startDownload(url, pg.idNumber, callback); |
|
|
}; |
|
|
|
|
|
function findInPageCache(url) { |
|
|
for (var i=0; i<pg.cache.pages.length; ++i) if (url==pg.cache.pages.url) return i; |
|
|
return -1; |
|
|
}; |
|
|
|
|
|
function addPageToCache(download) { |
|
|
log('addPageToCache '+download.url); |
|
|
var page = {url: download.url, data: download.data, lastModified: download.lastModified}; |
|
|
return pg.cache.pages.push(page); |
|
|
}; |
|
|
|
|
|
// ENDFILE: getpage.js |
|
|
// STARTFILE: md5-2.2alpha.js |
|
|
/* |
|
|
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message |
|
|
* Digest Algorithm, as defined in RFC 1321. |
|
|
* Version 2.2-alpha Copyright (C) Paul Johnston 1999 - 2005 |
|
|
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet |
|
|
* Distributed under the BSD License |
|
|
* See http://pajhome.org.uk/crypt/md5 for more info. |
|
|
*/ |
|
|
|
|
|
/* |
|
|
* Configurable variables. You may need to tweak these to be compatible with |
|
|
* the server-side, but the defaults work in most cases. |
|
|
*/ |
|
|
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ |
|
|
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ |
|
|
|
|
|
/* |
|
|
* These are the functions you'll usually want to call |
|
|
* They take string arguments and return either hex or base-64 encoded strings |
|
|
*/ |
|
|
function hex_md5(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); } |
|
|
function b64_md5(s) { return rstr2b64(rstr_md5(str2rstr_utf8(s))); } |
|
|
function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); } |
|
|
function hex_hmac_md5(k, d) |
|
|
{ return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); } |
|
|
function b64_hmac_md5(k, d) |
|
|
{ return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); } |
|
|
function any_hmac_md5(k, d, e) |
|
|
{ return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); } |
|
|
|
|
|
/* |
|
|
* Perform a simple self-test to see if the VM is working |
|
|
*/ |
|
|
function md5_vm_test() |
|
|
{ |
|
|
return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72"; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Calculate the MD5 of a raw string |
|
|
*/ |
|
|
function rstr_md5(s) |
|
|
{ |
|
|
return binl2rstr(binl_md5(rstr2binl(s), s.length * 8)); |
|
|
} |
|
|
|
|
|
/* |
|
|
* Calculate the HMAC-MD5, of a key and some data (raw strings) |
|
|
*/ |
|
|
function rstr_hmac_md5(key, data) |
|
|
{ |
|
|
var bkey = rstr2binl(key); |
|
|
if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8); |
|
|
|
|
|
var ipad = Array(16), opad = Array(16); |
|
|
for(var i = 0; i < 16; i++) |
|
|
{ |
|
|
ipad = bkey ^ 0x36363636; |
|
|
opad = bkey ^ 0x5C5C5C5C; |
|
|
} |
|
|
|
|
|
var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); |
|
|
return binl2rstr(binl_md5(opad.concat(hash), 512 + 128)); |
|
|
} |
|
|
|
|
|
/* |
|
|
* Convert a raw string to a hex string |
|
|
*/ |
|
|
function rstr2hex(input) |
|
|
{ |
|
|
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; |
|
|
var output = ""; |
|
|
var x; |
|
|
for(var i = 0; i < input.length; i++) |
|
|
{ |
|
|
x = input.charCodeAt(i); |
|
|
output += hex_tab.charAt((x >>> 4) & 0x0F) |
|
|
+ hex_tab.charAt( x & 0x0F); |
|
|
} |
|
|
return output; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Convert a raw string to a base-64 string |
|
|
*/ |
|
|
function rstr2b64(input) |
|
|
{ |
|
|
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
|
|
var output = ""; |
|
|
var len = input.length; |
|
|
for(var i = 0; i < len; i += 3) |
|
|
{ |
|
|
var triplet = (input.charCodeAt(i) << 16) |
|
|
| (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0) |
|
|
| (i + 2 < len ? input.charCodeAt(i+2) : 0); |
|
|
for(var j = 0; j < 4; j++) |
|
|
{ |
|
|
if(i * 8 + j * 6 > input.length * 8) output += b64pad; |
|
|
else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F); |
|
|
} |
|
|
} |
|
|
return output; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Convert a raw string to an arbitrary string encoding |
|
|
*/ |
|
|
function rstr2any(input, encoding) |
|
|
{ |
|
|
var divisor = encoding.length; |
|
|
var remainders = Array(); |
|
|
var i, q, x, quotient; |
|
|
|
|
|
/* Convert to an array of 16-bit big-endian values, forming the dividend */ |
|
|
var dividend = Array(input.length / 2); |
|
|
for(i = 0; i < dividend.length; i++) |
|
|
{ |
|
|
dividend = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); |
|
|
} |
|
|
|
|
|
/* |
|
|
* Repeatedly perform a long division. The binary array forms the dividend, |
|
|
* the length of the encoding is the divisor. Once computed, the quotient |
|
|
* forms the dividend for the next step. We stop when the dividend is zero. |
|
|
* All remainders are stored for later use. |
|
|
*/ |
|
|
while(dividend.length > 0) |
|
|
{ |
|
|
quotient = Array(); |
|
|
x = 0; |
|
|
for(i = 0; i < dividend.length; i++) |
|
|
{ |
|
|
x = (x << 16) + dividend; |
|
|
q = Math.floor(x / divisor); |
|
|
x -= q * divisor; |
|
|
if(quotient.length > 0 || q > 0) |
|
|
quotient = q; |
|
|
} |
|
|
remainders = x; |
|
|
dividend = quotient; |
|
|
} |
|
|
|
|
|
/* Convert the remainders to the output string */ |
|
|
var output = ""; |
|
|
for(i = remainders.length - 1; i >= 0; i--) |
|
|
output += encoding.charAt(remainders); |
|
|
|
|
|
return output; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Encode a string as utf-8. |
|
|
* For efficiency, this assumes the input is valid utf-16. |
|
|
*/ |
|
|
function str2rstr_utf8(input) |
|
|
{ |
|
|
var output = ""; |
|
|
var i = -1; |
|
|
var x, y; |
|
|
|
|
|
while(++i < input.length) |
|
|
{ |
|
|
/* Decode utf-16 surrogate pairs */ |
|
|
x = input.charCodeAt(i); |
|
|
y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0; |
|
|
if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) |
|
|
{ |
|
|
x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF); |
|
|
i++; |
|
|
} |
|
|
|
|
|
/* Encode output as utf-8 */ |
|
|
if(x <= 0x7F) |
|
|
output += String.fromCharCode(x); |
|
|
else if(x <= 0x7FF) |
|
|
output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F), |
|
|
0x80 | ( x & 0x3F)); |
|
|
else if(x <= 0xFFFF) |
|
|
output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), |
|
|
0x80 | ((x >>> 6 ) & 0x3F), |
|
|
0x80 | ( x & 0x3F)); |
|
|
else if(x <= 0x1FFFFF) |
|
|
output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), |
|
|
0x80 | ((x >>> 12) & 0x3F), |
|
|
0x80 | ((x >>> 6 ) & 0x3F), |
|
|
0x80 | ( x & 0x3F)); |
|
|
} |
|
|
return output; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Encode a string as utf-16 |
|
|
*/ |
|
|
function str2rstr_utf16le(input) |
|
|
{ |
|
|
var output = ""; |
|
|
for(var i = 0; i < input.length; i++) |
|
|
output += String.fromCharCode( input.charCodeAt(i) & 0xFF, |
|
|
(input.charCodeAt(i) >>> 8) & 0xFF); |
|
|
return output; |
|
|
} |
|
|
|
|
|
function str2rstr_utf16be(input) |
|
|
{ |
|
|
var output = ""; |
|
|
for(var i = 0; i < input.length; i++) |
|
|
output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, |
|
|
input.charCodeAt(i) & 0xFF); |
|
|
return output; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Convert a raw string to an array of little-endian words |
|
|
* Characters >255 have their high-byte silently ignored. |
|
|
*/ |
|
|
function rstr2binl(input) |
|
|
{ |
|
|
var output = Array(input.length >> 2); |
|
|
for(var i = 0; i < output.length; i++) |
|
|
output = 0; |
|
|
for(var i = 0; i < input.length * 8; i += 8) |
|
|
output |= (input.charCodeAt(i / 8) & 0xFF) << (i%32); |
|
|
return output; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Convert an array of little-endian words to a string |
|
|
*/ |
|
|
function binl2rstr(input) |
|
|
{ |
|
|
var output = ""; |
|
|
for(var i = 0; i < input.length * 32; i += 8) |
|
|
output += String.fromCharCode((input >>> (i % 32)) & 0xFF); |
|
|
return output; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Calculate the MD5 of an array of little-endian words, and a bit length. |
|
|
*/ |
|
|
function binl_md5(x, len) |
|
|
{ |
|
|
/* append padding */ |
|
|
x |= 0x80 << ((len) % 32); |
|
|
x = len; |
|
|
|
|
|
var a = 1732584193; |
|
|
var b = -271733879; |
|
|
var c = -1732584194; |
|
|
var d = 271733878; |
|
|
|
|
|
for(var i = 0; i < x.length; i += 16) |
|
|
{ |
|
|
var olda = a; |
|
|
var oldb = b; |
|
|
var oldc = c; |
|
|
var oldd = d; |
|
|
|
|
|
a = md5_ff(a, b, c, d, x, 7 , -680876936); |
|
|
d = md5_ff(d, a, b, c, x, 12, -389564586); |
|
|
c = md5_ff(c, d, a, b, x, 17, 606105819); |
|
|
b = md5_ff(b, c, d, a, x, 22, -1044525330); |
|
|
a = md5_ff(a, b, c, d, x, 7 , -176418897); |
|
|
d = md5_ff(d, a, b, c, x, 12, 1200080426); |
|
|
c = md5_ff(c, d, a, b, x, 17, -1473231341); |
|
|
b = md5_ff(b, c, d, a, x, 22, -45705983); |
|
|
a = md5_ff(a, b, c, d, x, 7 , 1770035416); |
|
|
d = md5_ff(d, a, b, c, x, 12, -1958414417); |
|
|
c = md5_ff(c, d, a, b, x, 17, -42063); |
|
|
b = md5_ff(b, c, d, a, x, 22, -1990404162); |
|
|
a = md5_ff(a, b, c, d, x, 7 , 1804603682); |
|
|
d = md5_ff(d, a, b, c, x, 12, -40341101); |
|
|
c = md5_ff(c, d, a, b, x, 17, -1502002290); |
|
|
b = md5_ff(b, c, d, a, x, 22, 1236535329); |
|
|
|
|
|
a = md5_gg(a, b, c, d, x, 5 , -165796510); |
|
|
d = md5_gg(d, a, b, c, x, 9 , -1069501632); |
|
|
c = md5_gg(c, d, a, b, x, 14, 643717713); |
|
|
b = md5_gg(b, c, d, a, x, 20, -373897302); |
|
|
a = md5_gg(a, b, c, d, x, 5 , -701558691); |
|
|
d = md5_gg(d, a, b, c, x, 9 , 38016083); |
|
|
c = md5_gg(c, d, a, b, x, 14, -660478335); |
|
|
b = md5_gg(b, c, d, a, x, 20, -405537848); |
|
|
a = md5_gg(a, b, c, d, x, 5 , 568446438); |
|
|
d = md5_gg(d, a, b, c, x, 9 , -1019803690); |
|
|
c = md5_gg(c, d, a, b, x, 14, -187363961); |
|
|
b = md5_gg(b, c, d, a, x, 20, 1163531501); |
|
|
a = md5_gg(a, b, c, d, x, 5 , -1444681467); |
|
|
d = md5_gg(d, a, b, c, x, 9 , -51403784); |
|
|
c = md5_gg(c, d, a, b, x, 14, 1735328473); |
|
|
b = md5_gg(b, c, d, a, x, 20, -1926607734); |
|
|
|
|
|
a = md5_hh(a, b, c, d, x, 4 , -378558); |
|
|
d = md5_hh(d, a, b, c, x, 11, -2022574463); |
|
|
c = md5_hh(c, d, a, b, x, 16, 1839030562); |
|
|
b = md5_hh(b, c, d, a, x, 23, -35309556); |
|
|
a = md5_hh(a, b, c, d, x, 4 , -1530992060); |
|
|
d = md5_hh(d, a, b, c, x, 11, 1272893353); |
|
|
c = md5_hh(c, d, a, b, x, 16, -155497632); |
|
|
b = md5_hh(b, c, d, a, x, 23, -1094730640); |
|
|
a = md5_hh(a, b, c, d, x, 4 , 681279174); |
|
|
d = md5_hh(d, a, b, c, x, 11, -358537222); |
|
|
c = md5_hh(c, d, a, b, x, 16, -722521979); |
|
|
b = md5_hh(b, c, d, a, x, 23, 76029189); |
|
|
a = md5_hh(a, b, c, d, x, 4 , -640364487); |
|
|
d = md5_hh(d, a, b, c, x, 11, -421815835); |
|
|
c = md5_hh(c, d, a, b, x, 16, 530742520); |
|
|
b = md5_hh(b, c, d, a, x, 23, -995338651); |
|
|
|
|
|
a = md5_ii(a, b, c, d, x, 6 , -198630844); |
|
|
d = md5_ii(d, a, b, c, x, 10, 1126891415); |
|
|
c = md5_ii(c, d, a, b, x, 15, -1416354905); |
|
|
b = md5_ii(b, c, d, a, x, 21, -57434055); |
|
|
a = md5_ii(a, b, c, d, x, 6 , 1700485571); |
|
|
d = md5_ii(d, a, b, c, x, 10, -1894986606); |
|
|
c = md5_ii(c, d, a, b, x, 15, -1051523); |
|
|
b = md5_ii(b, c, d, a, x, 21, -2054922799); |
|
|
a = md5_ii(a, b, c, d, x, 6 , 1873313359); |
|
|
d = md5_ii(d, a, b, c, x, 10, -30611744); |
|
|
c = md5_ii(c, d, a, b, x, 15, -1560198380); |
|
|
b = md5_ii(b, c, d, a, x, 21, 1309151649); |
|
|
a = md5_ii(a, b, c, d, x, 6 , -145523070); |
|
|
d = md5_ii(d, a, b, c, x, 10, -1120210379); |
|
|
c = md5_ii(c, d, a, b, x, 15, 718787259); |
|
|
b = md5_ii(b, c, d, a, x, 21, -343485551); |
|
|
|
|
|
a = safe_add(a, olda); |
|
|
b = safe_add(b, oldb); |
|
|
c = safe_add(c, oldc); |
|
|
d = safe_add(d, oldd); |
|
|
} |
|
|
return Array(a, b, c, d); |
|
|
} |
|
|
|
|
|
/* |
|
|
* These functions implement the four basic operations the algorithm uses. |
|
|
*/ |
|
|
function md5_cmn(q, a, b, x, s, t) |
|
|
{ |
|
|
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); |
|
|
} |
|
|
function md5_ff(a, b, c, d, x, s, t) |
|
|
{ |
|
|
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); |
|
|
} |
|
|
function md5_gg(a, b, c, d, x, s, t) |
|
|
{ |
|
|
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); |
|
|
} |
|
|
function md5_hh(a, b, c, d, x, s, t) |
|
|
{ |
|
|
return md5_cmn(b ^ c ^ d, a, b, x, s, t); |
|
|
} |
|
|
function md5_ii(a, b, c, d, x, s, t) |
|
|
{ |
|
|
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); |
|
|
} |
|
|
|
|
|
/* |
|
|
* Add integers, wrapping at 2^32. This uses 16-bit operations internally |
|
|
* to work around bugs in some JS interpreters. |
|
|
*/ |
|
|
function safe_add(x, y) |
|
|
{ |
|
|
var lsw = (x & 0xFFFF) + (y & 0xFFFF); |
|
|
var msw = (x >> 16) + (y >> 16) + (lsw >> 16); |
|
|
return (msw << 16) | (lsw & 0xFFFF); |
|
|
} |
|
|
|
|
|
/* |
|
|
* Bitwise rotate a 32-bit number to the left. |
|
|
*/ |
|
|
function bit_rol(num, cnt) |
|
|
{ |
|
|
return (num << cnt) | (num >>> (32 - cnt)); |
|
|
} |
|
|
// ENDFILE: md5-2.2alpha.js |
|
|
// STARTFILE: parensplit.js |
|
|
////////////////////////////////////////////////// |
|
|
// parenSplit |
|
|
|
|
|
// String.prototype.parenSplit should do what ECMAscript says |
|
|
// String.prototype.split does, interspersing paren matches between |
|
|
// the split elements |
|
|
|
|
|
if (String('abc'.split(/(b)/))!='a,b,c') { |
|
|
// broken String.split, e.g. konq, IE |
|
|
String.prototype.parenSplit=function (re) { |
|
|
re=nonGlobalRegex(re); |
|
|
var s=this; |
|
|
var m=re.exec(s); |
|
|
var ret=; |
|
|
while (m && s) { |
|
|
// without the following loop, we have |
|
|
// 'ab'.parenSplit(/a|(b)/) != 'ab'.split(/a|(b)/) |
|
|
for(var i=0; i<m.length; ++i) { |
|
|
if (typeof m=='undefined') m=''; |
|
|
} |
|
|
ret.push(s.substring(0,m.index)); |
|
|
ret = ret.concat(m.slice(1)); |
|
|
s=s.substring(m.index + m.length); |
|
|
m=re.exec(s); |
|
|
} |
|
|
ret.push(s); |
|
|
return ret; |
|
|
}; |
|
|
} else { |
|
|
String.prototype.parenSplit=function (re) {return this.split(re);}; |
|
|
} |
|
|
function nonGlobalRegex(re) { |
|
|
var s=re.toString(); |
|
|
flags=''; |
|
|
for (var j=s.length; s.charAt(j) != '/'; --j) { |
|
|
if (s.charAt(j) != 'g') flags += s.charAt(j); |
|
|
} |
|
|
var t=s.substring(1,j); |
|
|
return RegExp(t,flags); |
|
|
} |
|
|
|
|
|
// ENDFILE: parensplit.js |
|
|
// STARTFILE: tools.js |
|
|
// IE madness with encoding |
|
|
// ======================== |
|
|
// |
|
|
// suppose throughout that the page is in utf8, like wikipedia |
|
|
// |
|
|
// if a is an anchor DOM element and a.href should consist of |
|
|
// |
|
|
// http://host.name.here/foo?bar=baz |
|
|
// |
|
|
// then IE gives foo as "latin1-encoded" utf8; we have foo = decode_utf8(decodeURI(foo_ie)) |
|
|
// but IE gives bar=baz correctly as plain utf8 |
|
|
// |
|
|
// --------------------------------- |
|
|
// |
|
|
// IE's xmlhttp doesn't understand utf8 urls. Have to use encodeURI here. |
|
|
// |
|
|
// --------------------------------- |
|
|
// |
|
|
// summat else |
|
|
|
|
|
// Source: http://aktuell.de.selfhtml.org/artikel/javascript/utf8b64/utf8.htm |
|
|
function encode_utf8(rohtext) { |
|
|
// dient der Normalisierung des Zeilenumbruchs |
|
|
rohtext = rohtext.replace(/\r\n/g,"\n"); |
|
|
var utftext = ""; |
|
|
for(var n=0; n<rohtext.length; n++) |
|
|
{ |
|
|
// ermitteln des Unicodes des aktuellen Zeichens |
|
|
var c=rohtext.charCodeAt(n); |
|
|
// alle Zeichen von 0-127 => 1byte |
|
|
if (c<128) |
|
|
utftext += String.fromCharCode(c); |
|
|
// alle Zeichen von 127 bis 2047 => 2byte |
|
|
else if((c>127) && (c<2048)) { |
|
|
utftext += String.fromCharCode((c>>6)|192); |
|
|
utftext += String.fromCharCode((c&63)|128);} |
|
|
// alle Zeichen von 2048 bis 66536 => 3byte |
|
|
else { |
|
|
utftext += String.fromCharCode((c>>12)|224); |
|
|
utftext += String.fromCharCode(((c>>6)&63)|128); |
|
|
utftext += String.fromCharCode((c&63)|128);} |
|
|
} |
|
|
return utftext; |
|
|
} |
|
|
|
|
|
function decode_utf8(utftext) { |
|
|
var plaintext = ""; var i=0; var c=c1=c2=0; |
|
|
// while-Schleife, weil einige Zeichen uebersprungen werden |
|
|
while(i<utftext.length) |
|
|
{ |
|
|
c = utftext.charCodeAt(i); |
|
|
if (c<128) { |
|
|
plaintext += String.fromCharCode(c); |
|
|
i++;} |
|
|
else if((c>191) && (c<224)) { |
|
|
c2 = utftext.charCodeAt(i+1); |
|
|
plaintext += String.fromCharCode(((c&31)<<6) | (c2&63)); |
|
|
i+=2;} |
|
|
else { |
|
|
c2 = utftext.charCodeAt(i+1); c3 = utftext.charCodeAt(i+2); |
|
|
plaintext += String.fromCharCode(((c&15)<<12) | ((c2&63)<<6) | (c3&63)); |
|
|
i+=3;} |
|
|
} |
|
|
return plaintext; |
|
|
} |
|
|
|
|
|
|
|
|
function upcaseFirst(str) { |
|
|
if (typeof str != typeof '' || str=='') return ''; |
|
|
return str.charAt(0).toUpperCase() + str.substring(1); |
|
|
}; |
|
|
|
|
|
pop.runOnce=function(f, time) { |
|
|
var i=pop.runOnce.timers.length; |
|
|
var ff = function () { clearInterval(pop.runOnce.timers); f() }; |
|
|
var timer=setInterval(ff, time); |
|
|
pop.runOnce.timers.push(timer); |
|
|
} |
|
|
pop.runOnce.timers=; |
|
|
|
|
|
function literalizeRegex(str){ |
|
|
return str.replace(RegExp('(])', 'g'), '\\$1'); |
|
|
} |
|
|
|
|
|
// ENDFILE: tools.js |
|
|
// STARTFILE: dab.js |
|
|
////////////////////////////////////////////////// |
|
|
// Dab-fixing code |
|
|
// |
|
|
|
|
|
function listLinks(wikitext, oldTarget) { |
|
|
var reg=RegExp('\\*?)(\\||\\]\\])', 'gi'); |
|
|
var ret=; |
|
|
var splitted=wikitext.parenSplit(reg); |
|
|
// ^+ should match interwiki links, hopefully (case-insensitive) |
|
|
// and ^* should match those and ] style links too |
|
|
var omitRegex=RegExp('^*:|^pecial:|^mage|^ategory'); |
|
|
var friendlyCurrentArticleName=pg.current.article.split('_').join(' '); |
|
|
|
|
|
for (var i=1; i<splitted.length; i=i+3) { |
|
|
if (typeof splitted == typeof 'string' && splitted.length>0 && !omitRegex.test(splitted)) { |
|
|
ret.push(changeLinkTargetLink({newTarget: splitted, |
|
|
text: splitted.split(' ').join(' '), |
|
|
hint: tprintf('disambigHint', ]), |
|
|
summary: simplePrintf(getValueOf('popupFixDabsSummary'), ]), |
|
|
clickButton: 'wpDiff', minor: true, oldTarget: oldTarget, watch: getValueOf('popupWatchDisambiggedPages') |
|
|
})); |
|
|
} /* if */ |
|
|
} /* for loop */ |
|
|
|
|
|
ret = rmDupesFromSortedList(ret.sort()); |
|
|
|
|
|
ret.push(changeLinkTargetLink({ newTarget: null, |
|
|
text: popupString('remove this link').split(' ').join(' '), |
|
|
hint: popupString("remove all links to this disambig page from this article"), |
|
|
clickButton: "wpDiff", oldTarget: oldTarget, |
|
|
summary: simplePrintf(getValueOf('popupRmDabLinkSummary'), ), |
|
|
watch: getValueOf('popupWatchDisambiggedPages')})); |
|
|
return ret; |
|
|
|
|
|
} |
|
|
|
|
|
function rmDupesFromSortedList(list) { |
|
|
var ret=; |
|
|
for (var i=0; i<list.length; ++i) { |
|
|
if (ret.length===0 || list!=ret) { ret.push(list); } |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function makeFixDab(data, oldTarget) { |
|
|
var list=listLinks(data, oldTarget); |
|
|
if (list.length===0) { return null; } |
|
|
var html='<hr>' + popupString('Click to disambiguate this link to:') + '<br>'; |
|
|
html+=list; |
|
|
for (var i=1; i<list.length; ++i) { html += ', '+list; } |
|
|
return html; |
|
|
} |
|
|
|
|
|
|
|
|
function makeFixDabs(wikiText, oldTarget) |
|
|
{ |
|
|
if (getValueOf('popupFixDabs') && isDisambig(wikiText) && |
|
|
location.href.indexOf(pg.ns.special+':') == -1 && |
|
|
pg.current.article.talkPage() ) { |
|
|
setPopupHTML(makeFixDab(wikiText, oldTarget), 'popupFixDab', pg.idNumber); |
|
|
} |
|
|
} |
|
|
|
|
|
// ENDFILE: dab.js |
|
|
// STARTFILE: htmloutput.js |
|
|
// this has to use a timer loop as we don't know if the DOM element exists when we want to set the text |
|
|
function setPopupHTML (str, elementId, popupId, onSuccess) { |
|
|
//log('setPopupHTML, str='+str.substring(0,100)+'..., \n elementId='+elementId+', popupId='+popupId); |
|
|
if (typeof popupId === 'undefined') popupId = pg.idNumber; |
|
|
var popupElement=document.getElementById(elementId+popupId); |
|
|
var timer; |
|
|
|
|
|
if (typeof pg.timer.popupHTML == 'undefined') timer=null; |
|
|
else timer=pg.timer.popupHTML; |
|
|
|
|
|
if (popupElement != null) { |
|
|
if(timer) clearInterval(timer); |
|
|
pg.timer.popupHTML=null; |
|
|
popupElement.innerHTML=str; |
|
|
if (onSuccess) onSuccess(); |
|
|
return true; |
|
|
} else { |
|
|
// call this function again in a little while... |
|
|
var loopFunction=function() { setPopupHTML(str,elementId,popupId,onSuccess);} |
|
|
pg.misc.popupHTMLLoopFunctions = loopFunction; |
|
|
if (!timer) { |
|
|
var doThis = 'pg.misc.popupHTMLLoopFunctions()'; |
|
|
pg.timer.popupHTML = setInterval(doThis, 600); |
|
|
} |
|
|
} |
|
|
return null; |
|
|
} |
|
|
|
|
|
function setImageStatus(str, id) {return; } // setPopupHTML(str, 'popupImageStatus', id);}; |
|
|
function setPopupTrailer(str,id) {return setPopupHTML(str, 'popupData', id);}; |
|
|
|
|
|
|
|
|
function fillEmptySpans(args) { return fillEmptySpans2(args); } |
|
|
|
|
|
function fillEmptySpans2(args) { // if redir is present and true then redirTarget is mandatory |
|
|
var redir=true; |
|
|
if (typeof args != 'object' || typeof args.redir == 'undefined' || !args.redir) redir=false; |
|
|
var a=pg.current.link; |
|
|
//log('fillEmptySpans: a='+a+', pg.current.link='+pg.current.link); |
|
|
if (!a) { log('*****\nfillEmptySpans: a is no good\n*****'); return; } |
|
|
|
|
|
var article, hint, oldid; |
|
|
if (redir && typeof args.redirTarget == typeof {}) { |
|
|
article=args.redirTarget; hint=null; |
|
|
} else { |
|
|
article=(new Title()).fromAnchor(a); |
|
|
hint=a.originalTitle || article.hintValue(); |
|
|
oldid=(getValueOf('popupHistoricalLinks')) ? oldidFromAnchor(a) : null; |
|
|
} |
|
|
//limitAlert(fillEmptySpans2, 4, 'fillEmptySpans2\n' + article+'\n'+(typeof article)); |
|
|
var x={ article:article, |
|
|
hint: hint, oldid: oldid }; |
|
|
|
|
|
//log('fillEmptySpans: redir='+redir+', article='+article); |
|
|
|
|
|
var structure=pg.structures; |
|
|
if (typeof structure != 'object') { |
|
|
setPopupHTML('popupError', 'Unknown structure (this should never happen): '+ popupStructure); |
|
|
return; |
|
|
} |
|
|
var spans=flatten(pg.misc.layout); |
|
|
var numspans = spans.length; |
|
|
var redirs=pg.misc.redirSpans; |
|
|
//log('fillEmptySpans: spans.length='+spans.length); |
|
|
|
|
|
for (var i=0; i<numspans; ++i) { |
|
|
var f=findThis(redirs, spans); |
|
|
//log('redir='+redir+', f='+f+', spans='+spans); |
|
|
if ( (f!=null && !redir) || (f==null && redir) ) { |
|
|
//log('skipping this set of the loop'); |
|
|
continue; |
|
|
} |
|
|
var structurefn=structure]; |
|
|
switch (typeof structurefn) { |
|
|
case 'function': |
|
|
//log('running '+spans+'({article:'+x.article+', hint:'+x.hint+', oldid: '+x.oldid+'})'); |
|
|
setPopupHTML(structurefn(x), spans); |
|
|
break; |
|
|
case 'string': |
|
|
setPopupHTML(structurefn, spans); |
|
|
break; |
|
|
default: |
|
|
errlog('unknown thing with label '+spans); |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
// flatten an array |
|
|
function flatten(list, start) { |
|
|
var ret=; |
|
|
if (typeof start == 'undefined') start=0; |
|
|
for (var i=start; i<list.length; ++i) { |
|
|
if (typeof list == typeof ) { |
|
|
return ret.concat(flatten(list)).concat(flatten(list, i+1)); |
|
|
} |
|
|
else ret.push(list); |
|
|
} |
|
|
return ret; |
|
|
}; |
|
|
|
|
|
// Generate html for whole popup |
|
|
function popupHTML (a) { |
|
|
getValueOf('popupStructure'); |
|
|
var structure=pg.structures; |
|
|
if (typeof structure != 'object') return 'Unknown structure: '+popupStructure + |
|
|
( RegExp('^').test(popupStructure) ? '<p>There has been a breaking change - please do not<br>' + |
|
|
' quote strings in options like popupStructure twice any longer<br> in your user javascript file.' : ''); |
|
|
if (typeof structure.popupLayout != 'function') return 'Bad layout'; |
|
|
pg.misc.layout=structure.popupLayout(); |
|
|
if (typeof structure.popupRedirSpans == 'function') pg.misc.redirSpans=structure.popupRedirSpans(); |
|
|
else pg.misc.redirSpans=; |
|
|
return makeEmptySpans(pg.misc.layout); |
|
|
}; |
|
|
|
|
|
function makeEmptySpans (list) { |
|
|
var ret=''; |
|
|
for (var i=0; i<list.length; ++i) { |
|
|
if (typeof list == typeof '') { |
|
|
ret += emptySpanHTML(list, pg.idNumber); |
|
|
} else if (typeof list == typeof && list.length > 0 ) { |
|
|
ret = ret.parenSplit(RegExp('(</*?>$)')).join(makeEmptySpans(list)); |
|
|
} else if (typeof list == typeof {} && list.nodeType ) { |
|
|
ret += emptySpanHTML(list.name, pg.idNumber, list.nodeType); |
|
|
} |
|
|
} |
|
|
return ret; |
|
|
}; |
|
|
|
|
|
function popupRedlinkHTML() { |
|
|
var friendlyCurrentArticleName=pg.current.article.split('_').join(' '); |
|
|
|
|
|
return changeLinkTargetLink({ newTarget: null, |
|
|
text: popupString('remove this link').split(' ').join(' '), |
|
|
hint: popupString("remove all links to this page from this article"), |
|
|
clickButton: "wpDiff", |
|
|
//oldTarget: oldTarget, |
|
|
summary: simplePrintf(getValueOf('popupRedlinkSummary'), )}); |
|
|
} |
|
|
|
|
|
function emptySpanHTML(name, id, tag, classname) { |
|
|
tag = tag || 'span'; |
|
|
classname = classname || name; |
|
|
return simplePrintf('<%s id="%s" class="%s"></%s>', ); |
|
|
} |
|
|
|
|
|
// generate html for popup image |
|
|
// <a id="popupImageLinkn"><img id="popupImagen"> |
|
|
// where n=pg.idNumber |
|
|
function imageHTML(article) { |
|
|
var ret=''; |
|
|
ret += '<a id="popupImageLink' + pg.idNumber + '">'; |
|
|
ret += '<img align="right" valign="top" id="popupImg' + pg.idNumber + '" ' |
|
|
+ 'style="display: none;"></img>'; |
|
|
ret += '</a>'; |
|
|
return ret; |
|
|
}; |
|
|
|
|
|
// ENDFILE: htmloutput.js |
|
|
// STARTFILE: mouseout.js |
|
|
////////////////////////////////////////////////// |
|
|
// fuzzy checks |
|
|
|
|
|
function fuzzyCursorOffMenus(x,y, fuzz, parent) { |
|
|
if (!parent) parent=over; |
|
|
if (!parent) return null; |
|
|
var spans=parent.getElementsByTagName('span'); |
|
|
for (var i=0; i<spans.length; ++i) { |
|
|
if (spans.className=='popup_menu') { |
|
|
if (spans.offsetWidth > 0) return false; |
|
|
} // else {document.title+='.';} |
|
|
} |
|
|
return true; |
|
|
}; |
|
|
|
|
|
function checkPopupPosition () { // stop the popup running off the right of the screen |
|
|
pg.current.link.navpopup.limitHorizontalPosition(); |
|
|
}; |
|
|
|
|
|
|
|
|
function findPosX(obj) |
|
|
{ |
|
|
var curleft = 0; |
|
|
if (obj.offsetParent) |
|
|
{ |
|
|
while (obj.offsetParent) |
|
|
{ |
|
|
curleft += obj.offsetLeft |
|
|
obj = obj.offsetParent; |
|
|
} |
|
|
} |
|
|
else if (obj.x) |
|
|
curleft += obj.x; |
|
|
return curleft; |
|
|
} |
|
|
|
|
|
function findPosY(obj) |
|
|
{ |
|
|
var curtop = 0; |
|
|
if (obj.offsetParent) |
|
|
{ |
|
|
while (obj.offsetParent) |
|
|
{ |
|
|
curtop += obj.offsetTop |
|
|
obj = obj.offsetParent; |
|
|
} |
|
|
} |
|
|
else if (obj.y) |
|
|
curtop += obj.y; |
|
|
return curtop; |
|
|
} |
|
|
|
|
|
function mouseOutWikiLink () { |
|
|
log ('mouseOutWikiLink'); |
|
|
var a=this; |
|
|
if (a.navpopup==null) return; |
|
|
if ( ! a.navpopup.isVisible() ) { |
|
|
a.navpopup.banish(); |
|
|
return; |
|
|
} |
|
|
Navpopup.tracker.addHook(posCheckerHook(a.navpopup)); |
|
|
} |
|
|
|
|
|
function posCheckerHook(navpop) { |
|
|
return function() { |
|
|
//~ if the navpopup isn't visible, or if it is and should stop |
|
|
//~ being... |
|
|
if ( |
|
|
!navpop.isVisible() || |
|
|
(!navpop.isWithin(Navpopup.tracker.x, Navpopup.tracker.y) |
|
|
&& fuzzyCursorOffMenus(Navpopup.tracker.x, Navpopup.tracker.y, navpop.fuzz, navpop.mainDiv)) |
|
|
) { |
|
|
//~ stop it being visible or appearing from a @tt{showSoon} call |
|
|
log('calling banish() from tracker hook function'); |
|
|
navpop.banish(); |
|
|
return true; // remove this hook |
|
|
} |
|
|
// return true; // remove this immediately |
|
|
}; |
|
|
} |
|
|
|
|
|
function runStopPopupTimer(navpop) { |
|
|
// at this point, we should have left the link but remain within the popup |
|
|
// so we call this function again until we leave the popup. |
|
|
if (!navpop.stopPopupTimer) { |
|
|
navpop.stopPopupTimer=setInterval(posCheckerHook(navpop), 500); |
|
|
navpop.addHook(function(){clearInterval(navpop.stopPopupTimer);}, 'hide', 'before'); |
|
|
} |
|
|
} |
|
|
|
|
|
// ENDFILE: mouseout.js |
|
|
// STARTFILE: previewmaker.js |
|
|
/** |
|
|
@fileoverview |
|
|
Defines the {@link Previewmaker} object, which generates short previews from wiki markup. |
|
|
*/ |
|
|
|
|
|
/** |
|
|
Creates a new Previewmaker |
|
|
@constructor |
|
|
@class The Previewmaker class. Use an instance of this to generate short previews from Wikitext. |
|
|
@param {String} wikiText The Wikitext source of the page we wish to preview. |
|
|
*/ |
|
|
function Previewmaker(wikiText) { |
|
|
/** The wikitext which is manipulated to generate the preview. */ |
|
|
this.data=wikiText; |
|
|
} |
|
|
/** Remove HTML comments |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killComments = function () { |
|
|
// this also kills trailing spaces and one trailing newline, eg ] |
|
|
this.data=this.data.replace(RegExp('<!--(\\n|.)*?--> *\\n?', 'g'), ''); |
|
|
} |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killDivs=function () { |
|
|
// say goodbye, divs (can be nested, so use * not *?) |
|
|
this.data=this.data.replace(RegExp('< *div* *>*?< */ *div *>', |
|
|
'gi'), ''); |
|
|
} |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killGalleries=function () { |
|
|
this.data=this.data.replace(RegExp('< *gallery* *>*?< */ *gallery *>', |
|
|
'gi'), ''); |
|
|
} |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killBoxTemplates=function () { |
|
|
// taxobox hack... in fact, there's a saudiprincebox_begin, so let's be more general |
|
|
this.data=this.data.replace(RegExp('*box(begin|start)' + |
|
|
'*?*box(end|finish) *\\s', 'gi'), ''); |
|
|
|
|
|
// infoboxes etc |
|
|
// this should cope with templates in infoboxes, but only nested once |
|
|
this.data=this.data.replace(RegExp('*(info|element)box' + |
|
|
'\(\|\|*?\)*?\\s*', |
|
|
'gi'), ''); |
|
|
|
|
|
// also, have float_begin, ... float_end |
|
|
this.data=this.data.replace(RegExp('*floatbegin' + |
|
|
'*?*floatend.*?', |
|
|
'gi'), ''); |
|
|
|
|
|
// from ]: kill frames too |
|
|
this.data=this.data.replace(RegExp('*frame' + |
|
|
'*?*?end+frame', 'gi'), ''); |
|
|
} |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killTemplates = function () { |
|
|
this.data=this.data.replace(RegExp('(*|)*', 'g'), ' '); |
|
|
} |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killTables=function () { |
|
|
// tables are bad, too |
|
|
this.data=this.data.replace |
|
|
(RegExp('\\|((|\\||)*||\\||\\|)*\\|', 'g') |
|
|
, ''); |
|
|
// remove lines starting with a pipe |
|
|
this.data=this.data.replace(RegExp('^.*$', 'mg'), ''); |
|
|
} |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killImages=function () { |
|
|
// images are a nono |
|
|
// who says regexes aren't fun? |
|
|
// i think we should match: |
|
|
// ] where ....... consists of repetitions of any of: |
|
|
// 1. not |
|
|
// 2. )* ]] |
|
|
// 3. )* ] |
|
|
var imagedetector = |
|
|
'\\s*('+ |
|
|
pg.ns.image + '|' + pg.ns.category+ |
|
|
')\\s*:(]|\\]*\\]\\]|\\]*\\])*\\]\\]\\s*'; |
|
|
var crudeImageRegex = RegExp(imagedetector, 'gi'); |
|
|
this.data=this.data.replace(crudeImageRegex, ''); |
|
|
} |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killHTML=function () { |
|
|
// kill <ref>...</ref> |
|
|
this.data=this.data.replace(RegExp('<ref>*?</ref>', 'gi'), ''); |
|
|
|
|
|
// kill html tables // this doesn't cope with embedded tables |
|
|
// may this is good enough? |
|
|
this.data=this.data.replace(RegExp('<table.*?>*?</\\s*table\\s*>\\n+', 'gi'), ''); |
|
|
|
|
|
// let's also delete entire lines starting with <. it's worth a try. |
|
|
this.data=this.data.replace(RegExp('(^|\\n) *<.*', 'g'), '\n'); |
|
|
|
|
|
// and those pesky html tags, but not <nowiki> |
|
|
var splitted=this.data.parenSplit(/(<.*?>)/); |
|
|
var len=splitted.length; |
|
|
for (var i=1; i<len; i=i+2) { |
|
|
switch (splitted) { |
|
|
case '<nowiki>': |
|
|
case '</nowiki>': |
|
|
break; |
|
|
default: |
|
|
splitted=''; |
|
|
} |
|
|
} |
|
|
this.data=splitted.join(''); |
|
|
} |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killChunks=function() { // heuristics alert |
|
|
// chunks of italic text? you crazy, man? |
|
|
var italicChunkRegex=new RegExp |
|
|
("((^|\\n)\\s*:*\\s*''(|'''|'){20}(.|\\n)*''*\\n)*", 'g'); |
|
|
this.data=this.data.replace(italicChunkRegex, ''); |
|
|
} |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.mopup=function () { |
|
|
// we simply *can't* be doing with horizontal rules right now |
|
|
this.data=this.data.replace(RegExp('^-{4,}','mg'),''); |
|
|
|
|
|
// no indented lines |
|
|
this.data=this.data.replace(RegExp('(^|\\n) *:*','g'), '\n'); |
|
|
|
|
|
// replace __TOC__, __NOTOC__ and whatever else there is |
|
|
// this'll probably do |
|
|
this.data=this.data.replace(RegExp('^__*__ *$', 'gmi'),''); |
|
|
} |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.firstBit=function () { |
|
|
// dont't be givin' me no subsequent paragraphs, you hear me? |
|
|
/// first we "normalize" section headings, removing whitespace after, adding before |
|
|
|
|
|
this.data=this.data.replace(RegExp('\\s*(==+*==+)\\s*', 'g'), '\n\n$1 '); |
|
|
|
|
|
/// then we want to get rid of paragraph breaks whose text ends badly |
|
|
this.data=this.data.replace(RegExp('() *\\n{2,}', 'g'), '$1\n'); |
|
|
|
|
|
this.data=this.data.replace(RegExp('^*'), ''); |
|
|
stuff=(RegExp('^(|\\n)*')).exec(this.data); |
|
|
var d; |
|
|
if (stuff) d = stuff; |
|
|
if (!getValueOf('popupPreviewFirstParOnly')) d = this.data; |
|
|
|
|
|
/// now put \n\n after sections so that bullets and numbered lists work |
|
|
d=d.replace(RegExp('(==+*==+)\\s*', 'g'), '$1\n\n'); |
|
|
|
|
|
// superfluous sentences are RIGHT OUT. |
|
|
// note: exactly 1 set of parens here needed to make the slice work |
|
|
d = d.parenSplit(RegExp('(+*\\s)','g')); |
|
|
// leading space is bad, mmkay? |
|
|
d=d.replace(RegExp('^\\s*'), ''); |
|
|
|
|
|
var notSentenceEnds=RegExp('(|etc|sic|Dr|Mr|Mrs|Ms|St|\\]*|\\s)$', 'i'); |
|
|
|
|
|
d = this.fixSentenceEnds(d, notSentenceEnds); |
|
|
|
|
|
var maxChars=getValueOf('popupMaxPreviewCharacters'); |
|
|
var n=getValueOf('popupMaxPreviewSentences'); |
|
|
var dd; |
|
|
|
|
|
do {dd=this.firstSentences(d,n); --n; } |
|
|
while ( dd.length > maxChars && n > 0 ); |
|
|
|
|
|
this.data = dd; |
|
|
} |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.fixSentenceEnds=function(strs, reg) { |
|
|
// take an array of strings, strs |
|
|
// join strs to strs & strs if strs matches regex reg |
|
|
|
|
|
for (var i=0; i<strs.length-2; ++i) { |
|
|
if (reg.test(strs)) { |
|
|
a=; |
|
|
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 this.fixSentenceEnds(a,reg); |
|
|
} |
|
|
} |
|
|
return strs; |
|
|
} |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.firstSentences=function(strs, howmany) { |
|
|
var t=strs.slice(0, 2*howmany); |
|
|
return t.join(''); |
|
|
} |
|
|
/** |
|
|
Runs the various methods to generate the preview. |
|
|
The preview is stored in the <code>html</html> field. |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.makePreview=function() { |
|
|
if (!isInNamespace(pg.current.article, 'Template')) { |
|
|
this.killComments(); |
|
|
this.killDivs(); |
|
|
this.killGalleries(); |
|
|
this.killBoxTemplates(); |
|
|
|
|
|
if (getValueOf('popupPreviewKillTemplates')) { |
|
|
this.killTemplates(); |
|
|
} else { |
|
|
this.killMultilineTemplates(); |
|
|
} |
|
|
|
|
|
this.killTables(); |
|
|
this.killImages(); |
|
|
this.killHTML(); |
|
|
this.killChunks(); |
|
|
this.mopup(); |
|
|
|
|
|
this.firstBit(); |
|
|
} |
|
|
|
|
|
this.html=wiki2html(this.data); // needs livepreview |
|
|
this.fixHTML(); |
|
|
this.stripLongTemplates(); |
|
|
} |
|
|
|
|
|
/** Test function for debugging preview problems one step at a time. |
|
|
*/ |
|
|
function previewSteps(txt) { |
|
|
try { |
|
|
txt=txt || document.editform.wpTextbox1.value; |
|
|
} catch (err) { |
|
|
if (pg.cache.pages.length > 0) { |
|
|
txt=pg.cache.pages.data; |
|
|
} else { |
|
|
alert('provide text or use an edit page'); |
|
|
} |
|
|
} |
|
|
var p=new Previewmaker(txt); |
|
|
if (!isInNamespace(pg.current.article, 'Template')) { |
|
|
p.killComments(); if (!confirm('done killComments(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.killDivs(); if (!confirm('done killDivs(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.killGalleries(); if (!confirm('done killGalleries(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.killBoxTemplates(); if (!confirm('done killBoxTemplates(). Continue?\n---\n' + p.data)) { return; } |
|
|
|
|
|
if (getValueOf('popupPreviewKillTemplates')) { |
|
|
p.killTemplates(); if (!confirm('done killTemplates(). Continue?\n---\n' + p.data)) { return; } |
|
|
} else { |
|
|
p.killMultilineTemplates(); if (!confirm('done killMultilineTemplates(). Continue?\n---\n' + p.data)) { return; } |
|
|
} |
|
|
|
|
|
p.killTables(); if (!confirm('done killTables(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.killImages(); if (!confirm('done killImages(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.killHTML(); if (!confirm('done killHTML(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.killChunks(); if (!confirm('done killChunks(). Continue?\n---\n' + p.data)) { return; } |
|
|
p.mopup(); if (!confirm('done mopup(). Continue?\n---\n' + p.data)) { return; } |
|
|
|
|
|
p.firstBit(); if (!confirm('done firstBit(). Continue?\n---\n' + p.data)) { return; } |
|
|
} |
|
|
|
|
|
p.html=wiki2html(p.data); // needs livepreview |
|
|
p.fixHTML(); if (!confirm('done fixHTML(). Continue?\n---\n' + p.html)) { return; } |
|
|
p.stripLongTemplates(); if (!confirm('done stripLongTemplates(). Continue?\n---\n' + p.html)) { return; } |
|
|
alert('finished preview - end result follows.\n---\n' + p.html); |
|
|
} |
|
|
|
|
|
/** |
|
|
Works around a quoting bug in livepreview. |
|
|
<code>wiki2html(']')</code> gives @literal{<a href='Foo's "bar"'>} |
|
|
which doesn't do very well. We change this into @literal{<a href="Foo's %22bar%22">} |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.fixHTML=function() { |
|
|
if(!this.html) return; |
|
|
var splitted=this.html.parenSplit(/href='(*)'/g); |
|
|
var ret=''; |
|
|
for (var i=0; i<splitted.length; ++i) { |
|
|
if(i%2==0) { ret += splitted; continue; } |
|
|
if(i%2==1) { ret += 'href="' + splitted.split('"').join('%22') + '"'; } |
|
|
} |
|
|
this.html=ret; |
|
|
} |
|
|
/** |
|
|
Generates the preview and displays it in the current popup. |
|
|
|
|
|
Does nothing if the generated preview is invalid or consists of whitespace only. |
|
|
Also activates wikilinks in the preview for subpopups if the popupSubpopups option is true. |
|
|
*/ |
|
|
Previewmaker.prototype.showPreview=function () { |
|
|
this.makePreview(); |
|
|
if (typeof this.html != typeof '') return; |
|
|
if (RegExp('^\\s*$').test(this.html)) return; |
|
|
//if (getValueOf('popupNavLinks') || getValueOf('popupSummaryData')) |
|
|
var popTips=function() { setupTooltips(document.getElementById('popupPreview' + pg.idNumber)); }; |
|
|
setPopupHTML('<hr>'+this.html, 'popupPreview', pg.idNumber, |
|
|
getValueOf('popupSubpopups') ? function() { pop.runOnce( popTips, 250 ); } : null ); |
|
|
} |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.stripLongTemplates=function() { |
|
|
// operates on the HTML! |
|
|
this.html=this.html.replace(RegExp('^.{0,1000}*?(<(p|br)( /)?>\\s*){2,}(*?)?', 'gi'), ''); |
|
|
this.html=this.html.split('\n').join(' '); // workaround for <pre> templates |
|
|
this.html=this.html.replace(RegExp('*<pre>*','gi'), ''); |
|
|
//this.html=this.html.replace(RegExp('(*|\\s){0,50}?<pre>*?', 'gi'), ''); |
|
|
//window.h=this.html; |
|
|
//alert(this.html); |
|
|
} |
|
|
/** |
|
|
@private |
|
|
*/ |
|
|
Previewmaker.prototype.killMultilineTemplates=function() { |
|
|
this.data=this.data.replace(RegExp('\\s**\\n*', 'g'), ' '); |
|
|
//this.data=this.data.replace(RegExp('(*|)*\\n(*|)*', 'g'), ' '); |
|
|
} |
|
|
|
|
|
// ENDFILE: previewmaker.js |
|
|
// STARTFILE: debug.js |
|
|
//////////////////////////////////////////////////////////////////// |
|
|
// Debugging functions |
|
|
//////////////////////////////////////////////////////////////////// |
|
|
|
|
|
function time() { var d=new Date(); return d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds() + '.' + (d.getTime() % 1000); }; |
|
|
var gMsg=''; |
|
|
|
|
|
function limitAlert(fn,max,s) { |
|
|
if (!fn.alertCount) fn.alertCount=0; |
|
|
if (true && fn.alertCount < max) {alert(s); fn.alertCount++;} |
|
|
} |
|
|
|
|
|
function setupDebugging() { |
|
|
// debugging - change DEBUG to NONE to switch off |
|
|
if (window.popupDebug) { // popupDebug is set from .version |
|
|
window.log=function(x) { //if(gMsg!='')gMsg += '\n'; gMsg+=time() + ' ' + x; }; |
|
|
if (pg.debugLevel != log.None) window.logger.debug(x); |
|
|
} |
|
|
window.errlog=function(x) { |
|
|
if (pg.debugLevel != log.None) window.logger.error(x); |
|
|
} |
|
|
pg.debugLevel=Log.DEBUG; |
|
|
window.logger = new Log(pg.debugLevel, Log.popupLogger); |
|
|
log('Initializing logger'); |
|
|
} else { |
|
|
window.log = function(x) {}; |
|
|
window.errlog = function(x) {}; |
|
|
} |
|
|
} |
|
|
|
|
|
// ENDFILE: debug.js |
|
|
// STARTFILE: images.js |
|
|
// How the URLs for images in the popup come about |
|
|
|
|
|
// loadPreview |
|
|
// | |
|
|
// getWiki |
|
|
// |<----------------see other schematic for details |
|
|
// insertPreview (insertPreview = onComplete) |
|
|
// | |
|
|
// | insertPreview gets a wikiText fragment from |
|
|
// | the wikiText downloaded by getWiki |
|
|
// | |
|
|
// |
|
|
// | |
|
|
// | mouseOverWikiLink (gets an "address fragment", |
|
|
// | | no processing needed) |
|
|
// \->-loadThisImage---<----loadImages |
|
|
// | |
|
|
// -->--hopefully valid image urls |
|
|
|
|
|
function nextOne (array, value) { |
|
|
// NB if the array has two consecutive entries equal |
|
|
// then this will loop on successive calls |
|
|
if (typeof array.length == 'undefined') return null; |
|
|
for (var i=0; i<array.length-1; ++i) { |
|
|
if (array==value) return array; |
|
|
} |
|
|
return null; |
|
|
}; |
|
|
|
|
|
function findThis(array, value) { |
|
|
if (typeof array.length == 'undefined') return null; |
|
|
for (var i=0; i<array.length; ++i) { |
|
|
if (array==value) return i; |
|
|
} |
|
|
return null; |
|
|
}; |
|
|
|
|
|
// global array for 404'ed image urls |
|
|
pg.cache.badImageUrls=; |
|
|
|
|
|
function sequentialLoadThisImage (image) { |
|
|
if (!getValueOf('popupImages')) return false; |
|
|
if (!isValidImageName(image)) return false; |
|
|
|
|
|
var imageUrls=getImageUrls(image); |
|
|
if (!imageUrls) return null; |
|
|
|
|
|
var img=new Image(); |
|
|
img.isNew=true; |
|
|
img.pg.idNumber=pg.idNumber; |
|
|
img.counter=1; |
|
|
|
|
|
img.onload = function () { |
|
|
// clear status thingy |
|
|
setImageStatus(''); |
|
|
|
|
|
var i=findThis(imageUrls, this.src); |
|
|
var goodSrc=this.src; |
|
|
|
|
|
var setPopupImage=function () { |
|
|
var popupImage=document.getElementById("popupImage"+this.pg.idNumber); |
|
|
if (popupImage && typeof popupImage.src != 'undefined') { |
|
|
clearInterval(pg.timer.image); |
|
|
popupImage.src=goodSrc; |
|
|
popupImage.width=getValueOf('popupImageSize'); |
|
|
popupImage.style.display='inline'; |
|
|
setPopupImageLink(image, pg.wiki.imageSources.wiki); |
|
|
return true; |
|
|
} else return false; |
|
|
}; |
|
|
pg.timer.image=setInterval(setPopupImage, 250); |
|
|
pg.cache.images.push(goodSrc); |
|
|
}; |
|
|
|
|
|
img.onerror = function () { |
|
|
pg.cache.badImageUrls.push(this.src); |
|
|
// if the popup is visible, move on |
|
|
if (over && over.style.visibility=='visible' && this.pg.idNumber==pg.idNumber) this.setNext(); |
|
|
}; |
|
|
|
|
|
img.setNext = function () { |
|
|
var currentSrc=null; |
|
|
var newSrc; |
|
|
if (!this.isNew) currentSrc=this.src; |
|
|
this.isNew=false; |
|
|
|
|
|
newSrc= (currentSrc) ? nextOne(imageUrls, currentSrc) : imageUrls; |
|
|
|
|
|
while (findThis(pg.cache.badImageUrls, newSrc)) { |
|
|
newSrc=nextOne(imageUrls, newSrc); |
|
|
if (!newSrc) { |
|
|
setImageStatus (' :-('); |
|
|
return; |
|
|
} |
|
|
} |
|
|
setImageStatus(' '+findThis(imageUrls, newSrc)); |
|
|
this.src=newSrc; |
|
|
} |
|
|
|
|
|
// start the ball rolling |
|
|
img.setNext(); |
|
|
|
|
|
}; |
|
|
|
|
|
function loadThisImageAtThisUrl(image, url) { |
|
|
log('loading "best" image:\n'+url); |
|
|
pg.misc.gImage=image; |
|
|
pg.misc.imageArray = ; |
|
|
pg.misc.imageArray = new Image(); |
|
|
pg.misc.imageArray.src=url; |
|
|
if (pg.timer.image != null) { |
|
|
clearInterval(pg.timer.image); |
|
|
pg.counter.checkImages=0; |
|
|
} |
|
|
pg.timer.image=setInterval("checkImages()", 250); |
|
|
return; |
|
|
}; |
|
|
|
|
|
function loadImages(article) {if(! isImage(article) ) return null; return loadThisImage(article);}; |
|
|
|
|
|
|
|
|
// methinks this is unbelievably silly |
|
|
// it dovetails with the parallel image loader function |
|
|
function checkImages() { |
|
|
//log('checkImages: pg.counter.loop='+pg.counter.loop+'; pg.counter.checkImages='+pg.counter.checkImages); |
|
|
if (pg.timer.checkImages!=null) { |
|
|
clearInterval(pg.timer.checkImages); |
|
|
pg.timer.checkImages=null; |
|
|
if (pg.counter.loop > 10); {pg.counter.loop=0; log('too many iterations of checkImages'); return;} |
|
|
pg.counter.loop++; |
|
|
} else pg.counter.checkImages++; |
|
|
|
|
|
var status = ( pg.counter.checkImages % 2 ) ? ':' : '.' ; |
|
|
setImageStatus(status); |
|
|
|
|
|
if (pg.counter.checkImages > 100) {pg.counter.checkImages = 0; log ('pg.counter.checkImages too big in checkImages; returning'); clearInterval(pg.timer.image);} |
|
|
|
|
|
var popupImage=null; |
|
|
popupImage=document.getElementById("popupImg"+pg.idNumber); |
|
|
if (popupImage == null) { |
|
|
// this doesn't seem to happen any more in practise for some reason |
|
|
// still, I'll leave it in |
|
|
log('checkImages: document.getElementById("popupImg'+pg.idNumber+'") is null! retrying in 333ms...'); |
|
|
pg.timer.checkImages=setInterval("checkImages()",333); |
|
|
return; |
|
|
} |
|
|
|
|
|
log('checkImages: found element popupImg'+pg.idNumber+', and src='+popupImage.src); |
|
|
|
|
|
// get the first image to successfully load |
|
|
// and put it in the popupImage |
|
|
for(var i = 0; i < pg.misc.imageArray.length; ++i) { |
|
|
if(isImageOk(pg.misc.imageArray)) { |
|
|
// stop all the gubbins, assign the image and return |
|
|
|
|
|
log('checkImages: got at pos '+i+', src='+pg.misc.imageArray.src); |
|
|
clearInterval(pg.timer.image); |
|
|
|
|
|
if(isImage(pg.misc.gImage)) { |
|
|
popupImage.src=pg.misc.imageArray.src; |
|
|
popupImage.width=getValueOf('popupImageSize'); |
|
|
popupImage.style.display='inline'; |
|
|
// should we check to see if it's already there? maybe... |
|
|
pg.cache.images.push(pg.misc.imageArray.src); |
|
|
|
|
|
setPopupImageLink(pg.misc.gImage, pg.wiki.imageSources.wiki); |
|
|
stopImagesDownloading(); |
|
|
} |
|
|
|
|
|
setImageStatus(''); |
|
|
|
|
|
// reset evil nonconstant globals |
|
|
delete pg.misc.imageArray; pg.misc.imageArray=; |
|
|
pg.timer.image=null; |
|
|
|
|
|
pg.counter.checkImages=0; |
|
|
pg.counter.loop=0; |
|
|
|
|
|
return popupImage.src; |
|
|
} |
|
|
} |
|
|
log('checkImages: no good image found. retrying in a tic...'); |
|
|
pg.timer.checkImages=setInterval("checkImages()",333); |
|
|
}; |
|
|
|
|
|
function stopImagesDownloading() { |
|
|
pg.misc.gImage=null; |
|
|
if (pg.misc.imageArray == null) return null; |
|
|
var i; |
|
|
for (i=0; i<pg.misc.imageArray.length; ++i) { |
|
|
//pg.misc.imageArray.src=''; // this is a REALLY BAD IDEA |
|
|
delete pg.misc.imageArray; |
|
|
//pg.misc.imageArray = new Image(); |
|
|
} |
|
|
pg.misc.imageArray = ; |
|
|
return i; |
|
|
}; |
|
|
|
|
|
function toggleSize() { |
|
|
var imgContainer=this; |
|
|
if (!imgContainer) { alert('imgContainer is null :/'); return;} |
|
|
img=imgContainer.firstChild; |
|
|
if (!img) { alert('img is null :/'); return;} |
|
|
if (!img.style.width || img.style.width=='') img.style.width='100%'; |
|
|
else img.style.width=''; // popupImageSize+'px'; |
|
|
}; |
|
|
|
|
|
function setPopupImageLink (img, wiki) { |
|
|
if( wiki === null || img === null ) return null; |
|
|
|
|
|
var a=document.getElementById("popupImageLink"+pg.idNumber); |
|
|
if (a === null) return null; |
|
|
|
|
|
var linkURL = imageURL(img, wiki); |
|
|
if (linkURL != null) { |
|
|
if (getValueOf('popupImagesToggleSize')) { a.onclick=toggleSize; a.title=popupString('Toggle image size'); } |
|
|
else { a.href=linkURL; a.title=popupString('Open full-size image'); } |
|
|
} |
|
|
return linkURL; |
|
|
}; |
|
|
|
|
|
function isImageOk(img) |
|
|
{ |
|
|
// IE test |
|
|
if (!img.complete) |
|
|
return false; |
|
|
|
|
|
// gecko test |
|
|
if (typeof img.naturalWidth != "undefined" && img.naturalWidth == 0) |
|
|
return false; |
|
|
|
|
|
// test for konqueror and opera |
|
|
|
|
|
// note that img.width must not be defined in the html with a width="..." |
|
|
// for this to work. |
|
|
|
|
|
// konq seems to give "broken images" width 16, presumably an icon width |
|
|
// this test would probably work in gecko too, *except for very small images* |
|
|
if (typeof img.width == 'undefined' || img.width <= 16) return false; |
|
|
|
|
|
// No other way of checking: assume it's ok. |
|
|
return true; |
|
|
}; |
|
|
|
|
|
// those odd a/a5/ bits of image urls |
|
|
function imagePathComponent(article) { |
|
|
var stripped=stripNamespace(article); |
|
|
var forhash=safeDecodeURI(stripped).split(' ').join('_'); |
|
|
var hash=hex_md5(forhash); |
|
|
return hash.substring(0,1) + '/' + hash.substring(0,2) + '/'; |
|
|
} |
|
|
|
|
|
function getImageUrlStart(wiki) { // this returns a trailing slash |
|
|
switch (wiki) { |
|
|
case 'en.wikipedia.org': return 'http://upload.wikimedia.org/wikipedia/en/'; |
|
|
case pg.wiki.commons: return 'http://upload.wikimedia.org/wikipedia/commons/'; |
|
|
case 'en.wiktionary.org': return 'http://en.wiktionary.org/upload/en/'; |
|
|
default: // unsupported - take a guess |
|
|
if (pg.wiki.wikimedia) { |
|
|
return 'http://upload.wikimedia.org/wikipedia/' + pg.wiki.lang +'/'; |
|
|
} |
|
|
else /* this should work for wikicities */ |
|
|
return 'http://' + wiki + '/images/'; |
|
|
} |
|
|
} |
|
|
|
|
|
function imageURL(img, wiki) { |
|
|
if (getValueOf('popupImagesFromThisWikiOnly') && wiki != pg.wiki.hostname) return null; |
|
|
var imgurl=null; |
|
|
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 |
|
|
|
|
|
if (getValueOf('popupImagesFromThisWikiOnly') && wiki != pg.wiki.hostname) return null; |
|
|
if (getValueOf('popupNeverGetThumbs')) return null; |
|
|
|
|
|
var imgurl=null; |
|
|
if (isImage(img)) { |
|
|
var stripped=stripNamespace(img); |
|
|
var pathcpt; |
|
|
if (pg.wiki.wikimedia) pathcpt = imagePathComponent(stripped); |
|
|
else pathcpt = imagePathComponent(width+'px-'+stripped); |
|
|
imgurl=getImageUrlStart(wiki) + "thumb/" + pathcpt; |
|
|
if (pg.wiki.wikimedia) imgurl += stripped + '/'; |
|
|
imgurl += width +"px-" + stripped; |
|
|
} |
|
|
return imgurl; |
|
|
}; |
|
|
|
|
|
function loadThisImage(image) { |
|
|
if (getValueOf('popupLoadImagesSequentially')) return sequentialLoadThisImage(image); |
|
|
else return parallelLoadThisImage(image); |
|
|
}; |
|
|
|
|
|
function getImageUrls(image) { |
|
|
var imageUrls=; |
|
|
for (var i=0; i<pg.wiki.imageSources.length; ++i) { |
|
|
var url; |
|
|
if (pg.wiki.imageSources.thumb) |
|
|
url=imageThumbURL(image, pg.wiki.imageSources.wiki, pg.wiki.imageSources.width); |
|
|
else |
|
|
url=imageURL(image, pg.wiki.imageSources.wiki); |
|
|
for (var j=0; j<pg.cache.images.length; ++j) { |
|
|
if (url == pg.cache.images) { |
|
|
loadThisImageAtThisUrl(image, url); |
|
|
return null; |
|
|
} |
|
|
} |
|
|
if (url!=null) imageUrls.push(url); |
|
|
} |
|
|
return imageUrls; |
|
|
}; |
|
|
|
|
|
|
|
|
// this is probably very wasteful indeed of bandwidth |
|
|
// hey ho |
|
|
|
|
|
function parallelLoadThisImage (image) { |
|
|
if (!getValueOf('popupImages')) return; |
|
|
if (!isValidImageName(image)) return false; |
|
|
|
|
|
var imageUrls=getImageUrls(image); |
|
|
if (!imageUrls) return null; |
|
|
|
|
|
for (var i=0; i<imageUrls.length; ++i) { |
|
|
var url = imageUrls; |
|
|
pg.misc.imageArray=new Image(); |
|
|
pg.misc.imageArray.src=url; |
|
|
} |
|
|
if (pg.timer.image != null) { |
|
|
clearInterval(pg.timer.image); |
|
|
pg.counter.checkImages=0; |
|
|
} |
|
|
pg.misc.gImage=image; |
|
|
pg.timer.image=setInterval("checkImages()", 250); |
|
|
return true; |
|
|
}; |
|
|
function getValidImageFromWikiText(wikiText) { |
|
|
var imagePage=null; |
|
|
// nb in pg.re.image we're interested in the second bracketed expression |
|
|
// this may change if the regex changes :-( |
|
|
//var match=pg.re.image.exec(wikiText); |
|
|
var matched=null; |
|
|
var match; |
|
|
while ( match = pg.re.image.exec(wikiText) ) { |
|
|
// now find a sane image name - exclude templates by seeking { |
|
|
var m = match || pg.ns.image + ':' + match; |
|
|
var pxWidth=match; |
|
|
if ( isValidImageName(m) && (!pxWidth || parseInt(pxWidth) >= getValueOf('popupMinImageWidth')) ) { |
|
|
matched=m; |
|
|
break; |
|
|
} |
|
|
} |
|
|
pg.re.image.lastIndex=0; |
|
|
if (!matched) return null; |
|
|
return pg.ns.image+':'+upcaseFirst(matched); |
|
|
}; |
|
|
|
|
|
// ENDFILE: images.js |
|
|
// STARTFILE: namespaces.js |
|
|
// Set up namespaces and other non-strings.js localization |
|
|
// (currently that means redirs too) |
|
|
|
|
|
// Put the right namespace list into pg.ns.list, based on pg.wiki.lang |
|
|
// Default to english if nothing seems to fit |
|
|
function setNamespaceList() { |
|
|
var m="Media"; |
|
|
var list = ; |
|
|
var nsLists = { |
|
|
"af": , |
|
|
"als": , |
|
|
"ar": , |
|
|
"ast": , |
|
|
"be": , |
|
|
"bg": , |
|
|
"bm": , |
|
|
"bn": , |
|
|
"br": , |
|
|
"ca": , |
|
|
"cs": , |
|
|
"csb": , |
|
|
"cv": , |
|
|
"cy": , |
|
|
"da": , |
|
|
"de": , |
|
|
"el": , |
|
|
"eo": , |
|
|
"es": , |
|
|
"et": , |
|
|
"eu": , |
|
|
"fa": , |
|
|
"fi": , |
|
|
"fo": , |
|
|
"fr": , |
|
|
"fur": , |
|
|
"fy": , |
|
|
"ga": , |
|
|
"gu": , |
|
|
"he": , |
|
|
"hi": , |
|
|
"hr": , |
|
|
"hu": , |
|
|
"ia": , |
|
|
"id": , |
|
|
"is": , |
|
|
"it": , |
|
|
"ja": , |
|
|
"ka": , |
|
|
"ko": , |
|
|
"ku": , |
|
|
"la": , |
|
|
"li": , |
|
|
"lt": , |
|
|
"mk": , |
|
|
"ms": , |
|
|
"mt": , |
|
|
"nap": , |
|
|
"nds": , |
|
|
"nl": , |
|
|
"nn": , |
|
|
"no": , |
|
|
"nv": , |
|
|
"oc": , |
|
|
"os": , |
|
|
"pa": , |
|
|
"pl": , |
|
|
"pt": , |
|
|
"ro": , |
|
|
"ru": , |
|
|
"sc": , |
|
|
"sk": , |
|
|
"sl": , |
|
|
"sq": , |
|
|
"sr": , |
|
|
"sv": , |
|
|
"ta": , |
|
|
"th": , |
|
|
"tlh": , |
|
|
"tr": , |
|
|
"tt": , |
|
|
"uk": , |
|
|
"vi": , |
|
|
"wa": |
|
|
} |
|
|
pg.ns.list = nsLists || list; |
|
|
} |
|
|
|
|
|
function namespaceListToRegex(list) {return RegExp('^('+list.join('|').split(' ').join('')+'):');}; |
|
|
// function setNamespaceList is ugly as sin, moved to later in the code |
|
|
|
|
|
function setNamespaces() { |
|
|
setNamespaceList(); |
|
|
pg.ns.withTalkList=; // NB root (article) corresponds with this entry, null |
|
|
pg.ns.talkList=]; |
|
|
|
|
|
// if the number of namespaces changes then this will have to be changed |
|
|
// maybe the easiest way is to specify the arrays by hand as in the comments following the loop |
|
|
|
|
|
for (var i=3; i+1<pg.ns.list.length; i=i+2) { |
|
|
pg.ns.withTalkList.push(pg.ns.list); |
|
|
pg.ns.talkList.push(pg.ns.list); |
|
|
} |
|
|
|
|
|
// ALERT! SILLY HARDCODED VALUES FOLLOW! |
|
|
pg.ns.special = pg.ns.list; |
|
|
pg.ns.image = pg.ns.list; |
|
|
pg.ns.user = pg.ns.list; |
|
|
pg.ns.category = pg.ns.list; |
|
|
pg.ns.nonArticleList=pg.ns.list.slice(0,2).concat(pg.ns.list.slice(2)); |
|
|
} |
|
|
|
|
|
|
|
|
function setRedirs() { |
|
|
var r='redirect'; |
|
|
var R='REDIRECT'; |
|
|
var redirLists={ |
|
|
'be': , |
|
|
'bg': , |
|
|
'cs': , |
|
|
'cy': , |
|
|
'et': , |
|
|
'ga': , |
|
|
'is': , |
|
|
'mk': , |
|
|
'nds': , |
|
|
'nn': , |
|
|
'pt': , |
|
|
'ru': , |
|
|
'sk': , |
|
|
'sr': , |
|
|
'tt': , |
|
|
'vi': // no comma |
|
|
} |
|
|
var redirList=redirLists || ; |
|
|
// Mediawiki is very tolerant about what comes after the #redirect at the start |
|
|
pg.re.redirect=RegExp('^\\s*(' + redirList.join('|') + ').*?\\]*)(|]*)?\\]{2}\\s*(.*)', 'i'); |
|
|
} |
|
|
|
|
|
function setInterwiki() { |
|
|
if (pg.wiki.wikimedia) { |
|
|
pg.wiki.interwiki='aa|ab|af|ak|als|am|an|ang|ar|arc|as|ast|av|ay|az|ba|be|ber|bg|bh|bi|bm|bn|bdf|bo|br|bs|ca|ce|ceb|ch|cho|chr|chy|co|commons|cr|cs|csb|cu|cv|cy|da|de|dv|dz|el|en|eo|es|et|eu|fa|ff|fi|fiu-vro|fj|fo|fr|fur|fy|ga|gd|gil|gl|gn|got|gu|gv|ha|haw|he|hi|ho|hr|ht|hu|hy|hz|ia|id|ie|ig|ii|ik|io|is|it|iu|ja|jbo|jv|ka|kg|ki|kj|kk|kl|km|kn|ko|kr|ks|ku|kv|kw|ky|la|lad|lan|lb|lg|li|ln|lo|lt|lu|lv|mg|mh|mi|mk|ml|mn|mo|mr|ms|mt|mus|my|na|nah|nap|nb|nd|nds|ne|ng|nl|nn|no|nr|nv|ny|oc|oj|om|or|os|pa|pam|pi|pl|ps|pt|qu|rm|rn|ro|roa-rup|ru|rw|sa|sc|scn|sco|sd|se|sg|sh|si|simple|sk|sl|sm|smg|sn|so|sq|sr|ss|st|su|sv|sw|ta|te|tg|th|ti|tk|tl|tlh|tn|to|tpi|tr|ts|tt|tum|tw|ty|ug|uk|ur|uz|ve|vi|vk|vo|wa|war|wen|wo|xh|yi|yo|za|zh|zh-min-nan|zu'; |
|
|
pg.re.interwiki=RegExp('^'+pg.wiki.interwiki+':'); |
|
|
} else { |
|
|
pg.wiki.interwiki=null; |
|
|
pg.re.interwiki=RegExp('^$'); |
|
|
} |
|
|
} |
|
|
|
|
|
// ENDFILE: namespaces.js |
|
|
// STARTFILE: selpop.js |
|
|
window.getEditboxSelection=function() { |
|
|
// see http://www.webgurusforum.com/8/12/0 |
|
|
try { |
|
|
var editbox=document.editform.wpTextbox1; |
|
|
} catch (dang) { return; } |
|
|
// IE, Opera |
|
|
if (document.selection) { return document.selection.createRange().text; } |
|
|
// Mozilla |
|
|
var selStart = editbox.selectionStart; |
|
|
var selEnd = editbox.selectionEnd; |
|
|
return (editbox.value).substring(selStart, selEnd); |
|
|
} |
|
|
window.doSelectionPopup=function() { |
|
|
// popup if the selection looks like [[foo|anything afterwards at all |
|
|
// or ]text without ']]' |
|
|
// or ] |
|
|
var sel=getEditboxSelection(); |
|
|
var open=sel.indexOf(']'); |
|
|
if (open == -1 || ( pipe == -1 && close == -1) ) return; |
|
|
if (pipe != -1 && open > pipe || close != -1 && open > close) return; |
|
|
var article=sel.substring(open+2, (pipe < 0) ? close : pipe); |
|
|
if (close > 0 && sel.substring(close+2).indexOf('[[') >= 0) return; |
|
|
var a=document.createElement('a'); |
|
|
a.href=pg.wiki.titlebase + article; |
|
|
mouseOverWikiLink2(a); |
|
|
//mouseOutWikiLink.apply(a, ); |
|
|
if (a.navpopup) { |
|
|
a.navpopup.addHook(function(){runStopPopupTimer(a.navpopup);}, 'unhide', 'after'); |
|
|
} |
|
|
} |
|
|
|
|
|
// ENDFILE: selpop.js |
|
|
// STARTFILE: navpopup.js |
|
|
/** |
|
|
@fileoverview Defines two classes: {@link Navpopup} and {@link Mousetracker}. |
|
|
|
|
|
<code>Navpopup</code> describes popups: when they appear, where, what |
|
|
they look like and so on. |
|
|
|
|
|
<code>Mousetracker</code> "captures" the mouse using |
|
|
<code>document.onmousemove</code>. |
|
|
*/ |
|
|
|
|
|
|
|
|
/** |
|
|
Creates a new Mousetracker. |
|
|
@constructor |
|
|
@class The Mousetracker class. This monitors mouse movements and manages associated hooks. |
|
|
*/ |
|
|
function Mousetracker() { |
|
|
/** |
|
|
Flag - are we switched on? |
|
|
@type Boolean |
|
|
*/ |
|
|
this.active=false; |
|
|
/** |
|
|
Array of hook functions. |
|
|
@private |
|
|
@type Array |
|
|
*/ |
|
|
this.hooks=; |
|
|
} |
|
|
|
|
|
/** |
|
|
Adds a hook, to be called when we get events. |
|
|
@param {Function} f A function which is called as |
|
|
<code>f(x,y)</code>. It should return <code>true</code> when it |
|
|
wants to be removed, and <code>false</code> otherwise. |
|
|
*/ |
|
|
Mousetracker.prototype.addHook = function (f) { |
|
|
this.hooks.push(f); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Runs hooks, passing them the x |
|
|
and y coords of the mouse. Hook functions that return true are |
|
|
passed to {@link Mousetracker#removeHooks} for removal. |
|
|
@private |
|
|
*/ |
|
|
Mousetracker.prototype.runHooks = function () { |
|
|
var remove=false; |
|
|
var removeObj={}; |
|
|
// this method gets called a LOT - |
|
|
// pre-cache some variables |
|
|
var x=this.x, y=this.y, len = this.hooks.length; |
|
|
|
|
|
for (var i=0; i<len; ++i) { |
|
|
//~ run the hook function, and remove it if it returns true |
|
|
if (this.hooks(x, y)===true) { |
|
|
remove=true; |
|
|
removeObj=true; |
|
|
} |
|
|
} |
|
|
if (remove) { this.removeHooks(removeObj); } |
|
|
}; |
|
|
|
|
|
/** |
|
|
Removes hooks. |
|
|
@private |
|
|
@param {Object} removeObj An object whose keys are the index |
|
|
numbers of functions for removal, with values that evaluate to true |
|
|
*/ |
|
|
Mousetracker.prototype.removeHooks = function(removeObj) { |
|
|
var newHooks=; |
|
|
var len = this.hooks.length; |
|
|
for (var i=0; i<len; ++i) { |
|
|
if (! removeObj) { newHooks.push(this.hooks); } |
|
|
} |
|
|
this.hooks=newHooks; |
|
|
}; |
|
|
|
|
|
|
|
|
/** |
|
|
Event handler for mouse wiggles. |
|
|
We simply grab the event, set x and y and run the hooks. |
|
|
This makes the cpu all hot and bothered :-( |
|
|
@private |
|
|
@param {Event} e Mousemove event |
|
|
*/ |
|
|
Mousetracker.prototype.track=function (e) { |
|
|
//log ('Mousetracker.track'); |
|
|
//~ Apparently this is needed in IE. |
|
|
e = e || window.event; |
|
|
var x, y; |
|
|
if (e) { |
|
|
if (e.pageX) { x=e.pageX; y=e.pageY; } |
|
|
else if (typeof e.clientX!='undefined') { |
|
|
var left, top, docElt = window.document.documentElement; |
|
|
|
|
|
if (docElt) { left=docElt.scrollLeft; } |
|
|
left = left || window.document.body.scrollLeft || window.document.scrollLeft || 0; |
|
|
|
|
|
if (docElt) { top=docElt.scrollTop; } |
|
|
top = top || window.document.body.scrollTop || window.document.scrollTop || 0; |
|
|
|
|
|
x=e.clientX + left; |
|
|
y=e.clientY + top; |
|
|
} else { return; } |
|
|
this.x = x; |
|
|
this.y = y; |
|
|
if (this.hooks.length === 0) { return; } |
|
|
if (typeof this.lastHook_x != 'number') { this.lastHook_x = -100; this.lastHook_y=-100; } |
|
|
var diff = (this.lastHook_x - x)*(this.lastHook_y - y); |
|
|
diff = (diff >= 0) ? diff : -diff; |
|
|
if ( diff > 1 ) { |
|
|
this.lastHook_x=x; |
|
|
this.lastHook_y=y; |
|
|
this.runHooks(); |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
/** |
|
|
Sets things in motion, unless they are already that is, registering an event handler on <code>document.onmousemove</code>. |
|
|
A half-hearted attempt is made to preserve the old event handler if there is one. |
|
|
*/ |
|
|
Mousetracker.prototype.enable = function () { |
|
|
if (this.active) { return; } |
|
|
this.active=true; |
|
|
//~ Save the current handler for mousemove events. This isn't too |
|
|
//~ robust, of course. |
|
|
this.savedHandler=document.onmousemove; |
|
|
//~ Gotta save @tt{this} again for the closure, and use apply for |
|
|
//~ the member function. |
|
|
var savedThis=this; |
|
|
document.onmousemove=function (e) {savedThis.track.apply(savedThis, );}; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Disables the tracker, removing the event handler. |
|
|
*/ |
|
|
Mousetracker.prototype.disable = function () { |
|
|
if (!this.active) { return; } |
|
|
if (typeof this.savedHandler=='function') { |
|
|
document.onmousemove=this.savedHandler; |
|
|
} else { delete document.onmousemove; } |
|
|
this.active=false; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Creates a new Navpopup. |
|
|
Gets a UID for the popup and |
|
|
@param init Contructor object. If <code>init.draggable</code> is true or absent, the popup becomes draggable. |
|
|
@constructor |
|
|
@class The Navpopup class. This generates popup hints, and does some management of them. |
|
|
*/ |
|
|
function Navpopup(init) { |
|
|
//alert('new Navpopup(init)'); |
|
|
/** UID for each Navpopup instance. |
|
|
Read-only. |
|
|
@type integer |
|
|
*/ |
|
|
this.uid=Navpopup.uid++; |
|
|
/** |
|
|
Read-only flag for current visibility of the popup. |
|
|
@type boolean |
|
|
@private |
|
|
*/ |
|
|
this.visible=false; |
|
|
/** Flag to be set when we want to cancel a previous request to |
|
|
show the popup in a little while. |
|
|
@private |
|
|
@type boolean |
|
|
*/ |
|
|
this.noshow=false; |
|
|
/** Categorised list of hooks. |
|
|
@see #runHooks |
|
|
@see #addHook |
|
|
@private |
|
|
@type Object |
|
|
*/ |
|
|
this.hooks={ |
|
|
'create': , |
|
|
'unhide': , |
|
|
'hide': |
|
|
}; |
|
|
/** List of downloads associated with the popup. |
|
|
@private |
|
|
@type Array |
|
|
*/ |
|
|
this.downloads=; |
|
|
/** Number of uncompleted downloads. |
|
|
@type integer |
|
|
*/ |
|
|
this.pending=null; |
|
|
/** Tolerance in pixels when detecting whether the mouse has left the popup. |
|
|
@type integer |
|
|
*/ |
|
|
this.fuzz=5; |
|
|
/** Flag to toggle running {@link #limitHorizontalPosition} to regulate the popup's position. |
|
|
@type boolean |
|
|
*/ |
|
|
this.constrained=true; |
|
|
/** The popup width in pixels. |
|
|
@private |
|
|
@type integer |
|
|
*/ |
|
|
this.width=0; |
|
|
/** The popup width in pixels. |
|
|
@private |
|
|
@type integer |
|
|
*/ |
|
|
this.height=0; |
|
|
/** The main content DIV element. |
|
|
@type HTMLDivElement |
|
|
*/ |
|
|
this.mainDiv=null; |
|
|
this.createMainDiv(); |
|
|
|
|
|
if (!init || typeof init.draggable=='undefined' || init.draggable) { |
|
|
this.makeDraggable(); |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
A UID for each Navpopup. This constructor property is just a counter. |
|
|
@type integer |
|
|
@private |
|
|
*/ |
|
|
Navpopup.uid=0; |
|
|
|
|
|
/** |
|
|
Retrieves the {@link #visible} attribute, indicating whether the popup is currently visible. |
|
|
@type boolean |
|
|
*/ |
|
|
Navpopup.prototype.isVisible=function() { |
|
|
return this.visible; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Repositions popup using CSS style. |
|
|
@private |
|
|
@param {integer} x x-coordinate (px) |
|
|
@param {integer} y y-coordinate (px) |
|
|
@param {boolean} noLimitHor Don't call {@link #limitHorizontalPosition} |
|
|
*/ |
|
|
Navpopup.prototype.reposition= function (x,y, noLimitHor) { |
|
|
log ('reposition('+x+','+y+','+noLimitHor+')'); |
|
|
if (typeof x != 'undefined' && x!==null) { this.left=x; } |
|
|
if (typeof y != 'undefined' && y!==null) { this.top=y; } |
|
|
if (typeof this.left != 'undefined' && typeof this.top != 'undefined') { |
|
|
this.mainDiv.style.left=this.left + 'px'; |
|
|
this.mainDiv.style.top=this.top + 'px'; |
|
|
} |
|
|
if (!noLimitHor) { this.limitHorizontalPosition(); } |
|
|
}; |
|
|
|
|
|
/** |
|
|
Prevents popups from being in silly locations. Hopefully. |
|
|
Should not be run if {@link #constrained} is true. |
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.limitHorizontalPosition=function() { |
|
|
if (!this.constrained || this.tooWide) { return; } |
|
|
//var x=this.left; //findPosX(this.mainDiv); |
|
|
var x=parseInt(this.mainDiv.style.left, 10); |
|
|
var w=parseInt(this.mainDiv.offsetWidth, 10); |
|
|
var cWidth=document.body.clientWidth; |
|
|
log('limitHorizontalPosition: x='+x+ |
|
|
', this.left=' + this.left + |
|
|
', this.width=' + this.width + |
|
|
', cWidth=' + cWidth); |
|
|
if ( (x+w) >= cWidth || |
|
|
( x > 0 && this.maxWidth && this.width < this.maxWidth && this.height > this.width |
|
|
&& x > cWidth - this.maxWidth ) ) { |
|
|
// This is a very nasty hack. There has to be a better way! |
|
|
// We find the "natural" width of the div by positioning it at the far left |
|
|
// then reset it so that it should be flush right (well, nearly) |
|
|
this.mainDiv.style.left='-10000px'; |
|
|
this.mainDiv.style.width = this.maxWidth + 'px'; |
|
|
var naturalWidth=parseInt(this.mainDiv.offsetWidth, 10); |
|
|
var newLeft=cWidth - naturalWidth - 1; |
|
|
if (newLeft < 0) { newLeft = 0; this.tooWide=true; } // still unstable for really wide popups? |
|
|
log ('limitHorizontalPosition: moving to ('+newLeft + ','+ this.top+');' + |
|
|
' naturalWidth=' + naturalWidth + ', clientWidth=' + cWidth); |
|
|
this.reposition(newLeft, null, true); |
|
|
} |
|
|
}; |
|
|
|
|
|
/** |
|
|
Counter indicating the z-order of the "highest" popup. |
|
|
We start the z-index at 1000 so that popups are above everything |
|
|
else on the screen. |
|
|
@private |
|
|
@type integer |
|
|
*/ |
|
|
Navpopup.highest=1000; |
|
|
|
|
|
/** |
|
|
Brings popup to the top of the z-order. |
|
|
We increment the {@link #highest} property of the contructor here. |
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.raise = function () { |
|
|
this.mainDiv.style.zIndex=Navpopup.highest + 1; |
|
|
++Navpopup.highest; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Shows the popup provided {@link #noshow} is not true. |
|
|
Updates the position, brings the popup to the top of the z-order and unhides it. |
|
|
*/ |
|
|
Navpopup.prototype.show = function () { |
|
|
//document.title+='s'; |
|
|
if (this.noshow) { return; } |
|
|
//document.title+='t'; |
|
|
this.reposition(); |
|
|
this.raise(); |
|
|
this.unhide(); |
|
|
}; |
|
|
|
|
|
|
|
|
/** |
|
|
Runs the {@link #show} method in a little while, unless we're |
|
|
already visible. |
|
|
@param {integer} time Delay in milliseconds |
|
|
@see #showSoonIfStable |
|
|
*/ |
|
|
Navpopup.prototype.showSoon = function (time) { |
|
|
if (this.visible) { return; } |
|
|
this.noshow=false; |
|
|
//~ We have to save the value of @tt{this} so that the closure below |
|
|
//~ works. |
|
|
var savedThis=this; |
|
|
//this.start_x = Navpopup.tracker.x; |
|
|
//this.start_y = Navpopup.tracker.y; |
|
|
pop.runOnce(function () { |
|
|
if (Navpopup.tracker.active) { |
|
|
savedThis.reposition.apply(savedThis, ); |
|
|
} |
|
|
//~ Have to use apply to invoke his member function here |
|
|
savedThis.show.apply(savedThis, ); |
|
|
}, time); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Checks to see if the mouse pointer has |
|
|
stabilised (checking every <code>time</code>/2 milliseconds) and runs the |
|
|
{@link #show} method if it has. This method makes {@link #showSoon} redundant. |
|
|
@param {integer} time The minimum time (ms) before the popup may be shown. |
|
|
*/ |
|
|
Navpopup.prototype.showSoonIfStable = function (time) { |
|
|
log ('showSoonIfStable, time='+time); |
|
|
if (this.visible) { return; } |
|
|
this.noshow = false; |
|
|
|
|
|
//~ initialize these variables so that we never run @tt{show} after |
|
|
//~ just half the time |
|
|
this.stable_x = -10000; this.stable_y = -10000; |
|
|
|
|
|
var stableShow = function() { |
|
|
log('stableShow called'); |
|
|
var new_x = Navpopup.tracker.x, new_y = Navpopup.tracker.y; |
|
|
var dx = savedThis.stable_x - new_x, dy = savedThis.stable_y - new_y; |
|
|
var fuzz2 = 0; // savedThis.fuzz * savedThis.fuzz; |
|
|
//document.title += '.join(',') + '] '; |
|
|
if ( dx * dx <= fuzz2 && dy * dy <= fuzz2 ) { |
|
|
log ('mouse is stable'); |
|
|
clearInterval(savedThis.showSoonStableTimer); |
|
|
savedThis.reposition.apply(savedThis, ); |
|
|
savedThis.show.apply(savedThis, ); |
|
|
return; |
|
|
} |
|
|
savedThis.stable_x = new_x; savedThis.stable_y = new_y; |
|
|
}; |
|
|
var savedThis = this; |
|
|
this.showSoonStableTimer = setInterval(stableShow, time/2); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Makes the popup unhidable until we call {@link #unstick}. |
|
|
*/ |
|
|
Navpopup.prototype.stick=function() { |
|
|
this.noshow=false; |
|
|
this.sticky=true; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Allows the popup to be hidden. |
|
|
*/ |
|
|
Navpopup.prototype.unstick=function() { |
|
|
this.sticky=false; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Sets the {@link #noshow} flag and hides the popup. This should be called |
|
|
when the mouse leaves the link before |
|
|
(or after) it's actually been displayed. |
|
|
*/ |
|
|
Navpopup.prototype.banish = function () { |
|
|
log ('banish called'); |
|
|
// hide and prevent showing with showSoon in the future |
|
|
this.noshow=true; |
|
|
if (this.showSoonStableTimer) { |
|
|
log('clearing showSoonStableTimer'); |
|
|
clearInterval(this.showSoonStableTimer); |
|
|
} |
|
|
this.hide(); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Runs hooks added with {@link #addHook}. |
|
|
@private |
|
|
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide' |
|
|
@param {String} when Controls exactly when the hook is run: either 'before' or 'after' |
|
|
*/ |
|
|
Navpopup.prototype.runHooks = function (key, when) { |
|
|
if (!this.hooks) { return; } |
|
|
var keyHooks=this.hooks; |
|
|
var len=keyHooks.length; |
|
|
for (var i=0; i< len; ++i) { |
|
|
if (keyHooks.when == when) { keyHooks.hook.apply(this, ); } |
|
|
} |
|
|
}; |
|
|
|
|
|
/** |
|
|
Adds a hook to the popup. Hook functions are run with <code>this</code> set to refer to the Navpopup instance, and no arguments. |
|
|
@param {Function} hook The hook function. |
|
|
@param {String} key Key name of the {@link #hooks} array - one of 'create', 'unhide', 'hide' |
|
|
@param {String} when Controls exactly when the hook is run: either 'before' or 'after' |
|
|
*/ |
|
|
Navpopup.prototype.addHook = function ( hook, key, when ) { |
|
|
when = when || 'after'; |
|
|
if (!this.hooks) { return; } |
|
|
this.hooks.push( {hook: hook, when: when} ); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Creates the main DIV element, which contains all the actual popup content. |
|
|
Runs hooks with key 'create'. |
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.createMainDiv = function () { |
|
|
if (this.mainDiv) { return; } |
|
|
this.runHooks('create', 'before'); |
|
|
var mainDiv=document.createElement('div'); |
|
|
|
|
|
var savedThis=this; |
|
|
mainDiv.onclick=function(e) {savedThis.onclickHandler(e);}; |
|
|
mainDiv.className=(this.className) ? this.className : 'navpopup_maindiv'; |
|
|
mainDiv.id=mainDiv.className + this.uid; |
|
|
|
|
|
mainDiv.style.position='absolute'; |
|
|
mainDiv.style.display='none'; |
|
|
mainDiv.className='navpopup'; |
|
|
|
|
|
// easy access to javascript object through DOM functions |
|
|
mainDiv.navpopup=this; |
|
|
|
|
|
this.mainDiv=mainDiv; |
|
|
document.body.appendChild(mainDiv); |
|
|
this.runHooks('create', 'after'); |
|
|
}; |
|
|
/** |
|
|
Calls the {@link #raise} method. |
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.onclickHandler=function(e) { |
|
|
this.raise(); |
|
|
}; |
|
|
/** |
|
|
Makes the popup draggable, using a {@link Drag} object. |
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.makeDraggable=function(e) { |
|
|
if (!this.mainDiv) { this.createMainDiv(); } |
|
|
var drag=new Drag(); |
|
|
drag.startCondition=function(e) { |
|
|
try { if (!e.shiftKey) { return false; } } catch (err) { return false; } |
|
|
return true; |
|
|
}; |
|
|
var np=this; |
|
|
drag.endHook=function(x,y) { np.reposition(x,y); }; |
|
|
drag.init(this.mainDiv); |
|
|
}; |
|
|
|
|
|
/** Hides the popup using CSS. Runs hooks with key 'hide'. |
|
|
Sets {@link #visible} appropriately. {@link #banish} should be called externally instead of this method. |
|
|
|
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.hide = function () { |
|
|
this.runHooks('hide', 'before'); |
|
|
this.abortDownloads(); |
|
|
if (this.sticky) { return; } |
|
|
if (typeof this.visible != 'undefined' && this.visible) { |
|
|
this.mainDiv.style.display='none'; |
|
|
this.visible=false; |
|
|
} |
|
|
this.runHooks('hide', 'after'); |
|
|
}; |
|
|
|
|
|
/** Shows the popup using CSS. Runs hooks with key 'unhide'. |
|
|
Sets {@link #visible} appropriately. {@link #show} should be called externally instead of this method. |
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.unhide = function () { |
|
|
this.runHooks('unhide', 'before'); |
|
|
if (typeof this.visible != 'undefined' && !this.visible) { |
|
|
this.mainDiv.style.display='inline'; |
|
|
this.visible=true; |
|
|
} |
|
|
this.runHooks('unhide', 'after'); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Sets the <code>innerHTML</code> attribute of the main div containing the popup content. |
|
|
@param {String} html The HTML to set. |
|
|
*/ |
|
|
Navpopup.prototype.setInnerHTML = function (html) { |
|
|
this.mainDiv.innerHTML = html; |
|
|
}; |
|
|
|
|
|
/** |
|
|
Updates the {@link #width} and {@link #height} attributes with the CSS properties. |
|
|
@private |
|
|
*/ |
|
|
Navpopup.prototype.updateDimensions = function () { |
|
|
this.width=parseInt(this.mainDiv.offsetWidth, 10); |
|
|
this.height=parseInt(this.mainDiv.offsetHeight, 10); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Checks if the point (x,y) is within {@link #fuzz} of the |
|
|
{@link #mainDiv}. |
|
|
@param {integer} x x-coordinate (px) |
|
|
@param {integer} y y-coordinate (px) |
|
|
@type boolean |
|
|
*/ |
|
|
Navpopup.prototype.isWithin = function(x,y) { |
|
|
//~ If we're not even visible, no point should be considered as |
|
|
//~ being within the popup. |
|
|
if (!this.visible) { return false; } |
|
|
this.updateDimensions(); |
|
|
var fuzz=this.fuzz || 0; |
|
|
// document.title=''+fuzz+';'+x+';'+y+'|'+this.left+','+(this.left+this.width)+';'+this.top+','+(this.top+this.height); |
|
|
//~ Use a simple box metric here. |
|
|
return (x+fuzz >= this.left && x-fuzz <= this.left + this.width && |
|
|
y+fuzz >= this.top && y-fuzz <= this.top + this.height); |
|
|
}; |
|
|
|
|
|
/** |
|
|
Adds a download to {@link #downloads}. |
|
|
@param {Downloader} download |
|
|
*/ |
|
|
Navpopup.prototype.addDownload=function(download) { |
|
|
if (!download) { return; } |
|
|
this.downloads.push(download); |
|
|
}; |
|
|
/** |
|
|
Aborts the downloads listed in {@link #downloads}. |
|
|
@see Downloader#abort |
|
|
*/ |
|
|
Navpopup.prototype.abortDownloads=function() { |
|
|
for(var i=0; i<this.downloads.length; ++i) { |
|
|
var d=this.downloads; |
|
|
if (d && d.abort) { d.abort(); } |
|
|
} |
|
|
this.downloads=; |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
A {@link Mousetracker} instance which is a property of the constructor (pseudo-global). |
|
|
*/ |
|
|
Navpopup.tracker=new Mousetracker(); |
|
|
|
|
|
// ENDFILE: navpopup.js |
|
|
// STARTFILE: diff.js |
|
|
/* |
|
|
* Javascript Diff Algorithm |
|
|
* By John Resig (http://ejohn.org/) and ] |
|
|
* |
|
|
* More Info: |
|
|
* http://ejohn.org/projects/javascript-diff-algorithm/ |
|
|
*/ |
|
|
|
|
|
function diffEscape(n) { |
|
|
return n.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(RegExp('"','g'), """); |
|
|
} |
|
|
function delFmt(x) { |
|
|
if (!x.length) { return ''; } |
|
|
return "<del style='background:#FFE6E6;'>" + /*diffEscape*/(x.join('')).split('\n').join('¶\n') +"</del>"; |
|
|
} |
|
|
function insFmt(x) { |
|
|
if (!x.length) { return ''; } |
|
|
return "<ins style='background:#AAFFEE;'>" + /*diffEscape*/(x.join('')).split('\n').join('¶\n') +"</ins>"; |
|
|
} |
|
|
|
|
|
function copyDiffObj(x){ |
|
|
var ret=; |
|
|
for (var i=0; i<x.length; ++i) { |
|
|
if (typeof x == 'string') { ret=x; } |
|
|
else { |
|
|
ret={}; |
|
|
for (var prop in x) { ret=x; } |
|
|
} |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function countCrossings(a, b, i, eject) { |
|
|
// count the crossings on the edge starting at b |
|
|
if (b.row==null) { return -1; } |
|
|
var count=0; |
|
|
for (var j=0; j<a.length; ++j) { |
|
|
if (!a.row || a.row === 0) { continue; } |
|
|
if ( (j-b.row)*(i-a.row) > 0) { |
|
|
if(eject) { return true; } |
|
|
count++; |
|
|
} |
|
|
} |
|
|
return count; |
|
|
} |
|
|
|
|
|
//de=document.createElement('div'); |
|
|
//de.id='debug' |
|
|
//ti=document.getElementsByTagName('h1'); |
|
|
//ti.parentNode.appendChild(de); |
|
|
|
|
|
//function debug(s){ |
|
|
// try {document.getElementById('debug').innerHTML+=s+'<br>'; } catch(foo) {}; |
|
|
//} |
|
|
|
|
|
function untangle( a, b) { |
|
|
// try to remove crossing edges from an ordered bipartite graph, |
|
|
// removing the least number possible |
|
|
var aa=copyDiffObj(a); |
|
|
var bb=copyDiffObj(b); |
|
|
|
|
|
// remove the edge with the largest number of crossings until no |
|
|
// crossings remain |
|
|
do { |
|
|
var maxCrossings=0; |
|
|
var worstEdge=null; |
|
|
for (var i=0; i<bb.length; ++i) { |
|
|
var c=countCrossings(aa,bb,i); |
|
|
if (c > maxCrossings) { maxCrossings=c; worstEdge=i; } |
|
|
} |
|
|
if (worstEdge!=null) { |
|
|
aa.row ] = aa.row ].text; |
|
|
bb = bb.text; |
|
|
} |
|
|
} while (maxCrossings > 0); |
|
|
return { a: aa, b: bb }; |
|
|
} |
|
|
|
|
|
function max(a,b){return a<b ? b : a;} |
|
|
function min(a,b){return a>b ? b : a;} |
|
|
|
|
|
function shortenDiffString(str, context) { |
|
|
var re=RegExp('(<del*?</del>|<ins*?</ins>)'); |
|
|
var splitted=str.parenSplit(re); |
|
|
var ret=; |
|
|
for (var i=0; i<splitted.length; i+=2) { |
|
|
if (splitted.length < 2*context) { |
|
|
ret += splitted; |
|
|
if (i+1<splitted.length) { ret += splitted; } |
|
|
continue; |
|
|
} |
|
|
else { |
|
|
if (i > 0) { ret += splitted.substring(0,context); } |
|
|
if (i+1 < splitted.length) { ret.push(splitted.substring(splitted.length-context) + splitted); } |
|
|
} |
|
|
} |
|
|
while (ret.length > 0 && !ret) { |
|
|
ret = ret.slice(1); |
|
|
} |
|
|
return ret; |
|
|
} |
|
|
|
|
|
|
|
|
function diffString( o, n, simpleSplit, slow ) { |
|
|
var splitRe=RegExp('({2}|]{2}|{2,3}|{2,3}||=|+|\\b)'); |
|
|
|
|
|
o=diffEscape(o); n=diffEscape(n); |
|
|
var out, i; |
|
|
if (simpleSplit) { out = diff( o.split(/\b/), n.split(/\b/) ); } |
|
|
else out = diff( o.parenSplit(splitRe), n.parenSplit(splitRe) ); |
|
|
var str = ""; |
|
|
var acc=; // accumulator for prettier output |
|
|
|
|
|
// crossing pairings -- 'A B' vs 'B A' -- cause problems, so let's iron them out |
|
|
if (slow) { |
|
|
var untangled=untangle(out.o, out.n); // <-- too slow! |
|
|
out.o=untangled.a; out.n=untangled.b; |
|
|
} else { |
|
|
var maxOutputPair=0; |
|
|
for (i=0; i<out.n.length; ++i) { |
|
|
if ( out.n.row != null) { |
|
|
if( maxOutputPair > out.n.row ) { |
|
|
// tangle - delete pairing |
|
|
out.o.row ]=out.o.row ].text; |
|
|
out.n=out.n.text; |
|
|
} |
|
|
if (maxOutputPair < out.n.row) { maxOutputPair = out.n.row; } |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
// output the stuff preceding the first paired old line |
|
|
for (i=0; i<out.o.length && out.o.text == null; ++i) acc.push( out.o ); |
|
|
str += delFmt(acc); acc=; |
|
|
|
|
|
// main loop |
|
|
for ( i = 0; i < out.n.length; ++i ) { |
|
|
// output unpaired new "lines" |
|
|
while ( i < out.n.length && out.n.text == null ) { acc.push( out.n ); } |
|
|
str += insFmt(acc); acc=; |
|
|
if ( i < out.n.length ) { // this new "line" is paired with the (out.n.row)th old "line" |
|
|
str += out.n.text; |
|
|
// output unpaired old rows starting after this new line's partner |
|
|
var m = out.n.row + 1; |
|
|
while ( m < out.o.length && out.o.text == null ) { acc.push ( out.o ); } |
|
|
str += delFmt(acc); acc=; |
|
|
} |
|
|
} |
|
|
return str; |
|
|
} |
|
|
|
|
|
window.jsReservedProperties=RegExp('^((un)?watch|toString|eval)$'); |
|
|
window.diffBugAlerts=''; |
|
|
|
|
|
|
|
|
function diffBugAlert(word) { |
|
|
if (diffBugAlerts.indexOf(word+'\n')==-1) { |
|
|
diffBugAlerts+=word+'\n'; |
|
|
alert('Bad word: '+word+'\n\nPlease report this bug.'); |
|
|
} |
|
|
} |
|
|
|
|
|
function diff( o, n ) { |
|
|
var ns = {}; |
|
|
var os = {}; |
|
|
var i; |
|
|
|
|
|
// pass 1: make hashtable ns with new rows as keys |
|
|
for ( i = 0; i < n.length; i++ ) { |
|
|
if ( jsReservedProperties.test(n) ) { n += '<!-- -->'; } |
|
|
if ( ns ] == null ) { |
|
|
ns ] = { rows: , o: null }; |
|
|
} |
|
|
try { ns ].rows.push( i ); } catch (err) { diffBugAlert(n); } |
|
|
} |
|
|
|
|
|
// pass 2: make hashtable os with old rows as keys |
|
|
for ( i = 0; i < o.length; i++ ) { |
|
|
if ( jsReservedProperties.test(o) ) { o += '<!-- -->'; } |
|
|
if ( os ] == null ) { |
|
|
os ] = { rows: , n: null }; |
|
|
} |
|
|
try {os ].rows.push( i ); } catch (err) { diffBugAlert(n); } |
|
|
} |
|
|
|
|
|
// pass 3: pair unique new rows and matching unique old rows |
|
|
for ( i in ns ) { |
|
|
if ( ns.rows.length == 1 && typeof(os) != "undefined" && os.rows.length == 1 ) { |
|
|
n.rows ] = { text: n.rows ], row: os.rows }; |
|
|
o.rows ] = { text: o.rows ], row: ns.rows }; |
|
|
} |
|
|
} |
|
|
|
|
|
// pass 4: pair matching rows immediately following paired rows (not necessarily unique) |
|
|
for ( i = 0; i < n.length - 1; i++ ) { |
|
|
if ( n.text != null && n.text == null && |
|
|
n.row < o.length - 1 && o.row + 1 ].text == null && |
|
|
n == o.row + 1 ] ) { |
|
|
n = { text: n, row: n.row + 1 }; |
|
|
o.row+1] = { text: o.row+1], row: i + 1 }; |
|
|
} |
|
|
} |
|
|
|
|
|
// pass 5: pair matching rows immediately preceding paired rows (not necessarily unique) |
|
|
for ( i = n.length - 1; i > 0; i-- ) { |
|
|
if ( n.text != null && n.text == null && |
|
|
n.row > 0 && o.row - 1 ].text == null && |
|
|
n == o.row - 1 ] ) { |
|
|
n = { text: n, row: n.row - 1 }; |
|
|
o.row-1] = { text: o.row-1], row: i - 1 }; |
|
|
} |
|
|
} |
|
|
|
|
|
return { o: o, n: n }; |
|
|
} |
|
|
|
|
|
// ENDFILE: diff.js |
|
|
// STARTFILE: init.js |
|
|
function setSiteInfo() { |
|
|
pg.wiki.hostname = location.hostname; // use in preference to location.hostname for flexibility (?) |
|
|
pg.wiki.lang = pg.wiki.hostname.split('.'); |
|
|
pg.wiki.wikimedia=RegExp('wiki(edia|source|books|news|quote)\\.org|wiktionary\\.org').test(pg.wiki.hostname); |
|
|
pg.wiki.wikicites=RegExp('wikicitiescom$', 'i').test(pg.wiki.hostname); |
|
|
pg.wiki.isLocal=RegExp('^localhost').test(pg.wiki.hostname); |
|
|
pg.wiki.commons='commons.wikimedia.org'; |
|
|
} |
|
|
|
|
|
function setTitleBase() { |
|
|
pg.wiki.articlePath='/'; |
|
|
pg.wiki.botInterfacePath = '/wiki'; |
|
|
if (pg.wiki.wikimedia) { pg.wiki.botInterfacePath = '/w' } |
|
|
else if (pg.wiki.wikicities) { pg.wiki.botInterfacePath = ''; } |
|
|
titletail = pg.wiki.botInterfacePath + '/index.php?title='; |
|
|
// other sites may need to add code here to set titletail depending on how their urls work |
|
|
|
|
|
pg.wiki.titlebase = location.protocol + '//' + pg.wiki.hostname + titletail; |
|
|
pg.wiki.wikibase = location.protocol + '//' + pg.wiki.hostname + pg.wiki.botInterfacePath; |
|
|
} |
|
|
|
|
|
|
|
|
////////////////////////////////////////////////// |
|
|
// Global regexps |
|
|
|
|
|
function setMainRegex() { |
|
|
var reStart='*://'; |
|
|
var preTitles='wiki/|w/index\\.php\\?title='; |
|
|
|
|
|
if (!pg.wiki.wikimedia) { |
|
|
preTitles = 'wiki/index\\.php\\?title=|wiki/index\\.php/|' + preTitles + '|index\\.php\\?title=' ; |
|
|
} |
|
|
var reEnd='/(' + preTitles + ')(*)'; |
|
|
pg.re.main = RegExp(reStart + pg.wiki.hostname.split('.').join('\\.') + reEnd); |
|
|
} |
|
|
|
|
|
function setRegexps() { |
|
|
setMainRegex(); |
|
|
pg.re.stub= RegExp('stub|This .*-related article is a .*stub', 'im') ; |
|
|
pg.re.disambig=RegExp('(\\s*disambig|disambig\\s*|disamb\\s*|dab\\s*)' + |
|
|
'|\\s*(geo|hn)dis\\s*^+' + |
|
|
'|\\s*\\s*' + // explicit, whole template names on this line |
|
|
'|is a .*disambiguation.*page', 'im') ; |
|
|
|
|
|
pg.re.urlNoPopup=RegExp('((title=|/)' + pg.ns.special + ':|section=)') ; |
|
|
pg.re.contribs =RegExp('(title=|/)' + pg.ns.special + ':Contributions' + '(&target=|/|/' + pg.ns.user+':)(.*)') ; |
|
|
pg.re.email =RegExp('(title=|/)' + pg.ns.special + ':Emailuser' + '(&target=|/|/' + pg.ns.user+':)(.*)') ; |
|
|
|
|
|
// note: tries to get images in infobox templates too, e.g. movie pages, album pages etc |
|
|
// (^|\]* ]) * |
|
|
// (^|\]* ])(]*(+) *px)? |
|
|
// $4 = 120 as in 120px |
|
|
pg.re.image = RegExp('(^|\\]* ])(]*(+) *px)?|\\n *? *(image|cover) *= *(*?) *? *\\n', 'img') ; |
|
|
pg.re.imageBracketCount = 6; |
|
|
|
|
|
pg.re.category = RegExp('\\]* ]) *', 'i'); |
|
|
pg.re.categoryBracketCount = 1; |
|
|
|
|
|
pg.re.ipUser=RegExp('('+pg.ns.user+':)?' + '((25|2|1||)\\.){3}' + |
|
|
'(25|2|1||)'); |
|
|
|
|
|
// FIXME replace with general parameter parsing function, this is daft |
|
|
pg.re.oldid=RegExp('oldid=(*)'); |
|
|
pg.re.diff=RegExp('diff=(*)'); |
|
|
} |
|
|
|
|
|
|
|
|
////////////////////////////////////////////////// |
|
|
// Image sources |
|
|
|
|
|
function setImageSources() { |
|
|
pg.wiki.imageSources=; |
|
|
|
|
|
// frequently seen thumbs |
|
|
pg.wiki.imageSources.push( |
|
|
{wiki: pg.wiki.hostname, thumb: true, width: 180}, // default |
|
|
{wiki: pg.wiki.hostname, thumb: true, width: 120} // gallery |
|
|
); |
|
|
|
|
|
// frequently seen thumbs on commons |
|
|
if (pg.wiki.wikimedia && pg.wiki.hostname!=pg.wiki.commons) { |
|
|
pg.wiki.imageSources.push( |
|
|
{wiki: pg.wiki.commons, thumb: true, width: 180}, |
|
|
{wiki: pg.wiki.commons, thumb: true, width: 120} |
|
|
); |
|
|
} |
|
|
|
|
|
// unusual thumb sizes and full-size |
|
|
pg.wiki.imageSources.push( |
|
|
{wiki: pg.wiki.hostname, thumb: true, width: 200}, // common? |
|
|
{wiki: pg.wiki.hostname, thumb: true, width: 250}, // common? |
|
|
{wiki: pg.wiki.hostname, thumb: true, width: 300}, |
|
|
{wiki: pg.wiki.hostname, thumb: true, width: 210}, |
|
|
{wiki: pg.wiki.hostname, thumb: true, width: 230}, |
|
|
{wiki: pg.wiki.hostname, thumb: false, width: 0} // no comma |
|
|
); |
|
|
|
|
|
// full-size on commons |
|
|
if (pg.wiki.wikimedia && pg.wiki.hostname!=pg.wiki.commons) { |
|
|
pg.wiki.imageSources.push({wiki: pg.wiki.commons, thumb: false, width: 0}); |
|
|
} |
|
|
} |
|
|
|
|
|
////////////////////////////////////////////////// |
|
|
// miscellany |
|
|
|
|
|
function setMisc() { |
|
|
pg.current.link=null; |
|
|
|
|
|
// downloading images are put here |
|
|
pg.misc.imageArray=; |
|
|
|
|
|
// page caching |
|
|
pg.cache.pages = ; |
|
|
pg.cache.images = ; |
|
|
|
|
|
// FIXME what is this for? |
|
|
pg.misc.gImage=null; // global for image |
|
|
|
|
|
// check to see if images are done with this timer |
|
|
pg.timer.image=null; |
|
|
|
|
|
// These are for checkImages() |
|
|
pg.counter.checkImages=0; |
|
|
pg.timer.checkImages=null; |
|
|
pg.timer.checkPopupPosition=null; |
|
|
pg.counter.loop=0; |
|
|
|
|
|
// ids change with each popup: popupImage0, popupImage1 etc |
|
|
pg.idNumber=0; |
|
|
|
|
|
// for myDecodeURI |
|
|
pg.misc.decodeExtras = [ |
|
|
{from: '%2C', to: ',' }, |
|
|
{from: '_', to: ' ' }, |
|
|
{from: '%24', to: '$'}, |
|
|
{from: '%26', to: '&' } // no , |
|
|
]; |
|
|
|
|
|
// for setPopupHTML - needed for timers and stuff |
|
|
pg.timer.popupHTML=; |
|
|
pg.misc.popupHTMLLoopFunctions = ; |
|
|
|
|
|
// FIXME - eliminate this |
|
|
pg.counter.redir=0; |
|
|
} |
|
|
|
|
|
function setBrowserHacks() { |
|
|
// browser-specific hacks |
|
|
if (typeof window.opera != 'undefined') { |
|
|
setDefault('popupStructure','original'); |
|
|
} else if (navigator.appName=='Konqueror') { |
|
|
setDefault('popupNavLinkSeparator', ' • '); |
|
|
pg.flag.isKonq=true; |
|
|
} else if ( navigator.vendor && navigator.vendor.toLowerCase().indexOf('apple computer')===0) { |
|
|
pg.flag.isSafari=true; |
|
|
} else if (navigator.appName.indexOf("Microsoft")!=-1) { |
|
|
setDefault('popupNavLinkSeparator', ' · '); |
|
|
setDefault('popupStructure','original'); |
|
|
pg.flag.isIE=true; |
|
|
} |
|
|
if (pg.flag.isIE || pg.flag.isKonq || pg.flag.isSafari) { |
|
|
pg.flag.linksLikeIE=true; |
|
|
} |
|
|
} |
|
|
|
|
|
function setupPopups() { |
|
|
// NB translatable strings should be set up first (strings.js) |
|
|
|
|
|
// basics |
|
|
setSiteInfo(); |
|
|
setTitleBase(); |
|
|
|
|
|
// namespaces etc |
|
|
setNamespaces(); |
|
|
setInterwiki(); |
|
|
|
|
|
// regexps |
|
|
setRegexps(); |
|
|
setRedirs(); |
|
|
|
|
|
// other stuff |
|
|
setImageSources(); |
|
|
setOptions(); // see options.js |
|
|
setBrowserHacks(); |
|
|
setMisc(); |
|
|
setupDebugging(); |
|
|
setupLivePreview(); |
|
|
|
|
|
// main deal here |
|
|
setupTooltips(); |
|
|
Navpopup.tracker.enable(); |
|
|
|
|
|
} |
|
|
|
|
|
// ENDFILE: init.js |
|
|
// STARTFILE: navlinks.js |
|
|
////////////////////////////////////////////////// |
|
|
// navlinks... let the fun begin |
|
|
// |
|
|
|
|
|
function defaultNavlinkSpec() { |
|
|
var str=''; |
|
|
str += '<b><<mainlink|shortcut= >></b>'; |
|
|
if (getValueOf('popupLastEditLink')) str += '*<<lastEdit|shortcut=/>>|<<lastContrib>>|<<sinceMe>>if(oldid){|<<oldEdit>>|<<diffCur>>}'; |
|
|
str+='<<imagestatus>>'; |
|
|
|
|
|
// user links |
|
|
// contribs - log - count - email - block |
|
|
// count only if applicable; block only if popupAdminLinks |
|
|
str += 'if(user){<br><<contribs|shortcut=c>>*<<userlog|shortcut=L|log>>'; |
|
|
str+='if(ipuser){*<<arin>>}if(wikimedia){*<<count|shortcut=#>>}'; |
|
|
str+='if(ipuser){}else{*<<email|shortcut=E>>}if(admin){*<<block|shortcut=b>>}}'; |
|
|
|
|
|
// editing links |
|
|
// talkpage -> edit|new - history - un|watch - article|edit |
|
|
// other page -> edit - history - un|watch - talk|edit|new |
|
|
var editstr='<<edit|shortcut=e>>'; |
|
|
var editOldidStr='if(oldid){<<editOld|shortcut=e>>|<<revert|shortcut=v|rv>>|<<edit|cur>>}else{' + editstr + '}' |
|
|
var historystr='<<history|shortcut=h>>if(mainspace){|<<editors|shortcut=E|>>}'; |
|
|
var watchstr='<<unwatch|unwatchShort>>|<<watch|shortcut=w|watchThingy>>'; |
|
|
|
|
|
str+='<br>if(talk){' + |
|
|
editOldidStr+'|<<new|shortcut=+>>' + '*' + historystr+'*'+watchstr + '*' + '<b><<article|shortcut=a>></b>|<<editArticle|edit>>' |
|
|
+ '}else{' // not a talk page |
|
|
+ editOldidStr + '*' + historystr + '*' + watchstr + '*' + '<b><<talk|shortcut=t>></b>|<<editTalk|edit>>|<<newTalk|shortcut=+|new>>' |
|
|
+ '}'; |
|
|
|
|
|
// misc links |
|
|
str += '<br><<whatLinksHere|shortcut=l>>*<<relatedChanges|shortcut=r>>*<<move|shortcut=m>>'; |
|
|
|
|
|
// admin links |
|
|
str += 'if(admin){<br><<unprotect|unprotectShort>>|<<protect|shortcut=p>>*' + '<<undelete|undeleteShort>>|<<delete|shortcut=d>>}'; |
|
|
return str; |
|
|
}; |
|
|
|
|
|
function navLinksHTML (article, hint, oldid) { |
|
|
var str = '<span class="popupNavLinks">'; |
|
|
var style=getValueOf('popupNavLinkStyle'); |
|
|
switch (style) { |
|
|
case 'default': |
|
|
str += defaultNavlinkSpec(); |
|
|
break; |
|
|
default: |
|
|
if (typeof style == 'function') str += style(); |
|
|
else str+=String(style); |
|
|
} |
|
|
str += '</span>'; |
|
|
|
|
|
// BAM |
|
|
return navlinkStringToHTML(str, article, oldid); |
|
|
}; |
|
|
|
|
|
function expandConditionalNavlinkString(s,article,oldid,recursionCount) { |
|
|
// nested conditionals (up to 10 deep) are ok, hopefully! (work from the inside out) |
|
|
if (typeof recursionCount!=typeof 0) recursionCount=0; |
|
|
var conditionalSplitRegex=RegExp( |
|
|
//(1 if \\( (2 2) \\) {(3 3)} (4 else {(5 5)} 4)1) |
|
|
'(;?\\s*if\\s*\\(\\s*(*)\\s*\\)\\s*\\{(*)\\}(\\s*else\\s*\\{(*?)\\}|))', 'i'); |
|
|
var splitted=s.parenSplit(conditionalSplitRegex); |
|
|
// $1: whole conditional |
|
|
// $2: test condition |
|
|
// $3: true expansion |
|
|
// $4: else clause (possibly empty) |
|
|
// $5: false expansion (possibly null) |
|
|
var numParens=5; |
|
|
ret = splitted; |
|
|
for (var i=1; i<splitted.length; i=i+numParens+1) { |
|
|
|
|
|
var testString=splitted; |
|
|
var trueString=splitted; |
|
|
var falseString=splitted; |
|
|
if (typeof falseString=='undefined' || !falseString) falseString=''; |
|
|
var testResult=null; |
|
|
|
|
|
switch (testString) { |
|
|
case 'user': |
|
|
testResult=(article.userName())?true:false; |
|
|
break; |
|
|
case 'talk': |
|
|
testResult=(article.talkPage())?false:true; // talkPage converts _articles_ to talkPages |
|
|
break; |
|
|
case 'admin': |
|
|
testResult=getValueOf('popupAdminLinks')?true:false; |
|
|
break; |
|
|
case 'oldid': |
|
|
testResult=(typeof oldid != 'undefined' && oldid)?true:false; |
|
|
break; |
|
|
case 'ipuser': |
|
|
testResult=(article.isIpUser())?true:false; |
|
|
break; |
|
|
case 'mainspace': |
|
|
testResult=isInMainNamespace(article); |
|
|
break; |
|
|
case 'wikimedia': |
|
|
testResult=(pg.wiki.wikimedia) ? true : false; |
|
|
break; |
|
|
} |
|
|
|
|
|
switch(testResult) { |
|
|
case null: ret+=splitted; break; |
|
|
case true: ret+=trueString; break; |
|
|
case false: ret+=falseString; break; |
|
|
} |
|
|
|
|
|
// append non-conditional string |
|
|
ret += splitted; |
|
|
} |
|
|
if (conditionalSplitRegex.test(ret) && recursionCount < 10) |
|
|
return expandConditionalNavlinkString(ret,article,oldid,recursionCount+1); |
|
|
else return ret; |
|
|
}; |
|
|
|
|
|
function navlinkStringToArray(s, article, oldid) { |
|
|
s=expandConditionalNavlinkString(s,article,oldid); |
|
|
var splitted=s.parenSplit(RegExp('<<(.*?)>>')); |
|
|
var ret=; |
|
|
for (var i=0; i<splitted.length; ++i) { |
|
|
if (i%2) { // i odd, so s is a tag |
|
|
var t=new navlinkTag(); |
|
|
var ss=splitted.split('|'); |
|
|
t.id=ss; |
|
|
for (var j=1; j<ss.length; ++j) { |
|
|
var sss=ss.split('='); |
|
|
if (sss.length>1) |
|
|
t]=sss; |
|
|
else // no assignment (no "="), so treat this as a title (overwriting the last one) |
|
|
t.text=popupString(sss); |
|
|
} |
|
|
t.article=article; |
|
|
if (typeof oldid != 'undefined' && oldid != null) t.oldid=oldid; |
|
|
if (!t.text && t.id != 'mainlink') t.text=popupString(t.id); |
|
|
ret.push(t); |
|
|
} |
|
|
else { // plain HTML |
|
|
ret.push(splitted.split('*').join(getValueOf('popupNavLinkSeparator')) |
|
|
.split('<line>').join('<span class="popup_menu_row">').split('</line>').join('</span>')); |
|
|
} |
|
|
} |
|
|
return ret; |
|
|
}; |
|
|
|
|
|
// navlinkString: * becomes the separator |
|
|
// <<foo|bar=baz|fubar>> becomes a foo-link with attribute bar='baz' |
|
|
// and visible text 'fubar' |
|
|
// if(test){...} and if(test){...}else{...} work too (nested ok) |
|
|
|
|
|
function navlinkStringToHTML(s,article,oldid) { |
|
|
//limitAlert(navlinkStringToHTML, 5, 'navlinkStringToHTML\n' + article + '\n' + (typeof article)); |
|
|
var p=navlinkStringToArray(s,article,oldid); |
|
|
var html=''; |
|
|
for (var i=0; i<p.length; ++i) { |
|
|
if (typeof p == typeof '') { |
|
|
html+=p; |
|
|
} else if (typeof p.type != 'undefined' && p.type=='navlinkTag') { |
|
|
html+=p.html(); |
|
|
} |
|
|
} |
|
|
return html; |
|
|
} |
|
|
|
|
|
function navlinkTag() {this.type='navlinkTag';}; |
|
|
|
|
|
navlinkTag.prototype.html=function () { |
|
|
this.getNewWin(); |
|
|
this.getPrintFunction(); |
|
|
var html=''; |
|
|
if (typeof this.print!='function') { |
|
|
errlog ('Oh dear - invalid print function for a navlinkTag, id='+this.id); |
|
|
} else { |
|
|
html=this.print(this); |
|
|
if (typeof html != typeof '') {html='';} |
|
|
else if (typeof this.shortcut!='undefined') html=addPopupShortcut(html, this.shortcut); |
|
|
} |
|
|
return '<span class="popup_'+this.id+'">'+html+'</span>'; |
|
|
}; |
|
|
|
|
|
navlinkTag.prototype.getNewWin=function() { |
|
|
getValueOf('popupLinksNewWindow'); |
|
|
if (typeof pg.option.popupLinksNewWindow === 'undefined') this.newWin=null; |
|
|
this.newWin=pg.option.popupLinksNewWindow; |
|
|
} |
|
|
|
|
|
navlinkTag.prototype.getPrintFunction=function() { //think about this some more |
|
|
// this.id and this.article should already be defined |
|
|
if (typeof this.id!=typeof '' || typeof this.article!=typeof {} ) return; |
|
|
var html=''; |
|
|
var a,t; |
|
|
|
|
|
switch (this.id) { |
|
|
case 'email': case 'contribs': case 'block': case 'unblock': |
|
|
case 'userlog': case 'userSpace': |
|
|
this.article=this.article.userName(); |
|
|
} |
|
|
|
|
|
switch (this.id) { |
|
|
case 'userTalk': case 'newUserTalk': case 'editUserTalk': |
|
|
case 'userPage': case 'monobook': case 'editMonobook': case 'blocklog': |
|
|
delete this.oldid; |
|
|
this.article=this.article.userName(true); |
|
|
} |
|
|
|
|
|
if (this.id=='editMonobook' || this.id=='monobook') { this.article.append('/monobook.js'); } |
|
|
|
|
|
if (this.id != 'mainlink') { |
|
|
// FIXME anchor handling should be done differently with Title object |
|
|
// this.article=removeAnchor(this.article); |
|
|
// if (typeof this.text=='undefined') this.text=popupString(this.id); |
|
|
} |
|
|
|
|
|
switch (this.id) { |
|
|
case 'undelete': this.print=specialLink; this.specialpage='Undelete'; this.sep='/'; break; |
|
|
case 'whatLinksHere': this.print=specialLink; this.specialpage='Whatlinkshere'; break; |
|
|
case 'relatedChanges': this.print=specialLink; this.specialpage='Recentchangeslinked'; break; |
|
|
case 'move': this.print=specialLink; this.specialpage='Movepage'; break; |
|
|
case 'contribs': this.print=specialLink; this.specialpage='Contributions'; break; |
|
|
case 'email': this.print=specialLink; this.specialpage='Emailuser'; break; |
|
|
case 'block': this.print=specialLink; this.specialpage='Blockip'; this.sep='&ip='; break; |
|
|
case 'unblock': this.print=specialLink; this.specialpage='Ipblocklist'; this.sep='&action=unblock&ip='; break; |
|
|
case 'userlog': this.print=specialLink; this.specialpage='Log'; this.sep='&user='; break; |
|
|
case 'blocklog': this.print=specialLink; this.specialpage='Log'; this.sep='&type=block&page='; break; |
|
|
case 'userSpace': this.print=specialLink; this.specialpage='Prefixindex'; this.sep='&namespace=2&from='; break; |
|
|
case 'search': this.print=specialLink; this.specialpage='Search'; this.sep='&fulltext=Search&search='; break; |
|
|
case 'history': case 'unwatch': case 'watch': |
|
|
case 'unprotect': case 'protect': |
|
|
this.print=wikiLink; this.action=this.id; break; |
|
|
|
|
|
case 'delete': |
|
|
this.print=wikiLink; this.action='delete'; |
|
|
if (isImagePage(this.article)) { |
|
|
var img=this.article.stripNamespace(); |
|
|
this.action+='&image='+img; |
|
|
} |
|
|
break; |
|
|
|
|
|
case 'edit': case 'view': |
|
|
this.print=wikiLink; |
|
|
delete this.oldid; |
|
|
this.action=this.id; break; |
|
|
case 'new': |
|
|
this.print=wikiLink; this.action='edit§ion=new'; break; |
|
|
case 'mainlink': |
|
|
if (typeof this.text=='undefined') this.text=safeDecodeURI(this.article); |
|
|
if (getValueOf('popupSimplifyMainLink') && isInStrippableNamespace(this.article)) { |
|
|
var s=this.text.split('/'); this.text=s; |
|
|
if (this.text=='' && s.length > 1) this.text=s; |
|
|
} |
|
|
this.print=titledWikiLink; |
|
|
if (typeof this.title=='undefined' && pg.current.link && typeof pg.current.link.href != 'undefined') { |
|
|
this.title=safeDecodeURI((pg.current.link.originalTitle)?pg.current.link.originalTitle:this.article); |
|
|
if (typeof this.oldid != 'undefined' && this.oldid) { |
|
|
this.title=tprintf('Revision %s of %s', ); |
|
|
} |
|
|
} |
|
|
this.action='view'; break; |
|
|
case 'userPage': |
|
|
case 'article': |
|
|
case 'monobook': |
|
|
case 'editMonobook': |
|
|
case 'editArticle': |
|
|
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article); |
|
|
this.article=this.article.articleFromTalkOrArticle(); |
|
|
//alert(this.id+'\n'+this.article + '\n'+ typeof this.article); |
|
|
this.print=wikiLink; |
|
|
this.action='view'; break; |
|
|
if (this.id.indexOf('edit')==0) { |
|
|
this.action='edit'; |
|
|
} else { this.action='view';} |
|
|
break; |
|
|
case 'userTalk': |
|
|
case 'talk': |
|
|
this.article=this.article.talkPage(); |
|
|
this.print=wikiLink; |
|
|
this.action='view'; break; |
|
|
case 'arin': |
|
|
this.print=arinLink; break; |
|
|
case 'count': |
|
|
this.print=kateLink; break; |
|
|
case 'google': |
|
|
this.print=googleLink; break; |
|
|
case 'contribsTree': |
|
|
this.print=contribsTreeLink; break |
|
|
case 'editors': |
|
|
this.print=editorListLink; break; |
|
|
case 'globalsearch': |
|
|
this.print=globalSearchLink; break; |
|
|
case 'lastEdit': |
|
|
this.print=titledDiffLink; |
|
|
this.title=popupString('Show the last edit'); |
|
|
this.from='prev'; this.to='cur'; break; |
|
|
case 'oldEdit': |
|
|
this.print=titledDiffLink; |
|
|
this.title=popupString('Show the edit made to get revision') + ' ' + this.oldid; |
|
|
this.from='prev'; this.to=this.oldid; break; |
|
|
case 'editOld': |
|
|
this.print=wikiLink; this.action='edit'; break; |
|
|
case 'revert': |
|
|
this.print=wikiLink; this.action='revert'; break; |
|
|
case 'nullEdit': |
|
|
this.print=wikiLink; this.action='nullEdit'; break; |
|
|
case 'diffCur': |
|
|
this.print=titledDiffLink; |
|
|
this.title=tprintf('Show changes since revision %s', ); |
|
|
this.from=this.oldid; this.to='cur'; break; |
|
|
case 'editUserTalk': |
|
|
case 'editTalk': |
|
|
this.article=this.article.talkPage(); |
|
|
this.action='edit'; this.print=wikiLink; break; |
|
|
case 'newUserTalk': |
|
|
case 'newTalk': |
|
|
this.article=this.article.talkPage(); |
|
|
this.action='edit§ion=new'; this.print=wikiLink; break; |
|
|
case 'imagestatus': |
|
|
this.print=function () { return emptySpanHTML('popupImageStatus', pg.idNumber); } |
|
|
break; |
|
|
case 'lastContrib': |
|
|
case 'sinceMe': |
|
|
this.print=magicHistoryLink; |
|
|
break; |
|
|
default: |
|
|
this.print=function () {return 'Unknown navlink type: '+this.id+''}; |
|
|
} |
|
|
}; |
|
|
|
|
|
// |
|
|
// end navlinks |
|
|
////////////////////////////////////////////////// |
|
|
|
|
|
// ENDFILE: navlinks.js |
|
|
// STARTFILE: shortcutkeys.js |
|
|
function popupHandleKeypress(evt) { |
|
|
var keyCode = window.event ? window.event.keyCode : ( evt.keyCode ? evt.keyCode : evt.which); |
|
|
if (!keyCode || !pg.current.link || !pg.current.link.navpopup) { return; } |
|
|
if (keyCode==27) { // escape |
|
|
killPopup(); |
|
|
return false; // swallow keypress |
|
|
} |
|
|
|
|
|
var letter=String.fromCharCode(keyCode); |
|
|
var links=pg.current.link.navpopup.mainDiv.getElementsByTagName('A'); |
|
|
var startLink=0; |
|
|
var i,j; |
|
|
|
|
|
if (popupHandleKeypress.lastPopupLinkSelected) { |
|
|
for (i=0; i<links.length; ++i) { |
|
|
if (links==popupHandleKeypress.lastPopupLinkSelected) startLink=i; |
|
|
} |
|
|
} |
|
|
for (j=0; j<links.length; ++j) { |
|
|
i=(startLink + j + 1) % links.length; |
|
|
if (links.getAttribute('popupkey')==letter) { |
|
|
if (evt && evt.preventDefault) evt.preventDefault(); |
|
|
links.focus(); |
|
|
popupHandleKeypress.lastPopupLinkSelected=links; |
|
|
return false; // swallow keypress |
|
|
} |
|
|
} |
|
|
|
|
|
// pass keypress on |
|
|
if (document.oldPopupOnkeypress) return document.oldPopupOnkeypress(evt); |
|
|
else return true; |
|
|
}; |
|
|
|
|
|
function addPopupShortcuts() { |
|
|
if (document.onkeypress && document.onkeypress.toString()==popupHandleKeypress.toString()) return; |
|
|
document.oldPopupOnkeypress=document.onkeypress; |
|
|
document.onkeypress=popupHandleKeypress; |
|
|
}; |
|
|
|
|
|
function rmPopupShortcuts() { |
|
|
popupHandleKeypress.lastPopupLinkSelected=null; |
|
|
try { |
|
|
if (document.oldPopupOnkeypress && document.oldPopupOnkeypress.toString()==popupHandleKeypress.toString()) { |
|
|
// panic |
|
|
document.onkeypress=null; //function () {}; |
|
|
return; |
|
|
} |
|
|
document.onkeypress=document.oldPopupOnkeypress; |
|
|
} catch (nasties) { /* IE goes here */ } |
|
|
}; |
|
|
|
|
|
|
|
|
function addLinkProperty(html, property) { |
|
|
// take "<a href=...>...</a> and add a property |
|
|
// not sophisticated at all, easily broken |
|
|
var i=html.indexOf('>'); |
|
|
if (i<0) return html; |
|
|
return html.substring(0,i) + ' ' + property + html.substring(i); |
|
|
}; |
|
|
|
|
|
function addPopupShortcut(html, key) { |
|
|
if (!getValueOf('popupShortcutKeys')) return html; |
|
|
var ret= addLinkProperty(html, 'popupkey="'+key+'"'); |
|
|
if (key==' ') key=popupString('spacebar'); |
|
|
return ret.replace(RegExp('^(.*?)(title=")(.*?)(".*)$', 'i'),'$1$2$3 $4'); |
|
|
}; |
|
|
|
|
|
// ENDFILE: shortcutkeys.js |
|
|
// STARTFILE: diffpreview.js |
|
|
function loadDiff(article, oldid, diff) { |
|
|
pg.diffData={}; // FIXME this is very unreliable; should be done on a per-popup basis |
|
|
var oldRev, newRev; |
|
|
switch (diff) { |
|
|
case 'cur': |
|
|
if ( oldid===null || oldid=='' ) { |
|
|
// eg newmessages diff link |
|
|
oldRev='0&direction=prev'; |
|
|
newRev=0; |
|
|
} else { |
|
|
oldRev = oldid; |
|
|
newRev = 0; |
|
|
} |
|
|
break; |
|
|
case 'prev': |
|
|
oldRev = ( oldid || 0 ) + '&direction=prev'; newRev = oldid; break; |
|
|
case 'next': |
|
|
oldRev = oldid; newRev = oldid + '&direction=next'; |
|
|
break; |
|
|
default: |
|
|
oldRev = oldid || 0; newRev = diff || 0; break; |
|
|
} |
|
|
oldRev = oldRev || 0; |
|
|
newRev = newRev || 0; |
|
|
|
|
|
getWiki(article, doneDiffNew, newRev); |
|
|
getWiki(article, doneDiffOld, oldRev); |
|
|
} |
|
|
|
|
|
function doneDiffNew(download) { |
|
|
if (download.id != pg.idNumber) { return; } |
|
|
pg.diffData.New=download; |
|
|
if (pg.diffData.Old && pg.diffData.Old.id == pg.idNumber) { insertDiff(); } |
|
|
} |
|
|
|
|
|
function doneDiffOld(download) { |
|
|
if (download.id != pg.idNumber) { return; } |
|
|
pg.diffData.Old=download; |
|
|
if (pg.diffData.New && pg.diffData.New.id == pg.idNumber) { insertDiff(); } |
|
|
} |
|
|
|
|
|
|
|
|
function rmBoringLines(a,b,context) { |
|
|
|
|
|
if (typeof context == 'undefined') { context=2; } |
|
|
// this is fairly slow... i think it's quicker than doing a word-based diff from the off, though |
|
|
var aa=, aaa=; |
|
|
var bb=, bbb=; |
|
|
var i, j; |
|
|
|
|
|
// first, gather all disconnected nodes in a and all crossing nodes in a and b |
|
|
for (i=0; i<a.length; ++i ) { |
|
|
if(!a.row || a.row===0) { aa=1; } |
|
|
else if (countCrossings(b,a,i, true)) { |
|
|
aa=1; |
|
|
bb.row ] = 1; |
|
|
} |
|
|
} |
|
|
|
|
|
// pick up remaining disconnected nodes in b |
|
|
for (i=0; i<b.length; ++i ) { |
|
|
if (bb==1) { continue; } |
|
|
if(!b.row || b.row===0) { bb=1; } |
|
|
} |
|
|
|
|
|
// another pass to gather context: we want the neighbours of included nodes which are not yet included |
|
|
// we have to add in partners of these nodes, but we don't want to add context for *those* nodes in the next pass |
|
|
for (i=0; i<b.length; ++i) { |
|
|
if ( bb == 1 ) { |
|
|
for (j=max(0,i-context); j < min(b.length, i+context); ++j) { |
|
|
if ( !bb ) { bb = 1; aa.row ] = 0.5; } |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
for (i=0; i<a.length; ++i) { |
|
|
if ( aa == 1 ) { |
|
|
for (j=max(0,i-context); j < min(a.length, i+context); ++j) { |
|
|
if ( !aa ) { aa = 1; bb.row ] = 0.5; } |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
for (i=0; i<bb.length; ++i) { |
|
|
if (bb > 0) { // it's a row we need |
|
|
if (b.row || b.row===0) { bbb.push(b.text); } // joined; partner should be in aa |
|
|
else { |
|
|
bbb.push(b); |
|
|
} |
|
|
} |
|
|
} |
|
|
for (i=0; i<aa.length; ++i) { |
|
|
if (aa > 0) { // it's a row we need |
|
|
if (a.row || a.row===0) { aaa.push(a.text); } // joined; partner should be in aa |
|
|
else { |
|
|
aaa.push(a); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
return { a: aaa, b: bbb}; |
|
|
} |
|
|
|
|
|
function stripOuterCommonLines(a,b,context) { |
|
|
var i=0; |
|
|
while (i<a.length && i < b.length && a==b) { ++i; } |
|
|
var j=a.length-1; var k=b.length-1; |
|
|
while ( j>=0 && k>=0 && a==b ) { --j; --k; } |
|
|
|
|
|
return { a: a.slice(max(0,i - 1 - context), min(a.length+1, j + context+1)), |
|
|
b: b.slice(max(0,i - 1 - context), min(b.length+1, k + context+1)) }; |
|
|
} |
|
|
|
|
|
function insertDiff() { |
|
|
// for speed reasons, we first do a line-based diff, discard stuff that seems boring, then do a word-based diff |
|
|
// FIXME: sometimes this gives misleading diffs as distant chunks are squashed together |
|
|
var oldlines=pg.diffData.Old.data.split('\n'); |
|
|
var newlines=pg.diffData.New.data.split('\n'); |
|
|
getValueOf('popupDiffContextLines'); |
|
|
var inner=stripOuterCommonLines(oldlines,newlines,pg.option.popupDiffContextLines); |
|
|
oldlines=inner.a; newlines=inner.b; |
|
|
var truncated=false; |
|
|
getValueOf('popupDiffMaxLines'); |
|
|
if (oldlines.length > pg.option.popupDiffMaxLines || newlines.length > pg.option.popupDiffMaxLines) { |
|
|
// truncate |
|
|
truncated=true; |
|
|
inner=stripOuterCommonLines(oldlines.slice(0,pg.option.popupDiffMaxLines), |
|
|
newlines.slice(0,pg.option.popupDiffMaxLines), |
|
|
pg.option.popupDiffContextLines); |
|
|
oldlines=inner.a; newlines=inner.b; |
|
|
} |
|
|
|
|
|
var lineDiff=diff(oldlines, newlines); |
|
|
var lines2=rmBoringLines(lineDiff.o, lineDiff.n); |
|
|
var oldlines2=lines2.a; var newlines2=lines2.b; |
|
|
|
|
|
var simpleSplit = (String.prototype.parenSplit.toString().indexOf('native code')==-1); |
|
|
var html='<hr>'; |
|
|
try { |
|
|
if (getValueOf('popupDiffDates')) { |
|
|
html+='<table class="popup_diff_dates">'; |
|
|
html += '<tr><td>' + tprintf('New revision') + '</td><td>' + pg.diffData.New.lastModified.toLocaleString() + '</td></tr>'; |
|
|
html += '<tr><td>' + tprintf('Old revision') + '</td><td>' + pg.diffData.Old.lastModified.toLocaleString() + '</td></tr>'; |
|
|
html += '</table><hr>'; |
|
|
} |
|
|
} catch (cockup) { |
|
|
// nothing here - maybe the download failed or something. anyway, not too fussed. |
|
|
} |
|
|
html += shortenDiffString( |
|
|
diffString(oldlines2.join('\n'), newlines2.join('\n'), simpleSplit), |
|
|
getValueOf('popupDiffContextCharacters') ).join('<hr>'); |
|
|
setPopupHTML(html.split('\n').join('<br>') + |
|
|
(truncated ? '<hr><b>'+popupString('Diff truncated for performance reasons')+'</b>' : '') , |
|
|
'popupPreview'); |
|
|
} |
|
|
|
|
|
// ENDFILE: diffpreview.js |
|
|
// STARTFILE: links.js |
|
|
///////////////////// |
|
|
// LINK GENERATION // |
|
|
///////////////////// |
|
|
|
|
|
// titledDiffLink --> titledWikiLink --> generalLink |
|
|
// wikiLink --> titledWikiLink --> generalLink |
|
|
// kateLink --> generalLink |
|
|
|
|
|
function titledDiffLink(l) { // article, text, title, from, to) { |
|
|
return titledWikiLink({article: l.article, action: l.to + '&oldid=' + l.from, |
|
|
newWin: l.newWin, |
|
|
text: l.text, title: l.title, |
|
|
/* hack: no oldid here */ |
|
|
actionName: 'diff'}); |
|
|
}; |
|
|
|
|
|
|
|
|
window.wikiLink=function(l) { |
|
|
//{article:article, action:action, text:text, oldid}) { |
|
|
if (! (typeof l.article == typeof {} |
|
|
&& typeof l.action == typeof '' && typeof l.text==typeof '')) return null; |
|
|
if (typeof l.oldid == 'undefined') l.oldid=null; |
|
|
if (l.action!='edit' && l.action!='view' && l.action != 'revert') l.oldid=null; |
|
|
var hint=popupString(l.action + 'Hint'); |
|
|
switch (l.action) { |
|
|
case 'edit§ion=new': hint = popupString('newSectionHint'); break; |
|
|
case 'revert': |
|
|
l.action='edit&autoclick=wpSave&autosummary=' + revertSummary(l.oldid); |
|
|
if (getValueOf('popupRevertSummaryPrompt')) { l.action += '&autosummaryprompt=true'; } |
|
|
break; |
|
|
case 'nullEdit': |
|
|
l.action='edit&autoclick=wpSave&autosummary=null'; |
|
|
break; |
|
|
} |
|
|
|
|
|
if (hint) { |
|
|
if (l.oldid) { |
|
|
hint = simplePrintf(hint, )]); |
|
|
} |
|
|
else { |
|
|
hint = simplePrintf(hint, ); |
|
|
} |
|
|
} |
|
|
else hint = safeDecodeURI(l.article + '&action=' + l.action) |
|
|
+ (l.oldid) ? '&oldid='+l.oldid : ''; |
|
|
|
|
|
return titledWikiLink({article: l.article, action: l.action, text: l.text, newWin:l.newWin, |
|
|
title: hint, oldid: l.oldid}); |
|
|
}; |
|
|
|
|
|
function revertSummary(oldid) { |
|
|
var historyPage=(document.title.split(' - ') === popupString('History')); |
|
|
if (historyPage) { |
|
|
var links=document.links; |
|
|
var numlinks=links.length; |
|
|
var date=null, editor=null; |
|
|
for (var i=0; i<numlinks-1; ++i) { |
|
|
if (RegExp('oldid='+oldid).test(links.href) |
|
|
&& RegExp('^{2}:{2},.*{3}$').test(links.innerHTML)) { |
|
|
date=links.innerHTML; |
|
|
editor=Title.fromURL(links.href).userName(); |
|
|
break; |
|
|
} |
|
|
} |
|
|
if (date && editor) { |
|
|
return simplePrintf(getValueOf('popupExtendedRevertSummary'), ); |
|
|
} |
|
|
} |
|
|
return simplePrintf(getValueOf('popupRevertSummary'), ); |
|
|
} |
|
|
|
|
|
function titledWikiLink(l) { |
|
|
// possible properties of argument: |
|
|
// article, action, text, title, oldid, actionName, className |
|
|
// oldid = null is fine here |
|
|
|
|
|
// article and action are mandatory args |
|
|
|
|
|
if (typeof l.article == 'undefined' || typeof l.action=='undefined') { |
|
|
errlog('got undefined article or actino in titledWikiLink'); |
|
|
return null; |
|
|
} |
|
|
|
|
|
var base = pg.wiki.titlebase + l.article.urlString(); |
|
|
var url=base; |
|
|
|
|
|
if (typeof l.actionName=='undefined' || !l.actionName) { l.actionName='action'; } |
|
|
|
|
|
// no need to add &action=view, and this confuses anchors |
|
|
if (l.action != 'view') { url = base + '&' + l.actionName + '=' + l.action; } |
|
|
|
|
|
if (typeof l.oldid!='undefined' && l.oldid) { url+='&oldid='+l.oldid; } |
|
|
|
|
|
var cssClass=pg.misc.defaultNavlinkClassname; |
|
|
if (typeof l.className!='undefined' && l.className) { cssClass=l.className; } |
|
|
|
|
|
return generalNavLink({url: url, newWin: l.newWin, |
|
|
title: (typeof l.title != 'undefined') ? l.title : null, |
|
|
text: (typeof l.text!='undefined')?l.text:null, |
|
|
className: cssClass}); |
|
|
}; |
|
|
|
|
|
function getLastContrib(wikipage, newWin) { |
|
|
getHistoryInfo(wikipage, function(x){processLastContribInfo(x,{page: wikipage, newWin: newWin})}); |
|
|
} |
|
|
function processLastContribInfo(info, stuff) { |
|
|
if(!info.edits || !info.edits.length) { alert('Popups: an odd thing happened. Please retry.'); return; } |
|
|
if(!info.firstNewEditor) { |
|
|
alert(tprintf('Only found one editor: %s made %s edits', .editor,info.edits.length])); |
|
|
return; |
|
|
} |
|
|
var newUrl=pg.wiki.titlebase + stuff.page + '&diff=cur&oldid='+info.firstNewEditor.oldid; |
|
|
displayUrl(newUrl, stuff.newWin); |
|
|
} |
|
|
function getDiffSinceMyEdit(wikipage, newWin) { |
|
|
getHistoryInfo(wikipage, function(x){processDiffSinceMyEdit(x,{page: wikipage, newWin: newWin})}); |
|
|
} |
|
|
function processDiffSinceMyEdit(info, stuff) { |
|
|
if(!info.edits || !info.edits.length) { alert('Popups: something fishy happened. Please try again.'); return; } |
|
|
var friendlyName=stuff.page.split('_').join(' '); |
|
|
if(!info.myLastEdit) { |
|
|
alert(tprintf('Couldn\'t find an edit by %s\nin the last %s edits to\n%s', |
|
|
)); |
|
|
return; |
|
|
} |
|
|
if(info.myLastEdit.index==0) { |
|
|
alert(tprintf("%s seems to be the last editor to the page %s", )); |
|
|
return; |
|
|
} |
|
|
var newUrl=pg.wiki.titlebase + stuff.page + '&diff=cur&oldid='+ info.myLastEdit.oldid; |
|
|
displayUrl(newUrl, stuff.newWin); |
|
|
} |
|
|
function displayUrl(url, newWin){ |
|
|
if(newWin) window.open(url); |
|
|
else document.location=url; |
|
|
} |
|
|
|
|
|
|
|
|
function magicHistoryLink(l) { |
|
|
// FIXME use onclick change href trick to sort this out instead of window.open |
|
|
|
|
|
var jsUrl='', title=''; |
|
|
switch(l.id) { |
|
|
case 'lastContrib': |
|
|
jsUrl=simplePrintf('javascript:getLastContrib(\'%s\', %s)', ); |
|
|
title=popupString('lastContribHint'); |
|
|
break; |
|
|
case 'sinceMe': |
|
|
jsUrl=simplePrintf('javascript:getDiffSinceMyEdit(\'%s\', %s)', ); |
|
|
title=popupString('sinceMeHint'); |
|
|
break; |
|
|
} |
|
|
|
|
|
return generalNavLink({url: jsUrl, newWin: false, // can't have new windows with JS links, I think |
|
|
title: title, |
|
|
text: l.text}); |
|
|
} |
|
|
|
|
|
function specialLink(l) { |
|
|
// properties: article, specialpage, text, sep |
|
|
if (typeof l.specialpage=='undefined'||!l.specialpage) return null; |
|
|
var base = pg.wiki.titlebase + pg.ns.special+':'+l.specialpage; |
|
|
if (typeof l.sep == 'undefined' || l.sep===null) l.sep='&target='; |
|
|
var article=l.article.urlString(); |
|
|
var hint=popupString(l.specialpage+'Hint'); |
|
|
switch (l.specialpage) { |
|
|
case 'Log': hint=(l.sep=='&user=') ? popupString('userLogHint') : popupString('blockLogHint'); break; |
|
|
case 'Search': article=l.article.toString(); break; |
|
|
} |
|
|
if (hint) hint = simplePrintf(hint, ); |
|
|
else hint = safeDecodeURI(l.specialpage+':'+l.article) ; |
|
|
|
|
|
var url = base + l.sep + article; |
|
|
return generalNavLink({url: url, title: hint, text: l.text, newWin:l.newWin}); |
|
|
}; |
|
|
|
|
|
function generalLink(l) { |
|
|
// l.url, l.text, l.title, l.newWin, l.className |
|
|
|
|
|
if (typeof l.url=='undefined') return null; |
|
|
|
|
|
// only quotation marks in the url can screw us up now... I think |
|
|
var url=l.url.split('"').join('%22'); |
|
|
|
|
|
var ret='<a href="' + url + '"'; |
|
|
if (typeof l.title!='undefined' && l.title) ret += ' title="' + l.title + '"'; |
|
|
var newWin; |
|
|
if (typeof l.newWin=='undefined' || l.newWin===null) newWin=getValueOf('popupNewWindows'); |
|
|
else newWin=l.newWin; |
|
|
if (newWin) ret += ' target="_blank"'; |
|
|
if (typeof l.className!='undefined'&&l.className) ret+=' class="'+l.className+'"'; |
|
|
ret += '>'; |
|
|
if (typeof l.text==typeof '') ret+= l.text; |
|
|
ret +='</a>'; |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function appendParamsToLink(linkstr, params) { |
|
|
var sp=linkstr.parenSplit(RegExp('(href="+?)"', 'i')); |
|
|
if (sp.length<2) return null; |
|
|
var ret=sp.shift() + sp.shift(); |
|
|
ret += '&' + params + '"'; |
|
|
ret += sp.join(''); |
|
|
return ret; |
|
|
}; |
|
|
|
|
|
function changeLinkTargetLink(x) { // newTarget, text, hint, summary, clickButton, minor) { |
|
|
if (x.newTarget) { |
|
|
log ('changeLinkTargetLink: newTarget=' + x.newTarget); |
|
|
} |
|
|
// optional: oldTarget (in wikitext) |
|
|
// if x.newTarget omitted or null, remove the link |
|
|
|
|
|
// escape '&' and other nasties |
|
|
if(x.newTarget) { |
|
|
x.newTarget=encodeURI(x.newTarget); |
|
|
log('changeLinkTargetLink: newTarget encoded to ' + x.newTarget); |
|
|
} |
|
|
//x.text=encodeURI(x.text); // this buggers things up on zh.wikipedia.org and doesn't seem necessary |
|
|
x.clickButton=encodeURI(x.clickButton); |
|
|
|
|
|
// this'll break if charAt(0) is nasty |
|
|
if (typeof x.oldTarget != typeof '') x.oldTarget=safeDecodeURI(pg.current.article); |
|
|
var cA=literalizeRegex(x.oldTarget); |
|
|
var chs=cA.charAt(0).toUpperCase(); |
|
|
chs=''; |
|
|
var currentArticleRegexBit=chs+cA.substring(1); |
|
|
currentArticleRegexBit=currentArticleRegexBit |
|
|
.split(RegExp('+', 'g')).join('+') |
|
|
.split('\\(').join('(?:%2528|\\()') |
|
|
.split('\\)').join('(?:%2529|\\))'); |
|
|
currentArticleRegexBit = '\\s*(' + currentArticleRegexBit + ')\\s*'; |
|
|
// e.g. Computer (archaic) -> \s*(omputer(?:%2528|\()archaic(?:%2528|\)))\s* |
|
|
|
|
|
// autoedit=s~\ad)\]\]~]~g;s~\AD)~[[Computer-aided%20design|~g |
|
|
|
|
|
// get the page to edit from the title |
|
|
try { |
|
|
//var title=document.getElementsByTagName('h1').innerHTML.replace(RegExp(' ', 'g'), '_'); |
|
|
var title=document.title.split(' - '); |
|
|
title=''; |
|
|
title=title.join(' - ').replace(/ - $/, ''); |
|
|
} catch (err) { return; } |
|
|
|
|
|
var lk=titledWikiLink({article: new Title(title), newWin:x.newWin, |
|
|
action: 'edit', |
|
|
text: x.text, |
|
|
title: x.hint, |
|
|
className: 'popup_change_title_link' |
|
|
}); |
|
|
var cmd=''; |
|
|
if (x.newTarget) { |
|
|
cmd +='s~\\\\]~]~g;'; |
|
|
cmd += 's~\\~[['+x.newTarget+'|~g'; |
|
|
} else { |
|
|
cmd += 's~\\\\]~$1~g;'; |
|
|
cmd += 's~\\(.*?)\\]\\]~$2~g'; |
|
|
} |
|
|
cmd += '&autoclick='+x.clickButton; |
|
|
cmd += ( x.minor == null ) ? '' : '&autominor='+x.minor; |
|
|
cmd += ( x.watch == null ) ? '' : '&autowatch='+x.watch; |
|
|
cmd += '&autosummary='+x.summary; |
|
|
return appendParamsToLink(lk, 'autoedit='+cmd); |
|
|
} |
|
|
|
|
|
|
|
|
function redirLink(redirMatch) { |
|
|
// NB redirMatch is in wikiText |
|
|
var ret=''; |
|
|
|
|
|
if (getValueOf('popupAppendRedirNavLinks') && getValueOf('popupNavLinks')) { |
|
|
ret += '<hr>'; |
|
|
if (getValueOf('popupFixRedirs') && typeof autoEdit != 'undefined' && autoEdit) { |
|
|
log('redirLink: newTarget=' + redirMatch); |
|
|
ret += addPopupShortcut( |
|
|
changeLinkTargetLink({newTarget: redirMatch, text: popupString('Redirects'), hint: popupString('Fix this redirect'), |
|
|
summary: simplePrintf(getValueOf('popupFixRedirsSummary'), |
|
|
), |
|
|
clickButton: getValueOf('popupRedirAutoClick'), minor: true, |
|
|
watch: getValueOf('popupWatchRedirredPages')}) |
|
|
, 'R'); |
|
|
ret += popupString(' to '); |
|
|
} |
|
|
else ret += popupString('Redirects') + popupString(' to '); |
|
|
return ret; |
|
|
} |
|
|
|
|
|
else return '<br> ' + popupString('Redirects') + popupString(' to ') + |
|
|
titledWikiLink({article: new Title().fromWikiText(redirMatch), action: 'view', /* FIXME: newWin */ |
|
|
text: safeDecodeURI(redirMatch), title: popupString('Bypass redirect')}); |
|
|
} |
|
|
|
|
|
function arinLink(l) { |
|
|
if (!saneLinkCheck(l)) { return null; } |
|
|
if ( ! l.article.isIpUser() || ! pg.wiki.wikimedia) return null; |
|
|
|
|
|
var uN=safeDecodeURI(l.article.userName()); |
|
|
|
|
|
return generalNavLink({url:'http://ws.arin.net/cgi-bin/whois.pl?queryinput=' + uN, newWin:l.newWin, |
|
|
title: tprintf('Look up %s in ARIN whois database', ), |
|
|
text: l.text}); |
|
|
} |
|
|
|
|
|
function toolDbName() { |
|
|
var ret=null; |
|
|
var theWiki=pg.wiki.hostname.split('.'); |
|
|
switch(theWiki) { |
|
|
case 'wikipedia': |
|
|
ret = pg.wiki.lang + 'wiki'; |
|
|
break; |
|
|
default: |
|
|
ret = theWiki; |
|
|
break; |
|
|
} |
|
|
ret+= '_p'; |
|
|
return ret; |
|
|
} |
|
|
|
|
|
function saneLinkCheck(l) { |
|
|
if (typeof l.article != typeof {} || typeof l.text != typeof '') { return false; } |
|
|
return true; |
|
|
} |
|
|
function kateLink(l) { |
|
|
if(!saneLinkCheck(l)) return null; |
|
|
if (! pg.wiki.wikimedia) return null; |
|
|
var uN=safeDecodeURI(l.article.userName()); |
|
|
|
|
|
var url='http://tools.wikimedia.de/~' + getValueOf('popupEditCounterTool') + '/cgi-bin/count_edits?dbname='; |
|
|
url += toolDbName() + '&user=' + uN; |
|
|
|
|
|
return generalNavLink({url:url, title: tprintf('katelinkHint', ), newWin:l.newWin, text: l.text}); |
|
|
}; |
|
|
|
|
|
|
|
|
function contribsTreeLink(l) { |
|
|
if(!saneLinkCheck(l)) return null; |
|
|
if (! pg.wiki.wikimedia) return null; |
|
|
var uN=safeDecodeURI(l.article.userName()); |
|
|
|
|
|
var url='http://tools.wikimedia.de/~interiot/cgi-bin/contribution_tree?dbname='; |
|
|
url += toolDbName() + '&user='+ uN; |
|
|
|
|
|
return generalNavLink({url:url, title: tprintf('contribsTreeHint', ), newWin:l.newWin, text: l.text}); |
|
|
} |
|
|
|
|
|
function globalSearchLink(l) { |
|
|
if(!saneLinkCheck(l)) return null; |
|
|
|
|
|
var base='http://vs.aka-online.de/cgi-bin/globalwpsearch.pl?timeout=120&search='; |
|
|
var article=safeDecodeURI(l.article); |
|
|
|
|
|
return generalNavLink({url:base + article, newWin:l.newWin, |
|
|
title: tprintf('globalSearchHint', ), |
|
|
text: l.text}); |
|
|
} |
|
|
|
|
|
function googleLink(l) { |
|
|
if(!saneLinkCheck(l)) return null; |
|
|
|
|
|
var base='http://www.google.com/search?q='; |
|
|
var article=safeDecodeURI(l.article); |
|
|
|
|
|
return generalNavLink({url:base + '%22' + article + '%22', newWin:l.newWin, |
|
|
title: tprintf('googleSearchHint', ), |
|
|
text: l.text}); |
|
|
} |
|
|
|
|
|
function editorListLink(l) { |
|
|
if(!saneLinkCheck(l)) return null; |
|
|
var article= safeDecodeURI(l.article.articleFromTalkPage() || l.article); |
|
|
var base='http://tools.wikimedia.de/~tim/counter/?page='; |
|
|
return generalNavLink({url:base+article, title: tprintf('editorListHint', ), newWin:l.newWin, text: l.text}); |
|
|
} |
|
|
|
|
|
function generalNavLink(l) { |
|
|
l.className = (l.className==null) ? 'popupNavLink' : l.className; |
|
|
return generalLink(l); |
|
|
} |
|
|
|
|
|
////////////////////////////////////////////////// |
|
|
// magic history links |
|
|
// |
|
|
|
|
|
window.getHistoryInfo=function(wikipage, whatNext) { |
|
|
getHistory(wikipage, whatNext ? function(d){whatNext(processHistory(d));} : processHistory); |
|
|
} |
|
|
window.getHistory=function(wikipage, onComplete) { |
|
|
var url = pg.wiki.titlebase + removeAnchor(wikipage) + '&action=history' + '&limit=' + getValueOf('popupHistoryLimit'); |
|
|
return startDownload(url, pg.idNumber+'history', onComplete); |
|
|
} |
|
|
window.processHistory=function(download) { |
|
|
// screen scrape alert |
|
|
var histInfo={}; |
|
|
|
|
|
var d=download.data; |
|
|
// pg.misc.data=d; // for debugging |
|
|
|
|
|
var edits=; |
|
|
var split=d.split('<li>'); |
|
|
|
|
|
for (var i=0; i<split.length; ++i) { |
|
|
var match=RegExp('^.*?type="radio" value="(*)".*?class=\'history-user\'><a href="(/User:|/search/title=(Special:Contributions&target=|User:))(*)').exec(split); |
|
|
if (match) { |
|
|
edits.push({ oldid: match, editor: match }); |
|
|
} |
|
|
} |
|
|
histInfo.edits=edits; |
|
|
|
|
|
var userName=getValueOf('popupUserName') || Cookie.read('enwikiUserName').split('+').join('_'); |
|
|
histInfo.userName=userName; |
|
|
|
|
|
for (var i=0; i<edits.length; ++i) { |
|
|
if (typeof histInfo.myLastEdit == 'undefined' && userName && edits.editor==userName) |
|
|
histInfo.myLastEdit={index: i, oldid: edits.oldid, previd: (i==0 ? null : edits.oldid)} ; |
|
|
if (typeof histInfo.firstNewEditor == 'undefined' && edits.editor != edits.editor) |
|
|
histInfo.firstNewEditor={index:i, oldid:edits.oldid, previd: (i==0 ? null : edits.oldid)}; |
|
|
} |
|
|
//pg.misc.historyInfo=histInfo; |
|
|
return histInfo; |
|
|
} |
|
|
|
|
|
// ENDFILE: links.js |
|
|
// STARTFILE: options.js |
|
|
////////////////////////////////////////////////// |
|
|
// options |
|
|
|
|
|
// check for cookies and existing value, else use default |
|
|
function defaultize(x) { |
|
|
var val=null; |
|
|
if (x!='popupCookies') { |
|
|
defaultize('popupCookies'); |
|
|
if (pg.option.popupCookies && (val=Cookie.read(x))) { |
|
|
pg.option=val; |
|
|
return; |
|
|
} |
|
|
} |
|
|
if (pg.option===null) { |
|
|
if (typeof window != 'undefined' ) pg.option=window; |
|
|
else pg.option=pg.optionDefault; |
|
|
} |
|
|
}; |
|
|
|
|
|
function newOption(x, def) { |
|
|
if (typeof pg.option=='undefined') { |
|
|
pg.option=null; |
|
|
} |
|
|
pg.optionDefault=def; |
|
|
}; |
|
|
|
|
|
function setDefault(x, def) { return newOption(x, def); } |
|
|
|
|
|
function getValueOf(varName) { |
|
|
defaultize(varName); |
|
|
return pg.option; |
|
|
} |
|
|
|
|
|
function setOptions() { |
|
|
// user-settable parameters and defaults |
|
|
|
|
|
// Basic options |
|
|
newOption('popupDelay', 0.5); |
|
|
newOption('simplePopups', false); |
|
|
newOption('popupStructure', 'menus'); // see later - default for popupStructure is 'original' if simplePopups is true |
|
|
newOption('popupActionsMenu', true); |
|
|
newOption('popupAdminLinks', false); |
|
|
newOption('popupShortcutKeys', false); |
|
|
newOption('popupDragging', true); |
|
|
newOption('popupHistoricalLinks', true); |
|
|
newOption('popupOnlyArticleLinks', true); |
|
|
newOption('removeTitles', true); |
|
|
newOption('popupMaxWidth', 350); |
|
|
newOption('popupInitialWidth', false); // integer or false |
|
|
newOption('popupSimplifyMainLink', true); |
|
|
newOption('popupAppendRedirNavLinks', true); |
|
|
newOption('popupTocLinks', false); |
|
|
newOption('popupSubpopups', true); |
|
|
|
|
|
// images |
|
|
newOption('popupImages', true); |
|
|
newOption('imagePopupsForImages', true); |
|
|
newOption('popupNeverGetThumbs', false); |
|
|
newOption('popupImagesFromThisWikiOnly', false); |
|
|
newOption('popupMinImageWidth', 50); |
|
|
newOption('popupLoadImagesSequentially', false); |
|
|
newOption('popupImagesToggleSize', true); |
|
|
newOption('popupImageSize', 60); |
|
|
|
|
|
// redirs, dabs, reversion |
|
|
newOption('popupFixRedirs', false); |
|
|
newOption('popupRedirAutoClick', 'wpDiff'); |
|
|
newOption('popupFixDabs', false); |
|
|
newOption('popupRevertSummaryPrompt', false); |
|
|
newOption('popupRedlinkRemoval', false); |
|
|
newOption('popupWatchDisambiggedPages', null); |
|
|
newOption('popupWatchRedirredPages', null); |
|
|
|
|
|
// navlinks |
|
|
newOption('popupNavLinks', true); |
|
|
newOption('popupNavLinkStyle', 'default'); // FIXME what's this do? |
|
|
newOption('popupNavLinkSeparator', ' ⋅ '); |
|
|
newOption('popupLastEditLink', true); |
|
|
newOption('popupEditCounterTool', 'interiot'); |
|
|
newOption('popupExtraUserMenu', ''); |
|
|
|
|
|
// previews etc |
|
|
newOption('popupPreviews', true); |
|
|
newOption('popupSummaryData', true); |
|
|
newOption('popupMaxPreviewSentences', 4); |
|
|
newOption('popupMaxPreviewCharacters', 600); |
|
|
newOption('popupLastModified', true); |
|
|
newOption('popupPreviewKillTemplates', true); |
|
|
newOption('popupPreviewRawTemplates', false); |
|
|
newOption('popupPreviewFirstParOnly', true); |
|
|
|
|
|
// diffs |
|
|
newOption('popupPreviewDiffs', true); |
|
|
newOption('popupDiffMaxLines', 100); |
|
|
newOption('popupDiffContextLines', 2); |
|
|
newOption('popupDiffContextCharacters', 40); |
|
|
newOption('popupDiffDates', true); |
|
|
|
|
|
// edit summaries |
|
|
newOption('popupFixDabsSummary', popupString('defaultpopupFixDabsSummary') ); |
|
|
newOption('popupExtendedRevertSummary', popupString('defaultpopupExtendedRevertSummary') ); |
|
|
newOption('popupRevertSummary', popupString('defaultpopupRevertSummary') ); |
|
|
newOption('popupFixRedirsSummary', popupString('defaultpopupFixRedirsSummary') ); |
|
|
newOption('popupRedlinkSummary', popupString('defaultpopupRedlinkSummary') ); |
|
|
newOption('popupRmDabLinkSummary', popupString('defaultpopupRmDabLinkSummary') ); |
|
|
|
|
|
// misc |
|
|
newOption('popupLiveOptions', false); |
|
|
newOption('popupLiveOptionsExpanded', false); |
|
|
newOption('popupCookies', false); |
|
|
newOption('popupUnsimplifyLink', false); |
|
|
newOption('popupHistoryLimit', 50); |
|
|
newOption('popupFilters', [popupFilterStubDetect, popupFilterDisambigDetect, |
|
|
popupFilterPageSize, popupFilterCountLinks, |
|
|
popupFilterCountImages, popupFilterCountCategories, |
|
|
popupFilterLastModified]); |
|
|
newOption('extraPopupFilters', ); |
|
|
newOption('popupOnEditSelection', true); |
|
|
|
|
|
// new windows |
|
|
newOption('popupNewWindows', false); |
|
|
newOption('popupLinksNewWindow', {'lastContrib': true, 'sinceMe': true}); |
|
|
} |
|
|
|
|
|
// ENDFILE: options.js |
|
|
// STARTFILE: strings.js |
|
|
////////////////////////////////////////////////// |
|
|
// Translatable strings |
|
|
////////////////////////////////////////////////// |
|
|
// |
|
|
// Notes for translators |
|
|
// --------------------- |
|
|
// |
|
|
// If there's a string that I've not included below, please drop a |
|
|
// note at ]. |
|
|
// |
|
|
// These strings can be changed if they're impossible to translate to |
|
|
// more flexible versions. Just ask. |
|
|
// |
|
|
// New translation method: copy the pg.string block below, translate |
|
|
// the strings on the right and change the line |
|
|
// |
|
|
// pg.string = { |
|
|
// |
|
|
// into |
|
|
// |
|
|
// popupStrings = { |
|
|
// |
|
|
// Save this file on your wiki and load that file '''as well as''' |
|
|
// popups.js or popupsdev.js from your user javascript file. |
|
|
|
|
|
pg.string = { |
|
|
///////////////////////////////////// |
|
|
// summary data, searching etc. |
|
|
///////////////////////////////////// |
|
|
'#': '#', |
|
|
'article': 'article', |
|
|
'category': 'category', |
|
|
'categories': 'categories', |
|
|
'image': 'image', |
|
|
'images': 'images', |
|
|
'stub': 'stub', |
|
|
'Empty page': 'Empty page', |
|
|
'kB': 'kB', |
|
|
'bytes': 'bytes', |
|
|
'day': 'day', |
|
|
'days': 'days', |
|
|
'hour': 'hour', |
|
|
'hours': 'hours', |
|
|
'minute': 'minute', |
|
|
'minutes': 'minutes', |
|
|
'second': 'second', |
|
|
'seconds': 'seconds', |
|
|
'week': 'week', |
|
|
'weeks': 'weeks', |
|
|
'search': 'search', |
|
|
'SearchHint': 'Find English Misplaced Pages articles containing %s', |
|
|
'web': 'web', |
|
|
'global': 'global', |
|
|
'globalSearchHint': 'Search across Wikipedias in different languages for %s', |
|
|
'google': 'google', |
|
|
'googleSearchHint': 'Google for %s', |
|
|
///////////////////////////////////// |
|
|
// article-related actions and info |
|
|
// (some actions also apply to user pages) |
|
|
///////////////////////////////////// |
|
|
'actions': 'actions', ///// view articles and view talk |
|
|
'space': 'space', |
|
|
'spacebar': 'space', |
|
|
'view article': 'view article', |
|
|
'viewHint': 'Go to %s', |
|
|
'talk': 'talk', |
|
|
'talk page': 'talk page', |
|
|
'this revision': 'this revision', |
|
|
'revision %s of %s': 'revision %s of %s', |
|
|
'Revision %s of %s': 'Revision %s of %s', |
|
|
'Toggle image size': 'Toggle image size', |
|
|
'del': 'del', ///// delete, protect, move |
|
|
'delete': 'delete', |
|
|
'deleteHint': 'Delete %s', |
|
|
'undeleteShort': 'un', |
|
|
'UndeleteHint': 'Show the deletion history for %s', |
|
|
'protect': 'protect', |
|
|
'protectHint': 'Restrict editing rights to %s', |
|
|
'unprotectShort': 'un', |
|
|
'unprotectHint': 'Allow %s to be edited by anyone again', |
|
|
'move': 'move', |
|
|
'move page': 'move page', |
|
|
'MovepageHint': 'Change the title of %s', |
|
|
'edit': 'edit', ///// edit articles and talk |
|
|
'edit article': 'edit article', |
|
|
'editHint': 'Change the content of %s', |
|
|
'edit talk': 'edit talk', |
|
|
'new': 'new', |
|
|
'new topic': 'new topic', |
|
|
'newSectionHint': 'Start a new section on %s', |
|
|
'null edit': 'null edit', |
|
|
'nullEditHint': 'Submit an edit to %s, making no changes ', |
|
|
'hist': 'hist', ///// history, diffs, editors, related |
|
|
'history': 'history', |
|
|
'History': 'History', // what appears in the titles of history pages |
|
|
'historyHint': 'List the changes made to %s', |
|
|
'last': 'last', |
|
|
'lastEdit': 'lastEdit', |
|
|
'show last edit': 'most recent edit', |
|
|
'Show the last edit': 'Show the effects of the most recent change', |
|
|
'lastContrib': 'lastContrib', |
|
|
'last set of edits': 'latest edits', |
|
|
'lastContribHint': 'Show the net effect of changes made by the last editor', |
|
|
'cur': 'cur', |
|
|
'diffCur': 'diffCur', |
|
|
'Show changes since revision %s': 'Show changes since revision %s', |
|
|
'Diff truncated for performance reasons': 'Diff truncated for performance reasons', |
|
|
'old': 'old', |
|
|
'oldEdit': 'oldEdit', |
|
|
'Show the edit made to get revision': 'Show the edit made to get revision', |
|
|
'sinceMe': 'sinceMe', |
|
|
'changes since mine': 'diff my edit', |
|
|
'sinceMeHint': 'Show changes since my last edit', |
|
|
'Couldn\'t find an edit by %s\nin the last %s edits to\n%s': 'Couldn\'t find an edit by %s\nin the last %s edits to\n%s', |
|
|
'eds': 'eds', |
|
|
'editors': 'editors', |
|
|
'editorListHint': 'List the users who have edited %s', |
|
|
'related': 'related', |
|
|
'relatedChanges': 'relatedChanges', |
|
|
'related changes': 'related changes', |
|
|
'RecentchangeslinkedHint': 'Show changes in articles related to %s', |
|
|
'editOld': 'editOld', ///// edit old version, or revert |
|
|
'rv': 'rv', |
|
|
'revert': 'revert', |
|
|
'revertHint': 'Revert to %s', |
|
|
'defaultpopupRedlinkSummary': 'Removing link to empty page ] using ]', |
|
|
'defaultpopupFixDabsSummary': 'Disambiguate ] to ] using ]', |
|
|
'defaultpopupFixRedirsSummary': 'Redirect bypass from ] to ] using ]', |
|
|
'defaultpopupExtendedRevertSummary': 'Revert to revision dated %s by %s, oldid %s using ]', |
|
|
'defaultpopupRevertSummary': 'Revert to revision %s using ]', |
|
|
'defaultpopupRmDabLinkSummary': 'Remove link to dab page ] using ]', |
|
|
'Redirects': 'Redirects', // as in Redirects to ... |
|
|
' to ': ' to ', // as in Redirects to ... |
|
|
'Bypass redirect': 'Bypass redirect', |
|
|
'Fix this redirect': 'Fix this redirect', |
|
|
'disambig': 'disambig', ///// add or remove dab etc. |
|
|
'disambigHint': 'Disambiguate this link to ]', |
|
|
'Click to disambiguate this link to:': 'Click to disambiguate this link to:', |
|
|
'remove this link': 'remove this link', |
|
|
'remove all links to this page from this article': 'remove all links to this page from this article', |
|
|
'remove all links to this disambig page from this article': 'remove all links to this disambig page from this article', |
|
|
'mainlink': 'mainlink', ///// links, watch, unwatch |
|
|
'wikiLinks': 'wikiLinks', |
|
|
'links here': 'links here', |
|
|
'whatLinksHere': 'whatLinksHere', |
|
|
'what links here': 'what links here', |
|
|
'WhatlinkshereHint': 'List the pages that are hyperlinked to %s', |
|
|
'unwatchShort': 'un', |
|
|
'watchThingy': 'watch', // called watchThingy because {}.watch is a function |
|
|
'watchHint': 'Add %s to my watchlist', |
|
|
'unwatchHint': 'Remove %s from my watchlist', |
|
|
///////////////////////////////////// |
|
|
// user-related actions and info |
|
|
///////////////////////////////////// |
|
|
'user': 'user', ///// user page, talk, email, space |
|
|
'user page': 'user page', |
|
|
'user talk': 'user talk', |
|
|
'edit user talk': 'edit user talk', |
|
|
'leave comment': 'leave comment', |
|
|
'email': 'email', |
|
|
'email user': 'email user', |
|
|
'EmailuserHint': 'Send an email to %s', |
|
|
'space': 'userspace', |
|
|
'PrefixindexHint': 'Show pages in the userspace of %s', |
|
|
'count': 'count', ///// contributions, tree, log |
|
|
'edit counter': 'edit counter', |
|
|
'katelinkHint': 'Count the countributions made by %s', |
|
|
'contribs': 'contribs', |
|
|
'contributions': 'contributions', |
|
|
'ContributionsHint': 'List the contributions made by %s', |
|
|
'tree': 'tree', |
|
|
'contribsTree': 'contribsTree', |
|
|
'contribsTreeHint': 'Explore %s\'s contributions by namespace and by article', |
|
|
'log': 'log', |
|
|
'user log': 'user log', |
|
|
'userLogHint': 'Show %s\'s user log', |
|
|
'arin': 'ARIN lookup', ///// ARIN lookup, block user or IP |
|
|
'Look up %s in ARIN whois database': 'Look up %s in the ARIN whois database', |
|
|
'unblockShort': 'un', |
|
|
'block': 'block', |
|
|
'block user': 'block user', |
|
|
'IpblocklistHint': 'Unblock %s', |
|
|
'BlockipHint': 'Prevent %s from editing', |
|
|
'block log': 'block log', |
|
|
'blockLogHint': 'Show the block log for %s', |
|
|
///////////////////////////////////// |
|
|
// Popups setup |
|
|
///////////////////////////////////// |
|
|
'Display navigation links at the top of the popup': 'Display navigation links at the top of the popup', |
|
|
'Download preview data': 'Download preview data from the Misplaced Pages servers', |
|
|
'Load images': 'Load images', |
|
|
'Never download extra stuff for images/previews': 'Never download extra stuff for images/previews', |
|
|
'Only start downloading when told to do so': 'Only start downloading when told to do so', |
|
|
'Open full-size image': 'Open full-size image', |
|
|
'Preview only on click': 'Preview only on click', |
|
|
'Show/hide options': 'Show/hide options', |
|
|
'Show image previews': 'Show image previews', |
|
|
'Show navigation links': 'Show navigation links', |
|
|
'Show page summary data': 'Show page summary data', |
|
|
'Show previews': 'Show previews', |
|
|
'Show summary data': 'Show summary data', |
|
|
'Show text previews': 'Show text previews', |
|
|
'Simple popups': 'Simple popups', |
|
|
'Toggle this option': 'Toggle this option', |
|
|
'cookies': 'cookies', |
|
|
'Use cookies to store popups options': 'Use cookies to store popups options', |
|
|
'zxy': 'zxy' |
|
|
}; |
|
|
|
|
|
function popupString(str) { |
|
|
if (typeof popupStrings != 'undefined' && popupStrings && popupStrings) return popupStrings; |
|
|
if (pg.string) return pg.string; |
|
|
return str; |
|
|
} |
|
|
|
|
|
function simplePrintf(str, subs) { |
|
|
if (!subs) return str; |
|
|
var ret=; |
|
|
var s=str.split('%s'); |
|
|
var i=0; |
|
|
do { |
|
|
if (i >= subs.length) { ret.push(s.join('%s')); break; } |
|
|
ret.push(s.shift()); |
|
|
if (s.length == 0) break; |
|
|
ret.push(subs); |
|
|
++i; |
|
|
} while (s.length > 0); |
|
|
return ret.join(''); |
|
|
} |
|
|
function tprintf(str,subs) { |
|
|
if (typeof subs != typeof ) { subs = ; } |
|
|
return simplePrintf(popupString(str), subs); |
|
|
} |
|
|
|
|
|
// ENDFILE: strings.js</pre> |
|