MediaWiki:Functions.js: Difference between revisions

Jump to navigation Jump to search
m moved MediaWiki:Function.js to MediaWiki:Functions.js: makes it easier to ste..erm..copy from wookiepedia
mNo edit summary
 
Line 1: Line 1:
/* <pre style="overflow: scroll; height: 25em"><nowiki> */
/* <pre style="overflow: scroll; height: 25em"><nowiki> */


/* This file is a collection of reusable functions from Wookieepedia. */
/*
    This file is a collection of reusable functions from Wookieepedia.
*/


/* This global variable specifies if client-side persistent storage is available. Currently, only Firefox 2 supports this specification. On Wookieepedia, this global storage is used to store information about which infoboxes are hidden. */
/*
    This global variable specifies if client-side persistent storage is available. Currently, only Firefox 2 supports this specification. On Wookieepedia, this global storage is used to store information about which infoboxes are hidden.
*/
window.storagePresent = (typeof(globalStorage) != 'undefined');
window.storagePresent = (typeof(globalStorage) != 'undefined');


/* Stores the (unmodified) page title. */
/*
function storePageName() {
    Adds a trim method to string variables.
window.pageName = getFirstHeading().childNodes[0].nodeValue.trim();
*/
}
 
/* Adds a trim method to string variables. */
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ""); };
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ""); };


/* Searches an array for an element and returns its index, or -1 if it's not in the array. */
/*
function arrayFind( array, value ) {
    Searches an array for an element and returns its index, or -1 if it's not in the array.
for( var i = 0; i < array.length; i++ ) {
*/
if( array[i] == value ) {
function arrayFind(array, value)
return i;
{
}
    for(var i = 0; i < array.length; i++)
}
    {
 
        if(array[i] == value)
return -1;
            return i;
    }
    return -1;
}
}


/* Removes the first occurrence of an element in an array, if it is there. */
/*
function arrayRemove( array, value ) {
    Removes the first occurrence of an element in an array, if it is there.
var i = arrayFind( array, value );
*/
 
function arrayRemove(array, value)
if( i != -1 ) {
{
array.splice( i, 1 );
    var i = arrayFind(array, value);
}
    if(i != -1)
        array.splice(i, 1);
}
}


/*
/*
the ContentLoader class to encapsulate "creative differences" with XHR
the ContentLoader class to encapsulate "creative differences" with XHR
 
Usage:
    Usage:
- construct a ContentLoader object: var loader = new ContentLoader();
        - construct a ContentLoader object: var loader = new ContentLoader();
- set necessary state parameters (via fields); e.g. loader.myvar = 'mytext';
        - set necessary state parameters (via fields); e.g. loader.myvar = 'mytext';
- set the callback: loader.callback = myfunc;
        - set the callback: loader.callback = myfunc;
- send the request:
        - send the request:
loader.send(url, postdata = null, contentType = 'application/x-www-form-urlencoded');
            loader.send(url, postdata = null, contentType = 'application/x-www-form-urlencoded');
(if postdata isn't null or omitted, POST is used, otherwise GET)
            (if postdata isn't null or omitted, POST is used, otherwise GET)
- the callback function is called when the content is loaded
        - the callback function is called when the content is loaded
- the ContentLoader object is this
            - the ContentLoader object is this
- the raw response data is this.text
            - the raw response data is this.text
- the XML DOM object, if any, is this.document
            - the XML DOM object, if any, is this.document
*/
*/
function ContentLoader() {
function ContentLoader()
this.cache = true;
{
    this.cache = true;
}
}


ContentLoader.prototype.enableCache = function( caching ) {
ContentLoader.prototype.enableCache = function(caching)
this.cache = ( caching == null ) ? true : this.cache;
{
    this.cache = (caching == null) ? true : this.cache;
}
}


ContentLoader.prototype.createRequest = function() {
ContentLoader.prototype.createRequest = function()
if( typeof( XMLHttpRequest ) != 'undefined' ) {
{
if(typeof(XMLHttpRequest) != 'undefined')
{
return new XMLHttpRequest();
return new XMLHttpRequest();
} else if( typeof( ActiveXObject ) != 'undefined' ) {
}
else if(typeof(ActiveXObject) != 'undefined')
{
return new ActiveXObject("Msxml2.XMLHTTP");
return new ActiveXObject("Msxml2.XMLHTTP");
}
}
 
return null;
return null;
}
}


