User:Kethyga/Gadget-ToolsRedirect.js

//From https://zh.wikipedia.org/zh-cn/MediaWiki:Gadget-ToolsRedirect.js //User:Interaccoonale/ToolsRedirect/tools-redirect.js // /* vim:set noexpandtab ft=javascript ts=4 sw=4: */

( function ( $ ) {	"use strict";

var _TR, origPageName = mw.config.get( "wgPageName" ), scriptPath = mw.config.get( "wgScriptPath" ), nsNumber = mw.config.get( "wgNamespaceNumber" ), isCategory = nsNumber === 14, _findRedirectCallbacks = [], _pageWithRedirectTextSuffix = {}, _redirectExcludes = {}, SUFFIX_APPEND = 0, SUFFIX_REPLACE = 1, SUFFIX_SETDEFAULT = 2, _nsCanonPrefix = origPageName.split( ":" )[ 0 ] + ":", _nsPrefixPattern = $.map(			mw.config.get( "wgNamespaceIds" ), function ( nsid, text ) {				return nsid === nsNumber ? text : null;			} ).join( "|" ); _nsPrefixPattern = new RegExp( "^(" + _nsPrefixPattern + "):", "i" );

if ( nsNumber === 0 ) { // articles _nsCanonPrefix = ""; _nsPrefixPattern = /^/; }

function fixNamespace( title ) { if ( nsNumber === 0 ) { // do nothing if it's articles return title; } else if ( _nsPrefixPattern.test( title ) ) { // canonize the namespace return title.replace( _nsPrefixPattern, _nsCanonPrefix ); } else { // don't have a namespace return _nsCanonPrefix + title; }	}

mw.toolsRedirect = {

SUFFIX_APPEND: SUFFIX_APPEND, SUFFIX_REPLACE: SUFFIX_REPLACE, SUFFIX_SETDEFAULT: SUFFIX_SETDEFAULT,

findRedirectCallback: function ( callback ) { /* Add new custom callback for finding new * potential redirect titles. *			 * @param {function} callback( pagename, $content, titles ) -> title list */			if ( arguments.length === 1 ) { _findRedirectCallbacks.push( callback ); } else { $.merge( _findRedirectCallbacks, arguments ); }			return this; },

findRedirectBySelector: function ( selector ) { /* A shortcut to add CSS selectors as rule to find new potential * redirect titles. *			 * @param {string} selector */			_findRedirectCallbacks.push( function {				return $( selector ).map( function  { var title = $( this ).text; return title || null; } );			} );			return this; },

setRedirectTextSuffix: function ( title, suffix, flag ) { var flag_set = false, flag_append = false; flag = flag || SUFFIX_APPEND; // default append flag_set = flag === SUFFIX_REPLACE; title = fixNamespace( title ); if ( title in _pageWithRedirectTextSuffix ) { flag_append = flag === SUFFIX_APPEND; } else { // if not exist, every flag can set flag_set = true; }

if ( flag_set ) { _pageWithRedirectTextSuffix[ title ] = suffix; } else if ( flag_append ) { _pageWithRedirectTextSuffix[ title ] = _pageWithRedirectTextSuffix[ title ] + suffix; }		}

};

_TR = { msg: null, tabselem: null, tagselem: null, variants: [ "zh-hans", "zh-hant", "zh-cn", "zh-hk", "zh-mo", "zh-sg", "zh-tw" ],		init: function { if ( !this.msg ) { return; } // not setup correctly var self = this, btn = $(					" " +					this.msg.btntitle + " " ); btn.click( function ( evt ) {				evt.preventDefault;				self.dialog;			} ); $( "li#ca-history" ).after( btn ); },		dialog: function { var dlg = $( "" ) .dialog( {					bgiframe: true, resizable: false, modal: true, width: 600 } ); this.tabselem = $( "" ).appendTo( dlg ); this.tagselem = $( "" ).appendTo( this.tabselem ); this.addTabs; this.tabselem.tabs; },		addTabs: function { for ( var kname in this.tabs ) { if ( this.tabs[ kname ] === null ) { this.tabs[ kname ] = this[ "_initTab" + kname[ 0 ].charAt( 0 ).toUpperCase + kname.slice( 1 ) ]; }				var tab = this.tabs[ kname ]; this.tagselem.append( tab.tag ); this.tabselem.append( tab.cont ); }			// default tab, autoload when dialog initiate this.loadView; },		createTab: function ( tabname, tabtitle, onClick ) { var self = this, tag = $( "" + tabtitle + "" ), cont = $( "" ); $( "a", tag ).on( "click", function {				onClick.call( self );			} ); return { tag: tag, cont: cont, loaded: false }; },		_initTabView: function { return this.createTab( "view", this.msg.tabviewtitle, this.loadView ); },		_initTabCreate: function { return this.createTab( "create", this.msg.tabcreatetitle, this.loadCreate ); },		tabs: { view: null, create: null },		fix: function ( pagenames ) { var self = this; $( "p.desc", this.tabs.view.cont ).text( this.msg.fixloading ); $( "p[class!=desc]", this.tabs.view.cont ).remove; this.loading( this.tabs.view.cont ); this .bulkEdit(					pagenames,					this.msg.fixtext.replace( "$1", origPageName ),					this.msg.fixsummary				) .done( function {				// delay load before the asynchronous tasks on server finished					setTimeout( function  { self.loaded( self.tabs.view.cont ); self.loadView( true ); }, 3000 );				} );		},		create: function ( pagenames ) { var self = this; $( "p.desc", this.tabs.create.cont ).text( this.msg.createloading ); $( "p[class!=desc]", this.tabs.create.cont ).remove; this.loading( this.tabs.create.cont ); this .bulkEdit(					pagenames,					this.msg.createtext.replace( "$1", origPageName ),					this.msg.createsummary.replace( "$1", origPageName )				) .done( function {				// delay load before the asynchronous tasks on server finished					setTimeout( function  { self.loaded( self.tabs.create.cont ); self.tabs.view.loaded = false; self.loadCreate( true ); }, 500 );				} );		},		addRedirectTextSuffix: function ( title, text ) { if ( title in _pageWithRedirectTextSuffix ) { text = text + _pageWithRedirectTextSuffix[ title ]; }			return text; },		bulkEdit: function ( titles, text, summary ) { var self = this; titles = titles.filter( function ( v, i, arr ) {				return arr.indexOf( v ) === i;			} ); titles = titles.join( "|" );

return $.ajax( this.buildQuery( { action: "query", prop: "info", titles: titles, meta: "tokens" } ) ) .then( function ( data ) {					var deferreds = [];					$.each( data.query.pages, function ( idx, page ) { deferreds.push( $.ajax( self.buildQuery( {							action: "edit",							title: page.title,							token: data.query.tokens.csrftoken,							text: self.addRedirectTextSuffix( page.title, text ),							summary: summary,							// tags: "ToolsRedirect"						} ) ) ); } );					return $.when.apply( $, deferreds );				} ); },		loadTabCont: function ( tabname, callback, reload ) { var self = this, tab = this.tabs[ tabname ]; if ( reload ) { tab.loaded = false; }			if ( !tab.loaded ) { tab.cont.html( "" ); var $desc = $( "" +					this.msg.rediloading + " " ).appendTo( tab.cont ), $text = $desc.find( "> .desc-text" ); callback.apply( this ) .done( function { $text.text( self.msg[ "tab" + tabname + "desc" ] ); } ) .fail( function { $text.text( self.msg[ "tab" + tabname + "notfound" ] ); } ) .always( function {						self.addMethods( $desc, [ { href: "#refresh", title: self.msg.refresh, click: function ( evt ) { evt.preventDefault; self.loadTabCont( tabname, callback, true ); }						} ] );					} );				tab.loaded = true; }		},		loading: function ( container ) { if ( container.prop( "tagName" ).toLowerCase === "span" ) { container.addClass( "mw-ajax-loader" ); } else if ( $( "span.mw-ajax-loader", container ).length === 0 ) { $( " " ).appendTo( container ); }		},		loaded: function ( container ) { if ( container.prop( "tagName" ).toLowerCase === "span" ) { container.removeClass( "mw-ajax-loader" ); } else { $( "span.mw-ajax-loader", container ).remove; }		},		selectAll: function ( cont ) { $( "input[type=checkbox]:not(:disabled)", cont ) .prop( "checked", true ); },		selectInverse: function ( cont ) { $( "input[type=checkbox]:not(:disabled)", cont ) .each( function {					var e = $( this );					e.prop( "checked", !e.prop( "checked" ) );				} ); },		selectAction: function ( cont, cb ) { var pagenames = []; $( "input[type=checkbox]:checked", cont ) .each( function {					pagenames.push( $( this ).data( "page-title" ) );				} ); if ( pagenames.length ) { cb.call( this, pagenames ); }		},		clickAction: function ( cont, cb ) { var pagename = $( "input[type=\"checkbox\"]", cont ).data( "page-title" ); cb.call( this, [ pagename ] ); },		buildLink: function ( attr ) { var a = $(				"" + attr.title + "</a>" ); if ( attr.click ) { a.on( "click", attr.click ); }			if ( attr.classname ) { a.addClass( attr.classname ); }			return $( "<span class=\"tools-redirect_link\">" ).append( a ); },		addMethods: function ( $parent, methods ) { var self = this, $container = $parent.find( "> .tools-redirect_methods" );

function methodExist( method ) { return $container.find(					"a[href=" + JSON.stringify( method.href ) + "]" ).length > 0; }

if ( $container.length === 0 ) { $container = $( "<span class=\"tools-redirect_methods\">" ).appendTo( $parent ); }

$.each( methods, function ( idx, method ) {				if ( !methodExist( method ) ) {					self.buildLink( method ).appendTo( $container );				}			} ); },		buildSelection: function ( main, metd, mt, dsab ) { var cont = $( " " ), sele = $( "<input type=\"checkbox\"/>" ).appendTo( cont ); this.buildLink( main ).appendTo( cont ); this.addMethods( cont, metd ); sele.data( "page-title", mt ); if ( dsab ) { sele.attr( "disabled", true ); }			return cont; },		loadView: function ( reload ) { var $container = this.tabs.view.cont; this.loadTabCont( "view", function {				return this.loadRedirect( origPageName, $container, 0 );			}, reload ); },		loadCreate: function ( reload ) { this.loadTabCont( "create", function {				return this.findRedirect( origPageName );			}, reload ); },		loadRedirect: function ( pagename, container, deep, loaded ) { this.loading( container ); var self = this, deferObj = $.Deferred, top = deep ? $( "<dl>" ).appendTo( container ) : container;

if ( !loaded ) { loaded = {}; loaded[ pagename ] = true; }

function onClickFix( evt ) { /* jshint validthis: true */ var entry = $( this ).parents( "dd, p" ).first; evt.preventDefault; self.clickAction( entry, self.fix ); }

$.ajax( this.buildQuery( { action: "query", prop: "redirects", titles: pagename, rdlimit: "max" } ) ) .done( function ( data ) {					self.loaded( container );					var has_redirect = false,						desc = $( "p.desc", self.tabs.view.cont ),						maximumRedirectDepth = mw.config.get( "toolsRedirectMaximumRedirectDepth", 10 );

$.each( data.query.pages, function ( _, page ) {						if ( !( "redirects" in page ) ) {							return;						}						$.each( page.redirects, function ( _, rdpage ) { var $container, isCycleRedirect, rdtitle = rdpage.title, ultitle = rdtitle.replace( / /g, "_" ), baseuri = scriptPath + "/index.php?title=" + encodeURIComponent( ultitle ), entry = ( deep ? $( "<dd>" ) : $( " " ) ).appendTo( top ), methods = [ { href: baseuri + "&action=edit", title: self.msg.rediedit } ];							isCycleRedirect = rdtitle in loaded; loaded[ rdtitle ] = true; if ( !isCycleRedirect && deep ) { methods.push( {									href: "#fix-redirect",									title: self.msg.tabviewfix,									click: onClickFix								} ); }							$container = self.buildSelection(								{ href: baseuri + "&redirect=no", title: rdtitle },								methods, ultitle, !deep ).appendTo( entry ); if ( isCycleRedirect ) { $container.append(									"<span class=\"error\">" + self.msg.errcycleredirect + " " ); } else if ( deep < maximumRedirectDepth ) { deferObj.done( function {									return self.loadRedirect( rdtitle, entry, deep + 1, loaded );								} ); }							has_redirect = true; } );					} );

if ( has_redirect && deep === 1 ) { self.addMethods( desc, [ {							href: "#select-all",							title: self.msg.selectall,							click: function ( evt ) {								evt.preventDefault;								self.selectAll( self.tabs.view.cont );							}						}, {							href: "#select-inverse",							title: self.msg.selectinverse,							click: function ( evt ) {								evt.preventDefault;								self.selectInverse( self.tabs.view.cont );							}						}, {							href: "#fix-selected",							title: self.msg.tabviewfix,							click: function ( evt ) {								evt.preventDefault;								self.selectAction( self.tabs.view.cont, self.fix );							}						} ] ); }

if ( has_redirect ) { deferObj.resolveWith( self ); } else { deferObj.rejectWith( self ); }

} );

return deferObj.promise; },		findVariants: function ( pagename, titles ) { var self = this, suffixReg = /^.+?( \(.+?\))$/, retTitles = [], deferreds = [], simpAndTrad = { "zh-hans": true, "zh-hant": true }; $.each( this.variants, function ( _, variant ) {				var xhr = $.ajax( self.buildQuery( {					action: "parse",					page: pagename,					prop: "displaytitle",					variant: variant				} ) )					.then( function ( data ) { var title = fixNamespace( data.parse.displaytitle ); if ( variant in simpAndTrad ) { mw.toolsRedirect.setRedirectTextSuffix(								title, "\n", SUFFIX_APPEND ); }						return title; } );				if ( isCategory ) {					xhr = xhr.then( function ( origTitle ) { return $.ajax( self.buildQuery( { action: "parse", text: pagename, prop: "text", variant: variant } ) )							.then( function ( data ) {								var tmpTitle = $( data.parse.text[ "*" ] )									.text.replace( /(^\s*|\s*$)/g, "" );								// should not create redirect categories								// if the conversion is already in global table,								// or it will mess up a lot								_redirectExcludes[ tmpTitle ] = true;								return origTitle;							} ); } );				}				deferreds.push( xhr );			} ); return $.when.apply( $, deferreds ) .then( function {					var suffixes = [];

$.each( arguments, function {						var suffix,							title = this;

// find title suffix, // for example " (济南市)" to "市中区 (济南市)" suffix = suffixReg.exec( title ); if ( suffix && suffix.length === 2 ) { suffix = suffix[ 1 ]; } else { suffix = ""; }

retTitles.push( title ); suffixes.push( suffix );

} );

// append suffixes $.each( $.uniqueSort( suffixes ), function ( _, suffix ) {						$.merge( retTitles, $.map( titles, function ( title ) {							title = fixNamespace( title );							return suffixReg.test( title ) ? title : title + suffix;						} ) );					} );

return self.findNotExists( $.uniqueSort( retTitles ) ); } );		},

findNotExists: function ( titles ) { var self = this, deferreds = [], alltitles = [], excludes = [ "用字模式" ]; titles = titles.join( "|" );

$.each( [ "zh-hans", "zh-hant" ], function ( idx, variant ) {				deferreds.push( $.ajax( self.buildQuery( { action: "parse", text: titles, prop: "text", variant: variant } ) ) );			} );

return $.when.apply( $, deferreds ) .then( function {					$.each( arguments, function  { alltitles = alltitles.concat(							$( this[ 0 ].parse.text[ "*" ] )								.text.replace( /(^\s*|\s*$)/g, "" ).split( "|" )						); } );					alltitles = alltitles.filter( function ( v, i, arr ) { return arr.indexOf( v ) === i;					} );					alltitles = alltitles.join( "|" );					return $.ajax( self.buildQuery( {						action: "query",						prop: "info",						titles: alltitles					} ) )						.then( function ( data ) { titles = []; $.each( data.query.pages, function ( pageid, page ) {								var title = page.title;								if ( pageid < 0 && excludes.indexOf( title ) === -1 ) {

if ( title in _redirectExcludes ) { // exclude special titles return; }

titles.push( title );

if ( isCategory ) { var target = origPageName.replace( /^Category:/, "" ); mw.toolsRedirect.setRedirectTextSuffix(											title,											"\n".replace( "$1", target )										); }

// only set default suffix mw.toolsRedirect.setRedirectTextSuffix(										title, "\n", SUFFIX_SETDEFAULT ); }							} );							return titles;						} ); } );		},

findRedirect: function ( pagename ) { var self = this, titles = [], frcDeferreds = [], container = this.tabs.create.cont, $content = $( "#mw-content-text > div.mw-parser-output" ), deferObj = $.Deferred; this.loading( container );

$.each( _findRedirectCallbacks, function ( _, callback ) {				var ret = callback( pagename, $content, titles );				if ( typeof ret === "string" ) {					titles.push( ret );				} else if ( "done" in ret ) {					// is Deferred					frcDeferreds.push( ret );				} else {					$.merge( titles, ret );				}			} );

// remove all empty titles titles = $.map( titles, function ( title ) {				return title || null;			} );

function onClickCreate( evt ) { /* jshint validthis: true */ var entry = $( this ).parents( "p:first" ); evt.preventDefault; self.clickAction( entry, self.create ); }

// handles the deferred callbacks $.when.apply( $, frcDeferreds ) .then( function {					$.each( arguments, function ( _, ret ) { if ( typeof ret === "string" ) { titles.push( ret ); } else { $.merge( titles, ret ); }					} );					return self.findVariants( pagename, titles );				} ) .done( function ( titles ) {				// build HTML					self.loaded( container );					$.each( titles, function ( _, title ) { var ultitle = title.replace( " ", "_" ), baseuri = scriptPath + "/index.php?title=" + encodeURIComponent( ultitle ), entry = $( " " ).appendTo( container ); self.buildSelection(							{								href: baseuri + "&action=edit&redlink=1",								title: title,								classname: "new"							},							[ {								href: "#create-redirect",								title: self.msg.tabcreatetitle,								click: onClickCreate							} ],							ultitle, false						).appendTo( entry ); } );					var desc = $( "p.desc", container );					if ( titles.length > 0 ) {						self							.addMethods( desc, [ { href: "#select-all", title: self.msg.selectall, click: function ( evt ) { evt.preventDefault; self.selectAll( container ); }							}, {								href: "#select-inverse", title: self.msg.selectinverse, click: function ( evt ) { evt.preventDefault; self.selectInverse( container ); }							}, {								href: "#create-selected", title: self.msg.tabcreatetitle, click: function ( evt ) { evt.preventDefault; self.selectAction( container, self.create ); }							} ] );						deferObj.resolveWith( self, [ titles ] );					} else {						deferObj.rejectWith( self, [ titles ] );					}				} );

return deferObj.promise; },		buildQuery: function ( data ) { var query = { url: scriptPath + "/api.php", dataType: "json", type: "POST" }; query.data = data; query.data.format = "json"; return query; }	};

$.when(		mw.loader.getScript( "/w/index.php?title=User:Kethyga/Gadget-ToolsRedirect-msg-zh-hans.js&action=raw&ctype=text/javascript" ),		$.ready	).done( function {		_TR.msg = window.tools_redirect_msg;		_TR.init;		mw.toolsRedirect.findRedirectBySelector( 'div#mw-content-text p > b' );		mw.toolsRedirect.findRedirectBySelector( 'div#mw-content-text p > i > b' );	} ); }( jQuery ) ); //