User:Pathoschild/Scripts/Ajax sysop/experimental.js
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
// <source lang="javascript">
/*#############################################
### Ajax Sysop
### by [[user:Pathoschild]] (Jesse Plamondon-Willard)
### see http://meta.wikimedia.org/wiki/User:Pathoschild/Scripts/Ajax_sysop#Installation
##############################################*/
mw.loader.load('//meta.wikimedia.org/w/index.php?title=User:Pathoschild/Scripts/Ajax_sysop.css&action=raw&ctype=text/css&r2', 'text/css');
var pathoschild = {
revision: 70,
/*############################
## AJAX Sysop
## Enhances MediaWiki with AJAX features for sysops and stewards.
############################*/
ajax_mw: {
/*############################
## Properties & objects
############################*/
/*********
** Properties
*********/
config: {
ajaxPatrol: true,
ajaxRollback: true,
botModeRollback: true,
specialDeleteHelpers: true
},
/*############################
## Initialization methods
############################*/
/*********
** Initialize
*********/
Initialize: function () {
/* load interface enhancements */
if (this.config.ajaxPatrol) {
this.InitAjaxPatrol();
}
if (this.config.ajaxRollback) {
this.InitAjaxRollback();
}
if (this.config.botModeRollback) {
this.InitBotModeRollback();
}
if (this.config.specialDeleteHelpers) {
this.InitSpecialDeleteHelpers();
}
},
/*********
** Initialize AJAX patrol
*********/
InitAjaxPatrol: function () {
/* wrap links in containers */
var links = $('#bodyContent a[href*="rcid="]');
links.each(function (i, link) {
/* get link & rcid */
link = $(link);
var rcid = link.attr('href').match(/rcid=(\d+)/)[1];
/* add container */
link.wrap(
$(document.createElement('span'))
.attr({
'id': 'ajax-mediawiki-patrol-' + rcid,
'class': 'ajax-mediawiki-patrol'
})
);
/* add links */
link.parent().append(
$(document.createElement('sup'))
.append(
$(document.createElement('a'))
.text('ajax')
.attr('href', '#')
.bind('click', function () { pathoschild.ajax_mw.OnPatrolClick(rcid); })
).append(
$(document.createElement('span'))
)
);
});
},
/*********
** Initialize AJAX rollback
*********/
InitAjaxRollback: function () {
var links = $('#bodyContent .mw-rollback-link a[href*="from="]');
links.each(function (i, link) {
/* get link & values */
link = $(link);
var href = link.attr('href');
var title = href.match(/title=([^&]+)/)[1];
var user = href.match(/from=([^&]+)/)[1];
title = decodeURIComponent(title);
user = decodeURIComponent(user);
var guid = pathoschild.GetID();
/* add container */
link.wrap(
$(document.createElement('span'))
.attr({
'id': 'ajax-mediawiki-rollback-' + guid,
'class': 'ajax-mediawiki-rollback'
})
);
/* add links */
link.parent().append(
$(document.createElement('sup'))
.append(
$(document.createElement('a'))
.text('ajax')
.attr('href', '#')
.bind('click', function () { pathoschild.ajax_mw.OnRollbackClick(guid, title, user); })
).append(
$(document.createElement('span'))
)
);
});
},
/*********
** Initialize bot-mode rollback (Special:Contributions)
*********/
InitBotModeRollback: function () {
if (mw.config.get( 'wgCanonicalSpecialPageName' ) != 'Contributions') {
return;
}
/* get elements */
var form = $('#newbie').parents('form').first();
var botField = $('input[name=bot]').first();
/* if bot mode is already enabled, remove the hidden field but remember that it's enabled */
var toggleTo = false;
if (botField.length) {
toggleTo = true;
botField.remove();
}
/* add options box */
form.append(
this.BuildFormattedBox()
.append(
$(document.createElement('input'))
.attr({
'type': 'checkbox',
'id': 'bot',
'name': 'bot',
'checked': toggleTo
})
.bind('change', function () {
var enabled = ($('#bot').attr('checked') ? '1' : '0');
$('.mw-rollback-link a[href*="from="]').each(function (i, link) {
link = $(link);
link.attr('href', link.attr('href') + '&bot=' + enabled);
});
})
).append(
$(document.createElement('label'))
.attr('for', 'bot')
.text('Bot rollback flag (hide rollbacks on watchlists and Special:RecentChanges).')
)
);
},
/*********
** Initialize Special:Delete helpers
*********/
InitSpecialDeleteHelpers: function () {
/*********
** Initialize
*********/
if (mw.config.get( 'wgAction' ) != 'delete') {
return;
}
/* prepare namespaces */
var ns_main = mw.config.get( 'wgNamespaceNumber' );
if (ns_main % 2) {
ns_main--;
}
var ns_talk = ns_main + 1;
/*********
** Build layout
*********/
var container;
$('#deleteconfirm, #mw-img-deleteconfirm').first().append(
/* 'subpages' header */
container = this.BuildFormattedBox().append(
$(document.createElement('h2'))
.text('Subpages')
/* main subpages */
).append(
$(document.createElement('h3'))
.text('main subpages')
).append(
$(document.createElement('ul'))
.attr('id', 'ajax-mediawiki-subpages-main')
.append(
$(document.createElement('li'))
.append(
this.BuildLoadingIndicator()
)
)
/* talk subpages */
).append(
$(document.createElement('h3'))
.text('talk subpages')
).append(
$(document.createElement('ul'))
.attr('id', 'ajax-mediawiki-subpages-talk')
.append(
$(document.createElement('li'))
.append(
this.BuildLoadingIndicator()
)
)
)
);
/* prepare block log layout */
var blockLog = null;
if (ns_main == 2) { // user or user_talk page
container.append(
$(document.createElement('h2'))
.text('Block log')
).append(
blockLog = $(document.createElement('ul'))
.attr('id', 'ajax-mediawiki-block-log')
.append(
$(document.createElement('li'))
.append(
this.BuildLoadingIndicator()
)
)
);
}
/*********
** Fetch subpages
*********/
function _GetSubpages(ul, namespace, prefix) {
pathoschild.ajax.FetchPrefixIndex({
'namespaceNumber': namespace,
'prefix': prefix,
'callback': function (pages, query) {
/* error */
if (query.Error()) {
ul.find('li').empty()
.append(
$(document.createElement('li'))
.append(
$(document.createElement('span'))
.text(query.Error())
.addClass('ajax-mediawiki-error-inline')
)
);
return;
}
/* list pages */
ul.empty();
if (!pages.length) {
ul
.addClass('ajax-mediawiki-subpages-none')
.append(
$(document.createElement('li'))
.text('none.')
);
}
else {
for (var i = 0, len = pages.length; i < len; i++) {
ul.append(
$(document.createElement('li'))
.append(
$(document.createElement('a'))
.attr({
'href': mw.config.get( 'wgServer' ) + mw.config.get( 'wgArticlePath' ).replace('$1', encodeURIComponent(pages[i].title)),
'alt': pages[i].title
})
.text(pages[i].title)
)
);
}
}
}
});
}
_GetSubpages($('#ajax-mediawiki-subpages-main'), ns_main, mw.config.get( 'wgTitle' ) + '/');
_GetSubpages($('#ajax-mediawiki-subpages-talk'), ns_talk, mw.config.get( 'wgTitle' ) + '/');
/*********
** Fetch whatlinkshere
*********/
/*********
** Fetch block log
*********/
if (blockLog !== null) {
pathoschild.ajax.FetchBlockLog({
'callback': function (entries, query) {
/* error */
if (query.Error()) {
blockLog.empty().append(
$(document.createElement('li'))
.append(
$(document.createElement('span'))
.text(query.Error())
.addClass('ajax-mediawiki-error-inline')
)
);
return;
}
/* success! */
blockLog.empty();
for (var i = 0, len = entries.length; i < len; i++) {
var item = entries[i];
console.log(i + '/' + len, entries[i]);
/* add entry */
var blockDetails, blockFlags;
blockLog.append(
$(document.createElement('li'))
/* date */
.append(
$(document.createElement('small'))
.text(item.timestamp.replace(/(\d+-\d+-\d+)T(\d+:\d+).+/, '$1 $2') + ' ')
)
/* blocker */
.append(
$(document.createElement('a'))
.attr({
'href': pathoschild.parser.BuildLocalUrl(item.title),
'title': item.title
})
.text(item.title)
)
.append(' ' + item.action + 'ed ')
/* block details */
.append(
blockDetails = $(document.createElement('span'))
)
/* comment */
.append(' — ' + pathoschild.parser.ParseWikiLinksIntoHtml(item.comment))
/* flags */
.append(' ')
.append(
blockFlags = $(document.createElement('small'))
)
);
console.log(' adding block details...');
if (item.action == 'block') {
blockDetails.append(
$(document.createElement('span'))
.attr({
'title': item.block.expiry,
'style': 'border-bottom:1px dotted gray;'
})
.text(item.block.duration)
);
if (item.block.flags) {
blockFlags.append('[' + item.block.flags + ']');
}
}
}
}
});
}
},
/*############################
## Helper methods
############################*/
BuildFormattedBox: function () {
return $(document.createElement('div'))
.addClass('ajax-mediawiki-box')
.append(
$(document.createElement('span'))
.addClass('ajax-mediawiki-box-title')
.text('Ajax sysop')
);
},
BuildLoadingIndicator: function () {
return $(document.createElement('img'))
.attr({
'src': stylepath + '/common/images/spinner.gif',
'alt': 'loading...'
});
},
/*############################
## Event handlers
############################*/
/*********
** AJAX patrol link clicked
*********/
OnPatrolClick: function (rcid) {
/* fetch elements */
var container = $('#ajax-mediawiki-patrol-' + rcid);
var span = container.find('sup span').first();
/* patrol through API */
span.text(' > loading...');
pathoschild.ajax.Patrol({
'rcid': rcid,
'callback': function (success, query) {
if (!success) {
span.text(' > ').append(
$(document.createElement('span'))
.addClass('ajax-mediawiki-error-inline')
.text('\u2718' + query.Error())
);
}
else {
container.addClass('.ajax-mediawiki-patrol-done');
span.parent().text(' \u2713');
}
}
});
},
/*********
** AJAX rollback link clicked
*********/
OnRollbackClick: function (guid, title, user) {
/* decode values & fetch elements */
title = decodeURIComponent(title);
user = decodeURIComponent(user);
var container = $('#ajax-mediawiki-rollback-' + guid);
var span = container.find('sup span');
/* check bot mode */
var markAsBot = false;
if ($('#bot').length) {
markAsBot = $('#bot').attr('checked');
alert(markAsBot);
}
/* rollback through API */
span.text(' > loading... ');
pathoschild.ajax.Rollback({
'title': title,
'user': user,
'markAsBot': markAsBot,
'callback': function (success, query) {
if (!success) {
span.text(' > ').append(
$(document.createElement('span'))
.addClass('ajax-mediawiki-error-inline')
.text('\u2718' + query.Error())
);
}
else {
container.addClass('.ajax-mediawiki-rollback-done');
span.parent().text(' \u2713');
}
}
});
}
},
/*###############################################
### Generic methods
###############################################*/
revision: 27, // revision number of this code
verbose: true, // trace all function calls to console?
/*********
* Enforce a schema defining valid arguments and default values on a key:value object.
*
* @param {object} args An argument object to conform to the schema.
* @param {object} schema An argument schema to apply. Every argument key must have an
* equivalent key in the schema. If a schema key is missing from the args
* object, the default value is assigned.
* @param {bool} throwInvalid Indicates whether to throw an error if an invalid argument is
* found in args. (By default, it will silently remove invalid arguments.)
* @returns {object} The schema-conforming object.
* @throws Error An exception indicating that some arguments were invalid.
*********/
ApplyArgumentSchema: function (args, schema, throwInvalid) {
/* check key validity */
for (var i in args) {
if (typeof (schema[i]) == typeof (undefined)) {
if (throwInvalid) {
var valid_args = [];
for (var x in schema) {
valid_args.push(x);
}
throw new Error('Invalid argument "' + i + '"; valid arguments are [' + valid_args.toString() + '].');
}
delete args[i];
}
}
/* enforce default values */
for (var n in schema) {
if (typeof (args[n]) == typeof (undefined) || args[n] === null) {
args[n] = schema[n];
}
}
/* return schema-conformant object */
return args;
},
/*********
* Deep-copy the properties of an object into a new object.
*
* @param {object} obj An object to copy properties from.
* @returns {object} A duplicate of the object given.
*********/
DuplicateObject: function (obj) {
return obj; // good idea, but implement later
// /* array */
// if( obj instanceof Array ) {
// var out = [];
// for( var i = 0, len = obj.length; i < len; i++ )
// out.push( this.DuplicateObject(obj[i]) );
// return out;
// }
// /* object */
// else if( obj instanceof Object ) {
// var out = {};
// for ( var i in obj )
// out[i] = this.DuplicateObject(obj[i]);
// return out;
// }
// /* scalar */
// else
// return obj;
},
/*********
* Adopt the properties of an object.
*
* @param {object} host An object to copy properties into.
* @param {object} source An object to copy properties from.
* @returns {object} the modified host object.
*********/
AdoptProperties: function (host, source) {
var _source = this.DuplicateObject(source);
for (var i in _source) {
host[i] = _source[i];
}
return host;
},
/*********
* Gets a sequential ID, used when a unique ID is needed within a controlled context.
*
* @returns {Number} The next unused ID, starting at 0.
*********/
_guid: -1,
GetID: function () {
return ++this._guid;
},
/*###############################################
### Text and parse methods
###############################################*/
parser: {
/*********
* Perform a strictly literal search.
*
* @param {string} text The text that will be searched for a match.
* @param {string} search The string to find in the text.
*
* @returns {string} The first literal match.
*********/
LiteralSearch: function (text, search) {
var index = text.indexOf(search);
var length = search.length;
return text.substr(index, index + length);
},
/*********
* Perform a strictly literal replace of one match.
*
* @param {string} text The text that will be modified.
* @param {string} search The string to find in the text.
* @param {string} replace The string to substitute for the match.
*
* @returns {string} The modified string.
*********/
LiteralReplace: function (text, search, replace) {
var index = text.indexOf(search);
if (index == -1) {
return text;
}
var length = search.length;
return text.substr(0, index) + replace + text.substr(index + length);
},
/*********
* Build a local URL.
*
* @param {string} targetTitle The title of the page to link to.
*
* @returns {string} The equivalent string with HTML links.
*********/
BuildLocalUrl: function (targetTitle) {
return mw.config.get( 'wgServer' ) + mw.config.get( 'wgScript' ) + '?title=' + encodeURIComponent(targetTitle);
},
/*********
* Parse MediaWiki links into HTML links (eg, in edit summaries or block reasons)
*
* @param {string} text The
*
* @returns {string} The equivalent string with HTML links.
*********/
ParseWikiLinksIntoHtml: function (text) {
/* extract links */
var links = text.match(/\[\[[^\]]+\]\]/g);
if (!links || !links.length) {
return text;
}
/* parse each link */
for (var i = 0, len = links.length; i < len; i++) {
/* extract link target & text */
var parts = links[i].toString().match(/\[\[([^\]]+?)(?:\|([^\]]+))?\]\]/);
var link_title = parts[1];
var link_text = parts[2] || link_title;
/* build link */
var link = '<a' + ' href="' + this.BuildLocalUrl(link_title) + '"' + ' title="' + link_title.replace('"', '\\"') + '"' + '>' + link_text.replace(/^User:/, '') + '</a>';
/* replace link */
text = this.LiteralReplace(text, links[i], link);
}
return text;
}
},
/*###############################################
### AJAX methods
###############################################*/
ajax: {
/*###############################################
### Query class
###############################################*/
/*********
* Generic class instantiated to encapsulate a single generic API query, with relevant
* parsing and error-handling. Executes the query immediately upon instantiation, with the
* properties passed in the args object.
*
* @params {object} args An argument object containing any of the following keys:
* (function) callback [= null]
* The function to invoke when the query completes. This callback will be passed two
* arguments, response and query. The response will be a preparsed representation of
* the response text, normally a JSON object. The query will be this Query instance.
*
* (object) context [= null]
* An arbitrary object accessible to the callback as query.context; default null.
*
* (str) method [= 'GET']
* The HTTP method to use when submitting the data.
*
* (str) url [= mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + '/api.php']
* The URL of the page to query.
*
* (object) data [= {}]
* The query data to submit to the URL, as a key:value object.
*
* (string) format [= 'json']
* The API format to request. The result will be parsed automatically if known.
*
* (bool) deadQuery [= false]
* Indicates whether to prevent dispatching the query. This should only be used when
* you need a query object, but don't need to execute a query.
*
* @returns Query instance.
* @notes Upon query completion, the following additional properties will be populated:
* (object) response
* The parsed representation of the query response.
*
* (object) xhr
* The XmlHttpRequest object representing the query state, or the equivalent vendor
* object for the client's browser.
**********/
Query: function (args) {
/* set properties */
pathoschild.ApplyArgumentSchema(
args,
{
'callback': null,
'context': null,
'url': mw.config.get( 'wgServer' ) + mw.config.get( 'wgScriptPath' ) + '/api.php',
'data': {},
'method': 'GET',
'format': 'json',
'deadQuery': false
},
true
);
pathoschild.AdoptProperties(this, args);
this.data.format = this.format;
this.response = null;
this.xhr = null;
this._error = null;
/* set methods */
this.Callback = pathoschild.ajax._Query_Callback;
this.Query = pathoschild.ajax._Query_Query;
this.Error = pathoschild.ajax._Query_Error;
/* launch query */
if (!this.deadQuery) {
this.Query();
}
},
/*********
** Callback
** Invokes the callback, if one is defined.
*********/
_Query_Callback: function () {
if (this.callback) {
this.callback(this.response, this);
}
return this;
},
/*********
** executes a request to the API.
*********/
_Query_Query: function () {
var _this = this;
$.ajax({
type: this.method,
url: this.url,
data: this.data,
error: function (xhr, textStatus, error) {
_this.xhr = xhr;
_this.response = null;
_this._error = error;
_this.Callback();
},
success: function (response, textStatus, xhr) {
_this.xhr = xhr;
_this.response = response;
_this._error = null;
_this.Callback();
}
});
return this;
},
/*********
** Get a human-readable error message.
*********/
_Query_Error: function () {
if (this._error === null) {
if (this.xhr && this.xhr.status != 200) { // HTTP error
this._error = this.xhr.status + ': ' + this.xhr.statusText;
}
else if (this.response && this.response.error) { // API error
this._error = this.response.error.code + ': ' + this.response.error.info;
}
else {
this._error = '';
}
}
return this._error;
},
/*############################
## Generic queries
############################*/
/*********
* Get a token from the API for a write action.
*
* @params {object} args An argument object containing any of the following keys:
* (str) type [= 'edit']
* The type of token to request from the toolserver.
*
* (str) title [= 'Sandbox']
* The name of the page to get a token for (not relevant for editing).
*
* (function) callback [= null]
* The function to invoke when the query completes, with the signature
* callback( token, query ).
*
* (object) context [= null]
* An arbitrary object accessible to the callback as query.context; default null.
*********/
_token_cache: {},
tokenTypes: {
EDIT: 'edit',
PATROL: 'patrol',
ROLLBACK: 'rollback'
},
GetToken: function (args) {
/* get arguments */
pathoschild.ApplyArgumentSchema(
args,
{
'type': this.tokenTypes.EDIT,
'title': 'Sandbox',
'callback': null,
'context': null
},
true
);
if (args.type == this.tokenTypes.EDIT) {
args.title = 'Sandbox'; // optimize, edit tokens are title-independent
}
/* get token from cache */
if (this._token_cache[args.type] && this._token_cache[args.type][args.title]) {
args.callback(this._token_cache[args.type][args.title], new pathoschild.ajax.Query({ 'context': args.context, 'deadQuery': true }));
return;
}
/* query API */
var _this = this;
if (!this._token_cache[args.type]) {
this._token_cache[args.type] = {};
}
if (args.type == this.tokenTypes.ROLLBACK) {
new pathoschild.ajax.Query({
'data': {
'action': 'query',
'prop': 'revisions',
'titles': args.title,
'rvtoken': 'rollback',
'rvprop': ''
},
'callback': function (data, query) {
if (!query.Error()) {
for (var i in data.query.pages) {
_this._token_cache[args.type][args.title] = data.query.pages[i].revisions[0].rollbacktoken;
break;
}
}
if (args.callback) {
args.callback(_this._token_cache[args.type][args.title], query);
}
},
'context': args.context
});
}
else {
new pathoschild.ajax.Query({
'data': {
'action': 'query',
'prop': 'info',
'indexpageids': '1',
'intoken': 'edit',
'titles': args.title
},
'callback': function (data, query) {
if (!query.Error()) {
var pageId = data.query.pageids[0];
_this._token_cache[args.type][args.title] = data.query.pages[pageId].edittoken;
}
if (args.callback) {
args.callback(_this._token_cache[args.type][args.title], query);
}
},
'context': args.context
});
}
},
/*********
* Patrol a revision.
*
* @params {object} args An argument object containing any of the following keys:
* (str) rcid [= null]
* The RCID of the revision to patrol.
*
* (function) callback [= null]
* The function to invoke when the query completes, with the signature
* callback( success_bool, query ).
*
* (object) context [= null]
* An arbitrary object accessible to the callback as query.context; default null.
*********/
Patrol: function (args) {
/* get arguments */
pathoschild.ApplyArgumentSchema(
args,
{
'rcid': null,
'callback': null,
'context': null
},
true
);
/* query API */
this.GetToken({
'type': pathoschild.ajax.tokenTypes.EDIT,
'callback': function (token, query) {
new pathoschild.ajax.Query({
'data': {
'action': 'patrol',
'token': token,
'rcid': args.rcid
},
'callback': function (data, query) {
if (args.callback) {
args.callback(!query.Error(), query);
}
},
'context': args.context
});
}
});
},
/*********
* Rollback latest revisions to a page by a user.
*
* @params {object} args An argument object containing any of the following keys:
* (str) title [= null]
* The title of the page to rollback.
*
* (str) user [= null]
* The name of the user to rollback.
*
* (bool) markAsBot [= false]
* Indicates whether to hide the rollback on recentchanges and watchlists.
*
* (function) callback [= null]
* The function to invoke when the query completes, with the signature
* callback( success_bool, query ).
*
* (object) context [= null]
* An arbitrary object accessible to the callback as query.context; default null.
*********/
Rollback: function (args) {
/* get arguments */
pathoschild.ApplyArgumentSchema(
args,
{
'title': null,
'user': null,
'markAsBot': false,
'callback': null,
'context': null
},
true
);
/* query API */
this.GetToken({
'type': pathoschild.ajax.tokenTypes.ROLLBACK,
'title': args.title,
'callback': function (token, query) {
new pathoschild.ajax.Query({
'method': 'POST',
'data': {
'action': 'rollback',
'token': token,
'title': args.title,
'user': args.user,
'markbot': (args.markAsBot ? '1' : '0')
},
'callback': function (data, query) {
if (args.callback) {
args.callback(!query.Error(), query);
}
},
'context': args.context
});
}
});
},
/*********
* Fetch a list of pages matching a prefix.
*
* @params {object} args An argument object containing any of the following keys:
* (Number) namespaceNumber [= null]
* The numeric ID of the namespace to get the pages from.
*
* (str) prefix [= null]
* The prefix matched against page titles.
*
* (function) callback [= null]
* The function to invoke when the query completes, with the signature
* callback( success_bool, query ).
*
* (object) context [= null]
* An arbitrary object accessible to the callback as query.context; default null.
*********/
FetchPrefixIndex: function (args) {
/* get arguments */
pathoschild.ApplyArgumentSchema(
args,
{
'namespaceNumber': mw.config.get( 'wgNamespaceNumber' ),
'prefix': mw.config.get( 'wgTitle' ),
'callback': null,
'context': null
},
true
);
/* collect pages */
var pages = [];
new pathoschild.ajax.Query({
'data': {
'action': 'query',
'list': 'allpages',
'apprefix': args.prefix,
'aplimit': 500,
'apnamespace': args.namespaceNumber
},
'callback': function (data, query) {
if (!query.Error()) {
data = data.query.allpages;
}
if (args.callback) {
args.callback(data, query);
}
},
'context': args.context
});
},
/*********
* Fetch a user's block history.
*
* @params {object} args An argument object containing any of the following keys:
* (str) user [= null]
* The name of the user (including 'User:' prefix) to match against page titles.
*
* (function) callback [= null]
* The function to invoke when the query completes, with the signature
* callback( success_bool, query ).
*
* (object) context [= null]
* An arbitrary object accessible to the callback as query.context; default null.
*********/
FetchBlockLog: function (args) {
/* get arguments */
pathoschild.ApplyArgumentSchema(
args,
{
'user': mw.config.get( 'wgPageName' ).match(/[^\/]+/, '').toString(),
'callback': null,
'context': null
},
true
);
/* get log entries */
new pathoschild.ajax.Query({
'data': {
'action': 'query',
'list': 'logevents',
'letype': 'block',
'letitle': args.user,
'lelimit': 500
},
'callback': function (data, query) {
/* parse */
if (!query.Error()) {
data = data.query.logevents;
}
if (args.callback) {
args.callback(data, query);
}
},
'context': args.context
});
}
},
/*###############################################
### Debug methods
###############################################*/
debug: {
/*********
** Return a string representation of an object
*********/
GetObjectSchema: function (obj) {
var str = '{\n';
for (var i in obj) {
str += ' ' + i + ' => ' + obj[i] + ',\n';
}
return str + '}';
}
}
};
pathoschild.ajax_mw.Initialize();
// </source>