ContentLoader.prototype.send = function( url, postdata, contentType ) {
ContentLoader.prototype.send = function(url, postdata, contentType)
var method = ( postdata == null ) ? 'GET' : 'POST';
{
var method = (postdata == null) ? 'GET' : 'POST';
this.request = this.createRequest();
this.request = this.createRequest();
this.request.open( method, url );
this.request.open(method, url);


if( !this.cache ) {
if(!this.cache)
this.request.setRequestHeader( 'If-Modified-Since', 'Sat, 1 Jan 2000 00:00:00 GMT' );
this.request.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" );
}


var request = this.request;
var request = this.request;
var loader = this;
var loader = this;
 
if( postdata == null ) {
if(postdata == null)
if( contentType == null ) {
{
contentType = 'application/x-www-form-urlencoded';
    if(contentType == null)
}
        contentType = 'application/x-www-form-urlencoded';
 
request.setRequestHeader( 'Content-type', contentType );
    request.setRequestHeader('Content-type', contentType);
}
 
var f = function() {
if( request.readyState == 4 ) {
loader.text = request.responseText;
loader.document = request.responseXML;
request = null;
loader.request = null;
loader.callback();
}
}
}
 
var f = function()
    {
    if(request.readyState == 4)
    {
    loader.text = request.responseText;
    loader.document = request.responseXML;
    request = null;
    loader.request = null;
    loader.callback();
    }
    }
this.request.onreadystatechange = f;
this.request.onreadystatechange = f;
this.request.send( postdata );
this.request.send(postdata);
}
}
/* end ContentLoader */
/*
end ContentLoader
*/


/*
/*
Source: http://www.dustindiaz.com/getelementsbyclass/
    Source: http://www.dustindiaz.com/getelementsbyclass/
getElementsByClass, which complements getElementById and getElementsByTagName, returns an array of all subelements of ''node'' that are tagged with a specific CSS class (''searchClass'') and are of the tag name ''tag''. If tag is null, it searches for any suitable elements regardless of the tag name.
    getElementsByClass, which complements getElementById and getElementsByTagName, returns an array of all subelements of ''node'' that are tagged with a specific CSS class (''searchClass'') and are of the tag name ''tag''. If tag is null, it searches for any suitable elements regardless of the tag name.
Example: getElementsByClass('infobox', document.getElementById('content'), 'div') selects the same elements as the CSS declaration #content div.infobox
    Example: getElementsByClass('infobox', document.getElementById('content'), 'div') selects the same elements as the CSS declaration #content div.infobox
*/
*/
function getElementsByClass( searchClass, node, tag ) {
function getElementsByClass(searchClass, node, tag)
{
var classElements = new Array();
var classElements = new Array();


if( node == null ) {
if(node == null)
node = document;
node = document;
}


if( tag == null ) {
if(tag == null)
tag = '*';
tag = '*';
}


var els = node.getElementsByTagName( tag );
var els = node.getElementsByTagName(tag);
var elsLen = els.length;
var elsLen = els.length;
var tester = new ClassTester( searchClass );
var tester = new ClassTester(searchClass);


for( i = 0, j = 0; i < elsLen; i++ ) {
for(i = 0, j = 0; i < elsLen; i++)
if( tester.isMatch( els[i] ) ) {
{
if(tester.isMatch(els[i]))
{
classElements[j] = els[i];
classElements[j] = els[i];
j++;
j++;
}
}
}
}
 
   
return classElements;
return classElements;
}
}


function ClassTester( className ) {
function ClassTester(className)
this.regex = new RegExp("(^|\\s)" + className + "(\\s|$)");
{
    this.regex = new RegExp("(^|\\s)" + className + "(\\s|$)");
}
}


ClassTester.prototype.isMatch = function( element ) {
ClassTester.prototype.isMatch = function(element)
return this.regex.test( element.className );
{
    return this.regex.test(element.className);
}
}
/* end getElementsByClass */
/*
    end getElementsByClass
*/


