User:Jdlrobson/rl.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
![]() | Documentation for this user script can be added at User:Jdlrobson/rl. |
mw.loader.implement( "ext.gadget.readinglist@", {
"main": "resources/ext.gadget.readinglist/gadget.js",
"files": {
"resources/ext.gadget.readinglist/gadget.js": function ( require, module, exports ) {
/**
* When re-generating this file use:
* http://localhost:8888/w/load.php?modules=ext.gadget.readinglist&debug=true
*/
// <nowiki>
const { showOverlay, createMembersOverlay, editListOverlay } = require( './overlays.js');
const { getCollectionsWithMembership, fromBase64, saveReadingList,
deleteCollection, getReadingListUrl,
addToList, toBase64 } = require( './api.js' );
const READING_LIST_URL = getReadingListUrl( mw.user.getName() );
(function () {
if ( mw.user.isAnon() ) {
return;
}
const mwuibutton = ( text, className ) => {
const editBtn = document.createElement('button');
editBtn.classList.add( 'mw-ui-button', className );
editBtn.textContent = text;
return editBtn;
};
function addImportFromTable() {
Array.from( document.querySelectorAll('.wikitable') )
.filter((node) => node.querySelectorAll('a[title]').length > 4)
.forEach((table) => {
const titles = {
[window.location.host]: Array.from(table.querySelectorAll('a[title]')).map((link) => link.getAttribute('title'))
};
const link = document.createElement('a');
link.textContent = 'Import into list';
const row = document.createElement('tr');
const col = document.createElement('td');
col.setAttribute('colspan', table.querySelectorAll('thead').length);
table.appendChild(row);
const base64 = toBase64(`Imported from tablez on ${mw.config.get('wgTitle')}`, '', titles);
link.setAttribute('href', `${READING_LIST_URL}?limport=${base64}`);
col.appendChild(link);
table.appendChild(col);
} );
}
function addToUserMenu() {
mw.util.addPortletLink( 'p-personal', READING_LIST_URL, 'My reading lists', 'pt-readinglists', null, 'l', 'pt-watchlist' );
}
function addBookmarkNextToWatchAction() {
let link = mw.util.addPortletLink('p-views', '#', 'Bookmark', 'pt-bookmark');
if ( !link ) {
link = mw.util.addPortletLink('page-actions', '#', 'Bookmark', 'pt-bookmark' );
}
if (!link ) {
return;
}
// Hack: no native support for vector icons.
setTimeout( () => {
link.classList.remove( 'vector-tab-noicon' );
}, 300 );
link.querySelector( 'a' ).classList.add(
'mw-ui-icon',
'mw-ui-button',
'mw-ui-quiet',
'mw-ui-icon-element',
'mw-ui-icon-bookmark',
'mw-ui-icon-small'
)
link.addEventListener( 'click', () => {
const title = mw.config.get( 'wgPageName' );
showOverlay(
getCollectionsWithMembership( mw.user.getName(), title ).then(( collections ) => {
return createMembersOverlay(
title,
collections
);
} )
);
} );
}
const isSpecialReadingList = mw.config.get( 'wgTitle').indexOf('ReadingListz/') > -1 ||
(mw.config.get( 'wgTitle').indexOf('ReadingList/') > -1 && !location.host.match(/(mediawiki.org|meta.wikimedia.org)/) );
function editList() {
const pathSplit = window.location.pathname.split('/');
const id = parseInt(pathSplit[4],10);
const h1 = document.querySelector('.readinglist-collection-summary h1');
const desc = document.querySelector('.readinglist-collection-description');
const title = h1 ? h1.textContent : '';
const description = desc ? desc.textContent : '';
showOverlay(
Promise.resolve(
editListOverlay( id, title, description, null, () => {
window.location.pathname = `${READING_LIST_URL}/${id}?updated=${new Date()}`;
} )
)
);
}
function createList() {
showOverlay(
Promise.resolve(
editListOverlay( null, '', '', null, () => {
window.location.pathname = `${READING_LIST_URL}/?updated=${new Date()}`;
} )
)
);
}
function deleteList() {
const pathSplit = window.location.pathname.split('/');
const id = parseInt(pathSplit[4],10);
const ok = confirm('Are you sure you want to delete this list?');
if ( ok ) {
deleteCollection(id).then(() => {
mw.notify('List has been deleted.');
window.location.pathname = READING_LIST_URL;
})
}
}
// Makes Special:ReadingListz loook like Special:ReadingList
function registerTemporaryReadingListPage() {
if ( isSpecialReadingList ) {
const action = mwuibutton( '' );
const container = document.createElement('div')
container.setAttribute('id','reading-list-container')
$('#mw-content-text').html('').append(container)
$('#firstHeading').text('Reading lists');
if ( document.querySelectorAll( '.mw-portlet-associated-pages a' ).length === 0 ) {
const link = mw.util.addPortletLink('p-associated-pages', READING_LIST_URL, 'Your lists');
if ( link ) {
link.classList.add( 'vector-tab-noicon' );
}
}
mw.loader.using( 'special.readinglist.scripts' );
const callback = (mutationList, observer) => {
for (const mutation of mutationList) {
if (mutation.type === 'childList') {
action.removeEventListener('click', createList );
const pathSplit = window.location.pathname.split('/');
// create at username
if ( pathSplit.length === 4 ) {
action.removeEventListener( 'click', deleteList );
action.textContent = 'Create list';
action.addEventListener( 'click', createList );
const editBtn = document.querySelector( '.readinglist-collection-summary .rl-edit-btn' );
if ( editBtn ) {
editBtn.parentNode.removeChild(editBtn);
}
} else {
action.textContent = 'Delete list';
action.removeEventListener( 'click', createList );
action.addEventListener( 'click', deleteList );
const summaryArea = document.querySelector( '.readinglist-collection-summary' );
const existingEdit = summaryArea.querySelector('.rl-edit-btn');
if ( summaryArea && !existingEdit ) {
const editBtn = mwuibutton( 'Edit list', 'rl-edit-btn' );
editBtn.addEventListener( 'click', editList );
summaryArea.appendChild(editBtn)
}
}
}
}
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
const config = { attributes: true, childList: true, subtree: true };
observer.observe(document.querySelector('#reading-list-container'), config);
mw.util.$content[0].appendChild( action );
}
}
function importFunctionality() {
const importValue = mw.util.getParamValue('limport') || mw.util.getParamValue('lexport');
if ( importValue ) {
const btn = mwuibutton( 'Import this list!' );
mw.util.$content[0].appendChild( btn );
btn.addEventListener( 'click', () => {
const list = fromBase64( importValue );
saveReadingList( null, list.name, list.description ).then(( id ) => {
Promise.all(
Object.keys(list.list).map((project) =>
addToList( id, list.list[project], project)
)
).then(() => {
mw.notify('List successfully imported!');
window.location.pathname = READING_LIST_URL;
});
});
} );
}
}
// Can be removed when these are added to menus
addToUserMenu();
addBookmarkNextToWatchAction();
// Experimental feature for testing import feature: remove whenever necessary.
addImportFromTable();
// @todo: Can removed when $wgReadingListsWebAuthenticatedPreviews is true everywhere.
registerTemporaryReadingListPage();
importFunctionality();
}());
},
"resources/ext.gadget.readinglist/icons.json": {
"cdxIconBookmark": "\u003Cpath d=\"M5 1a2 2 0 00-2 2v16l7-5 7 5V3a2 2 0 00-2-2z\"/\u003E",
"cdxIconBookmarkOutline": "\u003Cpath d=\"M5 1a2 2 0 00-2 2v16l7-5 7 5V3a2 2 0 00-2-2zm10 14.25-5-3.5-5 3.5V3h10z\"/\u003E"
},
"resources/ext.gadget.readinglist/api.js": function ( require, module, exports ) {
const api = new mw.Api();
const DEFAULT_READING_LIST_NAME = 'Saved';
const DEFAULT_READING_LIST_DESCRIPTION = 'Default list for your saved articles.';
//
// Existing API functions
//
/**
*
* @param {number} id of list
* @return {JQuery.Promise<any>}
*/
function deleteCollection( id ) {
return api.postWithToken( 'csrf', {
action: 'readinglists',
list: id,
command: 'delete'
} );
}
/**
* @param {string} name
* @param {string} description
* @param {Object} list
* @return {string}
*/
function toBase64( name, description, list ) {
const str = JSON.stringify( { name, description, list } );
try {
return window.btoa( dataStr );
} catch ( e ) {
return window.btoa(
encodeURIComponent( str ).replace(
/%([0-9A-F]{2})/g,
function toSolidBytes( match, p1 ) {
return String.fromCharCode( '0x' + p1 );
}
)
);
}
}
/**
* Adapted from https://stackoverflow.com/questions/30106476/using-javascripts-atob-to-decode-base64-doesnt-properly-decode-utf-8-strings
*
* @param {string} data
* @return {Object}
*/
function fromBase64( data ) {
let plainData;
try {
plainData = window.atob( data );
} catch ( e ) {
plainData = decodeURIComponent( window.atob( str ).split( '' ).map( ( c ) => {
return '%' + ( '00' + c.charCodeAt( 0 ).toString( 16 ) ).slice( -2 );
} ).join( '' ) );
}
return JSON.parse( plainData );
}
/**
* Sets up the reading list feature for new users who have never used it before.
*
* @return {jQuery.Promise<any>}
* @param {string|null} id if an existing collection
* @param {string} name of list
* @param {string} description of list
* @return {JQuery.Promise<any>}
*/
function saveReadingList( id, name, description ) {
if ( id ) {
return api.postWithToken( 'csrf', {
action: 'readinglists',
list: id,
name,
description,
command: 'update'
} );
} else {
return api.postWithToken( 'csrf', {
action: 'readinglists',
name,
description,
command: 'create'
} ).then( ( result ) => result && result.create && result.create.id );
}
}
/**
* @param {string} ownerName person who owns the list
* @param {number} [id] of the list
* @param {string} [title] of the list
* @return {string}
*/
const getReadingListUrl = ( ownerName, id, title ) => {
const READING_LIST_HOST = location.host.indexOf( 'localhost' ) > -1 ?
'' : 'https://meta.wikimedia.org';
let titlePath = 'ReadingLists';
if ( ownerName ) {
titlePath += `/${ownerName}`;
}
if ( id ) {
titlePath += `/${id}`;
}
const titleWithName = title ? `${titlePath}/${encodeURIComponent( title )}` : titlePath;
try {
return READING_LIST_HOST + (
new mw.Title( titleWithName, -1 )
).getUrl();
} catch ( e ) {
// Uncaught Error: Unable to parse title
// e.g. Special:ReadingLists/1/<script>
return READING_LIST_HOST + (
new mw.Title( titlePath, -1 )
).getUrl();
}
};
/**
* Converts API response to WVUI compatible response.
*
* @param {ApiQueryResponseReadingListItem} collection from API response
* @param {string} ownerName of collection
* @return {Card} modified collection
*/
const readingListToCard = ( collection, ownerName ) => {
const description = collection.default ?
DEFAULT_READING_LIST_DESCRIPTION : collection.description;
const name = collection.default ? DEFAULT_READING_LIST_NAME : collection.name;
const url = getReadingListUrl( ownerName, collection.id, name );
return Object.assign( {}, collection, { ownerName, name, description, url } );
};
/**
*
* @param {string} ownerName (username)
* @param {number[]} marked a list of collection IDs which have a certain title
* @return {Promise<Card[]>}
*/
function getCollections( ownerName, marked ) {
return new Promise( ( resolve, reject ) => {
api.get( {
action: 'query',
format: 'json',
rldir: 'descending',
rlsort: 'updated',
meta: 'readinglists',
formatversion: 2
} ).then( function ( /** @type {ApiQueryResponseReadingLists} */ data ) {
resolve(
( data.query.readinglists || [] ).map( ( collection ) =>
readingListToCard( collection, ownerName, marked ) )
);
}, function ( /** @type {string} */ err ) {
// setup a reading list and try again.
if ( err === 'readinglists-db-error-not-set-up' ) {
setupCollections().then( () => getCollections( ownerName, marked ) )
.then( ( /** @type {Card[]} */ collections ) => resolve( collections ) );
} else {
reject( err );
}
} );
} );
}
/**
* Sets up the reading list feature for new users who have never used it before.
*
* @return {jQuery.Promise<any>}
*/
function setupCollections() {
return api.postWithToken( 'csrf', {
action: 'readinglists',
command: 'setup'
} );
}
//
// New API functions
//
/**
*
* @return {string}
*/
function getCurrentProjectName() {
// Use wgServer to avoid issues with ".m." domain
const server = mw.config.get( 'wgServer' );
if ( server.indexOf( 'wikipedia.beta.wmflabs' ) > -1 ) {
return server.replace( 'wikipedia.beta.wmflabs', 'wikipedia' ).replace( 'https://', '' );
}
return server.indexOf( '//' ) === 0 ?
window.location.protocol + server : server;
}
/**
* Return the collections belonging to ownerName collections but with isMember key.
*
* @param {string} ownerName
* @param {string} title to check for existence in those collections
* @return {Function}
*/
function getCollectionsWithMembership( ownerName, title ) {
// make sure it's an array
return getCollections( ownerName ).then((cards) => {
return api.get( {
action: 'query',
format: 'json',
meta: 'readinglists',
rldir: 'descending',
rlsort: 'updated',
rlproject: getCurrentProjectName(),
rltitle: title,
formatversion: 2
} ).then( function ( /** @type {ApiQueryResponseReadingLists} */data ) {
const marked = data.query.readinglists.map( ( collection ) => collection.id );
return cards.map( ( card ) => {
return Object.assign( card, {
marked: marked.indexOf( card.id ) > -1
} );
} );
} );
});
}
/**
* @param {number} id
* @param {string|array} titleOrTitles
* @param {string} [projectName]
* @return {JQuery.Promise<any>}
*/
function addToList( id, titleOrTitles, projectName ) {
let project = projectName || getCurrentProjectName();
const title = typeof titleOrTitles === 'string' ? titleOrTitles : undefined;
const batch = typeof titleOrTitles !== 'string' ? JSON.stringify(
titleOrTitles.map(
( title ) => ( {
title,
project
} )
)
) : undefined;
project = batch ? undefined : project;
return api.postWithToken( 'csrf', {
action: 'readinglists',
list: id,
project,
title,
batch,
command: 'createentry'
} ).then( ( result ) => ( { id } ) );
}
/**
* @param {number} id of list
* @param {string} title of page
* @return {JQuery.Promise<ApiQueryResponseReadingListEntryItem>}
*/
function findItemInList( id, title ) {
return api.get( { action: 'query', format: 'json',
list: 'readinglistentries',
rlelists: id
} ).then( function ( data ) {
const items = data.query.readinglistentries.filter(
( /** @type {ApiQueryResponseReadingListEntryItem} */ item ) => item.title === title
);
if ( items.length === 0 ) {
throw new Error( 'findItemInList doesn\'t know how to deal with pagination yet.' );
} else {
return items[ 0 ];
}
} );
}
// Note the remove from list function currently doesn't work.
// See https://phabricator.wikimedia.org/T198990
/**
* @param {number} id
* @param {string} title
* @return {JQuery.Promise<any>}
*/
function removeFromList( id, title ) {
return findItemInList( id, title ).then( function ( entry ) {
return api.postWithToken( 'csrf', {
action: 'readinglists',
entry: entry.id,
command: 'deleteentry'
} );
} );
}
module.exports = {
getReadingListUrl,
fromBase64,
toBase64,
saveReadingList,
// New:
deleteCollection,
removeFromList,
addToList,
getCollectionsWithMembership
};
},
"resources/ext.gadget.readinglist/CollectionDialog.vue": function ( require, module, exports ) {
const { CdxButton, CdxCard, CdxIcon } = require( '@wikimedia/codex' );
const { getReadingListUrl } = require( './api.js' );
const { cdxIconBookmark, cdxIconBookmarkOutline } = require( './icons.json' );
const CdxDialog = require( './Dialog.vue' );
// @vue/component
module.exports = {
name: 'CollectionDialog',
components: {
CdxDialog,
CdxButton,
CdxIcon,
CdxCard
},
props: {
collections: {
type: Array
}
},
computed: {
collectionsUrl: () => getReadingListUrl( mw.user.getName() ),
collectionsWithThumb() {
return this.collections;
},
markedIcon: () => cdxIconBookmark,
unmarkedIcon: () => cdxIconBookmarkOutline
},
data: function () {
const selected = {};
this.collections.forEach(( collection ) => {
selected[collection.id] = collection.marked;
});
return {
selected
};
},
methods: {
createList: function () {
this.$emit( 'create' );
},
hide: function () {
this.$emit( 'hide' );
},
getName: function ( id ) {
const collections = this.collections.filter( ( c ) => c.id === id );
if ( !collections.length ) {
throw new Error( 'Unable to locate collection with id ' + id );
}
return collections[ 0 ].name;
},
select: function ( id ) {
this.$emit(
'select',
id,
this.selected[ id ],
this.getName( id ),
() => {
this.selected[ id ] = !this.selected[ id ];
}
);
}
},
props: {
collections: []
}
};;
module.exports.template = "<cdx-dialog class=\"dialog-collection\" @cancel=\"hide\" :simple=\"false\" cancel-msg=\"Cancel\" title=\"Add to existing list\"> \
<ul> \
<li v-for=\"(collection, i) in collectionsWithThumb\" :key=\"i\" @click=\"select(collection.id)\"> \
<cdx-card :key=\"collection.id\" :data-selected=\"selected[collection.id]\"> \
<template #title=\"\"> \
{{ collection.name }} \
<\/template> \
<template #description=\"\"> \
{{ collection.description }} \
<\/template> \
<template #supporting-text=\"\"> \
<cdx-icon v-if=\"selected[collection.id]\" :icon=\"markedIcon\"><\/cdx-icon> \
<cdx-icon v-else=\"\" :icon=\"unmarkedIcon\"><\/cdx-icon> \
<\/template> \
<\/cdx-card> \
<\/li> \
<\/ul> \
<footer> \
<cdx-button @click=\"createList\">Add to new list<\/cdx-button> \
<a :href=\"collectionsUrl\">Manage lists<\/a> \
<\/footer> \
<\/cdx-dialog>";
},
"resources/ext.gadget.readinglist/CollectionEditorDialog.vue": function ( require, module, exports ) {
const { CdxTextInput, CdxCard, CdxButton } = require( '@wikimedia/codex' );
const CdxDialog = require( './Dialog.vue' );
// @vue/component
module.exports = {
name: 'CollectionDialog',
components: {
CdxDialog,
CdxTextInput,
CdxButton,
CdxCard
},
data() {
return {
exists: !!this.initialTitle,
title: this.initialTitle,
description: this.initialDescription
};
},
computed: {
label() {
return this.exists ? 'Edit list' : 'Create list';
},
getDialogTitle() {
return !this.initialDescription ? 'Create reading list' : undefined;
},
isSaveDisabled() {
return !this.title;
},
suggestion() {
return {
title: this.title,
description: this.description
};
}
},
props: {
initialTitle: {
type: String,
default: ''
},
initialDescription: {
type: String,
default: ''
}
},
methods: {
cancel() {
this.$emit( 'hide' );
},
save() {
this.$emit( 'save', this.title, this.description );
}
}
};;
module.exports.template = "<cdx-dialog @cancel=\"cancel\" :simple=\"true\" :title=\"getDialogTitle\"> \
<div class=\"dialog-collection-editor-panel\"> \
<cdx-card class=\"dialog-collection-editor-panel-preview\"> \
<template #title=\"\"> \
{{ title }} \
<\/template> \
<template #description=\"\"> \
{{ description }} \
<\/template> \
<\/cdx-card> \
<label>Name<\/label> \
<cdx-text-input v-model=\"title\" placeholder=\"Name this list\" class=\"dialog-collection-editor-panel-input\"><\/cdx-text-input> \
<label>Description<\/label> \
<cdx-text-input v-model=\"description\" placeholder=\"Describe this list\" class=\"dialog-collection-input dialog-collection-editor-panel-input-description\"><\/cdx-text-input> \
<\/div> \
<template #footer=\"\"> \
<cdx-button :disabled=\"isSaveDisabled\" @click=\"save\">{{ label }}<\/cdx-button> \
<\/template> \
<\/cdx-dialog>";
},
"resources/ext.gadget.readinglist/Dialog.vue": function ( require, module, exports ) {
const wvuiIconClose = 'M4.34 2.93l12.73 12.73-1.41 1.41L2.93 4.35z M17.07 4.34L4.34 17.07l-1.41-1.41L15.66 2.93z';
const { CdxButton, CdxIcon } = require( '@wikimedia/codex' );
module.exports = {
name: 'CdxDialog',
components: {
CdxButton,
CdxIcon
},
computed: {
rootClass() {
return {
'wvui-dialog': true,
'wvui-dialog-simple': this.simple,
'wvui-dialog-complex': !this.simple
};
}
},
methods: {
onContinue() {
this.$emit( 'continue' )
},
onCancel() {
this.$emit( 'cancel' );
}
},
props: {
continueDisabled: {
type: Boolean,
default: false
},
closeIcon: {
type: String,
default: wvuiIconClose
},
continueMsg: {
type: String,
default: ''
},
cancelMsg: {
type: String,
default: ''
},
title: {
type: String,
default: 'Title of dialog'
},
simple: {
type: Boolean,
default: true
}
}
};;
module.exports.template = "<div :class=\"rootClass\"> \
<div class=\"wvui-dialog-shield\" @click=\"onCancel\"><\/div> \
<div class=\"wvui-dialog-container\" @click.stop=\"\"> \
<header class=\"wvui-dialog-container-heading\"> \
<h2>{{ title }}<\/h2> \
<cdx-icon v-if=\"cancelMsg && !simple\" class=\"wvui-dialog-container-heading-cancel\" :icon=\"closeIcon\" @click=\"onCancel\">{{ cancelMsg }}<\/cdx-icon> \
<\/header> \
<div class=\"wvui-dialog-container-content\"> \
<slot><\/slot> \
<\/div> \
<nav class=\"wvui-dialog-container-footer\"> \
<slot name=\"footer\"><\/slot> \
<\/nav> \
<\/div> \
<\/div>";
},
"resources/ext.gadget.readinglist/overlays.js": function ( require, module, exports ) {
const CollectionDialog = require( './CollectionDialog.vue' );
const CollectionEditorDialog = require( './CollectionEditorDialog.vue' );
const Vue = require( 'vue' ).default || require( 'vue' );
const { removeFromList, addToList, saveReadingList } = require( './api.js' );
function registerOverlayArea() {
if ( document.querySelectorAll('.readinglist-overlay-area').length ) {
return;
}
const node = document.createElement('div');
node.classList.add('readinglist-overlay-area');
document.body.appendChild(node);
}
function hideOverlay( promise ) {
registerOverlayArea();
const container = document.querySelector( '.readinglist-overlay-area' );
container.innerHTML = '';
}
function showOverlay( promise ) {
registerOverlayArea();
return promise.then((app) => {
app.mount( '.readinglist-overlay-area' );
});
}
function editListOverlay( existingID, name, description, title, onSaveFn ) {
const onSave = onSaveFn || ( () => {} );
return Vue.createMwApp( CollectionEditorDialog, {
initialTitle: name,
initialDescription: description,
onSave: ( name, description ) => {
hideOverlay();
saveReadingList( existingID, name, description ).then((id) => {
mw.notify( existingID ? 'List edited!' : 'List created!');
if ( title ) {
addToList( id, title ).then(() => {
mw.notify('Added title to list!');
onSave();
});
} else {
onSave();
}
}, () => {
mw.notify('Error creating list');
})
},
onHide: () => {
hideOverlay();
}
} );
}
/**
*
* @return {Object}
*/
function createMembersOverlay( title, collections ) {
return Vue.createMwApp( CollectionDialog, {
collections,
onSelect: ( id, isSelected, name, callback ) => {
if ( isSelected ) {
removeFromList( id, title ).then( () => {
mw.notify( isSelected ? `Removed page from ${name}` : `Added page to ${name}.` );
callback();
} );
} else {
addToList( id, title ).then( () => {
mw.notify( isSelected ? `Removed page from ${name}` : `Added page to ${name}.` );
callback();
} );
}
},
onCreate: () => {
showOverlay( Promise.resolve( editListOverlay( null, '', '', title ) ) );
},
onHide: () => {
hideOverlay();
}
} );
}
module.exports = {
showOverlay,
hideOverlay,
editListOverlay,
createMembersOverlay
};
// </nowiki>
}
}
}, {
"css": [
".mw-ui-icon-vector-gadget-pt-readinglists:before {\n background-image: linear-gradient(transparent, transparent), url(\"data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2220%22 height=%2220%22 viewBox=%220 0 20 20%22%3E%3Ctitle%3E bullet list %3C/title%3E%3Cg fill=%22%23000%22%3E%3Cpath d=%22M7 15h12v2H7zm0-6h12v2H7zm0-6h12v2H7z%22/%3E%3Ccircle cx=%223%22 cy=%224%22 r=%222%22/%3E%3Ccircle cx=%223%22 cy=%2210%22 r=%222%22/%3E%3Ccircle cx=%223%22 cy=%2216%22 r=%222%22/%3E%3C/g%3E%3C/svg%3E\");\n}\n.mw-ui-icon-bookmark:before {\n background-image: linear-gradient(transparent, transparent), url(\"data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2220%22 height=%2220%22 viewBox=%220 0 20 20%22%3E%3Ctitle%3E bookmark outlined %3C/title%3E%3Cg fill=%22%23000%22%3E%3Cpath d=%22M5 1a2 2 0 0 0-2 2v16l7-5 7 5V3a2 2 0 0 0-2-2zm10 14.25-5-3.5-5 3.5V3h10z%22/%3E%3C/g%3E%3C/svg%3E\");\n}\n.readinglist-overlay-area {\n z-index: 9999;\n}\n\n.dialog-collection h2 {\n font-size: 1.2em;\n border: 0;\n font-weight: bold;\n padding: 0;\n margin: 0.5em 0 0;\n}\n.dialog-collection footer {\n text-align: center;\n position: absolute;\n bottom: 10px;\n left: 0;\n right: 0;\n}\n.dialog-collection footer a {\n color: #333;\n}\n.dialog-collection ul {\n height: calc(100% - 100px);\n overflow: scroll;\n}\n.dialog-collection li {\n display: flex;\n align-items: center;\n}\n.dialog-collection li .cdx-card__text,\n.dialog-collection li .cdx-card {\n flex-grow: 1;\n}\n.dialog-collection li .cdx-card__text__supporting-text {\n justify-content: end;\n display: flex;\n}\n\n.dialog-collection-editor-panel {\n min-width: 300px;\n}\n.dialog-collection-editor-panel-preview {\n border-top: 1px solid rgba(0, 0, 0, 0.2);\n border-bottom: 1px solid rgba(0, 0, 0, 0.2);\n}\n.dialog-collection-editor-panel label {\n margin-top: 2em;\n font-weight: bold;\n margin-bottom: 0.5em;\n display: block;\n}\n.dialog-collection-editor-panel-input {\n margin-bottom: 20px;\n display: block;\n}\n.dialog-collection-editor-panel-input-description {\n margin-bottom: 20px;\n}\n\n.wvui-dialog {\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n}\n.wvui-dialog-shield,\n.wvui-dialog {\n position: fixed;\n top: 0;\n bottom: 0;\n left: 0;\n right: 0;\n}\n.wvui-dialog-shield {\n opacity: 0.5;\n background: #ccc;\n}\n.wvui-dialog-container \u003E * {\n margin: 0;\n}\n.wvui-dialog-container-button + .wvui-dialog-container-button {\n margin-top: 0.5em;\n}\n.wvui-dialog-container-heading {\n display: flex;\n}\n.wvui-dialog-container-heading h2 {\n flex-grow: 1;\n margin: 0;\n}\n.wvui-dialog-container {\n position: absolute;\n background: white;\n margin: auto;\n}\n.wvui-dialog-container-heading-continue {\n display: none;\n}\n.wvui-dialog-simple {\n align-items: center;\n}\n.wvui-dialog-simple .wvui-dialog-container-heading {\n margin: 0 0 1.25em;\n}\n.wvui-dialog-simple .wvui-dialog-container {\n padding: 1.5em;\n position: absolute;\n background: white;\n margin: auto;\n max-width: 400px;\n}\n.wvui-dialog-complex .wvui-dialog-container {\n height: 100%;\n max-height: 500px;\n}\n.wvui-dialog-complex .wvui-dialog-container-content {\n overflow: scroll;\n height: 100%;\n}\n@media all and (max-width: 400px) {\n .wvui-dialog-complex {\n align-items: flex-start;\n }\n .wvui-dialog-complex .wvui-dialog-container {\n width: 100%;\n padding-bottom: 40px;\n box-sizing: border-box;\n }\n .wvui-dialog-complex .wvui-dialog-container-heading-cancel {\n order: 1;\n padding: 16px;\n }\n .wvui-dialog-complex .wvui-dialog-container-heading h2 {\n order: 2;\n margin-left: 16px;\n padding: 16px 0;\n }\n .wvui-dialog-complex .wvui-dialog-container-heading-continue {\n display: block;\n order: 3;\n }\n .wvui-dialog-complex .wvui-dialog-container-content {\n padding: 16px;\n box-sizing: border-box;\n }\n .wvui-dialog-complex .wvui-dialog-container-footer {\n display: none;\n }\n}\n@media all and (min-width: 400px) {\n .wvui-dialog-complex .wvui-dialog-container {\n width: 500px;\n max-width: 500px;\n }\n .wvui-dialog-complex .wvui-dialog-container-heading {\n margin: 1.25em 1.25em 1em;\n }\n .wvui-dialog-complex .wvui-dialog-container-content {\n margin: 0 1.25em;\n }\n .wvui-dialog-complex .wvui-dialog-container-footer {\n border-top: 1px solid gray;\n padding: 1.25em;\n text-align: right;\n }\n}\n.wvui-dialog-container-footer {\n margin-top: 1em;\n}"
]
} );