MediaWiki:Gadget-fastbuttons.js: diferenças entre revisões

Origem: Wikipédia, a enciclopédia livre.
Conteúdo apagado Conteúdo adicionado
m
Algumas correções na sintaxe da documentação, p/ que haja menos avisos ao executar JSDuck no toollabs:ptwikis/gadgets. Detalhes: Wikipédia Discussão:Ptwikis#JSDuck para Gadgets); +doc
Linha 18: Linha 18:


var fb = {
var fb = {
version: /*{{subst:Autossubstituição/Estampa com data e hora|js|.*/ '2013-12-04 15:12:08 (UTC)' /*}}.*/,
version: /*{{subst:Autossubstituição/Estampa com data e hora|js|.*/ '2013-12-05 10:51:00 (UTC)' /*}}.*/,
buttons: {},
buttons: {},
summaryDefault: ', usando [[WP:FastButtons|FastButtons]].',
summaryDefault: ', usando [[WP:FastButtons|FastButtons]].',
Linha 34: Linha 34:
* Log errors
* Log errors
*
*
* @param {string} The error text
* @param {string} errorText Text describing the error
*/
*/
fb.log = function( errorText ) {
fb.log = function( errorText ) {
Linha 43: Linha 43:
* Notifications
* Notifications
*
*
* @param {string} Notification text
* @param {string} text Notification text
* @param {object} (optional) additional options of notification
* @param {Object} [options] Additional options of notification
* @see [[mw:ResourceLoader/Default_modules#mediawiki.notify]]
* @see [[mw:ResourceLoader/Default modules#mediawiki.notify]]
* @return {jQuery.Promise}
*/
*/
fb.notify = function( text, options ) {
fb.notify = function( text, options ) {
Linha 63: Linha 64:
* Refresh or submit an edit (in edit page) the pages
* Refresh or submit an edit (in edit page) the pages
*
*
* @param {string} (optional) To verify if the action was redirect the page
* @param {string} [code] To verify if the action was redirect the page
*/
*/
fb.refreshPage = function( code ) {
fb.refreshPage = function( code ) {
Linha 80: Linha 81:
* Create new button links
* Create new button links
*
*
* @param {(jQuery|Array|Object)}
* @param {jQuery|Array|Object} $target A jQuery object representing the target or
* A jQuery object representing the target or
* an object representing a button (see 'buttons' param below) or
* an Array of this kind of objects
* an object representing a button (see 'buttons' param below) or
* (Defaults to fb.$menu if it is not an instance of jQuery)
* an Array of this kind of objects
* @param buttons {Object} Button atributes and others informations of the new button
* (Defaults to fb.$menu if the it is not an instance of jQuery)
* @param buttons.action {string|Function}
* @param {{action: (string|function), text: string, title: string, prompt: string, label: string, disable: boolean, after: string}=}
* @param buttons.text {string}
* Button atributes and others informations of the new button
* @param buttons.title {string}
* @param buttons.prompt {string}
* @param buttons.label {string}
* @param buttons.disable {boolean}
* @param buttons.after {string}
*/
*/
fb.addButton = function( $target, buttons ) {
fb.addButton = function( $target, buttons ) {
Linha 277: Linha 283:
* Creates a dialog
* Creates a dialog
*
*
* @param {object} Same props of jQuery.dialog.
* @param {Object} info Same props of jQuery.dialog.
*/
*/
fb.dialog = function( info ) {
fb.dialog = function( info ) {
Linha 306: Linha 312:
* Changes the content of the submenu
* Changes the content of the submenu
*
*
* @param {string} The new buttons for the new submenu
* @param {string} items The new buttons for the new submenu
* @param {boolean} Appends in submenu instead of replace all content
* @param {boolean} append Appends in submenu instead of replace all content
* @param {number|array} (optional) If needs include only some buttons of the list
* @param {number|array} [justEnable] If needs include only some buttons of the list.
* @example: 14 or [ 14, 16, 20 ]. The numbers are the index in the array of buttons ("items" param)
* The numbers are the index in the array of buttons ("items" param).
* @example:
* fb.changeSubmenu(
* fb.buttons.maintenance,
* false,
* [ 14, 16, 20 ]
* );
*/
*/
fb.changeSubmenu = function( items, append, justEnable ) {
fb.changeSubmenu = function( items, append, justEnable ) {
Linha 351: Linha 363:
* Callback function for API calls
* Callback function for API calls
*
*
* @param {string} Code an abbreviation such as "esb", "PV"
* @param {string} code An abbreviation such as "esb", "PV"
* @param (string} (optional) Used only when the "code" param is "pageQuality". Can be 'noCallback' or 'noBeforeRequest' (pageQuality)
* @param {string} [extra] Used only when the "code" param is "pageQuality". Can be 'noCallback' or 'noBeforeRequest' (pageQuality)
*/
*/
fb.callAPI = function( code, extra ) {
fb.callAPI = function( code, extra ) {
Linha 524: Linha 536:
* Callback function for the request of stub templates
* Callback function for the request of stub templates
*
*
* @param {Object} Query of data JSON content returned by API
* @param {Object} query Query of data JSON content returned by API
*/
*/
fb.process.stubs = function( query ) {
fb.process.stubs = function( query ) {
Linha 570: Linha 582:
* Callback function for the request of recent edits (from watchlist or recent changes)
* Callback function for the request of recent edits (from watchlist or recent changes)
*
*
* @param {Object} Query of data JSON content returned by API
* @param {Object} query Query of data JSON content returned by API
* @param {string} Code passed through the "fb.callAPI"
* @param {string} code Code passed through the "fb.callAPI"
*/
*/
fb.process.recentEdits = function( query, code ) {
fb.process.recentEdits = function( query, code ) {
Linha 704: Linha 716:
}
}
},
},
/**

* @param {Object} query
*/
callback: function( query ) {
callback: function( query ) {
var info = '',
var info = '',
Linha 758: Linha 772:
},
},


/**
* @param {Object} query
*/
callback: function( query ) {
callback: function( query ) {
var i, cats, pageids, cat, quality;
var i, cats, pageids, cat, quality;
Linha 837: Linha 854:
},
},


/**
* @param {Object} query
* @param {string} code
* @param {Object} data
*/
callback: function( query, code, data ) {
callback: function( query, code, data ) {
var contribs, regDate,
var contribs, regDate,
Linha 919: Linha 941:
* Callback function for the request of deleted contributions
* Callback function for the request of deleted contributions
*
*
* @param {Object} Query of data JSON content returned by API
* @param {Object} query Query of data JSON content returned by API
*/
*/
fb.process.deletedContribs = function( query ) {
fb.process.deletedContribs = function( query ) {
Linha 948: Linha 970:
* Callback function for the request of page info
* Callback function for the request of page info
*
*
* @param {Object} Query of data JSON content returned by API
* @param {Object} query Query of data JSON content returned by API
*/
*/
fb.process.pageInfo = function( query ) {
fb.process.pageInfo = function( query ) {
Linha 977: Linha 999:
* Sends the warn
* Sends the warn
*
*
* @param {string} User to be warned
* @param {string} userName User to be warned
* @param {string} Template warn
* @param {string} template Template warn
* @param {string} See "fb.run"
* @param {string} warnSummary See "fb.run"
* @param {boolean} [isPE]
*/
*/
fb.warn.send = function( userName, template, warnSummary, isPE ) {
fb.warn.send = function( userName, template, warnSummary, isPE ) {
Linha 1 003: Linha 1 026:
* Sends a warn elimination of a page
* Sends a warn elimination of a page
*
*
* @param {string} See "fb.run"
* @param {string} code See "fb.run"
* @param {string} (optional) Type of warn. Only if "code" param = 'subst:apagar'
* @param {string} [warnType] Type of warn. Only if "code" param = 'subst:apagar'
*/
*/
fb.warn.elimination = function( code, warnType ) {
fb.warn.elimination = function( code, warnType ) {
Linha 1 143: Linha 1 166:
/**
/**
* Checks if the page has already been proposed for deletion
* Checks if the page has already been proposed for deletion
* @param {boolean} justVerify
*/
*/
fb.verifyRequestDeletion = function( justVerify ) {
fb.verifyRequestDeletion = function( justVerify ) {
Linha 1 185: Linha 1 209:
* Manipulate the text page
* Manipulate the text page
*
*
* @param {string} See "fb.run"
* @param {string} code See "fb.run"
* @param {string} See "fb.run"
* @param {string} extraText See "fb.run"
* @param {string} Summary of edit
* @param {string} sum Summary of edit
* @param {string} Text of page
* @param {string} value Text of page
* @param {boolean} If is the edit page
* @param {boolean} isEditPage If is the edit page
*/
*/
fb.manipulateTextPage = function( code, extraText, sum, value, isEditPage ) {
fb.manipulateTextPage = function( code, extraText, sum, value, isEditPage ) {
Linha 1 357: Linha 1 381:
* Edit function
* Edit function
*
*
* @param: {string} A template name, possibly preceded by "subst:" and optionally followed by "|" and some parameter(s)
* @param {string} code A template name, possibly preceded by "subst:" and optionally followed by "|" and some parameter(s)
* @example:
* @example: "não remova"or "subst:ER|5" or "subst:ESR-matrad|1=~~' + '~~|língua='"
* "não remova"or "subst:ER|5" or "subst:ESR-matrad|1=~~' + '~~|língua='"
* @param {(string|null)=} (optional) Extra text for templates and redirects
* @param {string} Summary of edit
* @param {string|null} [extraText] Extra text for templates and redirects
* @param {string} sum Summary of edit
*
*
* @return: {boolean} Returns false if the user canceled or the page already has a template
* @return {boolean} False if the user canceled or the page already has a template
*/
*/
fb.run = function( code, extraText, sum ) {
fb.run = function( code, extraText, sum ) {
Linha 1 396: Linha 1 421:
* The prompt used in the gadget
* The prompt used in the gadget
*
*
* @param {string} Title of prompt
* @param {string} title Title of prompt
* @param {string} Labels of inputs
* @param {string} label Labels of inputs
* @example:
* @example: 'field1|2=field2|3=field3', results in 3 inputs with the respective labels
* 'field1|2=field2|3=field3', results in 3 inputs with the respective labels
* @param {function} Callback
* @param {Function} callback Function to be called after (...)
*/
*/
fb.prompt = function( title, label, callback ) {
fb.prompt = function( title, label, callback ) {
Linha 1 621: Linha 1 647:
* PE (Páginas para eliminar) prompt
* PE (Páginas para eliminar) prompt
*/
*/
//FIXME: don't work correctly in edit pages
// FIXME: don't work correctly in edit pages
fb.PEPrompt = function() {
fb.PEPrompt = function() {
var $justification, $warnType, $sendWarnType, sendWarn, createPE,
var $justification, $warnType, $sendWarnType, sendWarn, createPE,
Linha 1 978: Linha 2 004:
window.fastButtons = fb;
window.fastButtons = fb;


//Executes the gadget when document is ready
// Executes the gadget when document is ready
$( fb.init );
$( fb.init );



Revisão das 10h51min de 5 de dezembro de 2013

/**
 * FastButtons
 *
 * @author [[es:User:Racso]] (versão original do script, na Wikipédia em espanhol)
 * @author [[en:User:Macy]] (versão adaptada para a Wikipédia inglesa)
 * @author [[pt:User:Danilo.mac]]
 * @author [[pt:User:Helder.wiki]]
 * @author [[pt:User:!Silent]]
 * @source [[en:User:Macy/FastButtons.js]] ([[en:Special:PermaLink/230473471]])
 * @source [[es:Special:PrefixIndex/User:Racso/FB]]
 * @see [[MediaWiki:Gadget-fastbuttons.js/buttonsList.js]]
 * @help [[WP:Scripts/FastButtons]]
 */
/*global mediaWiki, jQuery, wikEd, WikEdUpdateFrame */

( function( mw, $, window ) {
'use strict';

var fb = {
		version: /*{{subst:Autossubstituição/Estampa com data e hora|js|.*/ '2013-12-05 10:51:00 (UTC)' /*}}.*/,
		buttons: {},
		summaryDefault: ', usando [[WP:FastButtons|FastButtons]].',
		$menu: $( '<div id="fb-menu"></div>' ),
		$submenu: $( '<div id="fb-submenu"></div>' ),
		$pageInfo: $( '<span id="fb-pageInfo"></span>' )
	},
	nsNum = mw.config.get( 'wgNamespaceNumber' ),
	title = mw.config.get( 'wgTitle' ),
	pageName = mw.config.get( 'wgPageName' ),
	action = mw.config.get( 'wgAction' ),
	api = new mw.Api();

/**
 * Log errors
 *
 * @param {string} errorText Text describing the error
 */
fb.log = function( errorText ) {
	throw new Error( 'FastButtons: ' + errorText );
};

/**
 * Notifications
 *
 * @param {string} text Notification text
 * @param {Object} [options] Additional options of notification
 * @see [[mw:ResourceLoader/Default modules#mediawiki.notify]]
 * @return {jQuery.Promise}
 */
fb.notify = function( text, options ) {
	var optionsDefault = {
		title: 'Fast Buttons',
		autoHide: false
	};

	if ( $.isPlainObject( options ) ) {
		$.extend( optionsDefault, options );
	}

	return mw.notify( text, optionsDefault );
};

/**
 * Refresh or submit an edit (in edit page) the pages
 *
 * @param {string} [code] To verify if the action was redirect the page
 */
fb.refreshPage = function( code ) {
	if ( typeof localStorage !== 'undefined' ) {
		localStorage[ 'fb-edit-sucess' ] = true;
	}

	if ( action === 'view' ) {
		location.href = mw.util.getUrl() + ( ( code === 'redirect' ) ? '?redirect=no' : '' );
	} else if ( $.inArray( action, [ 'edit', 'submit' ] ) !== -1 ) {
		$( '#editform' ).unbind( 'submit' ).trigger( 'submit' );
	}
};

/**
 * Create new button links
 *
 * @param {jQuery|Array|Object} $target A jQuery object representing the target or
 *  an object representing a button (see 'buttons' param below) or
 *  an Array of this kind of objects
 *  (Defaults to fb.$menu if it is not an instance of jQuery)
 * @param buttons {Object} Button atributes and others informations of the new button
 * @param buttons.action {string|Function}
 * @param buttons.text {string}
 * @param buttons.title {string}
 * @param buttons.prompt {string}
 * @param buttons.label {string}
 * @param buttons.disable {boolean}
 * @param buttons.after {string}
 */
fb.addButton = function( $target, buttons ) {
	var i, comment, props, button,
		doNothing = function( event ) {
			event.preventDefault();
		},
		buttonClick = function( btn ) {
			return function( event ) {
				var question = ( typeof btn.action === 'string' && btn.action.substr( 0, 2 ) === 'ER' ) ?
					'Se necessário, coloque uma observação.'
					: btn.prompt,
					promptCallback = function( comment ) {
						if ( $.isFunction( btn.action ) ) {
							btn.action();
						} else {
							fb.run( btn.action, comment, btn.sum );
						}
					};

				doNothing( event );

				if ( $( this ).parent().attr( 'id' ) === 'fb-menu' ) {
					$( '.fb-menu-open' ).removeClass( 'fb-menu-open' );
				}

				if ( fb.$submenu.text() !== 'Carregando...' &&
					typeof btn.action !== 'string' &&
					( fb.textButton === btn.text &&
						$.inArray( fb.textButton, [ 'Semirrápida', 'Consenso' ] ) === -1
					) || ( btn.text === 'Eliminação' &&
							$.inArray( fb.textButton, [ 'Semirrápida', 'Consenso', 'VDA', 'Suspeito de VDA' ] ) !== -1
					)
				) {
					fb.textButton = '';

					return fb.$submenu.empty().hide();
				}

				if ( fb.$submenu.html() !== '' &&
					( typeof btn.action === 'string' && $.inArray( btn.text, [ 'Eliminação', '#R', 'ER1' ] ) !== -1 )
				) {
					fb.$submenu.empty().hide();
				}

				if ( question ) {
					comment = fb.prompt( question, btn.label, promptCallback );
				} else {
					promptCallback( comment );
				}

				if ( $.isFunction( btn.action ) ) {
					fb.textButton = btn.text;
				}

				if ( $.isFunction( btn.action ) && $.inArray( btn.text, [ 'Semirrápida', 'Consenso' ] ) === -1 ) {
					$( this ).addClass( 'fb-menu-open' );
				}
			};
		};

	if ( !$target.jquery ) {
		//if $target is not an instance of jQuery, use it as 'buttons'
		buttons = $target;
		$target = fb.$menu;
	}

	if ( !$.isArray( buttons ) ) {
		buttons = [ buttons ];
	}

	for ( i = 0; i < buttons.length; i++ ) {
		button = buttons[ i ];
		props = {
			'title': button.title,
			'text': button.text,
			'class': 'fb-button '
		};

		if ( button.disable ) {
			props[ 'class' ] += 'fb-disabled-button';
			props.click = doNothing;
			props.title = '';
		} else {
			props[ 'class' ] += $.isFunction( button.action ) ?
				'fb-menu-button'
				: 'fb-action-button';
			props.click = buttonClick( button );
		}

		if ( button.after && $( '.fb-button' ).length ) {
			$( '.fb-button' ).eq( button.after ).after( $( '<a></a>', props ), ' ' );
		} else {
			$target.append( $( '<a></a>', props ), ' ' );
		}
	}
};

/**
 * Add an input for stubs
 */
fb.fieldForStubs = function() {
	var $stubInput, $okButton,
		ok = function( event ) {
			if ( event.which === 13 ) {
				event = 'ok';
			}

			if ( event === 'ok' ) {
				if ( $stubInput.val() === '' ) {
					return $stubInput.addClass( 'fb-fill-field' );
				}

				$stubInput.removeClass( 'fb-fill-field' );

				fb.run( 'esboço-' + $( '#fb-esb-input' ).val() );
			}
		};

	$stubInput = $( '<input type="text" id="fb-esb-input" size="14" />' )
		.keyup( function() {
			fb.callAPI( 'esb' );
		} )
		.keypress( function( event ) {
			ok( event );
		} );

	$okButton = $( '<input value="OK" type="button" class="fb-button fb-action-button" />' ).mousedown( function() {
		ok( 'ok' );
	} );

	fb.changeSubmenu( [ ' Esboço-', $stubInput, ' ', $okButton ], true );
};

/**
 * Add the search buttons
 */
fb.searchButtons = function() {
	var i,
		list = fb.buttons.search,
		search = encodeURIComponent(
			title.indexOf( ' ' ) !== -1 ?
				'"' + title + '"'
				: title
		);

	for ( i = 0; i < list.length; i++ ) {
		fb.changeSubmenu( $( '<a></a>', {
			'class': 'fb-button fb-link-button',
			'target': '_blank',
			'href': list[ i ].url + search,
			'text': list[ i ].text,
			'title': list[ i ].desc
		} ), true );
	}
};

/**
 * Add the CatScan buttons
 */
fb.catScanButtons = function() {
	var i,
		list = fb.buttons.cat,
		catScanUrl = '//toolserver.org/~daniel/WikiSense/CategoryIntersect.php?' +
			$.param( {
				wikilang:  mw.config.get( 'wgContentLanguage' ),
				wikifam: '.wikipedia.org',
				userlang: mw.config.get( 'wgUserLanguage' ),
				basecat: title,
				basedeep: '1',
				go: 'Examinar',
				format: 'html',
				mode: ''
			} );

	fb.changeSubmenu( 'Procurar nesta categoria: ' );

	for ( i = 0; i < list.length; i++ ) {
		fb.changeSubmenu( $('<a></a>', {
			'class': 'fb-button fb-link-button',
			'target': '_blank',
			'href': catScanUrl + list[ i ].url,
			'text': list[ i ].text,
			'title': list[ i ].desc
		} ), true );
	}
};

/**
 * Creates a dialog
 *
 * @param {Object} info Same props of jQuery.dialog.
 */
fb.dialog = function( info ) {
	var $fbDialog = $( '<div id="fb-dialog" class="ui-widget"></div>' ).append( info.content );

	if ( $( '#fb-dialog' ).length ) {
		$( '#fb-dialog' ).dialog( 'close' );
	}

	if ( !info.modal ) {
		info.modal = true;
	}

	$.extend( info, {
		title: 'FasButtons' + ( info.title ? ' - ' + info.title : '' ),
		open: function() {
			$( '.ui-dialog-titlebar-close' ).hide();
		},
		close: function() {
			$fbDialog.dialog( 'destroy' ).remove();
		}
	} );

	$fbDialog.dialog( info );
};

/**
 * Changes the content of the submenu
 *
 * @param {string} items The new buttons for the new submenu
 * @param {boolean} append Appends in submenu instead of replace all content
 * @param {number|array} [justEnable] If needs include only some buttons of the list.
 *  The numbers are the index in the array of buttons ("items" param).
 *    @example:
 *    fb.changeSubmenu(
 *        fb.buttons.maintenance,
 *        false,
 *        [ 14, 16, 20 ]
 *    );
 */
fb.changeSubmenu = function( items, append, justEnable ) {
	var i;

	$( '.tipsy' ).remove();

	if ( !append ) {
		fb.$submenu.empty().show();
	}

	if ( !$.isArray( items ) ) {
		items = [ items ];
	}

	if ( justEnable && !$.isArray( justEnable ) ) {
		justEnable = [ justEnable ];
	}

	for ( i = 0; i < items.length; i++ ) {
		if ( $.isFunction( items[ i ] ) ) {
			items[ i ]();
		} else if ( ( typeof items[ i ] === 'string' ) || ( items[ i ] instanceof jQuery ) ) {
			fb.$submenu.append( items[ i ] );
		} else {
			if ( justEnable && $.inArray( i, justEnable ) === -1 ) {
				items[ i ].disable = true;
			}

			fb.addButton( fb.$submenu, items[ i ] );

		}
	}

	fb.$submenu.find( 'a, span' ).tipsy( {
		html: true
	} );
};

/**
 * Callback function for API calls
 *
 * @param {string} code An abbreviation such as "esb", "PV"
 * @param {string} [extra] Used only when the "code" param is "pageQuality". Can be 'noCallback' or 'noBeforeRequest' (pageQuality)
 */
fb.callAPI = function( code, extra ) {
	var esb, apiParams, callbacks, userName;

	if ( $.inArray( code, [ 'PV', 'PN', 'MR' ] ) !== -1 &&
		fb.$submenu.html() === 'Carregando...'
	) {
		return;
	}

	userName = ( mw.config.get( 'wgCanonicalSpecialPageName' ) !== 'Contributions' ) ?
		title.split( '/' )[ 0 ]
		: window.decodeURI( mw.util.getUrl().split( '/' )[ 3 ] || mw.util.getParamValue( 'target' ) );

	apiParams = {
		esb: {
			list: 'allpages',
			aplimit: '1',
			apnamespace: '10'
		},

		PV: {
			list: 'watchlist',
			wlprop: 'user|comment|title|sizes',
			wlexcludeuser: mw.config.get( 'wgUserName' )
		},

		PN: {
			list: 'recentchanges',
			rctype: 'new',
			rcnamespace: '0',
			rcshow: '!patrolled',
			rcprop: 'user|comment|title|sizes|ids'
		},

		MR: {
			list: 'recentchanges',
			rctype: 'edit',
			rcnamespace: '0',
			rcshow: 'anon',
			rcprop: 'user|comment|title|sizes'
		},

		usu: {
			list: 'allusers',
			aulimit: '1',
			auprop: 'editcount|registration|groups',
			aufrom: userName
		},

		anon: {
			list: 'usercontribs',
			uclimit: '500',
			ucprop: 'timestamp',
			ucuser: userName
		},

		deletedContribs: {
			list: 'deletedrevs',
			titles: title,
			drlimit: '500'
		},

		backLinks: {
			list: 'backlinks',
			bllimit: '1',
			blfilterredir: 'nonredirects',
			bltitle: pageName
		},

		pageQuality: {
			prop: 'categories',
			indexpageids: '1',
			titles: mw.config.get( 'wgFormattedNamespaces' )[ nsNum + 1 ] + ':' + title
		},

		patrolPage: {
			action: 'patrol',
			rcid: mw.util.getParamValue(
				'rcid',
				mw.util.$content.find( 'div.patrollink a' ).attr( 'href' )
			),
			token: mw.user.tokens.get( 'patrolToken' )
		},

		pageRevisions: {
			prop: 'revisions',
			rvprop: 'user|comment|timestamp',
			rvlimit: '20',
			rvdir: 'newer',
			titles: pageName,
			format: 'json'
		},

		pageInfo: {
			prop: 'info|revisions',
			inprop: 'watchers',
			titles: pageName
		}
	};

	callbacks = {
		esb: fb.process.stubs,
		PV: fb.process.recentEdits,
		PN: fb.process.recentEdits,
		MR: fb.process.recentEdits,
		usu: fb.process.userInfo,
		anon: fb.process.userInfo,
		deletedContribs: fb.process.deletedContribs,
		backLinks: fb.process.backLinks,
		pageQuality: fb.process.pageQuality,
		pageInfo: fb.process.pageInfo
	};

	if ( code === 'esb' ) {
		esb = $( '#fb-esb-input' ).val();

		if ( esb === esb.substr( 0, ( esb.length - 1 ) ) ) {
			return;
		}

		apiParams.esb.apprefix = 'Esboço-' + esb;
	} else if ( $.inArray( code, [ 'MR', 'PN', 'PV' ] ) !== -1 ) {
		fb.changeSubmenu( 'Carregando...' );
	}

	if ( $.isPlainObject( callbacks[ code ] ) ) {
		if ( code === 'pageQuality' ) {
			if ( extra === 'noCallback' ) {
				return callbacks[ code ].beforeRequest();
			}
		} else {
			callbacks[ code ].beforeRequest();
		}

		callbacks[ code ] = callbacks[ code ].callback;
	}

	apiParams[ code ].format = 'json';

	if ( !apiParams[ code ].action ) {
		apiParams[ code ].action = 'query';
	}

	return $[ ( code === 'patrolPage' ) ? 'post' : 'get' ](
		mw.util.wikiScript( 'api' ),
		apiParams[ code ],
		function( data ) {
			if ( !data ) {
				fb.log( 'Unable get the list "' + apiParams[ code ].list + '" for the API.' );

				return;
			}

			if ( callbacks[ code ] ) {
				callbacks[ code ]( data.query, code, data );
			}
		}
	);
};

/**
 * Process methods
 *
 * @see "fb.callAPI"
 */
fb.process = {};

/**
 * Callback function for the request of stub templates
 *
 * @param {Object} query Query of data JSON content returned by API
 */
fb.process.stubs = function( query ) {
	var template, start, sel,
		esb = document.getElementById( 'fb-esb-input' );

	if ( query ) {
		template = query.allpages && query.allpages[ 0 ] && query.allpages[ 0 ].title;

		if ( !template ) {
			fb.log( 'The stub templates query returned incomplete data.' );

			return false;
		}

		template = template.replace( /^Predefinição:Esboço-/gi, '' );
	} else {
		fb.log( 'The stub templates query returned no data.' );

		return false;
	}

	if ( ( esb.setSelectionRange || esb.createTextRange || (
		esb.selectionStart !== undefined && esb.selectionEnd !== undefined
	) ) && esb.value === template.substr( 0, esb.value.length ) ) {
		//Shows sugestions. Based in HotCat script
		start = esb.value.length;
		esb.value = template;

		if ( esb.setSelectionRange ) {
			esb.setSelectionRange( start, template.length );
		} else if ( esb.createTextRange ) {
			sel = esb.createTextRange();
			sel.move( 'character', start );
			sel.moveEnd( 'character', template.length - start );
			sel.select();
		} else {
			esb.selectionStart = start;
			esb.selectionEnd = template.length;
		}
	}
};

/**
 * Callback function for the request of recent edits (from watchlist or recent changes)
 *
 * @param {Object} query Query of data JSON content returned by API
 * @param {string} code Code passed through the "fb.callAPI"
 */
fb.process.recentEdits = function( query, code ) {
	var i, j, item, titleItem, length, comment, colorBytes,
		pages = [],
		charnum = 0,
		listName = {
			PV: 'watchlist',
			MR: 'recentchanges',
			PN: 'recentchanges'
		},
		urlParam = {
			PV: 'diff=last',
			MR: 'diff=last',
			PN: 'redirect=no&rcid='
		},
		summaryChanges = {
			PV: [
				[ '[[Ajuda:SEA|?]]', '' ],
				[ '/*', '?' ],
				[ '*/', ':' ]
			],

			MR: [
				[ '/*', '?' ],
				[ '*/', ':' ]
			],

			PN: [
				[ '[[Ajuda:SEA|?]] ', '' ]
			]
		},
		list = query[ listName[ code ] ],
		max = ( list.length < 10 ) ? list.length : 10;

	for ( i = 0; i < max; i++ ) {
		item = list[ i ];

		if ( !item ) {
			continue;
		}

		titleItem = item.title;
		charnum += titleItem.length;

		if ( charnum > 180 ) {
			break;
		}

		length = item.newlen - item.oldlen;

		if ( length > 0 ) {
			length = '+' + length;
		}

		comment = item.comment || '';

		for ( j = 0; j < summaryChanges[ code ].length; j += 1 ) {
			comment = comment.replace(
				summaryChanges[ code ][ 0 ],
				summaryChanges[ code ][ 1 ]
			);
		}

		if ( comment ) {
			comment = '(' + comment + ')';
		}

		if ( code === 'PN' ) {
			urlParam[ code ] += item.rcid;
		}

		if ( parseInt( length ) === 0 ) {
			colorBytes = 'gray';
		} else if ( parseInt( length ) > 0 ) {
			colorBytes = 'green';
		} else {
			colorBytes = 'red';
		}

		pages.push(
			'<a href="' + mw.util.getUrl( titleItem ) + '?' +
			urlParam[ code ] + '" title="(<span style=\'color: ' + colorBytes + '; font-weight: bold;\'>' + length + '</span>) ' + item.user +
			' ' + comment + '">' + titleItem + '</a>'
		);
	}

	fb.changeSubmenu( ( pages.join( ' | ' ) || 'Nenhuma alteração recente.' ) );
};

/**
 * Works with the page info, and process the backlinks request
 */
fb.process.backLinks = {
	beforeRequest: function() {
		var key,
			$catLine = $( '#mw-normal-catlinks' ),
			info = {
				'cat': {
					condition: $catLine.length &&
						$catLine.html().indexOf( '><a href="' + mw.config.get( 'wgArticlePath' ).replace( '$1', '' ) ) !== -1,
					title: '$1 está categorizada'
				}
			};

		if ( $.inArray( nsNum, [ 0, 102 ] ) !== -1 ) {
			$.extend( info, {
				'ref': {
					condition: $( '.references' ).length,
					title: '$1 está referênciada'
				},

				'iw': {
					condition: $( '#p-lang' ).find('li').length,
					title: '$1 possui interwikis'
				}
			} );
		}

		fb.$pageInfo.append( '(' );

		if ( action !== 'history' && ( nsNum % 2 ) === 0 ) {
			for ( key in info ) {
				if ( info[ key ] ) {
					fb.$pageInfo.append( $( '<span></span>', {
						'class': 'fb-' + ( info[ key ].condition ? 'ok' : 'missing' ),
						'title': 'A página ' + info[ key ].title.replace( '$1 ', ( info[ key ].condition ? '' : 'não ' ) ),
						'text': key
					} ), ' · ' );
				}
			}
		}
	},
	/**
	 * @param {Object} query
	 */
	callback: function( query ) {
		var info = '',
			backlinks = query && query.backlinks;

		if ( !backlinks ) {
			return fb.log( 'The backlinks query returned no data.' );
		}

		if ( backlinks.length ) {
			info += '<a href="' +
				mw.util.getUrl( 'Especial:Páginas afluentes/' + pageName ) +
				'" title="Afluentes da página">afl</a>)</span>';
		} else {
			info += '<span class="fb-missing" title="Esta página ainda não possui afluentes">afl</span>)';
		}

		fb.$pageInfo.append( info, true );

		if ( $.inArray( nsNum, [ 0, 102 ] ) !== -1 ) {
			fb.callAPI( 'pageQuality', 'noCallback' );
		} else {
			fb.verifyRequestDeletion();
		}
	}
};

/**
 * Works with page quality of article/"anexo" and process the request of categories of the discussion page
 */
fb.process.pageQuality = {
	beforeRequest: function() {
		var $featuredContent = $( '#destaques1' );

		if ( !$featuredContent.length ) {
			return fb.callAPI( 'pageQuality', 'noBeforeRequest' );
		}

		$featuredContent = $( '#destaques1' ).find( 'img' ).attr( 'alt' );

		fb.$pageInfo.append(
			'<span title="Qualidade do artigo/anexo" style="cursor:default;"> Q-<b>' +
				$featuredContent.substring(
					( nsNum === 102 ) ? 16 : 17,
					$featuredContent.indexOf( '.' )
				) +
			'</b></span>'
		).find( 'span' ).tipsy();

		fb.verifyRequestDeletion();

		return false;
	},

	/**
	 * @param {Object} query
	 */
	callback: function( query ) {
		var i, cats, pageids, cat, quality;

		if ( query ) {
			cats = query.pages;
			pageids = query.pageids;

			if ( cats && pageids && pageids.length ) {
				cats = cats[ pageids[ 0 ] ].categories;
			} else {
				fb.log( 'The query for page quality categories returned incomplete data.' );

				return false;
			}
		} else {
			fb.log( 'The query for page quality categories returned no data.' );

			return false;
		}

		if ( cats ) {
			for ( i = 0; i < cats.length; i++ ) {
				cat = cats[ i ].title;

				if ( cat && cat.indexOf( '!Artigos de qualidade' ) !== -1 ) {
					// Categoria:!Artigos de qualidade 1 sobre ...
					//				...^
					quality = cat.substr( 32, 1 );

					if ( quality === 'd' ) {
						quality = '?';
					}

					break;
				}
			}
		}

		fb.$pageInfo.append(
			'<span title="Qualidade do artigo" style="cursor:default;"> Q-<b>' +
				( quality || '?' ) +
			'</b></span>'
		);

		fb.verifyRequestDeletion();
	}
};

/**
 * Works with user info and process the request of user editcount
 */
fb.process.userInfo = {
	beforeRequest: function() {
		var i,
			list = fb.buttons.userInfo;

		for ( i = 0; i < list.length; i++ ) {
			fb.changeSubmenu( $( '<a></a>', {
				'class': 'fb-button fb-link-button',
				'target': '_blank',
				'href': list[ i ].href,
				'title': list[ i ].title,
				'text': list[ i ].text
			} ), ( i !== 0 ) ? true : false );

			if ( list[ i ].disable ) {
				fb.$submenu.find( 'a' ).last()
					.addClass( 'fb-disabled-button' ).removeClass( 'fb-link-button' )
					.attr( 'original-title', '' ).removeAttr( 'href target' )
					.css( {
						'color': 'gray',
						'cursor': 'default'
					} );
			}
		}

		fb.changeSubmenu( ' <span id="fb-editInfo">Carregando...</span>', true );
	},

	/**
	 * @param {Object} query
	 * @param {string} code
	 * @param {Object} data
	 */
	callback: function( query, code, data ) {
		var contribs, regDate,
			user = {},
			groups = {
				'autoconfirmed': 'Autoconfirmado',
				'autoreviewer': 'Autorrevisor',
				'bureaucrat': 'Burocrata',
				'checkuser': 'Verificador de contas',
				'eliminator': 'Eliminador',
				'interface_editor': [ 'Editor de interface', 'Editores de interface/Pedidos de aprovação' ],
				'patroller': 'Patrulhador',
				'oversight': [ 'Supervisor', 'Oversight/Candidaturas' ],
				'reviewer': 'Revisor',
				'rollbacker': 'Reversor',
				'sysop': [ 'Administrador', 'Pedidos de administração' ]
			};

		if ( !query ) {
			fb.log( 'The user info query returned no data.' );

			return false;
		}

		if ( code === 'anon' ) {
			if ( !query.usercontribs ) {
				return;
			}

			contribs = query.usercontribs;
			user.name = contribs[ 0 ].user;
			user.editcount = contribs.length;
			user.registration = contribs[ contribs.length - 1 ].timestamp;

			if ( data[ 'query-continue' ] ) {
				user.editcount = 'mais de ' + user.editcount;
			}
		} else {
			user = query.allusers && query.allusers[ 0 ];
		}

		$( '#fb-editInfo' ).html(
			'Edições: <span style="font-weight:bold;">' + ( user.editcount ).toLocaleString() + '</span> — ' + ( ( code === 'anon' ) ? 'Primeira edição' : 'Data de registro' ) + ': ' +
			'<span style="font-weight:bold;" id="fb-dateRegister" ></span>'
		);

		if ( user ) {
			if ( user.registration ) {
				regDate = new Date( user.registration );

				$( '#fb-dateRegister' ).text(
					( data[ 'query-continue' ] && code === 'anon' ? 'antes de ' : '' ) +
					regDate.getDate() + ' de ' +
					// In JS and CSS subpages, the month name appears in English
					mw.config.get( 'wgMonthNames' )[ regDate.getMonth() + 1 ] +
					' de ' + regDate.getFullYear()
				);
			} else {
				$( '#fb-dateRegister' )
					.text( 'Indefinido' )
					.attr( 'title', 'Não foi possível encontrar a data de registro do editor.' )
					.css( 'cursor', 'help' );
			}

			if ( code !== 'anon' ) {
				user.groups = user.groups.join( ' ' ).replace( /(\*|\buser|\beditor)\s/g, '' ).split( ' ' );
				user.groups = user.groups.map( function( el ) {
					if ( typeof groups[ el ] === 'object' ) {
						return '<a href="' + mw.util.getUrl( 'Wikipédia:' + groups[ el ][ 1 ] + '/' + query.allusers[ 0 ].name ) + '">' + groups[ el ][ 0 ] + '</a>';
					}

					return groups[ el ];
				} );

				fb.changeSubmenu( ' — Grupos: ' + ( user.groups.join( ' · ' ) || '<span style="color: gray;">nenhum</span>' ), true );
			}
		}
	}
};

/**
 * Callback function for the request of deleted contributions
 *
 * @param {Object} query Query of data JSON content returned by API
 */
fb.process.deletedContribs = function( query ) {
	var numDeletedRevs,
		deletedRevs = query.deletedrevs[ 0 ];

	numDeletedRevs = ( !deletedRevs ) ? 0 : deletedRevs.revisions.length;
	numDeletedRevs = numDeletedRevs || 'Nenhuma';

	fb.$pageInfo.append(
		' · ',
		$( '<a></a>', {
			'href': mw.util.getUrl( 'Especial:Restaurar/' + title ),
			'title': 'Link para as edições eliminadas da página',
			'text': numDeletedRevs +
				( ( $.inArray( numDeletedRevs, [ 1, 'Nenhuma' ] ) !== -1 ) ?
					' edição eliminada'
					: ' edições eliminadas'
				)
		} )
	);

	fb.callAPI( 'pageInfo' );
};


/**
 * Callback function for the request of page info
 *
 * @param {Object} query Query of data JSON content returned by API
 */
fb.process.pageInfo = function( query ) {
	var info = query.pages[ mw.config.get( 'wgArticleId' ) ],
		lastEdit = new Date( info.revisions[ 0 ].timestamp );

	lastEdit = lastEdit.getHours().toString().replace( /^(\d)$/, '0$1' ) + 'h' + lastEdit.getMinutes() + 'min de ' + [
		lastEdit.getDate().toString().replace( /^(\d)$/, '0$1' ),
		mw.config.get( 'wgMonthNames' )[ lastEdit.getMonth() + 1 ],
		lastEdit.getFullYear()
	].join( ' de ' );

	fb.changeSubmenu(
		fb.$pageInfo.append( [
			' ·  Tamanho: <span style="font-weight:bold;">' +  ( info.length ).toLocaleString() + ' bytes </span>',
			'Vigilantes: <span style="font-weight:bold;">' + ( info.watchers ).toLocaleString() + '</span>',
			'Última edição: <a href="?diff=last">' + lastEdit + '</a>'
		].join( ' · ' ) ).show().html()
	);
};

/**
 * Warning methods
 */
fb.warn = {};

/**
 * Sends the warn
 *
 * @param {string} userName User to be warned
 * @param {string} template Template warn
 * @param {string} warnSummary See "fb.run"
 * @param {boolean} [isPE]
 */
fb.warn.send = function( userName, template, warnSummary, isPE ) {
	$( '#fb-dialog' ).dialog( 'close' );

	fb.notify( 'Enviando a notificação para o criador...' );

	api.editPage( {
		watchlist: 'preferences',
		minor: true,
		title: 'User talk:' + userName,
		appendtext: '\n\n{' + '{subst:Aviso-' + template + '}} ~~' + '~~',
		summary: warnSummary + fb.summaryDefault,
		done: function() {
			if ( !isPE ) {
				fb.refreshPage();
			}
		}
	} );
};

/**
 * Sends a warn elimination of a page
 *
 * @param {string} code See "fb.run"
 * @param {string} [warnType] Type of warn. Only if "code" param = 'subst:apagar'
 */
fb.warn.elimination = function( code, warnType ) {
	var revisions, userPageCreator,
		apiDeferred = $.Deferred(),
		eliminationType = ( ( code === 'subst:apagar' ) ?
			'PE'
			: 'E' + ( ( code.indexOf( 'ESR' ) !== -1 ) ? 'S' : '' ) + 'R' ) + '|' + pageName.replace( /_/g, ' ' ),
		isPE = eliminationType.indexOf( 'PE' ) !== -1,
		callback = function() {
			if ( $.inArray( '1', [ $( '#fb-send-message' ).val(), warnType ] ) !== -1 ) {
				apiDeferred.resolve();

				fb.warn.send(
					userPageCreator,
					eliminationType,
					'Aviso sobre a eliminação da página "[[' + pageName.replace( /_/g, ' ' ) + ']]"',
					isPE
				);
			} else if ( $.inArray( '2', [ $( '#fb-send-message' ).val(), warnType ] ) !== -1 ) {
				apiDeferred.resolve();

				fb.warn.send(
					userPageCreator,
					'não remova',
					'Aviso sobre remoção de marcações de eliminação das páginas',
					isPE
				);
			} else {
				$( '#fb-dialog' ).dialog( 'close' );
				fb.refreshPage();
			}
		};

	fb.callAPI( 'pageRevisions' ).done( function( data ) {
		revisions = data.query.pages[ mw.config.get( 'wgArticleId' ) ].revisions;
		userPageCreator = revisions[ 0 ].user;

		if ( userPageCreator === mw.config.get( 'wgUserName' ) ||
			( revisions.length === 1 && revisions[ 0 ].comment.search( /moveu \[\[.+?\]\] para \[\[.+?\]\]/ ) !== -1 ) ||
			( ( mw.util.isIPv4Address( userPageCreator ) || mw.util.isIPv6Address( userPageCreator ) ) &&
				( Date.parse( revisions[ 0 ].timestamp ) + 86400000 ) > new Date().getTime()
			)
		) {
			if ( !isPE ) {
				return fb.refreshPage();
			}

			return;
		}

		if ( eliminationType.indexOf( 'PE' ) !== -1 ) {
			return callback();
		}

		fb.dialog( {
			title: 'Enviar notificação',
			width: 'auto',
			height: 'auto',
			content: 'Selecione uma opção abaixo:<br /><br /><select id="fb-send-message">' +
					'<option value="1">Enviar um aviso de eliminação</option>' +
					'<option value="2">Enviar um aviso de remoção de marcações de eliminação nas páginas</option>' +
					'<option>Não enviar nenhum aviso</option>' +
				'</select><br /><br />',
			buttons: {
				'OK': callback,
				'Cancelar': function() {
					$( this ).dialog( 'close' );

					if ( action !== 'edit' ) {
						fb.refreshPage();
					}
				}
			}
		} );
	} );

	return apiDeferred.promise();
};

/**
 * Sends a warn after tags a page with "sem fontes" template
 *
 * @param {string} See "fb.run"
 */
fb.warn.noRef = function() {
	var i, revisions, revs_users, userPageCreator,
		sameEditor = true,
		page = pageName.replace( /_/g, ' ' );

	fb.callAPI( 'pageRevisions' ).done( function( data ) {
		revisions = data.query.pages[ mw.config.get( 'wgArticleId' ) ].revisions;
		userPageCreator = revisions[ 0 ].user;
		revs_users = revisions.map( function( rev ) {
			return rev.user;
		} );

		for ( i = 1; i < revs_users.length - 1; i++ ) {
			if ( revs_users[ i ] !== revs_users[ i - 1 ] ) {
				sameEditor = false;

				break;
			}
		}

		if ( userPageCreator === mw.config.get( 'wgUserName' ) ||
			!sameEditor ||
			( ( mw.util.isIPv4Address( userPageCreator ) || mw.util.isIPv6Address( userPageCreator ) ) &&
				( Date.parse( revisions[ 0 ].timestamp ) + 86400000 ) > new Date().getTime()
			)
		) {
			return fb.refreshPage();
		}

		fb.dialog( {
			title: 'Enviar notificação¹',
			content: 'Deseja notificar o criador?',
			width: 'auto',
			height: 'auto',
			buttons: {
				'Sim': function() {
					fb.warn.send(
						userPageCreator,
						'cite fonte|' + page,
						'Aviso sobre a marcação da página "[[' + page + ']]" com a "[[Predefinição:Sem fontes]]"'
					);
				},
				'Não': function() {
					fb.refreshPage();

					$( this ).dialog( 'close' );
				}
			}
		} );
	} );
};

/**
 * Checks if the page has already been proposed for deletion
 * @param {boolean} justVerify
 */
fb.verifyRequestDeletion = function( justVerify ) {
	var tagName,
		apiDeferred = $.Deferred();

	api.getCurrentPageText( 'Wikipédia:Páginas para eliminar/' + pageName ).done( function( value ) {
		if ( justVerify ) {
			return apiDeferred.resolve( !!value );
		}

		tagName = ( !value ) ? 'span' : 'a';

		fb.$pageInfo.append(
			$( '<' + tagName + ' id="fb-requestDeletion"></' + tagName + '>' )
				.before( ( $.inArray( nsNum, [ 0, 102 ] ) !== -1 ) ? ' · ' : ' ' )
				.html( 'Pedido de eliminação' )
		);

		if ( !value ) {
			$( '#fb-requestDeletion' )
				.attr( 'title', 'A página nunca foi proposta para eliminação' )
				.css( 'cursor', 'default' );
		} else {
			$( '#fb-requestDeletion' ).attr( {
				'title': 'Pedido de eliminação desta página',
				'href': mw.util.getUrl( 'Wikipédia:Páginas para eliminar/' + pageName )
			} );
		}

		if ( $.inArray( 'sysop', mw.config.get( 'wgUserGroups' ) ) !== -1 ) {
			fb.callAPI( 'deletedContribs' );
		} else {
			fb.callAPI( 'pageInfo' );
		}
	} );

	return apiDeferred.promise();
};

/**
 * Manipulate the text page
 *
 * @param {string} code See "fb.run"
 * @param {string} extraText See "fb.run"
 * @param {string} sum Summary of edit
 * @param {string} value Text of page
 * @param {boolean} isEditPage If is the edit page
 */
fb.manipulateTextPage = function( code, extraText, sum, value, isEditPage ) {
	var fbSummary,
		apiDeferred = $.Deferred(),
		isEliminationTag = code === 'subst:apagar' || ( code.indexOf( 'av-' ) === -1 &&
			code.search( /ES?R/ ) !== -1 &&
			code.search( /ER(\|(1\b|7|8|9|12|18|C1|D1|D2|U1|U2|R1|R2))/i ) === -1
		);

	if ( code !== 'redirect' &&
		value.search( /\{\{(?:subst:)?(?:ER|ESR(?:2?|-banda|-bio|-empresa|-bsre|-organização|-matrad)|apagar|VDA|Usuário(?:\(a\))?\:Salebot\/Impróprio)[\|\}]/i ) !== -1 &&
		nsNum !== 3
	) {
		alert( 'Já existe uma predefinição de eliminação nesta página.' );

		return false;
	}

	if ( code.indexOf( 'ER' ) === 0 ) {
		extraText = extraText ? '|3=' + extraText : '';
		extraText = '{' + '{' + code + '|2=~~' + '~~' + extraText + '}}\n';
		extraText = ( nsNum === 10 ) ? '<noinclude>' + extraText + '</noinclude>' : extraText;
		value = extraText + value;
		fbSummary = 'Página proposta para [[WP:ER|eliminação rápida]] (regra ' + code.substring( 3 ) + ')';
	} else if ( code === 'redirect' ) {
		extraText = extraText.split( '|2=' );

		if ( nsNum === 14 ) {
			value = '{' + '{Redirecionamento de categoria|' + extraText[ 0 ] + '|2=' + extraText[ 1 ] + '}}';
			fbSummary = 'Redirecionando para a [[categoria:' + extraText[ 0 ] + ']] (' + extraText[ 1 ] + ')';
		} else {
			value = '#REDIRECIONAMENTO [[' + extraText[ 0 ] + ']]';
			fbSummary = 'Feito redirecionamento para [[' + extraText[ 0 ] + ']]' +
				( ( extraText[ 1 ] !== '' ) ? ' (' + extraText[ 1 ] + ')' : '' );
		}
	} else {
		extraText = extraText ? '|1=' + extraText : '';
		extraText = '{' + '{' + code + extraText + '}}';

		if ( code.indexOf( 'matrad' ) !== -1 ) {
			extraText = extraText.replace( '1=', 'língua=' );
		}

		if ( nsNum === 3 ) {
			value += '\n\n' + extraText + ' ~~' + '~~';
			fbSummary = 'Adicionando mensagem';
		} else if ( code.indexOf( 'esboço' ) === 0 ) {
			if ( value.match( /\n\n\[\[/ ) ) {
				value = value.substring( 0, value.search( /\n\n\[\[/ ) ) + '\n\n\n' +
					extraText + value.substring( value.search( /\n\n\[\[/ ) );
			} else {
				value += '\n\n\n' + extraText;
			}

			fbSummary = 'Página marcada como [[WP:EBC|esboço]]';
		} else {
			value = extraText + '\n' + value;
			value = ( code.search( /subst:(apagar|ESR)/ ) !== -1 && nsNum === 10 ) ?
				'<noinclude>' + value + '</noinclude>'
				: value;

			if ( code.indexOf( 'subst:ESR' ) === 0 ) {
				code = 'subst:ESR';
			}

			fbSummary = sum || fbSummary || 'Adicionando marcação';

			if ( code.indexOf( 'ESR' ) !== -1 ) {
				fbSummary = 'Página proposta para [[WP:ESR|eliminação semirrápida]]';
			} else if ( code.search( /subst:(fu|f-de|f-com)/ ) !== -1 ) {
				fbSummary = 'Página marcada para [[WP:Fusão|fusão]]';
			}
		}

		if ( code.indexOf( 'subst:VDA' ) !== -1 ) {
			value = ( nsNum === 10 ) ? '<noinclude>' + extraText + '</noinclude>' : extraText;
		}
	}

	if ( isEditPage ) {
		$( '#wpTextbox1' ).val( value );

		if ( code.indexOf( 'ER' ) !== -1 ||
			code.indexOf( 'ESR' ) !== -1 ||
			$.inArray( code, [ 'redirect', 'subst:apagar' ] ) !== -1
		) {
			$( '#wpSummary' ).val( fbSummary + fb.summaryDefault );
		}

		//wikEd compatibility
		if ( typeof wikEd !== 'undefined' ) {
			if ( wikEd.useWikEd ) {
				/*jshint newcap: false*/
				WikEdUpdateFrame();
				/*jshint newcap: true*/
			}
		}

		$( '#editform' ).submit( function( e ) {
			e.preventDefault();

			if ( isEliminationTag ) {
				if ( code !== 'subst:apagar' ) {
					fb.warn.elimination( code );
				} else {
					apiDeferred.resolve();
				}
			} else if ( code === 'subst:s-fontes' ) {
				fb.warn.noRef();
			} else {
				fb.refreshPage( code );
			}
		} );

		return apiDeferred.promise();
	}

	$( '.fb-button' ).each( function() {
		$( this ).addClass( 'fb-disabled-button' ).unbind( 'click' );
		$( this ).attr( 'title', '' );
	} );

	fb.notify( 'Editando a página...' );

	api.editPage( {
		watchlist: 'preferences',
		minor: true,
		summary: fbSummary + fb.summaryDefault,
		text: value,
		done: {
			success: function() {
				if ( isEliminationTag ) {
					if ( code !== 'subst:apagar' ) {
						fb.warn.elimination( code );
					} else {
						apiDeferred.resolve();
					}
				} else if ( code === 'subst:s-fontes' ) {
					fb.warn.noRef();
				} else if ( code === 'em manutenção' ) {
					fb.maintenancePrompt();
				} else {
					fb.refreshPage( code );
				}
			},
			apiError: function( error ) {
				fb.notify(
					$.parseHTML(
						'Não foi possível editar a página.<br />A API retornou o código de erro "' +
						error.code + '": ' + error.info + '.<br />Se o problema persistir,' +
						' favor informar no <a href="' + mw.util.getUrl( 'WP:Café dos Programadores' ) + '">Café dos programadores</a>' +
						' ou em <a href="' + mw.util.getUrl( 'MediaWiki Discussão:Gadget-fastbuttons.js' ) + '">MediaWiki Discussão:Gadget-fastbuttons.js</a>'
					)
				);
			},
			unknownError: function() {
				fb.notify( 'Não foi possível editar a página devido a um erro desconhecido da API.' );
			}
		}
	} );

	return apiDeferred.promise();
};

/**
 * Edit function
 *
 * @param {string} code A template name, possibly preceded by "subst:" and optionally followed by "|" and some parameter(s)
 *    @example:
 *    "não remova"or "subst:ER|5" or "subst:ESR-matrad|1=~~' + '~~|língua='"
 * @param {string|null} [extraText] Extra text for templates and redirects
 * @param {string} sum Summary of edit
 *
 * @return {boolean} False if the user canceled or the page already has a template
 */
fb.run = function( code, extraText, sum ) {
	var rcid,
		apiDeferred = $.Deferred();

	if ( $.inArray( action, [ 'edit', 'submit' ] ) !== -1 ) {
		fb.manipulateTextPage( code, extraText, sum, $( '#wpTextbox1' ).val(), true );
	} else if ( action === 'view' ) {
		rcid = mw.util.getParamValue(
			'rcid',
			mw.util.$content.find( 'div.patrollink a' ).attr( 'href' ) || ''
		);

		if ( rcid ) {
			fb.notify( 'Patrulhando a página...' );
			fb.callAPI( 'patrolPage' );
		}

		fb.notify( 'Obtendo o conteúdo da página...' );

		api.getCurrentPageText().done( function( value ) {
			fb.manipulateTextPage( code, extraText, sum, value ).done( function() {
				apiDeferred.resolve();
			} );
		} );
	}

	return apiDeferred.promise();
};

/**
 * The prompt used in the gadget
 *
 * @param {string} title Title of prompt
 * @param {string} label Labels of inputs
 *    @example:
 *    'field1|2=field2|3=field3', results in 3 inputs with the respective labels
 * @param {Function} callback Function to be called after (...)
 */
fb.prompt = function( title, label, callback ) {
	if ( !label ) {
		label = 'Observação';
	}

	var i,
		dialogContent = '',
		multipleLabels = label.indexOf( '|2=' ) !== -1;

	if ( multipleLabels ) {
		for ( i = 1, label = label.split( /\|\d+=/ ); i <= label.length; i++ ) {
			dialogContent += '<label for="fb-nprompt-input' + i + '">' +
				label[ i - 1 ].replace( /\[.+\]/, '' ) +
				': <input type="text" class="fb-field" id="fb-nprompt-input' + i + '" /></label>';
		}
	} else {
		dialogContent = '<label for="fb-nprompt-input">' +
			label.replace( /\[.+\]/, '' ) +
			': <input type="text" class="fb-field" id="fb-nprompt-input" /></label>';
	}

	fb.dialog( {
		title: title,
		content: dialogContent,
		width: 400,
		height: 'auto',
		buttons: {
			'OK': function() {
				var awnsers,
					$inputs = $( '#fb-dialog input' );

				if ( label !== 'Observação' && label.length !== 1 ) {
					$inputs.each( function( i ) {
						if ( $( this ).val() === '' && label[ i ].search( /\[true\]/ ) === -1 ) {
							$( this ).addClass( 'fb-fill-field' );
						} else {
							$( this ).removeClass( 'fb-fill-field' );
						}
					} );
				}

				if ( $( '.fb-field' ).hasClass( 'fb-fill-field' ) ) {
					return;
				}

				if ( !multipleLabels ) {
					callback( $( '#fb-nprompt-input' ).val() );
				} else {
					for ( i = 1; i <= $inputs.length; i++ ) {
						awnsers = ( i === 1 ) ?
							$( '#fb-nprompt-input1' ).val()
							: awnsers + '|' + i + '=' + $( '#fb-nprompt-input' + i ).val();
					}

					callback( awnsers );
				}

				$( this ).dialog( 'close' );
			},
			'Cancelar': function() {
				$( this ).dialog( 'close' );
			}
		},
		focus: function() {
			$( ':input', this ).keyup( function( event ) {
				if ( event.keyCode === 13 ) {
					$( '.ui-dialog-buttonpane button:first' ).click();
				}
			} );
		}
	} );
};

/**
 * ESR (Eliminação semirrápida) prompt
*/
fb.ESRPrompt = function() {
	var i, subjects, defaultJustifications, $justification;

	if ( nsNum === 6 ) {
		fb.dialog( {
			title: 'Qual a justificativa para a eliminação do arquivo?',
			content: '<div id="fb-ESR-justification" style="border:none;">' +
					'<label for="fb-ESR-justification-field">Justificativa:<br />' +
						'<textarea style="height:170px;width:250px;" id="fb-ESR-justification-field" placeholder="Não é necessário assinar."></textarea>' +
					'</label>' +
				'</div>',
			buttons: {
				'OK': function() {
					$justification = $( '#fb-ESR-justification-field' );

					if ( $justification.val() === '' ) {
						return $justification.addClass( 'fb-fill-field' );
					}

					fb.run( 'subst:ESR-arquivo', $justification.val() );

					$( this ).dialog( 'close' );
				},
				'Cancelar': function() {
					$( this ).dialog( 'close' );
				}
			}
		} );

		$( '#fb-ESR-justification-field' ).placeholder();

		return;
	}

	defaultJustifications = fb.buttons.defaultJustifications_ESRPrompt;
	subjects = [
		'Arte', 'Carnaval', 'Ciência',  'Ciências sociais',
		'Cinema', 'Educação', 'Elementos de ficção', 'Empresas, produtos e serviços',
		'Entretenimento', 'Esporte/Desporto', 'Geografia', 'Ginástica',
		'História e sociedade', 'Medicina', 'Música', 'Países',
		'Pessoas', 'Política', 'Rádio e televisão'
	];

	fb.dialog( {
		title: 'Eliminação semirrápida',
		width: 'auto',
		height: 'auto',
		content: '<div id="fb-ESR">' +
				'<a id="fb-ESR-matrad" class="fb-button fb-action-button">Má-tradução</a><br /><br />' +
				'<div id="fb-ESR-justification">' +
					'<label for="fb-ESR-justification-field">Justificativa:<br />' +
						'<textarea style="height:170px;width:250px;" id="fb-ESR-justification-field" placeholder="Não é necessário assinar."></textarea>' +
					'</label>' +
				'</div>' +
				'<div id="fb-ESR-subject">' +
					'<label for="fb-ESR-subject">Assunto:<br />' +
						'<select id="fb-ESR-subject-select">' +
							'<option></option>' +
						'</select>' +
					'</label><br />' +
					'<label for="fb-ESR-subject-other">' +
						'<input type="checkbox" id="fb-ESR-subject-other" /> Outro' +
					'</label>' +
				'</div>' +
			'</div>',
		buttons: {
			'OK': function() {
				if ( $( '#fb-ESR-justification-field' ).val() === '' ) {
					$( '#fb-ESR-justification-field' ).addClass( 'fb-fill-field' );
				} else {
					$( '#fb-ESR-justification-field' ).removeClass( 'fb-fill-field' );
				}

				if ( $( '#fb-ESR-subject-select' ).val() === '' &&
					!$( '#fb-ESR-subject-other' ).prop( 'checked' )
				) {
					$( '#fb-ESR-subject-select' ).addClass( 'fb-fill-field' );
				} else {
					$( '#fb-ESR-subject-select' ).removeClass( 'fb-fill-field' );
				}

				if ( $( '#fb-ESR *' ).hasClass( 'fb-fill-field' ) ) {
					return;
				}

				fb.run(
					'subst:ESR|' + $( '#fb-ESR-justification-field' ).val() + ' ~~' + '~~' +
						( ( !$( '#fb-ESR-subject-other' ).prop( 'checked' ) &&
							$( '#fb-ESR-subject-select' ).val() !== '' ) ?
							'|assunto=' + $( '#fb-ESR-subject-select' ).val()
							: ''
						)
				);

				$( this ).dialog( 'close' );
			},
			'Cancelar': function() {
				$( this ).dialog( 'close' );
			}
		}
	} );

	for ( i = 0; i < subjects.length; i++ ) {
		$( '#fb-ESR-subject-select' ).append(
			'<option value="' + subjects[ i ].toLowerCase() + '">' + subjects[ i ] + '</option>'
		);
	}

	$( '#fb-ESR-subject-select' ).change( function() {
		if ( defaultJustifications[ $( this ).val() ] ) {
			$( '#fb-ESR-justification-field' ).val( defaultJustifications[ $( this ).val() ] );
		}
	} );

	$( '#fb-ESR-matrad' ).click( function() {
		$( '#fb-dialog' ).dialog( 'close' );

		fb.prompt(
			'De qual língua o artigo foi traduzido?',
			'Língua',
			function( lang ) {
				fb.run( 'subst:ESR-matrad', lang.toLowerCase() );
			}
		);
	} );

	$( '#fb-ESR-subject-other' ).click( function() {
		if ( $( '#fb-ESR-subject-other' ).attr( 'checked' ) ) {
			$( '#fb-ESR-subject-select' )
				.attr( 'disabled', true )
				.addClass( 'fb-disabled-button' );
		} else {
			$( '#fb-ESR-subject-select' )
				.attr( 'disabled', false )
				.removeClass( 'fb-disabled-button' );
		}
	} );

	$( '#fb-ESR-justification-field' ).placeholder();
};

/**
 * PE (Páginas para eliminar) prompt
 */
// FIXME: don't work correctly in edit pages
fb.PEPrompt = function() {
	var $justification, $warnType, $sendWarnType, sendWarn, createPE,
		createPE_callback = function() {
			fb.notify( 'Criando a página de eliminação...' );

			api.getCurrentPageText( 'Wikipédia:Páginas_para_eliminar/Novoapagar' ).done( function( value ) {
				value = value
					.replace( /<\/?includeonly>/g, '' )
					.replace( /<!--(.|\n)+canceladas -->/, $justification );

				api.editPage( {
					title: 'Wikipédia:Páginas_para_eliminar/' + pageName,
					watchlist: 'preferences',
					minor: true,
					text: value,
					summary: 'Criando página de eliminação' + fb.summaryDefault,
					done: function() {
						fb.refreshPage();
					}
				} );
			} );
		};

	fb.dialog( {
		title: 'Página para eliminar',
		width: 'auto',
		height: 'auto',
		content: '<div id="fb-PE">' +
				'<label for="fb-PE-sendWarn">' +
					'<input type="checkbox" id="fb-PE-sendWarn" /> Enviar um aviso para o criador da página' +
				'</label>' +
				'<div id="fb-PE-sendWarn-type" style="display: none;">' +
					'<label>' +
						'<input type="radio" name="fb-PE-sendWarn-type" value="1" /> Enviar um aviso de eliminação' +
					'</label>' +
					'<label>' +
						'<input type="radio" name="fb-PE-sendWarn-type" value="2" /> Enviar um aviso de remoção de marcações de eliminação nas páginas' +
					'</label>' +
				'</div>' +
				'<label for="fb-PE-createPE">' +
					'<input type="checkbox" id="fb-PE-createPE" /> Criar a discussão para a eliminação da página' +
					'<span id="fb-PE-createPE-warning"><br />Atenção: essa página já foi proposta para eliminação anteriormente' +
						', e caso proceda com a ação, a PE anterior não será arquivada</span></span>' +
				'</label>' +
				'<textarea id="fb-PE-createPE-justification" style="height:200px; width:100%; display: none;" placeholder="Justificativa para eliminação. Não precisa assinar."></textarea>' +
			'</div>',
		buttons: {
			'OK': function() {
				sendWarn = $( '#fb-PE-sendWarn' ).attr( 'checked' );
				$sendWarnType = $( 'input[name="fb-PE-sendWarn-type"]' );
				createPE = $( '#fb-PE-createPE' ).attr( 'checked' );
				$justification = $( '#fb-PE-createPE-justification' );

				if ( sendWarn && (
					!$sendWarnType.eq( 0 ).attr( 'checked' ) &&
					!$sendWarnType.eq( 1 ).attr( 'checked' )
				) ) {
					$( '#fb-PE-sendWarn-type' ).addClass( 'fb-fill-field' );
				} else {
					$( '#fb-PE-sendWarn-type' ).removeClass( 'fb-fill-field' );
				}

				if ( createPE && $justification.val() === '' ) {
					$justification.addClass( 'fb-fill-field' );
				} else {
					$justification.removeClass( 'fb-fill-field' );
				}

				if ( $( '#fb-PE *' ).hasClass( 'fb-fill-field' ) ) {
					return;
				}

				$sendWarnType = $( 'input[name="fb-PE-sendWarn-type"]:checked' ).val();
				$justification = $justification.val();

				fb.run( 'subst:apagar', '', 'Página proposta para [[WP:Eliminação por consenso|eliminação por consenso]]' ).done( function() {
					if ( sendWarn ) {
						fb.warn.elimination( 'subst:apagar', $sendWarnType ).done( function() {
							if ( createPE ) {
								createPE_callback();
							} else {
								fb.refreshPage();
							}
						} );
					} else if ( createPE ) {
						createPE_callback();
					} else {
						fb.refreshPage();
					}
				} );

				$( this ).dialog( 'close' );
			},
			'Cancelar': function() {
				$( this ).dialog( 'close' );
			}
		}
	} );

	$( '#fb-PE-createPE' ).click( function() {
		$justification = $( '#fb-PE-createPE-justification' );

		if ( $justification.css( 'display' ) === 'none' ) {
			$justification.show( 'fast' );

			fb.verifyRequestDeletion( true ).done( function( exists ) {
				if ( !!exists ) {
					$( '#fb-PE-createPE-warning' ).show( 'fast' );
				}
			} );
		} else {
			$( '#fb-PE-createPE-warning' ).hide();
			$justification
				.hide( 'fast' )
				.removeClass( 'fb-fill-field' )
				.val( '' );
		}
	} );

	$( '#fb-PE-sendWarn' ).click( function() {
		$warnType = $( '#fb-PE-sendWarn-type' );

		if ( $warnType.css( 'display' ) === 'none' ) {
			$warnType.show( 'fast' );
		} else {
			$( 'input[name="fb-PE-sendWarn-type"]' ).attr( 'checked', false );
			$warnType
				.hide( 'fast' )
				.removeClass( 'fb-fill-field' );
		}
	} );

	$( '#fb-PE-createPE-justification' ).placeholder();
};

/**
 * Prompt to the tag "Em manutenção"
 */
fb.maintenancePrompt = function() {
	var $justification;

	fb.dialog( {
		width: 380,
		content: '<div id="fb-maintenance-justification" style="border:none;">' +
				'<label for="fb-maintenance-justification-field">Escreva um breve resumo das condições atuais da página:<br />' +
					'<textarea style="height:170px; width:350px;" id="fb-maintenance-justification-field" placeholder="Não é necessário assinar."></textarea>' +
				'</label>' +
			'</div>',
		buttons: {
			'OK': function() {
				$justification = $( '#fb-maintenance-justification-field' );

				if ( $justification.val() === '' ) {
					return $justification.addClass( 'fb-fill-field' );
				}

				fb.notify( 'Editando a página "Wikipédia:Páginas precisando de manutenção' );

				api.editPage( {
					title: 'Wikipédia:Páginas precisando de manutenção',
					section: 'new',
					sectiontitle: '[[' + pageName.replace( '_', ' ' ) + ']]',
					watchlist: 'preferences',
					minor: true,
					summary: 'Adicionando a página "' + pageName + '"' + fb.summaryDefault,
					text: $justification.val() + ' ~~' + '~~',
					done: function() {
						fb.refreshPage();
					}
				} );

				$( this ).dialog( 'close' );
			}
		}
	} );
};

/**
 * Initialize the gadget
 */
fb.init = function() {
	var basePageName;

	if ( mw.util.getParamValue( 'printable' ) === 'yes' ) {
		return;
	}

	if ( typeof localStorage !== 'undefined' ) {
		if ( localStorage[ 'fb-edit-sucess' ] ) {
			fb.notify( 'A página foi editada com sucesso.', {
				autoHide: true
			} );
			window.history.pushState(
				{},
				'Fast Buttons',
				mw.util.getUrl() + ( ( location.href.indexOf( 'redirect=no' ) !== -1 ) ?
					'?redirect=no'
					: ''
				)
			);

			delete localStorage[ 'fb-edit-sucess' ];
		}
	}

	if ( mw.config.get( 'skin' ) === 'modern' ) {
		$( '#contentSub' ).before( fb.$menu ).before( fb.$submenu );
	} else {
		$( 'h1' ).first().after( fb.$submenu ).after( fb.$menu );
	}

	if ( pageName !== 'Wikipédia:Página_principal' &&
		$.inArray( action, [ 'view', 'edit', 'submit' ] ) !== -1
	) {
		/*** Menu Principal ***/
		if ( nsNum !== -1 ) {
			if ( $.inArray( nsNum, [ 2, 3 ] ) !== -1 &&
				title.indexOf( mw.config.get( 'wgUserName' ) ) === 0
			) {
				fb.addButton( {
					action: 'ER|1',
					text: 'ER1',
					title: 'Marcar subpágina do próprio usuário para eliminação'
				} );
			} else {
				fb.addButton( {
					action: function() {
						fb.changeSubmenu( fb.buttons.elimination );
					},
					text: 'Eliminação',
					title: 'Exibe as opções de eliminação para esta página'
				} );
			}
		}

		if ( $.inArray( nsNum, [ 0, 102 ] ) !== -1 ) {
			fb.addButton( [ {
					action: function() {
						fb.changeSubmenu( fb.buttons.esb );
					},
					text: 'Esboço',
					title: 'Exibir predefinições para esboços'
				}, {
					action: function() {
						fb.changeSubmenu( fb.searchButtons );
					},
					text: 'Busca',
					title: 'Exibir opções para a busca de fontes'
				}
			] );
		} else {
			if ( $.inArray( nsNum, [ 2, 3 ] ) !== -1 || mw.config.get( 'wgCanonicalSpecialPageName' ) === 'Contributions' ) {
				basePageName = ( $.inArray( nsNum, [ 2, 3 ] ) !== -1 ) ?
					title.split( '/' )[ 0 ]
					: ( mw.util.getUrl().split( '/' )[ 3 ] || mw.util.getParamValue( 'target' ) );

				if ( mw.util.isIPv4Address( basePageName ) || mw.util.isIPv6Address( basePageName ) ) {
					fb.addButton( {
						action: function() {
							fb.callAPI( 'anon' );
						},
						text: 'Sobre o IP',
						title: 'Exibir informações sobre este IP',
						disable: mw.config.get( 'wgCanonicalSpecialPageName' ) === 'Contributions' &&
							!( mw.util.getUrl().split( '/' )[ 3 ] || mw.util.getParamValue( 'target' ) )
					} );
				} else {
					fb.addButton( {
						action: function() {
							fb.callAPI( 'usu' );
						},
						text: 'Sobre a conta',
						title: 'Exibir informações sobre esta conta',
						disable: mw.config.get( 'wgCanonicalSpecialPageName' ) === 'Contributions' &&
							!( mw.util.getUrl().split( '/' )[ 3 ] || mw.util.getParamValue( 'target' ) )
					} );
				}
			}

			if ( nsNum === 3 ) {
				fb.addButton( {
					action: function() {
						fb.changeSubmenu( fb.buttons.warn );
					},
					text: 'Aviso',
					title: 'Exibir lista de predefinições para avisos',
					disable: mw.config.get( 'wgUserName' ) === window.unescape( mw.util.getUrl().split( ':' )[ 1 ] )
				} );
			} else if ( nsNum === 14 ) {
				fb.addButton( {
					action: function() {
						fb.changeSubmenu( fb.catScanButtons );
					},
					text: 'CatScan',
					title: 'Exibir opções do CatScan para procurar páginas nesta categoria'
				} );
			}
		}

		if ( $.inArray( nsNum, [ 0, 4, 12, 102 ] ) !== -1 ) {
			fb.addButton( {
				action: function() {
					fb.changeSubmenu(
						fb.buttons.maintenance,
						false,
						( $.inArray( nsNum, [ 4, 12 ] ) !== -1 ) ? [ 12, 13, 14 ] : undefined
					);
				},
				text: 'Manutenção',
				after: '0',
				title: 'Exibir predefinições para manutenção'
			} );
		}

		if ( nsNum % 2 === 0 ) {
			fb.addButton( {
				action: 'redirect',
				text: '#R',
				title: 'Redirecionar para outro título',
				prompt: 'Informe a página e o motivo do redirecionamento',
				label: 'Página|2=Motivo[' + ( nsNum !== 14 ) + ']',
				disable: $.inArray( nsNum, [ 6, 8, 14, 828 ] ) !== -1,
				after: '1'
			} );
		}
	}

	fb.addButton( {
		action: function() {
			fb.changeSubmenu( 'Carregando...' );
			fb.changeSubmenu( fb.$pageInfo.empty().hide(), true );

			if ( $.inArray( nsNum, [ 2, 3, 8, 828 ] ) === -1 ) {
				fb.callAPI( 'backLinks' );
			} else {
				fb.verifyRequestDeletion();
			}
		},
		text: 'Informações sobre a página',
		title: 'Exibe informações sobre a página',
		disable: nsNum === -1 || $.inArray( action, [ 'view', 'edit', 'submit', 'history' ] ) === -1
	} );

	fb.addButton( {
		action: function() {
			fb.changeSubmenu( fb.buttons.list );
		},
		text: 'Listar',
		title: 'Exibe os botões que listam edições nas páginas novas, mudanças recentes e páginas vigiadas'
	} );

	fb.$menu.find( 'a' ).tipsy();
};

window.fastButtons = fb;

// Executes the gadget when document is ready
$( fb.init );

}( mediaWiki, jQuery, window ) );