/* Returns the parameter as it appears in the query string. Equivalent to $_GET[p] in PHP. */
/*
function queryString( p ) {
    Returns the parameter as it appears in the query string. Equivalent to $_GET[p] in PHP.
var re = RegExp('[&?]' + p + '=([^&]*)');
*/
var matches;
function queryString(p)  
{
    var re = RegExp('[&?]' + p + '=([^&]*)');
    var matches;


if( matches = re.exec( document.location ) ) {
    if(matches = re.exec(document.location))
try {
    {
return decodeURI( matches[1] );
        try
} catch( e ) { }
        {
}
            return decodeURI(matches[1]);
        }
        catch(e) { }
    }


return null;
    return null;
}
}
/*
    end temporary per-page unique CSS (Splarka)
*/


/*
/*
Dynamically load a combobox's content by pagename (e. g. Template:Stdsummaries)
    Dynamically load a combobox's content by pagename (e. g. Template:Stdsummaries)
The page should be of the same format as http://starwars.wikia.com/wiki/Template:Stdsummaries
    The page should be of the same format as http://starwars.wikia.com/wiki/Template:Stdsummaries
*/
*/
function requestComboFill( id, page ) {
function requestComboFill(id, page)
var loader = new ContentLoader();
{
loader.comboID = id;
    var loader = new ContentLoader();
loader.callback = onComboDataArrival;
    loader.comboID = id;
loader.send( wgScriptPath + '/index.php?title=' + page + '&action=raw&ctype=text/plain' );
    loader.callback = onComboDataArrival;
    loader.send('/index.php?title=' + page + '&action=raw&ctype=text/plain');
}
}


function onComboDataArrival() {
function onComboDataArrival()
fillCombo( this.text, this.comboID );
{
    fillCombo(this.text, this.comboID);
}
}


function fillCombo( text, comboid ) {
function fillCombo(text, comboid)
var combo = document.getElementById( comboid );
{
var lines = text.split( "\n" );
    var combo = document.getElementById(comboid);
    var lines = text.split("\n");


for( var i = 0; i < lines.length; i++ ) {
    for(var i = 0; i < lines.length; i++)
var value = lines[i].indexOf("-- ") == 0 ? lines[i].substring(3) : '';
    {
var option = document.createElement( 'option' );
        var value = lines[i].indexOf("-- ") == 0 ? lines[i].substring(3) : "";
option.setAttribute( 'value', value );
        var option = document.createElement('option');
option.appendChild( document.createTextNode( lines[i] ) );
        option.setAttribute('value', value);
combo.appendChild( option );
        option.appendChild(document.createTextNode(lines[i]));
}
        combo.appendChild(option);
    }
}
}
/* end combo fill code */
/*
    end combo fill code
*/


/*
/*
Loads the current source of the page "pagename" (as stored in the database)
    Loads the current source of the page "pagename" (as stored in the database)
and inserts it at the cursor position
    and inserts it at the cursor position
*/
*/
function doPreload( pagename ) {
function doPreload(pagename)
var loader = new ContentLoader();
{
loader.callback = onPreloadArrival;
    var loader = new ContentLoader();
loader.send( wgScriptPath + '/index.php?title=' + pagename + '&action=raw&ctype=text/plain' );
    loader.callback = onPreloadArrival;
    loader.send('/index.php?title=' + pagename + '&action=raw&ctype=text/plain');
}
}


function insertAtCursor( myField, myValue ) {
function insertAtCursor(myField, myValue)
// IE support
{
if( document.selection ) {
    //IE support
myField.focus();
    if (document.selection)
sel = document.selection.createRange();
    {
sel.text = myValue;
        myField.focus();
} else if( myField.selectionStart || myField.selectionStart == '0' ) { // MOZILLA/NETSCAPE support
        sel = document.selection.createRange();
var startPos = myField.selectionStart;
        sel.text = myValue;
var endPos = myField.selectionEnd;
    }
myField.value = myField.value.substring(0, startPos)
    //MOZILLA/NETSCAPE support
+ myValue
    else if(myField.selectionStart || myField.selectionStart == '0')
+ myField.value.substring(endPos, myField.value.length);
    {
} else {
        var startPos = myField.selectionStart;
myField.value += myValue;
        var endPos = myField.selectionEnd;
}
        myField.value = myField.value.substring(0, startPos)
        + myValue
        + myField.value.substring(endPos, myField.value.length);
    }
    else
    {
        myField.value += myValue;
    }
}
}


