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)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
* ListGroupMembers
*
* A script that loads a list of all users in
* canonical (and custom) user groups.
*
* @author Ultimate Dark Carnage
* @version 1.7
**/
( function( window, $, mw ){
"use strict";
if ( window._LGMLoaded ) return;
window._LGMLoaded = true;
// Creates a utility no-op function
function noop( ) { }
// Seed generator
function seed( s ) {
const m = 0xffffffff;
var w = ( 123456789 + s ) & m, z = ( 987654321 - s ) & m;
return function h( ) {
z = ( 36969 * ( z & 65535 ) + ( z >>> 16 ) ) & m;
w = ( 18000 * ( w & 65535 ) + ( w >>> 16 ) ) & m;
var r = ( ( z << 16 ) + ( w & 65535 ) ) >>> 0;
r /= 4294967296;
return r;
};
}
// Creating the defaults function
function defaults( target ) {
const args = Array.from( arguments ).slice( 1 );
args.forEach( function( currentObject ) {
const keys = currentObject === Object( currentObject ) ?
Object.keys( currentObject ) : [ ];
keys.forEach( function( key ) {
if ( target[ key ] !== void 0 ) return;
target[ key ] = currentObject[ key ];
} );
} );
return target;
}
// Escaping RegExp characters
function regesc( s ) {
return s.replace( /[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g, "\\$&" );
}
// Creates a new object
function createObject( object ) {
return new Proxy( object , {
get: function( target, property ) {
if ( !target.hasOwnProperty( property ) ) return null;
return target[ property ];
}
} );
}
// Creates proxy traps for the ListGroupMembers object
const lgmTraps = Object.freeze( {
get: function( target, property ) {
return target[ property ] !== undefined ? target[ property ] : null;
}
} );
// Creates the primary object
const listGroupMembers = new Proxy( {
// MediaWiki configuration variables
mwConfig: mw.config.get( ),
// The current script name
name: "ListGroupMembers",
// The seed number for randomizing numbers
seed: 0,
// The founding date
foundingDate: "October 18, 2004",
// The current user group order
order: Object.freeze( [
"staff",
"wiki-specialist",
"soap",
"global-discussions-moderator",
"voldev",
"bureaucrat",
"sysop",
"threadmoderator",
"content-moderator",
"rollback",
"bot"
] ),
// The current user group registry
registry: Object.freeze( {
GLOBAL: Object.freeze( [
"staff",
"wiki-specialist",
"soap",
"global-discussions-moderator",
"voldev"
] ),
LOCAL: Object.freeze( [
"bureaucrat",
"sysop",
"threadmoderator",
"content-moderator",
"rollback",
"bot"
] )
} ),
// Creates the user group aliases
aliases: Object.freeze( {
"staff": [ "fandom-staff" ],
"wiki-specialist": [ "content-team-member" ],
"soap": [ "grasp", "vstf" ],
"bureaucrat": [ "bcrat", "crat" ],
"sysop": [ "admin" ],
"rollback": [ "rollbacker" ],
"threadmoderator": [ "discussions-moderator", "discussion-moderator" ]
} ),
// The default configuration objects for the script
defaults: Object.freeze( {
// The user groups to include
include: [ ],
// The user groups to exclude
exclude: [ ],
// Determines whether the list is separated into user groups
grouped: true,
// Sets the default sorting algorithm
sort: "ALPHA_ASCENDING",
// Determines whether to show a search bar on the UI
search: false,
// Determines whether to use the registry
useRegistry: false,
// Sets the delay for loading the script
delay: 30 * 1000,
// Sets the view of the UI
type: "card",
// An array of user actions
actions: [ ]
} ),
// Lists the sorting algoritms
sort: Object.freeze( {
ALPHA_ASCENDING: function( a, b ) {
return a.name.localeCompare( b.name );
},
ALPHA_DESCENDING: function( a, b ) {
return -a.name.localeCompare( b.name );
},
ALPHA: "ALPHA_ASCENDING",
ALPHA_ASC: "ALPHA_ASCENDING",
ALPHA_DESC: "ALPHA_DESCENDING",
REGISTRATION_ASCENDING: function( a, b ) {
return a.registration - b.registration;
},
REGISTRATION_DESCENDING: function( a, b ) {
return b.registration - a.registration;
},
REGISTRATION: "REGISTRATION_ASCENDING",
REGISTRATION_ASC: "REGISTRATION_ASCENDING",
REGISTRATION_DESC: "REGISTRATION_DESCENDING",
GROUP_ASCENDING: function( a, b ) {
if ( this.options.grouped ) return this.sort.ALPHA_ASCENDING.apply( this, [ a, b ] );
const aGroup = this.order.indexOf( a.group );
const bGroup = this.order.indexOf( b.group );
return aGroup - bGroup;
},
GROUP_DESCENDING: function( a, b ) {
if ( this.options.grouped ) return this.sort.ALPHA_ASCENDING.apply( this, [ a, b ] );
const aGroup = this.order.indexOf( a.group );
const bGroup = this.order.indexOf( b.group );
return bGroup - aGroup;
},
GROUP: "GROUP_ASCENDING",
GROUP_ASC: "GROUP_ASCENDING",
GROUP_DESC: "GROUP_DESCENDING",
RANDOM: function( ) {
return ( Math.random( ) - 0.5 ) * 2;
},
RANDOM_SEED: function( ) {
const seedFn = seed( this.seed++ );
return ( seedFn( ) - 0.5 ) * 2;
},
DEFAULT: "ALPHA_ASCENDING"
} ),
// The user cache
cache: new Map( ),
// The user list
users: createObject( { } ),
// The state object
state: createObject( {
resourcesLoaded: false,
messagesLoaded: false,
matchesPage: false,
globalUsersLoaded: false,
localUsersLoaded: false,
created: false,
// The object that determines how the UI is rendered
renderOptions: createObject( {
loaded: false,
sort: null
} ),
rendered: false,
renderInterval: null,
linkInToolbar: false
} ),
// Scripts and modules
scripts: Object.freeze( {
i18n: "u:dev:MediaWiki:I18n-js/code.js",
colors: "u:dev:MediaWiki:Colors/code.js",
wds: "u:dev:MediaWiki:WDSIcons/code.js",
dorui: "u:dev:MediaWikki:UI-js/code.js"
} ),
// Stylesheets
stylesheets: Object.freeze( [
"u:dev:MediaWiki:ListGroupMembers.css"
] ),
// MediaWiki dependencies
dependencies: Object.freeze( [
"mediawiki.util",
"mediawiki.api",
"mediawiki.Title"
] ),
// Fetches the sort function
getSort: function( name ) {
if ( arguments.length === 0 ) name = this.sort.DEFAULT;
if ( typeof name === "function" ) return name;
const sort = $.extend( { }, this.sort );
const finalName = String( name ).toUpperCase( );
const sortKeys = Object.keys( sort );
if ( !sortKeys.includes( finalName ) ) return noop;
const value = sort[ finalName ];
if ( typeof value === "function" ) return value;
if ( typeof value !== "string" ) return noop;
const finalSortFn = sort[ value ];
return finalSortFn;
},
// Fetches the canonical user group
getGroup: function( group ) {
const registry = arguments.length > 1 ? arguments[ 1 ] : this.order;
const aliases = $.extend( { }, this.aliases );
if ( !Array.isArray( registry ) ) return this.getGroupFromRegistry( group, registry );
const isCanonicalGroup = registry.includes( group );
if ( isCanonicalGroup ) return group;
const isAlias = registry.some( function( targetGroup ) {
return aliases.hasOwnProperty( targetGroup ) && aliases[ targetGroup ].includes( group );
}, this );
if ( isAlias ) return registry.find( function( targetGroup ) {
return aliases[ targetGroup ].includes( group );
} );
return group;
},
// Fetches the canonical user group from the registry object
getGroupFromRegistry: function( group, registry ) {
const aliases = $.extend( { }, this.aliases );
const keys = Object.keys( registry );
if ( keys.length === 0 ) return group;
while ( keys.length ) {
const groupType = keys.shift( );
const subRegistry = registry[ groupType ];
if ( subRegistry.includes( group ) ) return group;
}
const aliasKeys = Object.keys( aliases );
while ( aliasKeys.length ) {
const targetGroup = aliasKeys.shift( );
const aliasArray = aliases[ targetGroup ];
if ( aliasArray.includes( group ) ) return targetGroup;
}
return null;
},
// Fetches the group type
getGroupType: function( group ) {
const registry = $.extend( { }, this.registry ), types = Object.keys( registry );
return types.find( function( type ) {
const groups = registry[ type ];
return groups.includes( group );
}, this ) || "other";
},
// Orders all user groups
orderGroups: function( a, b ) {
return this.order.indexOf( a ) - this.order.indexOf( b );
},
// Loads all resources
load: function( callback ) {
const user = this.mwConfig.wgUserName;
const userStylesheet = user + "/lgm.css", userScript = user + "/lgm.js";
const importedScripts = Object.keys( this.scripts ).map( function( scriptId ) {
const scriptName = this.scripts[ scriptId ];
if ( window.dev.hasOwnProperty( scriptId ) ) return Promise.resolve( window.dev[ scriptId ] );
return importArticle( { type: "script", article: scriptName } );
}, this ).concat( importArticle( { type: "script", article: userScript } ) );
const importedStylesheets = Array.from( this.stylesheets.concat( userStylesheet ) ).map( function( stylesheet ) {
return importArticle( { type: "style", article: stylesheet } );
}, this );
const resources = [ mw.loader.using( this.dependencies ) ].concat( importedScripts, importedStylesheets );
return Promise.all( resources ).then( callback );
},
// Starts the script
start: function( ) {
return this.load( this.loadMessages.bind( this ) );
},
// Loads all messages
loadMessages: function( ) {
this.state.resourcesLoaded = true;
this.i18no = window.dev.i18n;
this.colors = window.dev.colors;
this.wds = window.dev.wds;
this.ui = window.dev.dorui;
// return this.i18no.loadMessages( this.name ).then( this.init.bind( this ) );
return this.i18no.loadMessages( this.name ).then( this.check.bind( this ) );
},
// Fetches the message
msg: function( key ) {
if ( !this.i18n ) return null;
const i18n = this.i18n;
function createPlainMessage( args ) {
return i18n.msg.apply( i18n, args ).plain( );
}
return createObject( {
plain: function( ) {
const a = [ key ].concat( Array.from( arguments ) );
return createPlainMessage( a );
},
escape: function( ) {
const a = [ key ].concat( Array.from( arguments ) );
return i18n.msg.apply( i18n, a ).escape( );
},
parse: function( ) {
const a = [ key ].concat( Array.from( arguments ) );
return i18n.msg.apply( i18n, a ).escape( );
},
encode: function( ) {
const a = [ key ].concat( Array.from( arguments ) );
const msg = createPlainMessage( a );
return encodeURIComponent( msg );
},
decode: function( ) {
const a = [ key ].concat( Array.from( arguments ) );
const msg = createPlainMessage( a );
return decodeURIComponent( msg );
},
replace: function( replacer ) {
const msg = createPlainMessage( [ key ] );
return Object.keys( replacer ).reduce( function( result, key ) {
return result.replace( new RegExp( "\\$" + regesc( key ), "g" ), replacer[ key ] );
}, msg );
},
toString: function( ) {
return this.plain( );
},
exists: i18n.msg( key ).exists
} );
},
// Checks if the page is correct
check: function( i18n ) {
this.i18n = i18n;
if ( this.mwConfig.wgCanonicalNamespace.toLowerCase( ) !== "special" ) return this.generateLink( i18n );
const isBlankPage = this.mwConfig.wgCanonicalSpecialPageName && this.mwConfig.wgCanonicalSpecialPageName.toLowerCase( ) === "blankpage";
const titleFallback = this.name, titleExists = this.msg( "page" ).exists;
const title = titleExists ? this.msg( "page" ).escape( ) : titleFallback;
const pageTitle = this.mwConfig.wgTitle.split( "/" )[ Number( isBlankPage ) ];
if ( pageTitle !== title ) return this.generateLink( );
this.state.matchesPage = true;
return this.init( );
},
// Initializes the script
init: function( ) {
this.state.messagesLoaded = true;
this.groups = Array.from( this.order );
this.options = defaults( $.extend( { }, window.LGMOptions ), this.defaults );
if ( Array.isArray( this.options.exclude ) && this.options.exclude.length ) {
this.groups = this.groups.filter( function( group ) {
return !this.options.exclude.some( function( excluded ) {
if ( excluded instanceof RegExp ) return excluded.test( group );
return excluded === group;
} );
}, this );
this.options.includes = [ ].concat( this.options.include || [ ] ).filter( function( group ) {
return !this.options.exclude.includes( group );
}, this );
}
if ( Array.isArray( this.options.include ) && this.options.include.length ) {
this.groups = this.groups.concat( this.options.includes ).filter( function( group ) {
return typeof group === "string";
} );
}
this.groups = Array.from( new Set( this.groups ) )
.map( this.getGroup, this )
.sort( this.orderGroups.bind( this ) );
if ( this.options.grouped ) {
if ( this.options.useRegistry ) {
this.users = Object.keys( this.registry ).reduce( function( target, type ) {
target[ type ] = this.registry[ type ].reduce( function( users, group ) {
users[ group ] = [ ];
return users;
}.bind( this ), { } );
return target;
}.bind( this ), { } );
} else {
this.users = this.groups.reduce( function( users, group ) {
users[ group ] = [ ];
return users;
}.bind( this ), { } );
}
}
this.state.renderOptions.groups = this.groups;
this.state.renderOptions.search = this.options.search;
this.state.renderOptions.actions = this.options.actions;
this.state.renderOptions.type = this.options.type;
this.state.renderOptions.sort = this.getSort( this.options.sort );
return this.create( );
},
// Utility function to load all global users
loadGlobalUsers: function( groups ) {
const globalGroups = groups.filter( function( group ) {
return this.registry.GLOBAL.includes( group );
}, this );
return this.loadGlobalUserPage( globalGroups )
.then( this.parseUsers.bind( this ) );
},
// Utility function to load the global user page
loadGlobalUserPage: function( groups ) {
const params = createObject( groups.reduce( function( target, group, index ) {
target[ "groups[" + index + "]" ] = group;
return target;
}, { } ) );
const listGlobalUsersPage = this.mwConfig.wgFormattedNamespaces[ "-1" ] + ":" + /* this.msg( "global-users" ).escape( ) */ "ListGlobalUsers";
return $.get( mw.util.getUrl( listGlobalUsersPage, params ) );
},
// Utility function to load all local users
loadLocalUsers: function( groups ) {
const localGroups = groups.filter( function( group ) {
return !this.registry.GLOBAL.includes( group );
}, this );
return Promise.all( localGroups.map( this.loadUsers, this ) );
},
// Utility function to load all users from a group
loadUsers: function( group ) {
return ( new mw.Api( ) ).get( {
format: "json",
action: "query",
list: "allusers",
augroup: group.split( "|" )[ 0 ],
aulimit: "max"
} ).then( this.handleUsers.bind( this, group ) );
},
// Utility function to load user data
loadUserData: function( user ) {
return $.get( this.mwConfig.wgScriptPath + "/wikia.php", {
controller: "UserProfile",
method: "getUserData",
format: "json",
userId: user.userid
} ).then( this.handleUserData.bind( this, user ) );
},
// Utility function to parse user data
parseUsers: function( response ) {
const $wrapper = $( response );
const fetchAllUsers = $wrapper.find( ".list-global-users-members > li" )
.map( this.parseUser.bind( this ) )
.toArray( );
return Promise.all( fetchAllUsers );
},
// Utility function to parse an individual user
parseUser: function( index, target ) {
const $target = $( target );
const $groups = $target.contents( ).filter( function( i, item ) {
return item.nodeType === 3 && !item.nextSibling;
} );
const name = $target.find( "bdi" ).text( );
const groups = $groups.text( ).trim( )
.slice( 1, -1 ).split( ", " )
.filter( function( group ) {
return this.groups.includes( group );
}, this )
.sort( this.orderGroups.bind( this ) );
const user = createObject( { name: name, group: groups[ 0 ] } );
return this.parseUserData( user ).then( this.handleGlobalUser.bind( this, user ) );
},
// Utility function to parse data from a user
parseUserData: function( user ) {
return ( new mw.Api( ) ).get( {
action: "query",
list: "users",
ususers: user.name,
format: "json"
} );
},
// Handles the global user
handleGlobalUser: function( user, response ) {
const userObject = response.query.users[ 0 ];
user.userid = userObject.userid;
return this.loadUserData( user );
},
// Handles the user list
handleUsers: function( group, response ) {
const query = response.query;
if ( query.allusers.length >= 500 ) return [ ];
const users = Array.from( query.allusers ).map( function( user ) {
return createObject( { name: user.name, group: group, userid: user.userid } );
} );
return Promise.all( users.map( this.loadUserData, this ) );
},
// Handles user data
handleUserData: function( user, response ) {
const userData = response.userData;
user.registration = new Date( userData.registration || this.foundingDate );
user.edits = parseInt( String( userData.edits ).split( "," ).join( "" ) );
user.avatar = userData.avatar;
user.groups = [ user.group ];
if ( this.cache.has( user.name ) ) {
const target = this.cache.get( user.name );
if ( this.options.grouped ) {
const originalGroup = target.groups[ 0 ];
if ( !this.useRegistry ) {
const index = this.users[ originalGroup ].findIndex( function( u ) {
return u.name === user.name;
} );
this.users[ originalGroup ][ index ].groups.push( user.group );
} else {
const type = this.getGroupType( originalGroup );
const index = this.users[ type ][ originalGroup ].findIndex( function( u ) {
return u.name === user.name;
} );
this.users[ type ][ originalGroup ][ index ].groups.push( group );
}
} else {
const index = this.users.findIndex( function( u ) {
return u.name === user.name;
} );
this.users[ index ].groups.push( user.group );
target.groups.push( user.group );
}
this.cache.set( user.name, target );
return target;
}
if ( this.options.grouped ) {
const type = this.getGroupType( user.group );
if ( this.options.useRegistry ) {
this.users[ type ][ user.group ].push( user );
} else this.users[ user.group ].push( user );
} else this.users.push( user );
this.cache.set( user.name, user );
return user;
},
// Creates the list
create: function( ) {
// Sets the document title to loading
document.title = "Loading all group members...";
// Step 1: Load all global users
return this.loadGlobalUsers( this.groups )
// Step 2: Load all local users
.then( this.loadLocalUsers.bind( this, this.groups ) )
// Step 3: Render the list
.then( this.renderList.bind( this ) );
},
renderList: function( ) {
this.state.globalUsersLoaded = true;
this.state.localUsersLoaded = true;
this.wrapper = this.ui.div( {
classes: [ "list-group-members-wrapper", "lgm-wrapper" ],
id: "lgm-wrapper",
children: [
// this.renderHeader( ),
this.ui.section( {
classes: [ "list-group-members-list-wrapper", "lgm-list-wrapper" ],
id: "lgm-list-wrapper",
child: this.renderContent( )
} )
]
} );
mw.hook( "wikipage.content" ).add( function( $content ) {
const el = $content.get( 0 );
el.innerHTML = "";
el.insertAdjacentElement( "afterbegin", this.wrapper );
}.bind( this ) );
this.state.created = true;
this.state.renderInterval = setTimeout(
this.loadUI.bind( this ),
this.options.delay || 30 * 1000
);
},
loadUI: function( ) {
clearTimeout( this.state.renderInterval );
if ( this.state.renderOptions.rendered ) return;
this.state.renderOptions.rendered = true;
const content = this.wrapper.querySelector( ".list-group-members" );
content.classList.remove( "lgm-loading" );
const allUsers = content.querySelectorAll( ".list-group-members-user" );
allUsers.forEach( this.loadUserUI, this );
document.title = this.msg( "doc-title" ).replace( {
"SITENAME": this.mwConfig.wgSiteName
} );
},
loadUserUI: function( userElement ) {
const username = userElement.dataset.user;
const user = this.cache.get( username );
const avatarElement = this.ui.img( {
classes: [ "list-group-members-avatar" ],
src: user.avatar
} );
const avatarWrapper = userElement.querySelector( ".list-group-members-user-avatar-wrapper" );
avatarWrapper.insertAdjacentElement( "afterbegin", avatarElement );
const userHeading = userElement.querySelector( ".list-group-members-user-heading" );
const userLink = this.ui.a( {
href: mw.util.getUrl( "User:" + user.name ),
text: user.name
} );
userHeading.insertAdjacentElement( "afterbegin", userLink );
const userGroup = userElement.querySelector( ".list-group-members-user-group" );
userGroup.textContent = this.msg( user.group ).escape( );
const content = userElement.querySelector( ".list-group-members-user-content" );
content.insertAdjacentElement( "afterbegin", this.ui.ul( {
classes: [ "list-group-members-user-info", "lgm-user-info" ],
children: [
user.groups.length > 1 && this.ui.li( {
classes: [ "list-group-members-user-groups", "lgm-user-groups", "lgm-section" ],
children: [
this.ui.div( {
classes: [ "lgm-section-title" ],
text: "Groups:"
} ),
this.ui.div( {
classes: [ "lgm-section-content" ],
text: user.groups
.map( this.getGroup, this )
.join( ", " )
} )
]
} ),
this.ui.li( {
classes: [ "list-group-members-user-registration", "lgm-user-registration" ],
children: [ ]
} ),
this.ui.li( { } )
]
} ) );
},
renderHeader: function( ) {
this.search = this.renderControl( {
label: "Search",
type: "text",
attrs: {
id: "lgm-search",
classes: [ "lgm-search", "list-group-members-search" ],
events: {
input: this.changeValue.bind( this )
}
}
} );
this.header = this.ui.header( {
classes: [ "list-group-members-header", "lgm-header" ],
id: "lgm-header",
child: this.ui.div( {
classes: [ "list-group-members-header-wrapper", "lgm-wrapper" ],
attrs: {
"data-state": "search"
},
children: [
this.search
]
} )
} );
return this.header;
},
changeValue: function( event ) {
const target = event.target;
const tagName = event.target.tagName.toLowerCase( );
const type = tagName === "input" ? event.target.type : tagName;
const options = createObject( { type: type, target: target } );
return this.changeRenderState( options );
},
changeRenderState: function( options ) {
const checkTypes = Object.freeze( [ "radio", "checkbox" ] );
const isCheck = checkTypes.includes( options.type );
const isSelect = options.type === "select";
const target = options.target;
const state = target.getAttribute( "data-state" );
if ( isSelect ) {
const selectedIndex = target.selectedIndex;
const options = target.options;
const value = options[ selectedIndex ].value;
this.state.renderOptions[ state ] = value;
}
else if ( isCheck ) this.state.renderOptions[ state ] = Boolean( target.checked );
else this.state.renderOptions[ state ] = target.value;
// return this.rerender( );
},
renderControl: function( options ) {
const inputTypes = Object.freeze( [
"text",
"radio",
"checkbox",
"email",
"search",
"tel",
"url",
"number",
"color",
"range",
"datetime-local",
"month",
"week",
"time",
"date"
] );
const controlTypes = Object.freeze( [
"select",
"option",
"optgroup",
"textarea"
] );
const type = options.type;
const classes = [ "lgm-form-control-el" ].concat( options.attr.classes );
delete options.attr.classes;
if ( inputTypes.includes( type ) ) return this.ui.frag( [
options.label && options.attr.id && this.ui.label( { "for": options.attr.id, text: options.label, classes: [ "list-group-members-label", "lgm-label" ] } ),
this.ui.input( $.extend( { type: type, classez: classes } ), options.attr )
] );
if ( controlTypes.includes( type ) ) return this.ui.frag( [
options.label && options.attr.id && this.ui.label( { "for": options.attr.id, text: options.label, classes: [ "list-group-members-label", "lgm-label" ] } ),
this.ui[ type ]( $.extend( { classes: classes }, options.attr ) )
] );
return false;
},
renderContent: function( ) {
return this.ui.nav( {
classes: {
"list-group-members": true,
"lgm-list": true,
"lgm-loading": true,
"lgm-grouped": this.options.grouped,
"lgm-use-type": this.options.useRegistry
},
id: "lgm-list",
attrs: {
"data-list-type": this.getDataListType( )
},
child: this.renderChild( )
} );
},
getDataListType: function( ) {
if ( !this.options.grouped ) return "allusers";
return this.options.useRegistry ? "type" : "grouped";
},
renderChild: function( ) {
if ( !this.options.grouped ) return this.renderAllUsers( );
return this.options.useRegistry ? this.renderTypes( ) : this.renderGroups( );
},
renderAllUsers: function( ) {
return this.ui.ul( {
classes: [ "list-group-members-users", "lgm-users" ],
id: "lgm-users",
children: this.users
.sort( this.state.renderOptions.sort.bind( this ) )
.map( this.renderUser, this )
} );
},
renderGroups: function( ) {
return this.ui.frag( Object.keys( this.users ).map( this.renderUsersByGroup, this ) );
},
renderUsersByGroup: function( group ) {
return this.users[ group ] && this.users[ group ].length && this.ui.section( {
classes: [ "list-group-members-group", "lgm-group" ],
attrs: {
"data-group": group
},
children: [
this.ui.header( {
classes: [ "list-group-members-group-header", "lgm-group-header" ],
child: this.ui.h3( {
text: this.msg( group ).escape( )
} )
} ),
this.ui.ul( {
classes: [ "list-group-members-users", "lgm-users" ],
id: "lgm-users",
children: this.users[ group ]
.sort( this.state.renderOptions.sort.bind( this ) )
.map( this.renderUser, this )
} )
]
} );
},
renderTypes: function( ) {
return this.ui.frag( Object.keys( this.users ).map( this.renderUsersByType, this ) );
},
renderUsersByType: function( type ) {
return Object.keys( this.users[ type ] ).length && this.ui.div( {
classes: [ "list-group-members-type-section", "lgm-type-section" ],
attrs: {
"data-type": type.toLowerCase( )
},
children: [
this.ui.header( {
classes: [ "list-group-members-type-header", "lgm-type-header" ],
child: this.ui.h2( {
text: this.msg( type ).escape( )
} )
} ),
this.ui.div( {
classes: [ "list-group-members-type-inner", "lgm-type-inner" ],
children: Object.keys( this.users[ type ] ).map( this.renderUsersFromType.bind( this, type ) )
} )
]
} );
},
renderUsersFromType: function( type, group ) {
return this.users[ type ][ group ] && this.users[ type ][ group ].length && this.ui.section( {
classes: [ "list-group-members-group", "lgm-group" ],
attrs: {
"data-group": group
},
children: [
this.ui.header( {
classes: [ "list-group-members-group-header", "lgm-group-header" ],
child: this.ui.h3( {
text: this.msg( group ).escape( )
} )
} ),
this.ui.ul( {
classes: [ "list-group-members-users", "lgm-users" ],
id: "lgm-users",
children: this.users[ type ][ group ].sort( this.state.renderOptions.sort.bind( this ) ).map( this.renderUser, this )
} )
]
} );
},
renderUser: function( user ) {
return this.ui.li( {
classes: [ "list-group-members-user", "lgm-user" ],
attrs: {
"data-user-id": user.userid,
"data-user": user.name
},
id: "list-group-members-user-" + user.userid,
children: [
this.ui.header( {
classes: [ "list-group-members-user-header", "lgm-user-header" ],
children: [
this.ui.div( {
classes: [ "list-group-members-user-avatar-wrapper", "lgm-user-avatar-wrapper" ]
} ),
this.ui.div( {
classes: [ "list-group-members-user-details", "lgm-user-details" ],
children: [
this.ui.h4( {
classes: [ "list-group-members-user-heading", "lgm-user-heading" ]
} ),
this.ui.bdi( {
classes: [ "list-group-members-user-group", "lgm-user-group" ]
} )
]
} )
]
} ),
this.ui.div( {
classes: [ "list-group-members-user-content" ]
} )
]
} );
},
rerender: function( ) { },
generateLink: function( ) {
this.state.messagesLoaded = true;
const toolbar = document.querySelector( ".wikia-bar .toolbar .tools" );
if ( !toolbar || this.state.linkInToolbar || this.state.matchesPage ) return;
this.state.linkInToolbar = true;
toolbar.insertAdjacentElement( "afterbegin", this.ui.li( {
classes: [ "overflow" ],
child: this.ui.a( {
href: mw.util.getUrl( this.mwConfig.wgFormattedNamespaces[ -1 ] + ":" + this.msg( "page" ).parse( ) ),
text: this.msg( "page-title" ).escape( )
} )
} ) );
}
}, lgmTraps );
window.LGM = listGroupMembers;
mw.hook( "list-group-members.start" ).fire( window.LGM.start( ) );
mw.hook( "list-group-members" ).fire( window.LGM );
} )( window, jQuery, mediaWiki );