Benutzer:P.Copp/scripts/parser.js
< Benutzer:P.Copp | scripts
Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.
- Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
- Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
- Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
/*************************************************************************************************
* parser.js
*
* Wikitext parsing library for MediaWiki
* To be documented...
*/
if( !String.prototype.trim ) {
String.prototype.trim = function() { return this.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); };
}
mw.api = new function() {
// returns an array [getdata, postdata]
function encodeQuery( obj ) {
var getData = '';
var postData = '';
for( var name in obj ) if( has.call( obj, name ) ) {
if( typeof obj[name] === 'object' ) {
obj[name] = obj[name].join( '|' );
}
var pair = encodeURIComponent( name ) + '=' + encodeURIComponent( obj[name] );
if( pair.length + getData.length > 2000 ) {
postData += ( postData && '&' ) + pair;
} else {
getData += ( getData && '&' ) + pair;
}
}
return [getData, postData];
}
function mergeQuery( q1, q2 ) {
if( !q2 ) return q1;
var res = q1 || {};
for( var key in q2 ) if( has.call( q2, key ) ) {
if( has.call( res, key ) ) {
if( !typeof res[key] == 'object' ) res[key] = [res[key]];
if( !typeof q2[key] == 'object' ) q2[key] = [q2[key]];
var unique = {};
for( var i = 0; i < res[key].length; i++ ) unique[res[key][i]] = true;
for( var i = 0; i < q2[key].length; i++ ) {
if( !unique[q2[key][i]] ) res[key].push( q2[key][i] );
}
} else res[key] = q2[key];
}
return res;
};
function walkTree( root, list, end ) {
if( !end || end < 0 ) var end = ( end || 0 ) + list.length;
var node = root;
for( var i = 0; i < end; i++ ) {
if( !has.call( node, list[i] ) ) node[list[i]] = {};
node = node[list[i]];
};
return node;
};
// Ajax function
this.call = function( query, callback ) {
var data = encodeQuery( query );
jQuery.ajax( {
url : wgScriptPath + '/api.php?format=json&' + data[0],
type : data[1] || query.action === 'edit' ? 'POST' : 'GET',
success : callback,
data : data[1] ? data[1] : undefined
} );
};
var BatchQuery = this.BatchQuery = function() {
this.items = {};
this.isEmpty = true;
};
BatchQuery.prototype = {
addItem : function( /* ... */ ) {
this.isEmpty = false;
var obj = walkTree( this.items, arguments, -1 );
obj[arguments[arguments.length - 1]] = true;
},
load : function( callback, cache ) {
if( this.isEmpty ) {
callback();
return;
}
this.queries = 0;
this.callback = callback;
this.cache = cache || mw.api.cache;
this.doQueries();
},
doQueries : function() {
var uniq = ( Math.random() * Math.pow( 2, 32 ) ).toString( 16 );
var metaQ = [];
var titleQ = [];
var infoQ = false;
for( var name in this.items ) if( has.call( this.items, name ) ) {
switch( name ) {
case 'page':
this.addQueries( titleQ, this.items[name], 'titles', {
action : 'query',
prop : 'info|imageinfo',
iiprop : 'url|mime|size'
} );
break;
case 'text':
this.addQueries( titleQ, this.items[name], 'titles', {
action : 'query',
rvprop : 'timestamp|content',
prop : 'revisions|info|imageinfo',
redirects : '',
iiprop : 'url|mime|size'
} );
break;
case 'message':
for( var lang in this.items[name] ) if( this.items[name].hasOwnProperty( lang ) ) {
this.addQueries( metaQ, this.items[name][lang], 'ammessages', {
action : 'query',
meta : ['allmessages'],
amlang : lang
} );
}
break;
case 'magicwords':
infoQ = mergeQuery( infoQ, {
action : 'query',
meta : ['siteinfo'],
siprop : ['magicwords','general']
} );
break;
case 'languages':
infoQ = mergeQuery( infoQ, {
action : 'query',
meta : ['siteinfo'],
siprop : ['languages']
} );
break;
case 'parsedText':
var arr = ['<div>'];
for( var text in this.items[name] ) if( this.items[name].hasOwnProperty( text ) ) {
arr.push( '\n' + text );
}
arr.push( '\n</div>' );
this.doQuery( {
action : 'parse',
prop : 'text',
text : arr.join( uniq ),
uniq : uniq
} );
break;
case 'expandedText':
var arr = [];
for( var text in this.items[name] ) if( this.items[name].hasOwnProperty( text ) ) {
arr.push( text );
}
this.doQuery( {
action : 'expandtemplates',
text : arr.join( uniq ),
uniq : uniq
} );
break;
}
}
if( titleQ[0] ) {
titleQ[0].intoken = 'edit';
}
metaQ[0] = mergeQuery( metaQ[0], infoQ );
for( var i = 0; i < metaQ.length; i++ ) {
this.doQuery( mergeQuery( metaQ[i], titleQ[i] ) );
}
for( ; i < titleQ.length; i++ ) this.doQuery( titleQ[i] );
},
addQueries : function( arr, keys, name, query ) {
query[name] = [];
for( var key in keys ) if( has.call( keys, key ) ) {
query[name].push( key );
if( query[name].length == 50 ) {
arr.push( mergeQuery( false, query ) );
query[name] = [];
}
}
if( query[name].length ) arr.push( query );
},
doQuery : function( query ) {
if( !query ) return;
this.queries++;
var that = this;
mw.api.call( query, function( res ) {
that.cache.storeResult( res, query );
if( !--that.queries && that.callback ) {
that.callback();
}
} );
}
};
var Cache = this.Cache = function() {
this.items = {};
}
Cache.prototype = {
getItem : function( type /* ,... */ ) {
if( type === 'text' ) {
return this.getText( arguments[1] );
}
var obj = walkTree( this.items, arguments, -1 );
var key = arguments[arguments.length - 1];
return has.call( obj, key ) ? obj[key] : null;
},
getText : function( title ) {
var pi = this.getItem( 'page', title );
if( pi && pi.redirect ) {
pi = this.getItem( 'page', pi.redirect );
}
return pi && pi.missing !== '' && pi.invalid !== '' && ( pi.revisions || null ) && ( pi.revisions[0]['*'] || '' );
},
getTarget : function( title ) {
var pi = this.getItem( 'page', title );
return ( pi && pi.redirect ) || title;
},
getItemNow : function( /* ..., callback */ ) {
var cb = arguments[arguments.length - 1];
arguments.length--;
var item = this.getItem.apply( this, arguments );
if( item === null ) {
var that = this, args = arguments;
var bq = new BatchQuery;
bq.addItem.apply( bq, args );
bq.load( function() {
cb( that.getItem.apply( that, args ) );
}, this );
} else {
cb( item );
}
},
storeItem : function( /* ... */ ) {
var obj = walkTree( this.items, arguments, -2 );
var key = arguments[arguments.length - 2];
var val = arguments[arguments.length - 1];
if( typeof val === 'object' && has.call( obj, key ) ) {
obj[key] = jQuery.extend( obj[key], val );
} else {
obj[key] = val;
}
},
storeResult : function( res, query ) {
if( !res ) return;
if( res.query ) {
var q = res.query;
if( q.pages ) {
for( var pid in q.pages ) if( has.call( q.pages, pid ) ) {
var p = q.pages[pid];
this.storeItem( 'page', p.title, p );
if( has.call( p, 'edittoken' ) ) {
this.storeItem( 'token', p.edittoken );
}
}
}
if( q.redirects ) {
var r = q.redirects;
for( var i = 0; i < r.length; i++ ) {
this.storeItem( 'page', r[i].from, { redirect : r[i].to } );
}
}
if( q.normalized ) {
var n = q.normalized;
for( var i = 0; i < n.length; i++ ) {
this.storeItem( 'page', n[i].from, { normalized : n[i].to } );
}
}
if( q.general ) {
this.storeItem( 'general', q.general );
}
if( q.magicwords ) {
this.storeItem( 'magicwords', q.magicwords );
}
if( q.languages ) {
var langs = {};
for( var i = 0; i < q.languages.length; i++ ) {
langs[q.languages[i].code] = q.languages[i]['*'];
}
this.storeItem( 'languages', langs );
}
if( q.allmessages ) {
var lang = ( query && query.amlang ) || wgContentLanguage;
for( var i = 0; i < q.allmessages.length; i++ ) {
this.storeItem( 'message', lang, q.allmessages[i].name, q.allmessages[i]['*'] );
}
}
}
if( res.expandtemplates ) {
var inputTexts = query.text.split( query.uniq );
var outputTexts = res.expandtemplates['*'].split( query.uniq );
if( inputTexts.length != outputTexts.length ) throw 'Unmatched results for expandtemplates';
for( var i = 0; i < inputTexts.length; i++ ) {
this.storeItem( 'expandedText', inputTexts[i], outputTexts[i] );
}
}
if( res.parse ) {
var inputTexts = query.text.split( query.uniq );
var outputTexts = res.parse['text']['*'].split( query.uniq );
if( inputTexts.length != outputTexts.length ) throw 'Unmatched results for parse';
for( var i = 0; i < inputTexts.length; i++ ) {
this.storeItem( 'parsedText', inputTexts[i].substring( 1 ), outputTexts[i].replace( /^\n/, '' ) );
}
}
if( has.call( query, 'titles' ) ) {
var titles = query.titles.split( '|' );
for( var i = 0; i < titles.length; i++ ) {
if( !has.call( this.items, 'page' ) || !has.call( this.items.page, titles[i] ) ) {
this.storeItem( 'page', titles[i], {} );
}
}
}
}
};
this.cache = new Cache;
var has = Object.prototype.hasOwnProperty;
}();
mw.parser = new function() {
// Default namespaces
var canonicalNsIds = {
'media' : -2,
'special' : -1,
'' : 0,
'talk' : 1,
'user' : 2,
'user_talk' : 3,
'project' : 4,
'project_talk' : 5,
'file' : 6,
'file_talk' : 7,
'mediawiki' : 8,
'mediawiki_talk' : 9,
'template' : 10,
'template_talk' : 11,
'help' : 12,
'help_talk' : 13,
'category' : 14,
'category_talk' : 15
};
var has = Object.prototype.hasOwnProperty;
var Base = this.Base = function( title ) {
this.contextTitle = this.getPage( title || wgPageName ).ptitle;
this.utcDate = new Date();
var offset = this.utcDate.getTimezoneOffset();
this.utcDate.setTime( this.utcDate.getTime() + offset * 60000 );
this.batch = new mw.api.BatchQuery;
};
Base.prototype = {
extensionTags : [
'categorytree',
'charinsert',
'gallery',
'hiero',
'imagemap',
'inputbox',
'math',
'nowiki',
'poem',
'pre',
'ref',
'references',
'source',
'syntaxhighlight',
'timeline'
],
linkTrailRX : /[a-zäöüß]*/g,
urlProtocols : wgUrlProtocols.split( '|' ),
articlePath : wgArticlePath,
capitalLinks : true,
contentLanguage : wgContentLanguage,
userLanguage : wgUserLanguage,
isRTL : document.body.className.indexOf( ' ltr ' ) === -1,
script : wgScript,
server : wgServer,
stylePath : stylepath,
thumbSize : 220, // TODO: extract from mw.user.options
version : wgVersion,
cache : mw.api.cache,
namespaceNames : wgFormattedNamespaces,
namespaceIds : jQuery.extend( {}, canonicalNsIds, wgNamespaceIds ),
numberTransformTable : { ',' : '.', '.' : ',' },
getExtensionTags : function() { return this.extensionTags; },
getLinkTrailRX : function() { return this.linkTrailRX; },
getUrlProtocols : function() { return this.urlProtocols; },
getContextTitle : function() { return this.contextTitle; },
getArticlePath : function() { return this.articlePath; },
getCapitalLinks : function() { return this.capitalLinks; },
getContentLanguage : function() { return this.contentLanguage; },
getRTL : function() { return this.isRTL; },
getScript : function() { return this.script; },
getServer : function() { return this.server; },
getStylePath : function() { return this.stylePath; },
getThumbSize : function() { return this.thumbSize; },
getUTCDate : function() { return this.utcDate; },
getVersion : function() { return this.version; },
getItem : function( /* ... */ ) {
var item = this.cache.getItem.apply( this.cache, arguments );
if( item === null ) {
this.batch.addItem.apply( this.batch, arguments );
}
return item;
},
getMessage : function( lang, key, params ) {
var msg = this.getItem( 'message', lang, key );
if( msg && params ) {
// Replace message parameters "$1"...
if( typeof params === 'string' ) {
return msg.replace( /\$1/g, params );
} else {
for( var i = 0; i < params.length; i++ ) {
var rx = new RegExp( '\\$'+ ( i + 1 ), 'g' );
msg = msg.replace( rx, params[i] );
}
}
}
return msg;
},
getContentMessage : function( key, params ) {
return this.getMessage( this.contentLanguage, key, params );
},
getUserMessage : function( key, params ) {
return this.getMessage( this.userLanguage, key, params );
},
getExpandedText : function( text ) {
return this.getItem( 'expandedText', text );
},
getFileDimensions : function( page ) {
var pi = this.getItem( 'page', page.ptitle );
if( pi && has.call( pi, 'imagerepository' ) ) {
var ii = pi.imageinfo;
return ii ? { width : ii[0].width, height : ii[0].height } : false;
} else if( pi === null ) {
return { width : 71, height : 92 };
} else {
return false;
}
},
getFileUrl : function( page ) {
var pi = this.getItem( 'page', page.ptitle );
return pi && pi.imageinfo && pi.imageinfo[0].url;
},
getLanguageName : function( code ) {
var langs = this.getItem( 'languages' );
return langs && has.call( langs, code ) && langs[code];
},
getLocalDate : function() {
if( !this.localDate ) {
var general = this.getItem( 'general' );
this.localDate = general && new Date( this.utcDate.getTime() + general.timeoffset * 60000 );
}
return this.localDate;
},
getPageExists : function( page ) {
if( page.iw || !page.title || page.ns === -1 ) {
return true;
} else {
// TODO: Media NS
var pi = this.getItem( 'page', page.ptitle );
return pi && pi.missing !== '' && pi.invalid !== '';
}
},
getRedirectTarget : function( page ) {
var pi = this.getItem( 'page', page.ptitle );
return pi && ( pi.redirect === '' || pi.redirect );
},
getParsedText : function( text ) {
return this.getItem( 'parsedText', text );
},
getPageText : function( page ) {
return this.getItem( 'text', page.ptitle );
},
getMagicWords : function() {
if( this.mag ) {
return this.mag;
}
var mi = this.getItem( 'magicwords' );
if( !mi ) {
return null;
}
this.mag = {
'var' : [{}, {}],
func : [{}, {}],
mod : [{}, {}],
underscore : [{}, {}],
image : [{}, {}],
other : [{}, {}],
imageRX : [],
underscoreRX : []
};
for( var i = 0; i < mi.length; i++ ) {
var flags = magicWordFlags[mi[i].name];
var cs = mi[i]['case-sensitive'] === '' ? 1 : 0;
for( var j = 0; j < mi[i].aliases.length; j++ ) {
var alias = cs ? mi[i].aliases[j] : mi[i].aliases[j].toLowerCase();
if( !flags ) {
this.mag.other[cs][alias] = mi[i].name;
}
if( flags & MAG_VARIABLE ) {
this.mag['var'][cs][alias] = mi[i].name;
}
if( flags & MAG_PFUNC ) {
this.mag.func[cs][alias.replace( /:$/, '' )] = mi[i].name;
}
if( flags & MAG_PFUNC_HASH ) {
this.mag.func[cs]['#' + alias.replace( /:$/, '' )] = mi[i].name;
}
if( flags & MAG_MODIFIER ) {
this.mag.mod[cs][alias] = mi[i].name;
}
if( flags & MAG_UNDERSCORE ) {
this.mag.underscore[cs][alias] = mi[i].name;
this.mag.underscoreRX.push( magic2RX( alias, cs ) );
}
if( flags & MAG_IMG_PARAM ) {
this.mag.image[cs][alias.replace( /\$1/, '' )] = mi[i].name.substring( 4 );
this.mag.imageRX.push( magic2RX( alias, cs, 'triple' ) );
}
}
}
this.mag.imageRX = new RegExp( '^(?:' + this.mag.imageRX.join( '|' ) + ')$' );
this.mag.underscoreRX = new RegExp( this.mag.underscoreRX.join( '|' ), 'g' );
return this.mag;
},
matchImageParam : function( s ) {
var mag = this.getMagicWords();
if( !mag ) {
return null;
}
var match = s.match( mag.imageRX );
if( !match ) {
return ['caption', s];
}
for( var i = 1; i < match.length; i += 3 ) {
if( match[i] || match[i + 2] ) {
return [this.matchMagic( 'image', match[i] + match[i + 2] ), match[i + 1].trim()];
}
}
},
matchMagic : function( type, s ) {
var mag = this.getMagicWords();
if( !mag ) {
return null;
}
if( has.call( mag[type][1], s ) ) {
return mag[type][1][s];
}
var lc = s.toLowerCase();
return has.call( mag[type][0], lc ) && mag[type][0][lc];
},
getNamespaceName : function( index ) {
return has.call( this.namespaceNames, index ) && this.namespaceNames[index];
},
getNamespaceIndex : function( name ) {
return has.call( this.namespaceIds, name ) && this.namespaceIds[name];
},
getThumbUrl : function( page, width ) {
var url = this.getFileUrl( page );
if( !url ) {
return url;
}
var ext = page.title.substring( page.title.lastIndexOf( '.' ) + 1 );
var force = ext === 'svg' || ext === 'bmp';
if( this.getFileDimensions( page ).width <= width && !force ) {
return url;
}
var pos = url.lastIndexOf( '/' );
pos = url.lastIndexOf( '/', pos - 1 );
pos = url.lastIndexOf( '/', pos - 1 );
return url.substring( 0, pos ) + '/thumb' + url.substring( pos ) + '/' + width + 'px-'
+ this.phpUrlencode( page.title ).replace( /\+/g, '_' ) + ( force ? '.png' : '' );
},
formatNum : function( s, nocommafy ) {
var s = s + '';
//First step: commafy
if( !nocommafy ) {
var rx = /(^|[^\d\.])(\d{4,})/g, m;
var result = '', index = 0;
while( m = rx.exec( s ) ) {
result += s.substring( index, m.index + m[1].length );
index = m.index + m[0].length;
result += commafy( m[2] );
}
s = result + s.substring( index );
}
//Second step: transform to local representation
if( !this.rvTable ) {
this.rvTable = {};
for( i in this.numbertransformTable ) if( has.call( this.numberTransformTable, i ) ) {
this.rvTable[this.numberTransformTable[i]] = i;
}
}
var local = '';
for( var i = 0; i < s.length; i++ ) {
var c = s.charAt( i );
local += has.call( this.rvTable, c ) ? this.rvTable[c] : c;
}
return local;
function commafy( s ) {
for( var i = s.length % 3, bits = []; i <= s.length; i += 3 ) {
if( i > 0 ) {
bits.push( s.substring( i - 3, i ) );
}
}
return bits.join( ',' );
}
},
parseFormattedNumber : function( s ) {
var plain = '';
for ( var i = 0; i < s.length; i++ ) {
var c = s.charAt( i );
plain += has.call( this.numberTransformTable, c )
? this.numberTransformTable[c]
: c;
}
return plain.replace( /,/g, '' );
},
lc : function( s ) { return s.toLowerCase(); },
uc : function( s ) { return s.toUpperCase(); },
lcfirst : function( s ) {
if( !s ) {
return s;
}
return this.lc( s.charAt( 0 ) ) + s.substring( 1 );
},
ucfirst : function( s ) {
if( !s ) {
return s;
}
return this.uc( s.charAt( 0 ) ) + s.substring( 1 );
},
// Construct a page info object from a string. Only some basic validity checks are performed,
getPage : function( s, defaultNS, subpage ) {
if( !s ) {
return false;
}
try{
s = decodeURIComponent( s );
} catch( e ) {}
// check for illegal chars
if( s.match( /[<>\[\]\|\{\}\x00-\x1F\xFF\x7F]/ ) ) {
return false;
}
// normalize whitespace
// TODO: check if this can be replaced by a simple [_\s]
s = s.replace( /[ _\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]+/g, ' ' ).trim();
// strip bidi-characters
s = s.replace( /[\u200e\u200f\u202a-\u202e]+/g, '' );
// Fragments
var fragment = false;
var pos = s.indexOf( '#' );
if( pos > -1 ) {
fragment = s.substring( pos + 1 );
s = s.substring( 0, pos ).trim();
}
if( subpage && s.charAt( 0 ) === '/' ) {
s = this.getContextTitle() + s;
}
// Leading colon overrides the specified default namespace
if( s.charAt( 0 ) === ':' ) {
s = s.substring( 1 );
defaultNS = 0;
}
// Namespace and interwiki
var ns = false, iw = false;
var pos = s.indexOf( ':' );
if( pos > -1 ) {
var prefix = this.lc( s.substring( 0, pos ).trim() ).replace( / /g, '_' );
ns = this.getNamespaceIndex( prefix );
if( ns === false && this.getLanguageName( prefix ) ) { // TODO: other interwikis
ns = 0;
iw = prefix;
}
}
if( ns === false ) {
ns = defaultNS || 0;
var title = s.trim();
} else {
var title = s.substring( pos + 1 ).trim();
}
if( this.getCapitalLinks() ) {
title = this.ucfirst( title );
}
if( !title && !iw && fragment === false ) {
return false;
}
var nsName = this.getNamespaceName( ns );
var ptitle = nsName ? nsName + ':' + title : title;
return { ns : ns, title : title, ptitle : ptitle, iw : iw, fragment : fragment };
}
};
var Preprocessor = this.Preprocessor = function() { Base.apply( this, arguments ); };
Preprocessor.prototype = jQuery.extend( new Base, {
// Mimic the behavior of PHP's urlencode function
phpUrlencode : function( s ) {
return encodeURIComponent(s)
.replace( /%20/g, '+' ).replace( /!/g, '%21' )
.replace( /'/g, '%27' ).replace( /\(/g, '%28' )
.replace( /\)/g, '%29' ).replace( /\*/g, '%2A' )
.replace( /~/g, '%7E' );
},
// This one imitates MediaWiki's wfUrlencode except that spaces are already changed to underscores
titleEncode : function( s ) {
return encodeURI( s.replace( / /g, '_' ) )
.replace( /\+/g, '%2B' ).replace( /'/g, '%27' )
.replace( /~/g, '%7E' ).replace( /#/g, '%23' )
.replace( /&/g, '%26' ).replace( /=/g, '%3D' )
.replace( /\?/g, '%3F' );
},
anchorEncode : function( s ) {
return this.phpUrlencode( s ).replace( /%/g, '.' ).replace( /\+/g, '_' ).replace( /\.3A/g, ':' );
},
padString : function( s, len, pad, dir ) {
if( !pad ) {
return s;
}
var remain = Math.min( len, 500 ) - ( s + '' ).length;
var padding = '';
while( remain > 0 ) {
padding += pad.substring( 0, remain );
remain -= pad.length;
}
return dir === 'right' ? s + padding : padding + s;
},
/**************************************************************************************************
* Turns a wikitext string into a document tree
* The returned data structure is a bit more compact than a real XML DOM, so
* some memory is saved, when the extra stuff is not needed.
* Use PPFrameXML to expand the compact form into an XML string
* with the same structure as returned by MediaWiki
*
* The returned object has the following structure:
* domnode = {
* type : ('root'|'link'|'template'|'tplarg'|'h'|'comment'|'ignore'|'ext'),
* offset: int,
* len : int,
* parts : [ [('text'|node)*], ... ],
* index, level : int, //only for heading nodes
* extname: 'name', //only for ext nodes
* }
*/
preprocess : function( text, forInclusion ) {
if( typeof text !== 'string' ) {
return text;
}
var start = 0;
var stack = [];
var top = new PPNode( 'root', text, 0 );
var headings = 0;
var enableOnlyInclude = false;
var match, pos, node;
//Line 145-156
if( forInclusion ) {
pos = text.indexOf( '<onlyinclude>' );
if( pos > -1 && text.indexOf( '</onlyinclude>' ) > -1 ) {
enableOnlyInclude = true;
node = new PPNode( 'ignore', text, 0 );
node.append( text.substring( 0, pos + 13 ) );
top.append( node.finish( pos + 13 ) );
start = pos + 13;
}
}
var ignoredtag = forInclusion ? /includeonly/i : /noinclude|onlyinclude/i;
var ignoredelement = forInclusion ? 'noinclude' : 'includeonly';
//Construct our main regex
var tags = ['noinclude', 'includeonly', 'onlyinclude'];
var tags = '(' + tags.concat( this.getExtensionTags() ).join( '|' ) + ')';
var specials = '\\{\\{+|\\[\\[+|\\}\\}+|\\]\\]+|\\||(\n)(=*)|(^=+)';
var regex = RegExp( specials + '|<' + tags + '(?:\\s[^>]*)?\\/?>|<\\/'
+ tags + '\\s*>|<!--|$', 'ig' );
for( ; ; ) {
regex.lastIndex = start;
match = regex.exec( text );
var s = match[0];
if( s == '<!--' ) { //Comment found
var span = getCommentSpan( match.index );
top.append( text.substring( start, span[0] ) );
start = span[1];
node = new PPNode( 'comment', text, span[0] );
node.append( text.substring( span[0], span[1] ) );
top.append( node.finish( span[1] ) );
continue;
}
//Process all text between the last and the current token
if( match.index > start ) {
top.append( text.substring( start, match.index ) );
}
start = regex.lastIndex;
if( ( match[1] || !s ) && top.type == 'h' ) {
//Newline or EOT found
//Check if we can close a heading
var next = stack.pop();
if( top.closing ) {
//Some extra info for headings
top.index = ++headings;
top.level = Math.min( top.count, top.closing, 6 );
next.append( top.finish( match.index ) );
} else {
//No correct closing, break the heading and continue
top.breakAndAppendTo( next );
}
top = next;
}
if( !s ) {
break; //End of text
}
if( match[1] || match[3] ) {
if( match[1] ) {
top.append( '\n' );
}
if( match[2] || match[3] ) {
//Check if we can open a heading
var len = ( match[2] || match[3] ).length;
//Line 352-355: Single '=' within a template part isn't treated as heading
if( len > 1 || top.type != '{' || top.parts.length == 1 || top.cur.split ) {
stack.push( top );
top = new PPNode( 'h', text, match.index + ( match[1] ? 1 : 0 ), len );
//Line 447-455: More than two '=' means we already have a correct closing
top.closing = Math.floor( ( len - 1 ) / 2 );
}
top.append( match[2] || match[3] );
}
continue;
}
if( match[4] ) { //Open <tag /?> found
if( match[4].match( ignoredtag ) ) {
node = new PPNode( 'ignore', text, match.index );
node.append( s );
top.append( node.finish( start ) );
continue;
}
var lc = match[4].toLowerCase();
if( lc == 'onlyinclude' ) {
//This can only happen, if we're in template mode (forInclusion=true) and
//the token we found is sth. like '<ONLYINCLUDE >'(i.e. unusual case or whitespace)
//Output it literally then, to match MediaWiki's behavior
top.append( s );
} else {
if( lc === ignoredelement ) {
node = new PPNode( 'ignore', text, match.index );
} else {
node = new PPNode( 'ext', text, match.index );
node.extname = lc;
}
node.append( s );
if( s.charAt( s.length - 2 ) == '/' ) {
//Immediately closed tag (e.g. <nowiki />)
top.append( node.finish( start ) );
} else {
//For ext nodes, we split the opening tag, content and closing tag into
//separate parts. This is to simplify further processing since we already have
//the information after all
if( lc !== ignoredelement ) {
node.parts.push( node.cur = [] );
}
//Search for the matching closing tag
var endRX = RegExp( '<\\/' + lc + '\\s*>|$', 'ig' );
endRX.lastIndex = start;
var endMatch = endRX.exec( text );
node.append( text.substring( start, endMatch.index ) );
if( lc !== ignoredelement ) {
node.parts.push( node.cur = [] );
}
node.append( endMatch[0] );
start = endRX.lastIndex;
top.append( node.finish( start ) );
}
}
continue;
} else if( match[5] ) { //Close </tag> found
if( match[5].match( ignoredtag ) ) {
node = new PPNode( 'ignore', text, match.index );
node.append( s );
top.append( node.finish( start ) );
} else if( enableOnlyInclude && s == '</onlyinclude>' ) {
//For onlyinclude, the closing tag is the start of the ignored part
node = new PPNode( 'ignore', text, match.index );
pos = text.indexOf( '<onlyinclude>', start );
if( pos === -1 ) {
pos = text.length - 13;
}
node.append( text.substring( match.index, pos + 13 ) );
top.append( node.finish( pos + 13 ) );
start = pos + 13;
} else {
//We don't have a matching opening tag, so output the closing literally
top.append( s );
}
continue;
}
//Special token found: '|', {+, [+, ]+, }+
var ch = s.charAt( 0 );
if( ch == '|' ) {
//For brace nodes, start a new part
if( top.type == '[' || top.type == '{' ) {
top.parts.push( top.cur = [] );
} else {
top.append( s );
}
} else if( ch == '{' || ch == '[' ) {
stack.push( top );
top = new PPNode( ch, text, match.index, s.length );
} else { // '}' or ']'
//Closing brace found, try to close as many nodes as possible
var open = ch == '}' ? '{' : '[';
var len = s.length;
while( top.type == open && len >= 2 ) {
while( len >= 2 && top.count >= 2 ) {
//Find the longest possible match
var mc = Math.min( len, top.count, open == '{' ? 3 : 2 );
top.count -= mc;
len -= mc;
//Record which type of node we found
top.type = open == '[' ? 'link' : mc == 2 ? 'template' : 'tplarg';
if( top.count >= 2 ) {
//if we're still open, create a new parent and embed the node there
var child = top;
top = new PPNode( open, text, child.offset, child.count );
top.append( child );
//Correct the child offset by the number of remaining open braces
child.offset += top.count;
child.finish( match.index + s.length - len );
}
}
if( top.count < 2 ) {
//Close the current node
var next = stack.pop();
//There might be one remaining brace open, add it to the parent first
if( top.count === 1 ) {
next.append( open );
}
top.offset += top.count;
next.append( top.finish( match.index + s.length - len ) );
top = next;
}
}
//Remaining closing braces are added as plain text
if( len ) {
top.append( ( new Array( len + 1 ) ).join( ch ) );
}
}
}
//We've reached the end, expand any remaining open pieces
stack.push( top );
for( var i = 1; i < stack.length; i++ ) {
stack[i].breakAndAppendTo( stack[0] );
}
return stack[0].finish( match.index );
//Helper function to calculate the start and end position of a comment
//We need this, because comments sometimes include the preceding and trailing whitespace
//See lines 275-313
function getCommentSpan( start ) {
var endpos = text.indexOf( '-->', start + 4 );
if( endpos == -1 ) {
return [start, text.length];
}
for( var lead = start - 1; text.charAt( lead ) == ' '; lead-- );
if( text.charAt( lead ) != '\n' ) {
return [start, endpos + 3];
}
for( var trail = endpos + 3; text.charAt( trail ) == ' '; trail++ );
if( text.charAt( trail ) != '\n' ) {
return [start, endpos + 3];
}
return [lead + 1, trail + 1];
}
},
expand : function( obj ) {
if( obj === undefined ) {
throw 'Impossible';
}
if( !obj.type ) {
return this.expandText( obj );
}
var func = this[obj.type];
try{
var result = func.call( this, obj );
} catch( e ) {
if( !this.safeMode && e === safeModeMissing ) {
var result = this.getReplacement( obj );
} else {
throw e;
}
}
return result;
},
root : function( obj ) { return this.expandPart( obj.parts[0] ); },
link : function( obj ) {
return this.expandText( '[[' ) + this.expandParts( obj.parts, '|' ) + this.expandText( ']]' );
},
template : function( obj ) {
return this.expandText( '{{' ) + this.expandParts( obj.parts, '|' ) + this.expandText( '}}' );
},
tplarg : function( obj ) {
return this.expandText( '{{{' ) + this.expandParts( obj.parts, '|' ) + this.expandText( '}}}' );
},
h : function( obj ) { return this.expandPart( obj.parts[0] ); },
comment : function( obj ) { return this.expandText( obj.parts[0][0] ); },
ignore : function( obj ) { return this.expandText( obj.parts[0][0] ); },
ext : function( obj ) { return this.expandParts( obj.parts ); },
getReplacement : function( obj ) {
return ( new Preprocessor ).expand( obj );
},
expandText : function( s ) { return s; },
expandPart : function( part, side ) {
var start = side === 'r' ? part.split + 1 : 0;
var end = side === 'l' ? part.split : part.length;
if( start === end - 1 ) {
return this.expand( part[start] );
}
var result = '';
for( var i = start; i < end; i++ ) {
result += this.expand( part[i] );
}
return result;
},
expandParts : function( parts, joiner ) {
var result = '';
for( var i = 0; i < parts.length; i++ ) {
if( joiner && i ) {
result += this.expandText( joiner );
}
result += this.expandPart( parts[i] );
}
return result;
},
extractParams : function( obj ) {
var params = { //numbered and named arguments must be stored separately
numbered : {},
named : {},
obj : obj
};
var num = 0;
for( var i = 1; i < obj.parts.length; i++ ) {
if( obj.parts[i].split ) {
var name = this.expandArgSafe( obj, i, 'l' );
params.named[name] = i;
} else {
params.numbered[++num] = i;
}
}
return params;
},
getParam : function( params, name ) {
for( var i = 0; i < 2; i++ ) {
var type = i ? 'named' : 'numbered';
var param = params[type][name];
if( param === undefined ) {
continue;
}
if( param.length !== undefined ) {
return param; //cached
}
//Param exists, but not yet expanded. Expand it and put the result in the cache
params[type][name] = this.expandArgSafe( params.obj, param, i ? 'r' : '' );
return params[type][name];
}
return false;
},
expandArg : function( obj, num, side ) {
var part = obj.parts[num];
if( !part ) {
return '';
}
return side ? this.expandPart( part, side ).trim() : this.expandPart( part );
},
expandArgSafe : function( obj, num, side ) {
if( this.safeMode ) {
return this.expandArg( obj, num, side );
}
this.safeMode = true;
try{
var result = this.expandArg( obj, num, side );
} catch( e ) {
this.safeMode = false;
throw e;
}
this.safeMode = false;
return result;
}
} );
var pp = new Preprocessor;
this.getPage = function() { return pp.getPage.apply( pp, arguments ); };
this.expandWiki = function() { return pp.expand.apply( pp, arguments ); };
this.preprocess = function() { return pp.preprocess.apply( pp, arguments ); };
/**************************************************************************************************
* PreprocessorXML : Transforms a document tree to an XML string
*/
var PreprocessorXML = this.PreprocessorXML = function() { Preprocessor.apply( this, arguments ); };
PreprocessorXML.prototype = jQuery.extend( new Preprocessor, {
expandText : function( s ) {
return s.replace( /&/g, '&' ).replace( /</g, '<' ).replace( />/g, '>' );
},
root : function( obj ) {
return XML( 'root', this.expandPart( obj.parts[0] ) );
},
template : function( obj ) {
var attr = obj.lineStart ? ' lineStart="1"' : '';
return this.XML( 'template', this.expandTemplateParts( obj.parts ), attr );
},
tplarg : function( obj ) {
var attr = obj.lineStart ? ' lineStart="1"' : '';
return this.XML( 'tplarg', this.expandTemplateParts( obj.parts ), attr );
},
h : function( obj ) {
return this.XML( 'h', this.expandPart( obj.parts[0] ),
' level="' + obj.level + '" i="' + obj.index + '"' );
},
comment : function( obj ) {
return this.XML( 'comment', this.expand( obj.parts[0][0] ) );
},
ignore : function( obj ) {
return this.XML( 'ignore', this.expand( obj.parts[0][0] ) );
},
ext : function( obj ) {
var m = obj.parts[0][0].match( /<([^\s\/>]*)([^>]*)>/ );
var content = this.XML( 'name', this.expand( m[1] ) );
content += this.XML( 'attr', this.expand( m[2].replace( /\/$/, '' ) ) );
if( obj.parts[1] ) content += this.XML( 'inner', this.expand( obj.parts[1][0] || '' ) );
if( obj.parts[2] && obj.parts[2][0] )
content += this.XML( 'close', this.expand( obj.parts[2][0] ) );
return this.XML( 'ext', content );
},
expandTemplateParts : function( parts ) {
var result = this.XML( 'title', this.expandPart( parts[0] ) );
var num = 1;
for( var i = 1; i < parts.length; i++ ) {
if( parts[i].split ) {
var content = this.XML( 'name', this.expandPart( parts[i], 'l' ) ) + this.expand( '=' );
content += this.XML( 'value', this.expandPart( parts[i], 'r' ) );
} else {
var content = this.XML( 'name', '', ' index="' + ( num++ ) + '"' );
content += this.XML( 'value', this.expandPart( parts[i] ) );
}
result += this.XML( 'part', content );
}
return result;
}
} );
this.expandXML = function( obj ) {
var pp = new PreprocessorXML;
if( !obj.type ) {
obj = pp.preprocess( obj );
}
return pp.expand.apply( pp, arguments );
};
/**************************************************************************************************
* TemplateExpander: Transform a document tree into expanded wikitext. Tries to approximate the
* behavior of MediaWiki's template expansion. Not all variables and parser functions have been
* implemented, though, others may work a bit differently from their original.
*/
var TemplateExpander = this.TemplateExpander = function() {
Preprocessor.apply( this, arguments );
this.stack = [];
this.domCache = {};
};
TemplateExpander.prototype = jQuery.extend( new Preprocessor, {
template : function( obj ) {
// Double brace expansion
var result = false;
var name = this.expandArgSafe( obj, 0 ).trim();
// Modifiers: subst, safesubst, msg...
var pos = name.indexOf( ':' );
if( pos > -1 ) {
var fname = this.safe( 'matchMagic', [ 'mod', name.substring( 0, pos + 1 ) ] );
if( fname === 'subst' ) {
return Preprocessor.prototype.template.apply( this, arguments );
} else if( fname ) {
name = name.substring( pos + 1 ).trim();
}
}
// Variables
var fname = this.safe( 'matchMagic', [ 'var', name ] );
if( fname ) {
result = this.expandFunc( obj, fname, false );
if( result !== false ) {
return result;
}
}
// Functions
var pos = name.indexOf( ':' );
if( pos > -1 ) {
var fname = this.safe( 'matchMagic', [ 'func', name.substring( 0, pos ) ] );
if( fname ) {
result = this.expandFunc( obj, fname, name.substring( pos + 1 ).trim() );
if( result !== false ) {
return result;
}
}
}
// Templates
var page = this.getPage( name, 10, true );
if( page && page.title && !page.iw && page.ns >= 0 ) {
return this.expandTemplatePage( obj, page );
} else {
return Preprocessor.prototype.template.apply( this, arguments );
}
},
tplarg : function( obj ) {
// Triple brace expansion
var name = this.expandArgSafe( obj, 0 ).trim();
if( this.params ) {
var current = this.params;
this.params = this.stack.pop();
try{
var value = this.getParam( current, name );
} catch( e ) {
this.stack.push( this.params );
this.params = current;
throw e;
}
this.stack.push( this.params );
this.params = current;
if( value !== false ) {
return value;
}
}
// No matching param found, try the default
if( obj.parts.length > 1 ) {
return this.expandArg( obj, 1 );
}
return Preprocessor.prototype.tplarg.apply( this, arguments );
},
ignore : function() { return ''; },
comment : function() { return ''; },
expandFunc : function( obj, name, arg ) {
var special = specialFuncs[name];
if( special === 'page' ) {
var arg = this.getPage( arg === false ? this.getContextTitle() : arg );
if( !arg ) {
return '';
}
} else if( special === 'utc' ) {
var arg = this.getUTCDate();
var name = 'var_' + name.substring( 7 );
} else if( special === 'local' ) {
var arg = this.safe( 'getLocalDate', [] );
var name = 'var_' + name.substring( 5 );
}
var result = null;
var func = this.parserFunctions[name];
if( func ) {
result = func.call( this, obj, arg );
}
if( result === null ) {
var call = Preprocessor.prototype.template.call( this, obj );
return this.safe( 'getExpandedText', [ call ] );
} else if( result === false ) {
return false;
} else {
//see Parser.php line 3026
if( !obj.lineStart && result.match( /^(?:\{\||:|;|#|\*)/ ) ) {
result = '\n' + result;
}
return result;
}
},
expandTemplatePage : function( obj, page ) {
if( !has.call( this.domCache, page.ptitle ) ) {
var text = this.safe( 'getPageText', [ page ] );
if( text === false && page.ns === 8 ) {
var text = this.safe( 'getContentMessage', [ page.title.toLowerCase() ] );
}
if( text === false ) {
return this.expandText( '[[:' + page.ptitle + ']]' );
}
this.domCache[page.ptitle] = this.preprocess( text, true );
}
var params = this.extractParams( obj );
params.title = this.getRedirectTarget( page ) || page.ptitle;
this.stack.push( this.params );
this.params = params;
try{
var result = this.expand( this.domCache[page.ptitle] );
} catch( e ) {
this.params = this.stack.pop();
throw e;
}
this.params = this.stack.pop();
//see Parser.php line 3026
if( !obj.lineStart && result.match( /^(?:\{\||:|;|#|\*)/ ) ) {
result = '\n' + result;
}
return result;
},
ext : function( obj ) {
var m = this.expandPart( obj.parts[0] ).match( /<([^\s\/>]*)([^>]*)>/ );
var inner = obj.parts[1] ? this.expandPart( obj.parts[1] ) : false;
var close = obj.parts[2] ? this.expandPart( obj.parts[2] ) : false;
return this.expandExtension( m[1], m[2], inner, close );
},
expandExtension : function( name, attr, inner, close ) {
return '<' + name + attr + '>' + ( inner || '' ) + ( close || '' );
},
safe : function( func, args ) {
var result = this[func].apply( this, args );
if( result === null ) {
throw safeModeMissing;
}
return result;
},
getLocalUrl : function( page, query ) {
var fragment = page.fragment ? '#' + this.anchorEncode( page.fragment ) : '';
if( !page.title && !page.iw && !query ) {
return '#' + this.anchorEncode( page.fragment );
} else if( !query ) {
return this.getArticlePath().replace( /\$1/, this.titleEncode( page.ptitle ) )
+ fragment;
} else {
return this.getScript() + '?title=' + this.titleEncode( page.ptitle ) + '&' + query
+ fragment;
}
},
// from Language::sprintfDate. returns null if it finds characters it can't handle yet
formatDate : function( date, format, rawToggle ) {
if( !format ) {
return format;
}
var result = '', num = false, raw = false;
for( var i = 0; i < format.length; i++ ) {
var code = format.charAt( i );
if( code === 'x' ) {
code += format.charAt( ++i );
}
switch( code ) {
case 'xi':
case 'xj':
case 'xk':
case 'xm':
case 'xo':
case 'xt':
case 'xr':
case 'xh':
case 'z' :
case 'W' :
case 't' :
case 'L' :
case 'o' :
case 'r' : //
return null;
case 'xg':
result += this.safe( 'getContentMessage', [ monthGenMessages[date.getMonth()] ] );
break;
case 'D' :
result += this.safe( 'getContentMessage', [ weekdayAbbrevMessages[date.getDay()] ] );
break;
case 'l' :
result += this.safe( 'getContentMessage', [ weekdayMessages[date.getDay()] ] );
break;
case 'F' :
result += this.safe( 'getContentMessage', [ monthMessages[date.getMonth()] ] );
break;
case 'M' :
result += this.safe( 'getContentMessage', [ monthAbbrevMessages[date.getMonth()] ] );
break;
case 'xx': result += 'x'; break;
case 'xn': raw = true; break;
case 'xN': var rawToggle = !rawToggle; break;
case 'd' : num = this.padString( date.getDate(), 2, '0' ); break;
case 'j' : num = date.getDate(); break;
case 'N' : num = date.getDay() || 7; break;
case 'w' : num = date.getDay(); break;
case 'm' : num = this.padString( date.getMonth() + 1, 2, '0' ); break;
case 'n' : num = date.getMonth() + 1; break;
case 'Y' : num = this.padString( date.getFullYear(), 4, '0' ); break;
case 'y' : num = this.padString( date.getYear(), 2, '0' ); break;
case 'a' : result += date.getHours() < 12 ? 'am' : 'pm'; break;
case 'A' : result += date.getHours() < 12 ? 'AM' : 'PM'; break;
case 'g' : num = date.getHours() % 12 || 12; break;
case 'G' : num = date.getHours(); break;
case 'h' : num = this.padString( date.getHours() % 12 || 12, 2, '0' ); break;
case 'H' : num = this.padString( date.getHours(), 2, '0' ); break;
case 'i' : num = this.padString( date.getMinutes(), 2, '0' ); break;
case 's' : num = this.padString( date.getSeconds(), 2, '0' ); break;
case 'c' : result += this.formatDate( date, 'Y-m-dTH:i:s+00:00', true ); break;
case 'U' : num = Math.floor( date.getTime() / 1000 ); break;
case '\\': result += format.charAt( ++i ) || '\\'; break;
case '"' :
var pos = format.indexOf( '"', i + 1 );
if( pos > -1 ) {
result += format.substring( i + 1, pos );
i = pos;
} else {
result += '"';
}
break;
default : result += format.charAt( i );
}
if( num ) {
if( raw || rawToggle ) {
result += num;
raw = false;
} else {
result += this.formatNum( num, true );
}
num = false;
}
}
return result;
},
/**************************************************************************************************
* evalExpression
* ported from http://svn.wikimedia.org/viewvc/mediawiki/trunk/extensions/ParserFunctions/Expr.php
*/
evalExpression : function( expr ) {
var precedence = {
'-':10,'+':10,'e':10,'sin':9,'cos':9,'tan':9,'asin':9,'acos':9,'atan':9,'exp':9,'ln':9,'abs':9,
'floor':9,'trunc':9,'ceil':9,'not':9,'^':8,'*':7,'/':7,'div':7,'mod':7,'++':6,'--':6,
'round':5,'=':4,'<':4,'>':4,'<=':4,'>=':4,'<>':4,'!=':4,'and':3,'or':2,'pi':0,'(':-1,')':-1
};
var arity = {
'-':1,'+':1,'e':2,'sin':1,'cos':1,'tan':1,'asin':1,'acos':1,'atan':1,'exp':1,'ln':1,'abs':1,
'floor':1,'trunc':1,'ceil':1,'not':1,'^':2,'*':2,'/':2,'div':2,'mod':2,'++':2,'--':2,
'round':2,'=':2,'<':2,'>':2,'<=':2,'>=':2,'<>':2,'!=':2,'and':2,'or':2
};
expr = expr.replace( /</g, '<' ).replace( />/g, '>' ).replace( /−/g, '-' ).replace( /−/g, '-' );
var operands = [];
var operators = [];
var p = 0;
var end = expr.length;
var expectExpression = true;
var numeric = '0123456789.';
var whitespace = ' \t\n\r';
while( p < end ) {
if( operands.length > 100 || operators.length > 100 ) {
throw 'Stack exhausted';
}
var ch = expr.charAt( p );
var ch2 = expr.substr( p, 2 );
if( whitespace.indexOf( ch ) > -1 ) {
p++;
continue;
} else if( numeric.indexOf( ch ) > -1 ) {
if( !expectExpression ) {
throw 'Unexpected number';
}
var num = expr.substr( p ).match( /^[0123456789\.]*/ )[0];
operands.push( parseFloat( num ) );
p += num.length;
expectExpression = false;
continue;
} else if( ch.match( /[A-Za-z]/ ) ) {
var word = expr.substr( p ).match( /^[A-Za-z]*/ )[0].toLowerCase();
p += word.length;
switch( word ) {
case 'e' :
if( !expectExpression ) {
break;
}
operands.push( Math.E );
expectExpression = false;
continue;
case 'pi' :
if( !expectExpression ) {
throw 'Unexpected number';
}
operands.push( Math.PI );
expectExpression = false;
continue;
case 'not' :
case 'sin' :
case 'cos' :
case 'tan' :
case 'asin' :
case 'acos' :
case 'atan' :
case 'exp' :
case 'ln' :
case 'abs' :
case 'floor':
case 'trunc':
case 'ceil' :
if( !expectExpression ) {
throw 'Unexpected ' + word + ' operator';
}
operators.push( word );
continue;
case 'mod' :
case 'and' :
case 'or' :
case 'round':
case 'div' : break;
default : throw 'Unrecognised word "' + word + '"';
}
} else if( ch2 === '<=' || ch2 === '>=' || ch2 === '<>' || ch2 === '!=' ) {
var word = ch2;
p += 2;
} else if( ch === '+' || ch === '-' ) {
p++;
if( expectExpression ) {
operators.push( ch );
continue;
} else {
var word = ch + ch;
}
} else if( ch === '*' || ch === '/' || ch === '^' || ch === '=' || ch === '<' || ch === '>' ) {
p++;
var word = ch;
} else if( ch === '(' ) {
if( !expectExpression ) {
throw 'Unexpected ( operator';
}
operators.push( ch );
p++;
continue;
} else if( ch === ')' ) {
var i = operators.length - 1;
while( i >= 0 && operators[i] !== '(' ) {
doOperation( operators[i], operands );
operators.pop();
i--;
}
if( i < 0 ) {
throw 'Unexpected closing bracket';
}
operators.pop();
expectExpression = false;
p++;
continue;
} else {
throw 'Unrecognised punctuation character "' + ch + '"';
}
if( expectExpression ) {
throw 'Unexpected ' + word + ' operator';
}
var i = operators.length - 1;
while( i >= 0 && precedence[word] <= precedence[operators[i]] ) {
doOperation( operators[i], operands );
operators.pop();
i--;
}
operators.push( word );
expectExpression = true;
}
var i = operators.length - 1;
while( i >= 0 ) {
if( operators[i] == '(' ) {
throw 'Unclosed bracket';
}
doOperation( operators[i], operands );
i--;
}
return operands.length ? operands[0] : '';
function doOperation( op, stack ) {
if( stack.length < arity[op] ) {
throw 'Missing operand for ' + op;
}
var right = stack.pop();
switch( op ) {
case '-' : stack.push( -right );return;
case '+' : stack.push( right );return;
case '*' : stack.push( stack.pop() * right );return;
case 'div' :
case '/' : if( right == 0 ) throw 'Division by zero';
stack.push( stack.pop() / right );return;
case 'mod' : if( right == 0 ) throw 'Division by zero';
right = right > 0 ? Math.floor( right ) : Math.ceil( right );
var left = stack.pop();
left = left >= 0 ? Math.floor( left ) : Math.ceil( left );
stack.push( left % right );return;
case '++' : stack.push( stack.pop() + right );return;
case '--' : stack.push( stack.pop() - right );return;
case 'and' : stack.push( stack.pop() && right ? 1 : 0 );return;
case 'or' : stack.push( stack.pop() || right ? 1 : 0 );return;
case '=' : stack.push( stack.pop() == right ? 1 : 0 );return;
case 'not' : stack.push( right ? 0 : 1 );return;
case 'round': var digits = Math.floor( right );
stack.push( Math.round( stack.pop() * Math.pow( 10, digits ) ) / Math.pow( 10, digits ) );
return;
case '<' : stack.push( stack.pop() < right ? 1 : 0 );return;
case '>' : stack.push( stack.pop() > right ? 1 : 0 );return;
case '<=' : stack.push( stack.pop() <= right ? 1 : 0 );return;
case '>=' : stack.push( stack.pop() >= right ? 1 : 0 );return;
case '<>' :
case '!=' : stack.push( stack.pop() == right ? 0 : 1 );return;
case 'e' : stack.push( stack.pop() * Math.pow( 10, right ) );return;
case 'sin' : stack.push( Math.sin( right ) );return;
case 'cos' : stack.push( Math.cos( right ) );return;
case 'tan' : stack.push( Math.tan( right ) );return;
case 'asin' : if( right < -1 || right > 1 ) throw 'Invalid argument for asin: < -1 or > 1';
stack.push( Math.asin( right ) );return;
case 'acos' : if( right < -1 || right > 1 ) throw 'Invalid argument for acos: < -1 or > 1';
stack.push( Math.acos( right ) );return;
case 'atan' : stack.push( Math.atan( right ) );return;
case 'exp' : stack.push( Math.exp( right ) );return;
case 'ln' : if( right <= 0 ) throw 'Invalid argument for ln: <= 0';
stack.push( Math.log( right ) );return;
case 'abs' : stack.push( Math.abs( right ) );return;
case 'floor': stack.push( Math.floor( right ) );return;
case 'ceil' : stack.push( Math.ceil( right ) );return;
case 'trunc': stack.push( right >= 0 ? Math.floor( right ) : Math.ceil( right ) );return;
case '^' : stack.push( Math.pow( stack.pop(), right) );return;
}
}
},
parserFunctions : {
anchorencode : function( obj, arg ) { return this.anchorEncode( arg ); },
basepagename : function( obj, page ) {
var pos = page.title.indexOf( '/' );
return pos > -1 ? page.title.substring( 0, pos ) : page.title;
},
basepagenamee : function( obj, page ) {
return this.titleEncode( this.parserFunctions.basepagename.call( this, obj, page ) );
},
contentlanguage : function( obj, arg ) { return this.getContentLanguage(); },
currentversion : function( obj, arg ) { return this.getVersion(); },
directionmark : function( obj, arg ) { return this.getRTL() ? '\u200f' : '\u200e'; },
defaultsort : function() { return ''; },
displaytitle : function() { return ''; },
expr: function( obj, arg ) {
try{
return this.evalExpression( arg ) + '';
} catch( e ){
return '<strong class="error">' + e + '</strong>';//TODO
}
},
filepath : function( obj, arg ) {
var page = this.getPage( arg, 6 );
if( !page ) return '';
return this.safe( 'getFileUrl', [ page ] );
},
formatnum: function( obj, arg ) {
if( this.expandArgSafe( obj, 1 ).indexOf( 'R' ) > -1 ) { // TODO
return this.parseFormattedNumber( arg );
} else {
return this.formatNum( arg );
}
},
fullpagename : function( obj, page ) { return page.ptitle; },
fullpagenamee : function( obj, page ) { return this.titleEncode( page.ptitle ); },
fullurl : function( obj, page ) {
return this.getServer() + this.parserFunctions.localurl.call( this, obj, page );
},
fullurle : function( obj, arg ) {
return mw.html.escape( this.parserFunctions.fullurl.call( this, obj, page ) );
},
gender : function( obj, arg ) {
// TODO
if( obj.parts.length > 3 ) {
return this.expandArg( obj, 3 );
}
return this.expandArg( obj, 1 );
},
grammar : function( obj, arg ) { return this.expandArg( obj, 1 ); }, // TODO
'if' : function( obj, arg ) {
if( arg ) {
return this.expandArg( obj, 1 ).trim();
} else {
return this.expandArg( obj, 2 ).trim();
}
},
ifeq : function( obj, arg ) {
var otherArg = this.expandArgSafe( obj, 1 ).trim();
//PHP compares two strings numerical, if both are valid numerals. We have to mimic
//this behavior here
if( arg === otherArg || ( !isNaN( +arg ) && arg && otherArg && +arg === +otherArg ) ) {
return this.expandArg( obj, 2 ).trim();
} else {
return this.expandArg( obj, 3 ).trim();
}
},
iferror: function( obj, arg ) {
if( arg.match( /<(?:strong|span|p|div)\s[^>]*\bclass="[^">]*\berror\b[^">]*"/ ) ) {
return this.expandArg( obj, 1 ).trim();
} else if( obj.parts.length > 2 ) {
return this.expandArg( obj, 2 ).trim();
} else {
return arg;
}
},
ifexist: function( obj, arg ) {
var page = this.getPage( arg );
if( page && this.safe( 'getPageExists', [ page ] ) ) {
return this.expandArg( obj, 1 ).trim();
} else {
return this.expandArg( obj, 2 ).trim();
}
},
ifexpr: function( obj, arg ) {
try{
var value = this.evalExpression( arg );
} catch( e ){
return '<strong class="error">' + e + '</strong>'; // TODO
}
if( value ) {
return this.expandArg( obj, 1 ).trim();
} else {
return this.expandArg( obj, 2 ).trim();
}
},
'int': function( obj, arg ) {
if( !arg ) {
return false;
}
var params = [];
for( var i = 1; i < obj.parts.length; i++ ) {
params.push( this.expandArgSafe( obj, i ).trim() );
}
return this.safe( 'getUserMessage', [ arg, params ] );
},
language : function( obj, arg ) {
var name = this.safe( 'getLanguageName', [ arg.toLowerCase() ] );
return name || arg;
},
lc: function( obj, arg ) { return arg.toLowerCase(); },
lcfirst: function( obj, arg ) { return this.lcfirst( arg ); },
localurl : function( obj, page ) {
var query = this.expandArgSafe( obj, 1 ).trim();
if( page.ns === -2 ) {
var page = this.getPage( page.title, 6 );
}
return this.getLocalUrl( page, query );
},
localurle : function( obj, page ) {
return mw.html.escape( this.parserFunctions.localurl.call( this, obj, page ) );
},
namespace : function( obj, page ) { return this.getNamespaceName( page.ns ); },
namespacee : function( obj, page ) {
return this.titleEncode( this.getNamespaceName( page.ns ) );
},
ns: function( obj, arg ) {
var index = parseInt( arg, 10 );
if( !index && arg !== '0' ) {
index = this.getNamespaceIndex( arg.toLowerCase().replace( /[ _]+/g, '_' ) );
}
if( index === false ) {
return false;
} else {
return this.getNamespaceName( index );
}
},
nse: function( obj, arg ) {
return this.titleEncode( this.parserFunctions.ns.call( this, obj, arg ) );
},
padleft: function( obj, arg ) {
var len = obj.parts.length > 1 ? parseInt( this.expandArgSafe( obj, 1 ).trim(), 10 ) : 0;
var pad = obj.parts.length > 2 ? this.expandArgSafe( obj, 2 ).trim() : '0';
return this.padString( arg, len, pad, 'left' );
},
padright: function( obj, arg ) {
var len = obj.parts.length > 1 ? parseInt( this.expandArgSafe( obj, 1 ).trim(), 10 ) : 0;
var pad = obj.parts.length > 2 ? this.expandArgSafe( obj, 2 ).trim() : '0';
return this.padString( arg, len, pad, 'right' );
},
pagename : function( obj, page ) { return page.title; },
pagenamee : function( obj, page ) { return this.titleEncode( page.title ); },
plural: function( obj, arg ) {
// TODO
var num = parseInt( this.parseFormattedNumber( arg ), 10 );
if( num === 1 ) {
return this.expandArg( obj, 1 ).trim();
} else {
return this.expandArgSafe( obj, 2 ).trim() || this.expandArg( obj, 1 ).trim();
}
},
rel2abs: function( obj, arg ) {
var from = this.expandArgSafe( obj, 1 ).trim();
if( !from ) {
from = this.getContextTitle();
}
var to = arg.replace( /[ \/]+$/, '' );
if( !to || to === '.' ) {
return from;
}
if( !to.match( /^\.?\.?\/|^\.\.$/ ) ) {
from = '';
}
var fullpath = '/' + from + '/' + to + '/';
fullpath = fullpath.replace( /\/(\.\/)+/g, '/' );
fullpath = fullpath.replace( /\/\/+/g, '/' );
fullpath = fullpath.replace( /^\/+|\/+$/g, '' );
var bits = fullpath.split( '/' );
var newbits = [];
for( var i = 0; i < bits.length; i++ ) {
if( bits[i] == '..' ) {
if( !newbits.length )
// TODO
return '<strong class="error">Error: Invalid depth in path: "' + fullpath
+ '" (tried to access a node above the root node)</strong>';
newbits.pop();
} else {
newbits.push( bits[i] );
}
}
return newbits.join( '/' );
},
subjectpagename : function( obj, page ) {
return this.getPage( page.title, page.ns & -2 ).ptitle;
},
subjectpagenamee : function( obj, page ) {
return this.titleEncode( this.parserFunctions.subjectpagename.call( this, obj, page ) );
},
subjectspace : function( obj, page ) { return this.getNamespaceName( page.ns & -2 ); },
subjectspacee : function( obj, page ) {
return this.titleEncode( this.parserFunctions.subjectspace.call( this, obj, page ) );
},
subpagename : function( obj, page ) {
//TODO: Namespaces without subpages
return page.title.substring( page.title.lastIndexOf( '/' ) + 1 );
},
subpagenamee : function( obj, page ) {
return this.titleEncode( this.parserFunctions.subpagename.call( this, obj, page ) );
},
'switch' : function( obj, arg ) {
var found = false;
var defaultFound = false;
var switchDefault = false;
for( var i = 1; i < obj.parts.length; i++ ) {
if( obj.parts[i].split ) {
var left = this.expandArgSafe( obj, i, 'l' );
if( found || left === arg ) {
return this.expandArg( obj, i, 'r' );
} else if( defaultFound || left === '#default' /* TODO */ ) {
switchDefault = i;
}
} else {
var left = this.expandArgSafe( obj, i ).trim();
if( left === arg ) {
found = true;
} else if( left === '#default' /* TODO */ ) {
defaultFound = true;
}
}
}
if( !obj.parts[i - 1].split ) {
return left;
} else if( switchDefault !== false ) {
return this.expandArg( obj, switchDefault, 'r' );
} else {
return '';
}
},
tag: function( obj, arg ) {
var tagName = arg.toLowerCase();
var inner = this.expandArgSafe( obj, 1 );
var tags = this.getExtensionTags();
if( !jQuery.inArray( tagName, tags ) ) {
return '<span class="error">Unknown extension tag "' + tagName + '"</span>'; // TODO
}
var attr = '';
for( var i = 2; i < obj.parts.length; i++ ) {
if( !obj.parts[i].split ) {
continue;
}
attr += ' ' + this.expandArgSafe( obj, i, 'l' ) + '="';
attr += this.expandArgSafe( obj, i, 'r' )
.replace( /^\s*["']([^"']*)["']\s*$/, '$1' ) + '"';
}
return this.expandExtension( tagName, attr, inner, '</' + tagName + '>' );
},
talkpagename : function( obj, page ) {
return this.getPage( page.title, page.ns | 1 ).ptitle;
},
talkpagenamee : function( obj, page ) {
return this.titleEncode( this.parserFunctions.talkpagename.call( this, obj, page ) );
},
talkspace : function( obj, page ) { return this.getNamespaceName( page.ns | 1 ); },
talkspacee : function( obj, page ) { return this.titleEncode( this.getNamespaceName( page.ns | 1 ) ); },
time: function( obj, arg, local ) {
var date = false;
if( obj.parts.length > 1 ) {
var timeArg = this.expandArgSafe( obj, 1 ).trim();
if( timeArg ) {
var secs = Date.parse( timeArg );
if( !secs ) {
return null;
}
date = new Date( secs );
}
}
if( !date ) {
date = local ? this.safe( 'getLocalDate', [] ) : this.getUTCDate();
}
result = this.formatDate( date, arg );
if( result ) {
return result;
} else {
return null;
}
},
timel: function( obj, arg ) {
return this.parserFunctions.time.call( this, obj, arg, true );
},
titleparts : function( obj, arg ) {
var page = this.getPage( arg );
if( !page ) {
return arg;
}
var bits = page.ptitle.split( '/', 25 );
var offset = Math.max( 0, ( parseInt( this.expandArgSafe( obj, 2 ), 10 ) || 0 ) - 1 );
var end = parseInt( this.expandArgSafe( obj, 1 ), 10 ) || 0;
end = end > 0 ? offset + end : bits.length + end;
return bits.slice( offset, end ).join( '/' );
},
uc: function( obj, arg ) { return this.uc( arg ); },
ucfirst: function( obj, arg ) { return this.ucfirst( arg ); },
urlencode: function( obj, arg ) { return this.phpUrlencode( arg ); },
var_day : function( obj, date ) { return this.formatDate( date, 'j' ); },
var_day2 : function( obj, date ) { return this.formatDate( date, 'd' ); },
var_dayname : function( obj, date ) { return this.formatDate( date, 'l' ); },
var_dow : function( obj, date ) { return this.formatDate( date, 'N' ); },
var_hour : function( obj, date ) { return this.formatDate( date, 'H' ); },
var_month : function( obj, date ) { return this.formatDate( date, 'm' ); },
var_month1 : function( obj, date ) { return this.formatDate( date, 'n' ); },
var_monthabbrev : function( obj, date ) { return this.formatDate( date, 'M' ); },
var_monthname : function( obj, date ) { return this.formatDate( date, 'F' ); },
var_monthnamegen : function( obj, date ) { return this.formatDate( date, 'xg' ); },
var_time : function( obj, date ) { return this.formatDate( date, 'H:i' ); },
var_timestamp : function( obj, date ) { return this.formatDate( date, 'YmdHis' ); },
var_week : function( obj, date ) { return this.formatDate( date, 'W' ); },
var_year : function( obj, date ) { return this.formatDate( date, 'Y' ); }
}
} );
this.expandTemplates = function( obj, title, cb ) {
if( !obj.type ) {
var expander = new TemplateExpander( title );
obj = expander.preprocess( obj );
}
return iterate( TemplateExpander, 'expand', obj, title, cb );
};
/***********************************************************************************************
* Parser
*/
var Parser = this.Parser = function() {
TemplateExpander.apply( this, arguments );
this.markerPrefix = '\x7fUNIQ' + ( Math.random() * Math.pow( 2, 32 ) ).toString( 16 ) + '-';
this.markerSuffix = '-QINU\x7f';
this.markerRX = new RegExp( this.markerPrefix + '(\\d+)' + this.markerSuffix, 'g' );
this.tocMarker = '<table id="' + this.markerPrefix + '"></table>';
this.stripMarkers = [];
this.autoNumber = 0;
this.refs = {};
this.refKey = 0;
this.refsGroup = false;
this.output = { underscores : {} };
};
Parser.prototype = jQuery.extend( new TemplateExpander, {
expandExtension : function( /* ... */ ) {
this.stripMarkers.push( arguments );
return this.markerPrefix + ( this.stripMarkers.length - 1 ) + this.markerSuffix;
},
getReplacement : function( obj ) {
return '';
},
parse : function( text ) {
text = this.parseInline( text );
text = this.doBlocks( text );
text = this.fixEntities( text );
this.output.text = text;
return this.output;
},
parseInline : function( text ) {
text = this.expand( this.preprocess( text, false ) );
text = this.fixTags( text );
text = this.doDoubleUnderscore( text );
text = this.doQuotes( text );
text = this.doLinks( text );
return text;
},
doDoubleUnderscore : function( text ) {
var that = this;
var mag = this.getMagicWords();
if( !mag ) {
return text;
}
return text.replace( mag.underscoreRX, function( m ) {
var name = that.matchMagic( 'underscore', m );
if( name === 'toc' && !that.output.underscores[name] ) {
var result = that.tocMarker;
} else {
var result = '';
}
that.output.underscores[name] = true;
return result;
} );
},
doQuotes : function( text ) {
var m, rx = /<[^>]*>|''+|\n|$/g, result = '', last = 0, state = 0, fail = false;
rx.lastIndex = 0;
while( m = rx.exec( text ) ) {
var l = m[0].length;
result += text.substring( last, m.index );
last = rx.lastIndex;
if( !m[0] || m[0] === '\n' ) {
if( state === 3 && !fail ) {
result = result.substring( 0, result.lastIndexOf( '\n' ) + 1 );
last = rx.lastIndex = text.lastIndexOf( '\n', m.index - 1 ) + 1;
state = 0;
fail = true;
} else {
fail = false;
changeState( 0 );
result += m[0];
if( !m[0] ) {
break;
}
}
} else if( m[0].charAt( 0 ) == '<' ) {
result += m[0];
} else {
if( l === 3 && fail ) {
result += "'";
l = 2;
fail = false;
}
if( l === 4 ) {
result += "'";
}
if( l > 5 ) {
result += m[0].substring( 5 );
l = 5;
}
changeState( state ^ ( l - 1 - ( l > 3 ) ) );
}
}
return result;
function changeState( newState ) {
if( state & 1 ) {
result += '</i>';
}
if( state & 2 && !( newState & 2 ) ) {
result += '</b>';
}
if( newState & 2 && !( state & 2 ) ) {
result += '<b>';
}
if( newState & 1 ) {
result += '<i>';
}
state = newState;
}
},
doLinks : function( text ) {
var m, rx = /(<[^>]*>)|(\[\[)|(\]\])|$/g, result = '', last = 0;
var link1 = false, link2 = false;
var linkTrailRX = this.getLinkTrailRX();
rx.lastIndex = 0;
while( m = rx.exec( text ) ) {
if( m[2] ) {
if( link1 === false ) {
result += this.doExtLinks( text.substring( last, m.index ) );
link1 = m.index;
} else if( link2 === false ) {
link2 = m.index;
} else {
//We found the third opening in a row, so link1 is broken
//There might have been correct links in between, though, so we have to start over
//at the last position
last = link1;
link1 = false;
link2 = false;
rx.lastIndex = last + 2;
}
} else if( m[3] && link1 !== false ) {
if( link2 === false ) {
var inner = text.substring( link1 + 2, m.index );
last = m.index + 2;
if( text.charAt( m.index + 2 ) == ']' && inner.indexOf( '[' ) > -1 ) {
inner += ']';
last++;
}
linkTrailRX.lastIndex = last;
var trail = linkTrailRX.exec( text );
last += trail[0].length;
result += this.doDoubleBracket( inner, trail[0] );
rx.lastIndex = m.index + m[0].length;
link1 = false;
} else {
link2 = false;
}
} else if( !m[0] ) {
if( link1 === false ) {
result += this.doExtLinks( text.substring( last ) );
break;
} else {
last = link1;
link1 = false;
link2 = false;
rx.lastIndex = last + 2;
}
}
}
return result;
},
doDoubleBracket : function( inner, trail ) {
if( inner.match( this.getUrlStartRX() ) ) {
return '[' + this.doLinks( '[' + inner + ']' ) + ']' + trail;
}
var forcelink = inner.charAt( 0 ) == ':';
var pos = inner.indexOf( '|' );
var title = pos > -1 ? inner.substring( forcelink, pos ) : inner.substring( forcelink );
// TODO: unstrip
var page = this.getPage( this.decodeEntities( title ), 0, true );
if( !page ) {
return '[' + this.doLinks( '[' + inner + ']' ) + ']' + trail;
}
if( inner.substring( 0, 4 ) === 'file' ) {
mw.parser;
}
if( !forcelink && page.ns == 6 ) {
return this.makeImage( page, pos > -1 ? inner.substring( pos + 1 ) : '' ) + trail;
} else if( !forcelink && page.ns == 14 ) {
return this.makeCategory( page, pos > -1 ? inner.substring( pos + 1 ) : false ) + trail;
} else if( !forcelink && page.iw ) {
return this.makeInterwiki( page ) + trail;
} else {
return this.makeLink( page, ( pos > -1 ? inner.substring( pos + 1 ) : title ) + trail );
}
},
doExtLinks : function( text ) {
var that = this;
return text.replace( this.getExtLinkRX(), function( m, open, ourl, text, magType, magNum, isbn ) {
if( ourl ) {
var pos = ourl.search( /&[lg]t;/ );
var url = pos > -1 ? ourl.substring( 0, pos ) : ourl;
var prefix = '', trail = '';
if( open && text ) {
var type = 'text';
var text = text.replace( /^ +|\]$/g, '' );
if( pos > -1 ) {
text = ourl.substring( pos ) + ' ' + text;
}
if( !text.trim() ) {
text = '[' + that.formatNum( ++that.autoNumber ) + ']';
}
} else {
var type = 'free';
prefix = open;
if( pos > -1 ) {
trail = ourl.substring( pos );
}
var sep = ',;\\.:!?';
if( url.indexOf( '(' ) === -1 ) {
sep += ')';
}
for( var i = url.length - 1; sep.indexOf( url.charAt( i ) ) > -1; i-- );
if( i < url.length - 1 ) {
trail = url.substring( i + 1 ) + trail;
url = url.substring( 0, i + 1 );
}
if( text ) {
trail += text;
}
var text = url;
}
url = that.decodeEntities( url );
url = url.replace( /[\[\]"\x00-\x20\x7f]/g, function( m ) { return that.phpUrlencode( m ); } );
return prefix + that.makeExtLink( url, text, type ) + trail;
} else if( magType ) {
return that.makeMagicLink( magType, magNum );
} else if( isbn ) {
return that.makeISBNLink( isbn );
} else {
return m;
}
} );
},
makeImage : function( page, params ) {
var ext = page.title.substring( page.title.length - 3 );
if( ext === 'ogg' ) {
return '';
}
var dim = this.getFileDimensions( page );
var p = this.getImageParams( params );
if( p.framed === '' || p.thumbnail === '' ) {
if( p.alt === undefined && p.caption === '' ) {
p.alt = page.title;
} else {
p.alt = '';
}
} else {
if( p.alt === undefined ) {
p.alt = p.caption || page.title;
}
p.title = p.caption;
}
if( p.width === undefined && dim ) {
p.width = dim.width;
if( p.thumbnail === '' || p.framed === '' || p.frameless === '' ) {
if( p.upright !== undefined && !parseFloat( p.upright ) ) {
p.upright = 0.75;
}
p.width = Math.min( p.width, Math.floor( ( this.getThumbSize() / 10 ) * ( parseFloat( p.upright ) || 1 ) ) * 10 );
}
}
if( p.height && dim && ( p.width * dim.height > p.height * dim.width ) ) {
var floatwidth = dim.width * p.height / dim.height;
if( Math.round( Math.ceil( floatwidth ) * dim.height / dim.width ) > p.height ) {
p.width = Math.floor( floatwidth );
} else {
p.width = Math.ceil( floatwidth );
}
}
var prefix = '', postfix = '';
if( p.align === 'center' ) {
prefix = '<div class="center">';
postfix = '</div>';
p.align = 'none';
}
if( p.thumbnail === '' || p.framed === '' ) {
return prefix + this.makeThumbImage( page, dim, p ) + postfix;
}
if( p.frameless === '' && dim && p.width > dim.width && ext !== 'svg' && ext !== 'bmp' ) {
p.width = dim.width;
}
if( p.align ) {
prefix += '<div class="float' + p.align + '">';
postfix = '</div>' + postfix;
}
return prefix + this.makeTransformedImage( page, dim, p ) + postfix;
},
makeThumbImage : function( page, dim, p ) {
if( p.align === undefined ) {
p.align = 'right';
}
if( p.width === undefined ) {
p.width = p.upright === undefined ? 180 : 130; // TODO: check if getThumbSize() should be used here
}
if( dim && p.framed === '' ) {
p.width = dim.width;
}
var result = '<div class="thumb t' + p.align + '"><div class="thumbinner" style="width:' + ( p.width + 2 ) + 'px;">';
result += this.makeTransformedImage( page, dim, p, 'thumbimage' );
result += ' <div class="thumbcaption">';
if( dim && p.framed === undefined ) {
result += this.makeZoomIcon( page );
}
result += ( p.caption || '' ) + '</div></div></div>';
return result;
},
makeTransformedImage : function( page, dim, p, imgclass ) {
if( dim === false ) {
return this.makeLink( page, p.caption || page.ptitle );
}
var link = {};
if( p.link === undefined ) {
link.href = this.getLocalUrl( page );
link['class'] = 'image';
} else if( p.link === '' ) {
link = false;
} else if( p.link.match( this.getUrlStartRX() ) ) {
link.href = p.link;
} else {
var linkPage = this.getPage( p.link );
if( linkPage ) {
link.href = this.getLocalUrl( linkPage );
} else {
link = false;
}
}
if( link && p.title ) {
link.title = p.title;
}
var img = {
alt : p.alt || '',
src : this.getThumbUrl( page, p.width ) || 'http://',
width : p.width,
height : Math.round( dim.height * p.width / dim.width )
};
if( imgclass ) {
img['class'] = imgclass;
}
if( p.valign ) {
img.style = 'vertical-align: ' + p.valign;
}
var result = link ? '<a' + this.encodeAttributes( link ) + '>' : '';
result += '<img' + this.encodeAttributes( img ) + ' />';
return result + ( link ? '</a>' : '' );
},
makeZoomIcon : function( page ) {
var link = '<a' + this.encodeAttributes( {
href : this.getLocalUrl( page ),
'class' : 'internal',
'title' : this.getUserMessage( 'thumbnail-more' )
} ) + '>';
return '<div class="magnify">' + link + '<img src="' + this.getStylePath()
+ '/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>';
},
getImageParams : function( params ) {
var text = this.doLinks( params ), start = -1, p = {};
while( start < text.length ) {
var end = this.find( text, '|', start + 1 );
var match = this.matchImageParam( text.substring( start + 1, end ).trim() ) || ['caption', ''];
if( match[0] === 'width' ) {
var size = match[1].match( /^(\d*)x(\d*)/ );
if( size ) {
if( +size[1] ) {
p.width = +size[1];
}
if( +size[2] ) {
p.height = +size[2];
}
} else {
var clean = match[1].replace( /\s*px\s*$/, '' );
if( +clean ) {
p.width = +clean;
} else {
p.caption = match[1];
}
}
} else if( match[0] === 'left' || match[0] === 'right' || match[0] === 'center' || match[0] === 'none' ) {
p.align = match[0];
} else if( match[0] === 'baseline' || match[0] === 'sub' || match[0] === 'super' || match[0] === 'top'
|| match[0] === 'text_top' || match[0] === 'middle' || match[0] === 'bottom' || match[0] === 'text_bottom' ) {
p.valign = match[0];
} else {
p[match[0]] = match[1];
}
start = end;
}
if( p.manualthumb !== undefined ) {
p.thumbnail = ''; //TODO: We don't support manualthumb now
}
return p;
},
makeCategory : function( page, sortkey ) {
return ''; // TODO: record in output
},
makeInterwiki : function( page ) {
return ''; // TODO: record in output
},
makeLink : function( page, text ) {
var exists = !page.title || this.getPageExists( page ); // TODO: selflink, file links, special pages
var link = {};
link.href = this.getLocalUrl( page, exists === false ? 'action=edit&redlink=1' : null );
if( exists === false ) {
link['class'] = 'new';
}
if( page.title ) {
if( exists === false ) {
link.title = this.getUserMessage( 'red-link-title', page.ptitle );
} else {
link.title = page.ptitle;
}
}
if( this.getRedirectTarget( page ) ) {
link['class'] = 'mw-redirect';
}
return '<a' + this.encodeAttributes( link ) + '>' + text + '</a>';
},
makeISBNLink : function( isbn ) {
var norm = isbn.replace( /[- ]+/g, '' ).replace( /x/g, 'X' );
return '<a' + this.encodeAttributes( {
href : this.getLocalUrl( this.getPage( 'Special:Booksources/' + norm ) ),
'class' : 'internal mw-magiclink-isbn'
} ) + '>ISBN ' + isbn + '</a>';
},
makeMagicLink : function( type, num ) {
return '<a' + this.encodeAttributes( {
href : this.getContentMessage( type === 'RFC' ? 'rfcurl' : 'pubmedurl', [ num ] ),
'class' : 'external mw-magiclink-' + type.toLowerCase()
} ) + '>' + type + ' ' + num + '</a>';
},
makeExtLink : function( url, text, type ) {
return '<a' + this.encodeAttributes( {
href : url,
'class' : 'external ' + type,
rel : 'nofollow'
} ) + '>' + text + '</a>';
},
storeRef : function( group, name, content ) {
if( !this.refs[group] ) {
this.refs[group] = [[], {}];
}
var g = this.refs[group];
if( !name || g[1][name] === undefined ) {
g[0].push( { key : this.refKey++, text : content, count : 0 } );
} else {
var ref = g[0][g[1][name]];
ref.count++;
if( !ref.text ) {
ref.text = content;
}
}
if( name && g[1][name] === undefined ) {
g[1][name] = g[0].length - 1;
}
},
makeRefLink : function( group, name ) {
var g = this.refs[group];
var num = name ? g[1][name] : g[0].length - 1;
var ref = g[0][num];
var refId = 'cite_ref-' + ref.key + ( ref.count ? '-' + ref.count : '' );
var refsId = 'cite_note-' + ref.key;
var label = ( group && group + ' ' ) + this.formatNum( num + 1 );
return this.parseInline( this.getContentMessage( 'cite_reference_link', [ refId, refsId, label ] ) || '' );
},
makeRefEntry : function( ref, num ) {
if( !ref.count ) {
var refsId = 'cite_note-' + ref.key;
var refId = 'cite_ref-' + ref.key;
return this.getContentMessage( 'cite_references_link_one', [ refsId, refId, ref.text ] );
} else {
var sep = this.getContentMessage( 'cite_references_link_many_sep' );
var and = this.getContentMessage( 'cite_references_link_many_and' );
var links = '';
for( var i = 0; i <= ref.count; i++ ) {
if( i ) {
links += i < ref.count - 1 ? sep : and;
}
var refId = 'cite_ref-' + ref.key + ( i ? '-' + i : '' );
var numLabel = this.makeRefNumLabel( num + 1, i, ref.count );
links += this.getContentMessage( 'cite_references_link_many_format', [ refId, numLabel, this.makeRefAltLabel( i ) ] );
}
var refsId = 'cite_note-' + ref.key;
return this.getContentMessage( 'cite_references_link_many', [ refsId, links, ref.text ] );
}
},
makeRefNumLabel : function( base, sub, max ) {
var l = ( max + '' ).length;
return this.formatNum( base + '.' + this.padString( sub, l, '0' ) );
},
makeRefAltLabel : function( num ) {
if( !this.refAltLabels ) {
var msg = this.getContentMessage( 'cite_references_link_many_format_backlink_labels' ) || '';
this.refAltLabels = msg.split( ' ' );
}
return this.refAltLabels[num] || '';
},
unstrip : function( text ) {
var that = this;
return text.replace( this.markerRX, function( m, num ) {
return that.replaceExtension.apply( that, that.stripMarkers[+num] );
} );
},
doBlocks : function( text ) {
function addHTML( s ) {
//add an HTML string to tidy
//assumes that all tags are valid
var m, last = 0;
var rx = new RegExp( that.markerPrefix + '(\\d+)' + that.markerSuffix + '|<(/?)(\\w+)([^>]*)>|$', 'g' );
rx.lastIndex = 0;
while( m = rx.exec( s ) ) {
if( m.index > last ) {
html.addText( s.substring( last, m.index ) );
}
if( !m[0] ) {
break;
}
if( m[1] ) {
addHTML( that.replaceExtension.apply( that, that.stripMarkers[+m[1]] ) );
} else if( m[2] ) {
html.addEndTag( m[3] );
} else {
html.addStartTag( m[3], m[4].trim() );
}
last = rx.lastIndex;
}
}
var that = this;
var lines = text.split( '\n' );
var html = new Tidy( this );
var tables = [], cur;
var lastprefix = '', lastsection = '';
for( var i = 0; i < lines.length; i++ ) {
var match = lines[i].match( /^(?:\s*(:*\{\|)|(----+)|(=+)(.*[^=])(=+)\s*|([;:#\*]+))(.*)$/ );
//list stuff
var prefix = ( match && html.getCurrentBlock() !== PRE && match[6] ) || '';
for( var j = lastprefix.length - 1; j >= 0 && lastprefix.charAt( j ) !== prefix.charAt( j ); j-- ) {
var ch = lastprefix.charAt( j );
addHTML( ch === '*' ? '</ul>\n' : ch === '#' ? '</ol>\n' : '</dl>\n' );
}
lastprefix = prefix;
if( prefix ) {
if( j >= 0 && j + 1 === prefix.length ) {
var ch = prefix.charAt( j );
html.nextListItem( ch === ';' ? 'dt' : ch === ':' ? 'dd' : 'li' );
}
while( prefix.length > j + 1 ) {
var ch = prefix.charAt( ++j );
addHTML( ch === '*' ? '<ul><li>' : ch === '#' ? '<ol><li>' : ch === ':' ? '<dl><dd>' : '<dl><dt>' );
}
if( ch === ';' ) {
var pos = this.find( match[7], ': ', 0 );
if( pos < match[7].length ) {
addHTML( match[7].substring( 0, pos ) + '</dt><dd>' + match[7].substring( pos + 1 ) + '</dd>\n' );
continue;
}
}
addHTML( match[7] + '\n' );
continue;
}
//table stuff
var line = lines[i].trim();
if( !match ) {
if( tables.length ) {
cur = tables[tables.length - 1];
var pipe = line.charAt( 0 ) === '|';
if( pipe && line.charAt( 1 ) === '}' ) {
tables.pop();
if( cur.cell ) {
addHTML( '</' + cur.cell + '>' );
}
if( cur.row ) {
addHTML( '</tr>' );
}
if( !cur.hasRows ) {
addHTML( '<tr><td></td></tr>' );
}
addHTML( '</table>' + ( new Array( cur.indent + 1 ) ).join( '</dd></dl>' ) + line.substring( 2 ) + '\n' );
continue;
} else if( pipe && line.charAt( 1 ) === '-' ) {
cur.rowAttr = this.fixAttributes( line.substring( 2 ) );
cur.hasRows = true; // this matches behavior of mw, although it is a bit illogical
if( cur.cell ) {
addHTML( '</' + cur.cell + '>' );
}
cur.cell = false;
if( cur.row ) {
addHTML( '</tr>' );
}
cur.row = false;
continue;
} else if( pipe || line.charAt( 0 ) === '!' ) {
if( pipe && line.charAt( 1 ) === '+' ) {
var tag = 'caption';
var start = 2;
} else {
var tag = pipe ? 'td' : 'th';
var start = 1;
}
do {
if( cur.cell ) {
addHTML( '</' + cur.cell + '>' );
}
if( !cur.row && tag !== 'caption' ) {
addHTML( '<tr' + ( cur.rowAttr || '' ) + '>\n' );
cur.row = cur.hasRows = true;
}
var end = this.find( line, '||', start );
if( !pipe ) {
end = Math.min( end, this.find( line, '!!', start ) );
}
var sep = this.find( line, '|', start );
if( sep < end ) {
var attr = this.fixAttributes( line.substring( start, sep ) );
addHTML( '<' + tag + attr + '>' + line.substring( sep + 1, end ) );
} else {
addHTML( '<' + tag + '>' + line.substring( start, end ) );
}
cur.cell = tag;
start = end + 2;
} while( end < line.length );
continue;
}
}
} else if( match[1] ) {
var indent = match[1].length - 2;
var attr = this.fixAttributes( match[7], 'table' );
tables.push( { indent : indent } );
addHTML( ( new Array( indent + 1 ) ).join( '<dl><dd>' ) + '<table' + attr + '>\n' );
continue;
} else if( match[2] ) {
addHTML( '<hr />' + match[7] + '\n' );
continue;
} else if( match[3] && !match[7] ) {
var level = Math.min( match[3].length, match[5].length, 6 );
addHTML( '<h' + level + '>' + match[3].substring( level ) + match[4] + match[5].substring( level ) + '</h' + level + '>' );
continue;
}
var block = html.getCurrentBlock();
if( block !== BLOCK_SKIP && block !== PARAGRAPH && block !== PRE ) {
var space = lines[i].charAt( 0 ) === ' ';
if( ( !line || space ) && block & PARAGRAPH ) {
html.addEndTag( 'p' );
}
if( space ) {
if( !( block & PRE ) ) {
html.addStartTag( 'pre', '', IMPLICIT );
}
addHTML( lines[i].substring( 1 ) + '\n' );
continue;
}
if( block & PRE ) {
html.addEndTag( 'pre' );
}
if( !( block & PARAGRAPH ) ) {
html.addStartTag( 'p', '', IMPLICIT );
}
}
addHTML( lines[i] + '\n' );
}
var text = html.toString();
if( this.output.underscores.toc ) {
return text.replace( new RegExp( this.tocMarker ), this.makeToc( html.headings ) );
} else if( !this.output.underscores.notoc ) {
var tocPos = text.search( /<h[1-6]/ );
if( tocPos > -1 ) {
return text.substring( 0, tocPos ) + this.makeToc( html.headings ) + text.substring( tocPos );
}
}
return text;
},
makeToc : function( headings ) {
if( headings.length < 4 && !this.output.underscores.forcetoc ) {
return '';
}
var toclevel = 0;
var level = 0;
var prev = 0;
var levels = [];
var counts = [];
var toc = '<table id="toc" class="toc"><tr><td><div id="toctitle"><h2>';
toc += mw.html.escape( this.getUserMessage( 'toc' ) || '' );
toc += '</h2></div>';
for( var i = 0; i < headings.length; i++ ) {
var h = headings[i];
while( h.level < level ) {
level = levels.pop();
toclevel--;
}
if( h.level > level ) {
levels.push( level );
level = h.level;
toclevel++;
}
if( toclevel > prev ) {
toc += '<ul>';
counts.push( 0 );
} else {
toc += '</li>';
}
while( toclevel < prev-- ) {
toc += '</ul></li>';
counts.pop();
}
counts[counts.length - 1]++;
prev = toclevel;
toc += '<li class="toclevel-' + toclevel + '">';
toc += '<a href="#' + h.id + '"><span class="tocnumber">';
toc += counts.join( '.' ) + '</span> <span class="toctext">' + h.name.replace( /<[^>]*>/g, '' );
toc += '</span></a>';
}
while( toclevel-- ) {
toc += '</li></ul>';
}
toc += '</td></tr></table>';
return toc;
},
makeError : function( text ) {
return '<strong class="error">' + text + '</strong>';
},
replaceExtension : function( name, attr, inner ) {
var lc = name.toLowerCase();
var func = this.extensionFunctions[lc];
if( func ) {
var attr = this.getAttributes( attr );
var result = func.call( this, attr, inner );
} else {
var call = '<' + lc + attr + '>' + ( inner === false ? '' : inner + '</' + lc + '>' );
var result = this.getParsedText( call ) || '';
}
return result;
},
extensionFunctions : {
nowiki : function( attr, content ) {
return content ? content.replace( /"/g, '"' ).replace( /</g, '<' ).replace( />/g, '>' ) : '';
},
pre : function( attr, content ) {
var attr = this.validateAttributes( attr );
return '<pre' + this.encodeAttributes( attr ) + '>'
+ ( content ? content.replace( /"/g, '"' ).replace( /</g, '<' ).replace( />/g, '>' ) : '' )
+ '</pre>';
},
ref : function( attr, content ) {
var group = attr.group || this.refsGroup || '';
if( this.refsGroup !== false ) {
if( group !== this.refsGroup ) {
return this.makeError( 'ref-group-mismatch' );
}
if( !attr.name ) {
return this.makeError( 'ref-no-key' );
}
var g = this.refs[group];
if( !g ) {
return this.makeError( 'ref-missing-group' );
}
if( g[1][attr.name] !== undefined ) {
g[0][g[1][attr.name]].text = content;
}
return '';
} else if( !content && !attr.name ) {
return this.makeError( 'ref-no-input' );
} else {
this.storeRef( group, attr.name, content );
return this.makeRefLink( group, attr.name );
}
},
references : function( attr, content ) {
if( this.refsGroup !== false ) {
return '';
}
var group = attr.group || '';
if( content ) {
this.refsGroup = group;
this.unstrip( this.parseInline( content ) );
this.refsGroup = false;
}
var g = this.refs[group];
if( !g || !g[0].length ) {
return '';
}
var result = this.getContentMessage( 'cite_references_prefix' );
for( var i = 0; i < g[0].length; i++ ) {
result += this.makeRefEntry( g[0][i], i ) + '\n';
}
result += this.getContentMessage( 'cite_references_suffix' );
delete this.refs[group];
return this.parseInline( result );
}
},
find : function( text, sub, start ) {
for( ; ; ) {
var pos = text.indexOf( sub, start );
if( pos < 0 ) {
return text.length;
}
var start = text.indexOf( '>', pos + 1 );
if( start < 0 ) {
return pos;
}
var lt = text.indexOf( '<', pos + 1 );
if( lt > -1 && lt < start ) {
return pos;
}
}
},
fixTags : function( text ) {
var that = this;
return text.replace( /<(\/?)(\w*)([^<>]*)(>?)|(>)/g, function( m, slash, tag, attr, close, gt ) {
if( gt ) {
return '>';
} else {
var lc = tag.toLowerCase();
if( close && tagFlags[lc] & USER_ALLOWED ) {
if( slash ) {
return '</' + lc + '>';
} else {
var slash = attr.charAt( attr.length - 1 ) === '/' ? '/' : '';
var attr = that.fixAttributes( attr, lc );
return '<' + lc + attr + slash + '>';
}
} else {
return '<' + slash + tag + attr + ( close && '>' );
}
}
} );
},
fixEntities : function( text ) {
return text.replace( / /g, ' ' ).replace( /|/g, '|' ).replace( /•/g, '•' );
},
fixAttributes : function( s, el ) {
if( !s.trim() ) {
return '';
}
var attr = this.validateAttributes( this.getAttributes( this.unstrip( s ) ) );
return this.encodeAttributes( attr );
},
getAttributes : function( s ) {
if( s.charAt( s.length - 1 ) === '/' ) {
s = s.substring( 0, s.length - 1 );
}
var attr = {}, m;
if( !s.trim() ) {
return attr;
}
var rx = /(?:^|[\t\n\r ])([A-Za-z\d]+)(?:[\t\n\r ]*=[\t\n\r ]*(?:("[^<"]*)"|('[^<']*)'|([\w!#$%&()*,\-.\/:;?@[\]^`{|}~^]+)))?/g;
while( m = rx.exec( s ) ) {
var next = s.charAt( m.index + m[0].length );
if( next && ' \t\n\r'.indexOf( next ) === -1 ) {
rx.lastIndex = m.index + 1;
continue;
}
var name = m[1].toLowerCase();
var value = m[4] || ( m[3] ? m[3].substring( 1 ) : ( m[2] ? m[2].substring( 1 ) : name ) );
value = value.replace( /[\t\n\r ]+/g, ' ' ).trim();
attr[name] = this.decodeEntities( value );
}
return attr;
},
validateAttributes : function( attr ) {
var hrefRX = new RegExp( '^(?:' + this.getUrlProtocols() + ')[^\s]+$' );
var newAttr = {};
for( var name in attr ) if( has.call( attr, name ) ) {
if( !attributeFlags[name] ) continue;
var value = attr[name];
if( name === 'style' ) value = this.validateStyle( value );
else if( name === 'id' ) value = this.anchorEncode( value );
else if( ( name === 'href' || name === 'src' ) && !value.match( hrefRX ) ) continue;
newAttr[name] = value;
if( name === 'lang' ) newAttr['xml:lang'] = value;
}
return newAttr;
},
encodeAttributes : function( attr ) {
var pairs = [];
for( var name in attr ) if( has.call( attr, name ) ) {
pairs.push( name + '="' + mw.html.escape( attr[name] + '' ) + '"' );
}
return pairs.length ? ' ' + pairs.join( ' ' ) : '';
},
validateStyle : function( s ) {
var s = s.replace( /\/\*[^*]*\*(?:\*|[^*\/][^*]*\*)*\//g, ' ' );
var stripped = s.replace( /\\([\dA-Fa-f]{1,6})/g, function( m, num ) { return String.fromCharCode( parseInt( num, 16 ) ); } );
stripped = stripped.replace( /\\/g, '' );
if( stripped.match( /expression|tps*:\/\/|url\s*\(/ ) ) return '';
return s;
},
decodeEntities : function( s ) {
var node = document.createElement( 'div' );
return s.replace( /&(?:#?[xX]?[^\x00-\x2F:;<=>\?@\[\\\]\^_`\{\|\}~\x7F]+;)?/g, function( m ) {
try{
node.innerHTML = m;
if( node.firstChild ) return node.firstChild.nodeValue;
} catch(e) {}
return m;
} );
},
getExtLinkRX : function() {
if( !this.extLinkRX ) {
this.extLinkRX = new RegExp(
'<[^>]*>|(\\[?)((?:' + this.getUrlProtocols().join( '|' )
+ ')[^\\[\\]"\x00-\x20\x7f<>]+)( *[^\\[\\]\n\r]*\\])?'
+ '|(RFC|PMID)\\s+(\\d+)'
+ '|ISBN\\s+((?:97[89][ \\-]?)?(?:\\d[ \\-]?){9}[\\dXx]\\b)', 'g'
);
}
return this.extLinkRX;
},
getUrlStartRX : function() {
if( !this.urlStartRX ) {
this.urlStartRX = new RegExp( '^(?:' + this.getUrlProtocols().join( '|' ) + ')' );
}
return this.urlStartRX;
}
} );
this.parse = function( text, title, cb ) {
return iterate( Parser, 'parse', text, title, cb );
};
function PPNode( type, text, offset, count ) {
this.type = type;
this.offset = offset;
this.lineStart = text.charAt( offset - 1 ) === '\n';
this.parts = [[]];
//cur and count are only for internal processing.
//They will be cleaned up later by finish()
this.cur = this.parts[0];
this.count = count;
}
PPNode.prototype = {
//Append text or a child to a node
append : function( node ) {
if( !node ) return;
var newstr = typeof node === 'string';
var oldstr = typeof this.cur[this.cur.length - 1] === 'string';
//For template nodes, record if and where an equal sign was found
//We put the '=' on its own index, so we can easily pull the name and value part separately
//later if necessary. We also make sure the index is >0 so we can do boolean checks on it
if( newstr && this.type == '{' && !this.cur.split ) {
var pos = node.indexOf( '=' );
if( pos > -1 ) {
if( oldstr ) {
this.cur[this.cur.length - 1] += node.substring( 0, pos );
} else if( pos > 0 || this.cur.length == 0 ) {
this.cur.push( node.substring( 0, pos ) );
}
this.cur.split = this.cur.length;
this.cur.push( '=' );
this.cur.push( node.substring( pos + 1 ) );
return;
}
}
if( newstr && oldstr ) {
this.cur[this.cur.length - 1] += node;
} else {
this.cur.push( node );
}
//For heading nodes, record if we have a correct closing
//A heading must end in one or more equal signs, followed only by
//whitespace or comments
if( this.type === 'h' ) {
if( newstr ) {
var match = node.match( /(=+)[ \t]*$/ );
if( match ) {
this.closing = match[1].length;
} else if( !node.match( /^[ \t]*$/ ) ) {
this.closing = false;
}
} else if( node.type != 'comment' ) {
this.closing = false;
}
}
},
//Break and append a child to a node
breakAndAppendTo : function( node ) {
//First add the opening braces
if( this.type != 'h' ) {
node.append( ( new Array( this.count + 1 ) ).join( this.type ) );
}
//Then the parts, separated by '|'
for( var i = 0; i < this.parts.length; i++ ) {
if( i > 0 ) {
node.append( '|' );
}
for( var j = 0; j < this.parts[i].length; j++ ) {
node.append( this.parts[i][j] );
}
}
},
//Clean up the extra stuff we put into the node for easier processing
finish : function( endOffset ) {
this.len = endOffset - this.offset;
delete this.cur;
delete this.count;
delete this.closing;
return this;
}
};
function XML( name, content, attr ) {
return '<' + name + ( attr || '' ) + ( content ? '>' + content + '</' + name + '>' : '/>' );
}
/************************************************************************************************
* Tidy
*/
function Tidy( parser ) {
this.parser = parser;
this.stack = [];
this.blocks = [];
this.inlines = [];
this.istacks = [];
this.headings = [];
this.open( 'root' );
}
Tidy.prototype = {
getCurrent : function() {
//returns the currently open element name, 'root' if none
return this.top.el;
},
open : function( el, flags, attr ) {
var f = tagFlags[el];
if( this.top ) {
this.stack.push( this.top );
}
var closing = false;
if( attr && attr.charAt( attr.length - 1 ) == '/' ) {
closing = true;
var attr = attr.substring( 0, attr.length - 1 ).trim();
}
if( attr ) {
var attr = ' ' + attr;
}
this.top = { el : el, attr : attr, flags : f | flags, content : '' };
var block = this.top.flags & ( BLOCK_NONE | BLOCK_SKIP | PARAGRAPH | PRE | IMPLICIT );
if( block && block !== IMPLICIT ) {
this.blocks.push( block );
}
if( el === 'table' ) {
this.istacks.push( this.inlines );
this.inlines = [];
}
if( closing ) {
this.addEndTag( el );
}
},
close : function() {
var next = this.stack.pop();
if( !this.top.exiled ) {
if( this.top.flags & CO_EMPTY ) {
next.content += '<' + this.top.el + ( this.top.attr || '' ) + ' />';
} else {
var content = this.top.content.trim();
if( content || this.top.flags & ALLOW_EMPTY || this.top.attr ) {
if( this.top.flags & HEADING ) {
this.headings.push( {
level : +this.top.el.charAt( 1 ),
name : content,
id : this.parser.anchorEncode( content )
} );
content = '<span class="mw-headline" id="' + this.parser.anchorEncode( content ) + '">' + content + '</span>';
} else if( this.top.flags & PRE ) {
content = this.top.content;
}
if( this.top.flags & INLINE ) {
next.content += this.top.content.match( /^\s*/ )[0];
}
next.content += '<' + this.top.el + ( this.top.attr || '' ) + '>' + content + '</' + this.top.el + '>';
if( this.top.flags & INLINE ) {
next.content += this.top.content.match( /\s*$/ )[0];
}
}
}
if( this.top.flags & ( BLOCK_NONE | BLOCK_SKIP | PARAGRAPH | PRE ) ) {
this.blocks.pop();
}
} else {
this.top.exiled = false;
}
if( this.top.el === 'table' ) {
this.inlines = this.istacks.pop();
}
this.top = next;
},
addText : function( s ) {
if( !s.trim() ) {
return this.top.content += s;
}
for( ; ; ) {
var o = tagFlags[this.top.el];
if( o & CO_EMPTY ) {
this.close();
continue;
}
if( o & CO_DEFLISTITEM ) {
this.open( 'dt' );
continue;
}
if( o & CO_LISTITEM ) {
this.open( 'li' );
continue;
}
if( o & CO_CELL || o & CO_ROW ) {
this.moveBeforeTable();
continue;
}
if( o & ROOT ) {
this.open( 'p', IMPLICIT );
continue;
}
if( o & BLOCK && !( o & INLINE ) ) {
this.propagateInlines();
}
this.top.content += s;
return;
}
},
moveBeforeTable : function() {
this.stack.push( this.top );
for( var i = this.stack.length - 1; !( this.stack[i].flags & CO_ROW ); i-- );
this.top = this.stack[i - 1];
this.top.exiled = true;
},
hasOpen : function( el ) {
if( this.top.el === el ) {
return true;
}
for( var i = this.stack.length - 1; i >= 0; i-- ) {
if( this.stack[i].el === el ) {
return true;
}
}
},
addStartTag : function( el, attr, flags ) {
var n = tagFlags[el];
for( ; ; ) {
var o = tagFlags[this.top.el];
//Check if we should close the current node before starting the new one
if( ( o & CELL && ( n & CELL || n & ROW ) )
|| ( o & LISTITEM && n & LISTITEM )
|| ( o & DEFLISTITEM && n & DEFLISTITEM )
|| ( this.top.exiled && ( n & ROW || n & CELL ) )
|| ( o & HEADING && n & HEADING && this.top.el != el )
|| ( el == 'a' && attr && this.hasOpen( 'a' ) )
|| ( o & CO_INLINE && !(n & INLINE) && n )
|| ( o & CO_EMPTY )
|| ( o & CO_DEFLISTITEM && !(n & DEFLISTITEM) && !(n & BLOCK) && !(n & INLINE) )
|| ( o & CO_CELL && n & CO_CELL )
|| ( o & CO_ROW && ( n & DEFLISTITEM || n & LISTITEM ) )
) {
this.close();
continue;
}
//Check if we should infer another node before starting the new one
if( !( o & CO_LISTITEM ) && n & LISTITEM ) {
this.open( 'ul' );
continue;
}
if( !( o & CO_DEFLISTITEM ) && n & DEFLISTITEM ) {
this.open( 'dl' );
continue;
}
if( !( o & CO_ROW ) && ( n & ROW || ( !( o & CO_CELL ) && n & CELL ) ) ) {
this.open( 'table' );
continue;
}
if( o & CO_DEFLISTITEM && !( n & DEFLISTITEM ) ) {
this.open( 'dd' );
continue;
}
if( o & CO_LISTITEM && !( n & LISTITEM ) ) {
this.open( 'li', 0, 'list-style: none' + ( n & BLOCK ? '; display : inline' : '' ) );
continue;
}
if( o & CO_ROW && ( n & CELL || n & CO_ROW ) ) {
this.open( 'tr' );
continue;
}
if( o & ROOT && !( n & BLOCK ) ) {
this.open( 'p', IMPLICIT );
continue;
}
if( o & PRE && n & PARAGRAPH ) {
this.open( 'br' );
return;
}
//Special cases
if( o & HEADING && n & HEADING && this.top.el === el ) {
this.close();
return;
}
if( o & INLINE && this.top.el === el && !attr && el !== 'big' && el !== 'small' ) {
this.popInline( el );
this.close();
return;
}
if( el === 'a' && this.hasOpen( 'a' ) ) {
this.addEndTag( 'a' );
return;
}
if( o & CO_CELL && n & CO_ROW ) return;
if( ( o & CO_CELL || o & CO_ROW ) && ( n & INLINE || n & BLOCK ) ) {
this.moveBeforeTable();
continue;
}
if( o & CO_CELL && !( n & CELL ) ) return;
//All is well, we can open the new node
if( o & BLOCK && n & INLINE && !( o & INLINE ) ) this.propagateInlines();
if( n & INLINE && el != 'a' && !( n & CO_EMPTY ) ) this.pushInline( el, attr );
this.open( el, flags, attr );
return;
}
},
addEndTag : function( el ) {
//TODO: <font..><a..>...</a></font> to <a..><font..>...</font></a> conversion
var n = tagFlags[el];
if( n & INLINE && el !== 'a' ) this.popInline( el );
for(;;) {
//Check if we got the correct tag for our current tag
if( el === this.top.el ) {
this.close();
return;
}
var o = this.top.flags;
//Check if we should close the current tag and try to match the end tag later
if( ( this.hasOpen( el ) && ( !(o & CO_CELL) || n & CO_ROW ) && ( !(o & CO_ROW) || n & DEFLISTITEM || n & LISTITEM ) )
|| ( o & HEADING && n & HEADING )
|| ( o & CO_EMPTY )
|| ( o & CO_ROW && !(n & ROW || n & CELL || n & INLINE) )
) {
this.close();
continue;
}
//Special cases
if( el == 'br' && ( o & CO_BLOCK || o & CO_INLINE ) ) {
this.open( 'br' );
}
return;
}
},
pushInline : function( el, attr ) {
for( var i = 0; i < this.inlines.length; i++ ) {
if( this.inlines[i][0] == el ) return;
}
this.inlines.push( [el,attr] );
},
popInline : function( el ) {
for( var i = 0; i < this.inlines.length; i++ ) {
if( this.inlines[i][0] == el ) break;
}
if( i < this.inlines.length ) this.inlines.splice( i, 1 );
},
propagateInlines : function() {
for( var i = 0; i < this.inlines.length; i++ ) {
this.open( this.inlines[i][0], 0, this.inlines[i][1] );
}
},
getCurrentBlock : function() {
return this.blocks[this.blocks.length - 1];
},
nextListItem : function( el ) {
var list = el === 'li' ? CO_LISTITEM : CO_DEFLISTITEM;
if( ( el === 'li' && ( this.hasOpen( 'ol' ) || this.hasOpen( 'ul' ) ) )
|| ( el !== 'li' && this.hasOpen( 'dl' ) ) ) {
while( !( this.top.flags & list ) ) this.close();
}
this.addStartTag( el );
},
toString : function() {
while( this.stack.length ) this.close();
return this.top.content;
}
};
function magic2RX( alias, cs, triple ) {
var rx = jQuery.escapeRE( alias );
if( !cs ) {
rx = rx.replace( /\w/g, function( ch ) {
return ch.toLowerCase() === ch.toUpperCase() ? ch : '[' + ch.toLowerCase() + ch.toUpperCase() + ']';
} );
}
if( !triple ) {
return rx;
}
var pos = rx.indexOf( '\\$1' );
if( pos > -1 ) {
return '(' + rx.substring( 0, pos ) + ')(.*)(' + rx.substring( pos + 3 ) + ')';
} else {
return '(' + rx + ')()()';
}
}
var iterate = this.iterate = function( cls, func, text, title, cb ) {
var obj = new cls( title );
var result = obj[func].call( obj, text );
if( cb ) {
if( obj.batch.isEmpty ) {
cb( result );
} else {
obj.batch.load( function() {
iterate( cls, func, text, title, cb );
}, obj.cache );
}
}
return result;
};
/************************************************************************************************
* private variables
*/
var safeModeMissing = new Object;
var specialFuncs = {
'basepagename' : 'page',
'basepagenamee' : 'page',
'currentday' : 'utc',
'currentday2' : 'utc',
'currentdayname' : 'utc',
'currentdow' : 'utc',
'currenthour' : 'utc',
'currentmonth' : 'utc',
'currentmonthabbrev' : 'utc',
'currentmonth1' : 'utc',
'currentmonthname' : 'utc',
'currentmonthnamegen' : 'utc',
'currenttime' : 'utc',
'currenttimestamp' : 'utc',
'currentweek' : 'utc',
'currentyear' : 'utc',
'fullpagename' : 'page',
'fullpagenamee' : 'page',
'fullurl' : 'page',
'fullurle' : 'page',
'localday' : 'local',
'localday2' : 'local',
'localdayname' : 'local',
'localdow' : 'local',
'localhour' : 'local',
'localmonth' : 'local',
'localmonthabbrev' : 'local',
'localmonth1' : 'local',
'localmonthname' : 'local',
'localmonthnamegen' : 'local',
'localtime' : 'local',
'localtimestamp' : 'local',
'localurl' : 'page',
'localurle' : 'page',
'localweek' : 'local',
'localyear' : 'local',
'namespace' : 'page',
'namespacee' : 'page',
'pagename' : 'page',
'pagenamee' : 'page',
'subjectpagename' : 'page',
'subjectpagenamee' : 'page',
'subjectspace' : 'page',
'subjectspacee' : 'page',
'subpagename' : 'page',
'subpagenamee' : 'page',
'talkpagename' : 'page',
'talkpagenamee' : 'page',
'talkspace' : 'page',
'talkspacee' : 'page'
};
var weekdayMessages = [
'sunday',
'monday',
'tuesday',
'wednesday',
'thursday',
'friday',
'saturday'
];
var weekdayAbbrevMessages = [
'sun',
'mon',
'tue',
'wed',
'thu',
'fri',
'sat'
];
var monthMessages = [
'january',
'february',
'march',
'april',
'may_long',
'june',
'july',
'august',
'september',
'october',
'november',
'december'
];
var monthGenMessages = [
'january-gen',
'february-gen',
'march-gen',
'april-gen',
'may-gen',
'june-gen',
'july-gen',
'august-gen',
'september-gen',
'october-gen',
'november-gen',
'december-gen'
];
var monthAbbrevMessages = [
'jan',
'feb',
'mar',
'apr',
'may',
'jun',
'jul',
'aug',
'sep',
'oct',
'nov',
'dec'
];
// Tag flags:
var USER_ALLOWED = 1 << 0;
var CO_INLINE = 1 << 1;
var CO_BLOCK = 1 << 2;
var CO_EMPTY = 1 << 3;
var CO_LISTITEM = 1 << 4;
var CO_ROW = 1 << 5;
var CO_DEFLISTITEM = 1 << 6;
var CO_CELL = 1 << 7;
var INLINE = 1 << 8;
var BLOCK = 1 << 9;
var ROW = 1 << 10;
var LISTITEM = 1 << 11;
var DEFLISTITEM = 1 << 12;
var HEADING = 1 << 13;
var CELL = 1 << 14;
var PARAGRAPH = 1 << 15;
var PRE = 1 << 16;
var ROOT = 1 << 17;
var ANCHOR = 1 << 18;
var IMPLICIT = 1 << 19;
var WIKI = 1 << 20;
var BLOCK_NONE = 1 << 21;
var BLOCK_SKIP = 1 << 22;
var ALLOW_EMPTY = 1 << 23;
var PROPAGATE = 1 << 24;
var tagFlags = {
'root' : CO_BLOCK | BLOCK | ROOT | BLOCK_NONE,
'a' : CO_INLINE | INLINE | ANCHOR,
'abbr' : USER_ALLOWED | CO_INLINE | INLINE,
'b' : USER_ALLOWED | CO_INLINE | INLINE,
'big' : USER_ALLOWED | CO_INLINE | INLINE,
'blockquote' : USER_ALLOWED | CO_BLOCK | BLOCK | BLOCK_SKIP,
'br' : USER_ALLOWED | CO_EMPTY | INLINE,
'caption' : USER_ALLOWED | CO_INLINE | ROW,
'center' : USER_ALLOWED | CO_BLOCK | BLOCK | BLOCK_NONE,
'cite' : USER_ALLOWED | CO_INLINE | INLINE,
'code' : USER_ALLOWED | CO_INLINE | INLINE,
'dd' : USER_ALLOWED | CO_BLOCK | DEFLISTITEM,
'del' : USER_ALLOWED | CO_BLOCK | INLINE | BLOCK,
'div' : USER_ALLOWED | CO_BLOCK | BLOCK | BLOCK_NONE | ALLOW_EMPTY,
'dl' : USER_ALLOWED | CO_DEFLISTITEM | BLOCK | BLOCK_SKIP,
'dt' : USER_ALLOWED | CO_INLINE | DEFLISTITEM,
'em' : USER_ALLOWED | CO_INLINE | INLINE,
'font' : USER_ALLOWED | CO_INLINE | INLINE,
'h1' : USER_ALLOWED | CO_INLINE | BLOCK | HEADING | BLOCK_SKIP,
'h2' : USER_ALLOWED | CO_INLINE | BLOCK | HEADING | BLOCK_SKIP,
'h3' : USER_ALLOWED | CO_INLINE | BLOCK | HEADING | BLOCK_SKIP,
'h4' : USER_ALLOWED | CO_INLINE | BLOCK | HEADING | BLOCK_SKIP,
'h5' : USER_ALLOWED | CO_INLINE | BLOCK | HEADING | BLOCK_SKIP,
'h6' : USER_ALLOWED | CO_INLINE | BLOCK | HEADING | BLOCK_SKIP,
'hr' : USER_ALLOWED | CO_EMPTY | BLOCK,
'i' : USER_ALLOWED | CO_INLINE | INLINE,
'img' : CO_EMPTY | INLINE,
'ins' : USER_ALLOWED | CO_BLOCK | INLINE | BLOCK,
'li' : USER_ALLOWED | CO_BLOCK | LISTITEM,
'ol' : USER_ALLOWED | CO_LISTITEM | BLOCK | BLOCK_SKIP,
'p' : USER_ALLOWED | CO_INLINE | BLOCK | PARAGRAPH,
'pre' : USER_ALLOWED | CO_INLINE | BLOCK | PRE,
'rb' : USER_ALLOWED | CO_INLINE | INLINE,
'rp' : USER_ALLOWED | CO_INLINE | INLINE,
'rt' : USER_ALLOWED | CO_INLINE | INLINE,
'ruby' : USER_ALLOWED | CO_INLINE | INLINE,
's' : USER_ALLOWED | CO_INLINE | INLINE,
'small' : USER_ALLOWED | CO_INLINE | INLINE,
'span' : USER_ALLOWED | CO_INLINE | INLINE,
'strike' : USER_ALLOWED | CO_INLINE | INLINE,
'strong' : USER_ALLOWED | CO_INLINE | INLINE,
'sub' : USER_ALLOWED | CO_INLINE | INLINE,
'sup' : USER_ALLOWED | CO_INLINE | INLINE,
'table' : USER_ALLOWED | CO_ROW | BLOCK | BLOCK_SKIP | ALLOW_EMPTY,
'td' : USER_ALLOWED | CO_BLOCK | CELL | ALLOW_EMPTY | BLOCK_NONE,
'th' : USER_ALLOWED | CO_BLOCK | CELL | ALLOW_EMPTY | BLOCK_NONE,
'tr' : USER_ALLOWED | CO_CELL | ROW,
'tt' : USER_ALLOWED | CO_INLINE | INLINE,
'u' : USER_ALLOWED | CO_INLINE | INLINE,
'ul' : USER_ALLOWED | CO_LISTITEM | BLOCK | BLOCK_SKIP,
'var' : USER_ALLOWED | CO_INLINE | INLINE
};
var attributeFlags = {
'abbr' : 1,
'align' : 1,
'alt' : 1,
'axis' : 1,
'bgcolor' : 1,
'border' : 1,
'cellpadding' : 1,
'cellspacing' : 1,
'char' : 1,
'charoff' : 1,
'cite' : 1,
'class' : 1,
'clear' : 1,
'color' : 1,
'colspan' : 1,
'datetime' : 1,
'dir' : 1,
'face' : 1,
'frame' : 1,
'headers' : 1,
'height' : 1,
'href' : 1,
'id' : 1,
'lang' : 1,
'noshade' : 1,
'nowrap' : 1,
'rel' : 1,
'rev' : 1,
'rowspan' : 1,
'rules' : 1,
'scope' : 1,
'size' : 1,
'span' : 1,
'start' : 1,
'style' : 1,
'summary' : 1,
'title' : 1,
'type' : 1,
'valign' : 1,
'value' : 1,
'width' : 1,
'xml:lang' : 1
};
var MAG_VARIABLE = 1 << 0;
var MAG_PFUNC = 1 << 1;
var MAG_PFUNC_HASH = 1 << 2;
var MAG_MODIFIER = 1 << 3;
var MAG_IMG_PARAM = 1 << 4;
var MAG_UNDERSCORE = 1 << 5;
var magicWordFlags = {
'anchorencode' : MAG_PFUNC,
'articlepath' : MAG_VARIABLE,
'basepagename' : MAG_VARIABLE | MAG_PFUNC,
'basepagenamee' : MAG_VARIABLE | MAG_PFUNC,
'categorytree' : MAG_PFUNC_HASH,//
'contentlanguage' : MAG_VARIABLE,
'currentday' : MAG_VARIABLE,
'currentday2' : MAG_VARIABLE,
'currentdayname' : MAG_VARIABLE,
'currentdow' : MAG_VARIABLE,
'currenthour' : MAG_VARIABLE,
'currentmonth' : MAG_VARIABLE,
'currentmonthabbrev' : MAG_VARIABLE,
'currentmonth1' : MAG_VARIABLE,
'currentmonthname' : MAG_VARIABLE,
'currentmonthnamegen' : MAG_VARIABLE,
'currenttime' : MAG_VARIABLE,
'currenttimestamp' : MAG_VARIABLE,
'currentversion' : MAG_VARIABLE,
'currentweek' : MAG_VARIABLE,
'currentyear' : MAG_VARIABLE,
'defaultsort' : MAG_PFUNC,
'directionmark' : MAG_VARIABLE,
'displaytitle' : MAG_PFUNC,
'expr' : MAG_PFUNC_HASH,
'filepath' : MAG_PFUNC,
'forcetoc' : MAG_UNDERSCORE,
'formatdate' : MAG_PFUNC_HASH,//
'formatnum' : MAG_PFUNC,
'fullpagename' : MAG_VARIABLE | MAG_PFUNC,
'fullpagenamee' : MAG_VARIABLE | MAG_PFUNC,
'fullurl' : MAG_PFUNC,
'fullurle' : MAG_PFUNC,
'gender' : MAG_PFUNC,
'grammar' : MAG_PFUNC,
'hiddencat' : MAG_UNDERSCORE,
'if' : MAG_PFUNC_HASH,
'ifeq' : MAG_PFUNC_HASH,
'iferror' : MAG_PFUNC_HASH,
'ifexist' : MAG_PFUNC_HASH,
'ifexpr' : MAG_PFUNC_HASH,
'img_alt' : MAG_IMG_PARAM,
'img_baseline' : MAG_IMG_PARAM,
'img_border' : MAG_IMG_PARAM,
'img_bottom' : MAG_IMG_PARAM,
'img_center' : MAG_IMG_PARAM,
'img_framed' : MAG_IMG_PARAM,
'img_frameless' : MAG_IMG_PARAM,
'img_left' : MAG_IMG_PARAM,
'img_link' : MAG_IMG_PARAM,
'img_manualthumb' : MAG_IMG_PARAM,
'img_middle' : MAG_IMG_PARAM,
'img_none' : MAG_IMG_PARAM,
'img_page' : MAG_IMG_PARAM,
'img_right' : MAG_IMG_PARAM,
'img_sub' : MAG_IMG_PARAM,
'img_super' : MAG_IMG_PARAM,
'img_text_bottom' : MAG_IMG_PARAM,
'img_text_top' : MAG_IMG_PARAM,
'img_thumbnail' : MAG_IMG_PARAM,
'img_top' : MAG_IMG_PARAM,
'img_upright' : MAG_IMG_PARAM,
'img_width' : MAG_IMG_PARAM,
'index' : MAG_UNDERSCORE,
'int' : MAG_PFUNC,
'language' : MAG_PFUNC, //In practice the func works with a hash, but for some reason the hash is part of the translation in this case !?
'lc' : MAG_PFUNC,
'lcfirst' : MAG_PFUNC,
'localday' : MAG_VARIABLE,
'localday2' : MAG_VARIABLE,
'localdayname' : MAG_VARIABLE,
'localdow' : MAG_VARIABLE,
'localhour' : MAG_VARIABLE,
'localmonth' : MAG_VARIABLE,
'localmonthabbrev' : MAG_VARIABLE,
'localmonth1' : MAG_VARIABLE,
'localmonthname' : MAG_VARIABLE,
'localmonthnamegen' : MAG_VARIABLE,
'localtime' : MAG_VARIABLE,
'localtimestamp' : MAG_VARIABLE,
'localurl' : MAG_PFUNC,
'localurle' : MAG_PFUNC,
'localweek' : MAG_VARIABLE,
'localyear' : MAG_VARIABLE,
'msg' : MAG_MODIFIER,
'msgnw' : MAG_MODIFIER,
'namespace' : MAG_VARIABLE | MAG_PFUNC,
'namespacee' : MAG_VARIABLE | MAG_PFUNC,
'newsectionlink' : MAG_UNDERSCORE,
'nocontentconvert' : MAG_UNDERSCORE,
'noeditsection' : MAG_UNDERSCORE,
'nogallery' : MAG_UNDERSCORE,
'noheader' : MAG_UNDERSCORE,
'noindex' : MAG_UNDERSCORE,
'nonewsectionlink' : MAG_UNDERSCORE,
'notitleconvert' : MAG_UNDERSCORE,
'notoc' : MAG_UNDERSCORE,
'ns' : MAG_PFUNC,
'nse' : MAG_PFUNC,
'numberingroup' : MAG_PFUNC,//
'numberofactiveusers' : MAG_VARIABLE | MAG_PFUNC,//
'numberofadmins' : MAG_VARIABLE | MAG_PFUNC,//
'numberofarticles' : MAG_VARIABLE | MAG_PFUNC,//
'numberofedits' : MAG_VARIABLE | MAG_PFUNC,//
'numberoffiles' : MAG_VARIABLE | MAG_PFUNC,//
'numberofpages' : MAG_VARIABLE | MAG_PFUNC,//
'numberofusers' : MAG_VARIABLE | MAG_PFUNC,//
'numberofviews' : MAG_VARIABLE | MAG_PFUNC,//
'padleft' : MAG_PFUNC,
'padright' : MAG_PFUNC,
'pagename' : MAG_VARIABLE | MAG_PFUNC,
'pagenamee' : MAG_VARIABLE | MAG_PFUNC,
'pagesincategory' : MAG_PFUNC,//
'pagesinnamespace' : MAG_PFUNC,//
'pagesize' : MAG_PFUNC,//
'plural' : MAG_PFUNC,
'protectionlevel' : MAG_PFUNC,//
'raw' : MAG_MODIFIER,
'rel2abs' : MAG_PFUNC_HASH,
'revisionday' : MAG_VARIABLE,//
'revisionday2' : MAG_VARIABLE,//
'revisionid' : MAG_VARIABLE,//
'revisionmonth' : MAG_VARIABLE,//
'revisionmonth1' : MAG_VARIABLE,//
'revisiontimestamp' : MAG_VARIABLE,//
'revisionuser' : MAG_VARIABLE,//
'revisionyear' : MAG_VARIABLE,//
'safesubst' : MAG_MODIFIER,
'scriptpath' : MAG_VARIABLE,//
'server' : MAG_VARIABLE,//
'servername' : MAG_VARIABLE,//
'sitename' : MAG_VARIABLE,//
'special' : MAG_PFUNC_HASH,//
'staticredirect' : MAG_UNDERSCORE,
'stylepath' : MAG_VARIABLE,//
'subjectpagename' : MAG_VARIABLE | MAG_PFUNC,
'subjectpagenamee' : MAG_VARIABLE | MAG_PFUNC,
'subjectspace' : MAG_VARIABLE | MAG_PFUNC,
'subjectspacee' : MAG_VARIABLE | MAG_PFUNC,
'subpagename' : MAG_VARIABLE | MAG_PFUNC,
'subpagenamee' : MAG_VARIABLE | MAG_PFUNC,
'subst' : MAG_MODIFIER,
'switch' : MAG_PFUNC_HASH,
'tag' : MAG_PFUNC_HASH,
'talkpagename' : MAG_VARIABLE | MAG_PFUNC,
'talkpagenamee' : MAG_VARIABLE | MAG_PFUNC,
'talkspace' : MAG_VARIABLE | MAG_PFUNC,
'talkspacee' : MAG_VARIABLE | MAG_PFUNC,
'time' : MAG_PFUNC_HASH,
'timel' : MAG_PFUNC_HASH,
'titleparts' : MAG_PFUNC_HASH,
'toc' : MAG_UNDERSCORE,
'uc' : MAG_PFUNC,
'ucfirst' : MAG_PFUNC,
'urlencode' : MAG_PFUNC
};
}();