function onPreloadArrival() {
function onPreloadArrival()
insertAtCursor( document.getElementById('wpTextbox1'), this.text );
{
    insertAtCursor(document.getElementById('wpTextbox1'), this.text);
}
}
/* end preload code */
/*
    end preload code
*/


/* Returns h1.firstHeading (the page title element). */
/*
function getFirstHeading() {
    Returns h1.firstHeading (the page title element).
var elements = getElementsByClass( 'firstHeading', document.getElementById( 'content' ), 'h1' );
*/
return ( elements != null && elements.length > 0 ) ? elements[0] : null;
function getFirstHeading()
{
    var elements = getElementsByClass('firstHeading', document.getElementById('content'), 'h1');
    return (elements != null && elements.length > 0) ? elements[0] : null;
}
}


/* Returns the element's nearest parent that has the specified CSS class. */
/*
function getParentByClass( className, element ) {
    Returns the element's nearest parent that has the specified CSS class.
var tester = new ClassTester( className );
*/
var node = element.parentNode;
function getParentByClass(className, element)
 
{
while( node != null && node != document ) {
    var tester = new ClassTester(className);
if( tester.isMatch( node ) ) {
    var node = element.parentNode;
return node;
   
}
    while(node != null && node != document)
 
    {
node = node.parentNode;
        if(tester.isMatch(node))
}
            return node;
 
           
return null;
        node = node.parentNode;
    }
   
    return null;
}
}


/*
/*
Makes the image on the search form, if one is present, point to the search page
    Makes the image on the search form, if one is present, point to the search page
instead of the Wikia main page.
    instead of the Wikia main page.
*/
*/
function rewriteSearchFormLink() {
function rewriteSearchFormLink() {
Line 251: Line 304:


/*
/*
Replaces {{USERNAME}} with the name of the user browsing the page.
    Replaces {{USERNAME}} with the name of the user browsing the page.
Requires copying Template:USERNAME.
    Requires copying Template:USERNAME.
*/
*/
function substUsername() {
function substUsername()
var userpage = document.getElementById( 'pt-userpage' );
{
    var spans = getElementsByClass('insertusername', null, 'span');


if( !userpage ) {
    for(var i = 0; i < spans.length; i++)
return;
    {
}
        spans[i].innerHTML = wgUserName;
 
    }
var username = userpage.firstChild.innerHTML;
var spans = getElementsByClass( 'insertusername', document.getElementById( 'content' ), 'span' );
 
for( var i = 0; i < spans.length; i++ ) {
spans[i].innerHTML = username;
}
}
}


/*
/*
Performs dynamic hover class rewriting to work around the IE6 :hover bug
    Performs dynamic hover class rewriting to work around the IE6 :hover bug
(needs CSS changes as well)
    (needs CSS changes as well)
*/
*/
function rewriteHover() {
function rewriteHover()
var gbl = document.getElementById( 'hover-global' );
{
  var gbl = document.getElementById("hover-global");


if( gbl == null ) {
  if(gbl == null)
return;
      return;
}
 
  var nodes = getElementsByClass("hoverable", gbl);


var nodes = getElementsByClass( 'hoverable', gbl );
  for (var i = 0; i < nodes.length; i++) {
    nodes[i].onmouseover = function() {
      this.className += " over";
    }
    nodes[i].onmouseout = function() {
      this.className = this.className.replace(new RegExp(" over\\b"), "");
    }
  }
}


for( var i = 0; i < nodes.length; i++ ) {
/*
nodes[i].onmouseover = function() {
    to call in onload hooks
this.className += ' over';
*/
}
function initFunctionsJS()
nodes[i].onmouseout = function() {
{
this.className = this.className.replace(new RegExp(" over\\b"), "");
    storePageName();
}
}
}
}


/* to call in onload hooks */
function storePageName()
function initFunctionsJS() {
{
storePageName();
    window.pageName = wgPageName;
}
}


/* </nowiki></pre> */
/* </nowiki></pre> */

Latest revision as of 14:33, 26 May 2010

/* <pre style="overflow: scroll; height: 25em"><nowiki> */

/*
    This file is a collection of reusable functions from Wookieepedia.
*/

/*
    This global variable specifies if client-side persistent storage is available. Currently, only Firefox 2 supports this specification. On Wookieepedia, this global storage is used to store information about which infoboxes are hidden.
*/
window.storagePresent = (typeof(globalStorage) != 'undefined');

/*
    Adds a trim method to string variables.
*/
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ""); };

/*
    Searches an array for an element and returns its index, or -1 if it's not in the array.
*/
function arrayFind(array, value)
{
    for(var i = 0; i < array.length; i++)
    {
        if(array[i] == value)
            return i;
    }
	
    return -1;
}

/*
    Removes the first occurrence of an element in an array, if it is there.
*/
function arrayRemove(array, value)
{
    var i = arrayFind(array, value);
	
    if(i != -1)
        array.splice(i, 1);
}

/*
	the ContentLoader class to encapsulate "creative differences" with XHR
	
    Usage:
        - construct a ContentLoader object: var loader = new ContentLoader();
        - set necessary state parameters (via fields); e.g. loader.myvar = 'mytext';
        - set the callback: loader.callback = myfunc;
        - send the request:
            loader.send(url, postdata = null, contentType = 'application/x-www-form-urlencoded');
            (if postdata isn't null or omitted, POST is used, otherwise GET)
        - the callback function is called when the content is loaded
            - the ContentLoader object is this
            - the raw response data is this.text
            - the XML DOM object, if any, is this.document
*/
function ContentLoader()
{
    this.cache = true;
}

ContentLoader.prototype.enableCache = function(caching)
{
    this.cache = (caching == null) ? true : this.cache;
}

ContentLoader.prototype.createRequest = function()
{
	if(typeof(XMLHttpRequest) != 'undefined')
	{
		return new XMLHttpRequest();
	}
	else if(typeof(ActiveXObject) != 'undefined')
	{
		return new ActiveXObject("Msxml2.XMLHTTP");
	}
	
	return null;
}

ContentLoader.prototype.send = function(url, postdata, contentType)
{
	var method = (postdata == null) ? 'GET' : 'POST';
	this.request = this.createRequest();
	this.request.open(method, url);

	if(!this.cache)
		this.request.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" );

	var request = this.request;
	var loader = this;
	
	if(postdata == null)
	{
	    if(contentType == null)
	        contentType = 'application/x-www-form-urlencoded';
	
	    request.setRequestHeader('Content-type', contentType);
	}
	
	var f = function()
    {
    	if(request.readyState == 4)
    	{
    		loader.text = request.responseText;
    		loader.document = request.responseXML;
    		request = null;
    		loader.request = null;
    		loader.callback();
    	}
    }
	
	this.request.onreadystatechange = f;
	this.request.send(postdata);
}
/*
	end ContentLoader
*/

/*
    Source: http://www.dustindiaz.com/getelementsbyclass/
    getElementsByClass, which complements getElementById and getElementsByTagName, returns an array of all subelements of ''node'' that are tagged with a specific CSS class (''searchClass'') and are of the tag name ''tag''. If tag is null, it searches for any suitable elements regardless of the tag name.
    Example: getElementsByClass('infobox', document.getElementById('content'), 'div') selects the same elements as the CSS declaration #content div.infobox
*/
function getElementsByClass(searchClass, node, tag)
{
	var classElements = new Array();

	if(node == null)
		node = document;

	if(tag == null)
		tag = '*';

	var els = node.getElementsByTagName(tag);
	var elsLen = els.length;
	var tester = new ClassTester(searchClass);

	for(i = 0, j = 0; i < elsLen; i++)
	{
		if(tester.isMatch(els[i]))
		{
			classElements[j] = els[i];
			j++;
		}
	}
    
	return classElements;
}

function ClassTester(className)
{
    this.regex = new RegExp("(^|\\s)" + className + "(\\s|$)");
}

ClassTester.prototype.isMatch = function(element)
{
    return this.regex.test(element.className);
}
/*
    end getElementsByClass
*/

/*
    Returns the parameter as it appears in the query string. Equivalent to $_GET[p] in PHP.
*/
function queryString(p) 
{
    var re = RegExp('[&?]' + p + '=([^&]*)');
    var matches;

    if(matches = re.exec(document.location))
    {
        try
        {
            return decodeURI(matches[1]);
        }
        catch(e) { }
    }

    return null;
}
/*
    end temporary per-page unique CSS (Splarka)
*/

/*
    Dynamically load a combobox's content by pagename (e. g. Template:Stdsummaries)
    The page should be of the same format as http://starwars.wikia.com/wiki/Template:Stdsummaries
*/
function requestComboFill(id, page)
{
    var loader = new ContentLoader();
    loader.comboID = id;
    loader.callback = onComboDataArrival;
    loader.send('/index.php?title=' + page + '&action=raw&ctype=text/plain');
}

function onComboDataArrival()
{
    fillCombo(this.text, this.comboID);
}

function fillCombo(text, comboid)
{
    var combo = document.getElementById(comboid);
    var lines = text.split("\n");

    for(var i = 0; i < lines.length; i++)
    {
        var value = lines[i].indexOf("-- ") == 0 ? lines[i].substring(3) : "";
        var option = document.createElement('option');
        option.setAttribute('value', value);
        option.appendChild(document.createTextNode(lines[i]));
        combo.appendChild(option);
    }
}
/*
    end combo fill code
*/

/*
    Loads the current source of the page "pagename" (as stored in the database)
    and inserts it at the cursor position
*/
function doPreload(pagename)
{
    var loader = new ContentLoader();
    loader.callback = onPreloadArrival;
    loader.send('/index.php?title=' + pagename + '&action=raw&ctype=text/plain');
}

function insertAtCursor(myField, myValue)
{
    //IE support
    if (document.selection)
    {
        myField.focus();
        sel = document.selection.createRange();
        sel.text = myValue;
    }
    //MOZILLA/NETSCAPE support
    else if(myField.selectionStart || myField.selectionStart == '0')
    {
        var startPos = myField.selectionStart;
        var endPos = myField.selectionEnd;
        myField.value = myField.value.substring(0, startPos)
        + myValue
        + myField.value.substring(endPos, myField.value.length);
    }
    else
    {
        myField.value += myValue;
    }
}

function onPreloadArrival()
{
    insertAtCursor(document.getElementById('wpTextbox1'), this.text);
}
/*
    end preload code
*/

/*
    Returns h1.firstHeading (the page title element).
*/
function getFirstHeading()
{
    var elements = getElementsByClass('firstHeading', document.getElementById('content'), 'h1');
    return (elements != null && elements.length > 0) ? elements[0] : null;
}

/*
    Returns the element's nearest parent that has the specified CSS class.
*/
function getParentByClass(className, element)
{
    var tester = new ClassTester(className);
    var node = element.parentNode;
    
    while(node != null && node != document)
    {
        if(tester.isMatch(node))
            return node;
            
        node = node.parentNode;
    }
    
    return null;
}

/*
    Makes the image on the search form, if one is present, point to the search page
    instead of the Wikia main page.
*/
function rewriteSearchFormLink() {
	var links = document.getElementById('searchform').getElementsByTagName('a');

	if( links.length > 0 ) {
		links[0].href = wgScriptPath + '/index.php?title=Special:Search&adv=1';
	}
}

/*
    Replaces {{USERNAME}} with the name of the user browsing the page.
    Requires copying Template:USERNAME.
*/
function substUsername()
{
    var spans = getElementsByClass('insertusername', null, 'span');

    for(var i = 0; i < spans.length; i++)
    {
        spans[i].innerHTML = wgUserName;
    }
}

/*
    Performs dynamic hover class rewriting to work around the IE6 :hover bug
    (needs CSS changes as well)
*/
function rewriteHover()
{
  var gbl = document.getElementById("hover-global");

  if(gbl == null)
      return;

  var nodes = getElementsByClass("hoverable", gbl);

  for (var i = 0; i < nodes.length; i++) {
    nodes[i].onmouseover = function() {
      this.className += " over";
    }
    nodes[i].onmouseout = function() {
      this.className = this.className.replace(new RegExp(" over\\b"), "");
    }
  }
}

/*
    to call in onload hooks
*/
function initFunctionsJS()
{
    storePageName();
}

function storePageName()
{
    window.pageName = wgPageName;
}


/* </nowiki></pre> */