User:Jdlrobson/rlNew.js

mw.loader.impl(function{return["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 * and copy to https://en.wikipedia.org/wiki/User:Jdlrobson/rlNew.js */ // const { showOverlay, createMembersOverlay, editListOverlay } = require( './overlays.js'); const { getCollectionsWithMembership, fromBase64, saveReadingList,	deleteCollection, getReadingListUrl,	addToList } = require( './api.js' ); const READING_LIST_URL = getReadingListUrl( mw.user.getName );

(function {	const button = ( text, className ) => {		const editBtn = document.createElement('button');		editBtn.classList.add( 'cdx-button', className );		editBtn.textContent = text;		return editBtn;	};

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 = button( '' ); 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 = button( '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 = button( '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;					}); });			} );		}	}

window.ReadingListShowOverlay = function { const title = mw.config.get( 'wgPageName' ); showOverlay(			getCollectionsWithMembership( mw.user.getName, title ).then(( collections ) => { return createMembersOverlay(					title,					collections				); } )		);	}	if ( window.READING_LIST_SHOW_OVERLAY ) { ReadingListShowOverlay; document.getElementById( 'pt-bookmark' ).addEventListener( 'click', function {			ReadingListShowOverlay;		} ); }

// @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/readinglist.scripts/config.json":{    "ReadingListAndroidAppDownloadLink": "",    "ReadingListiOSAppDownloadLink": "",    "ReadingListsAnonymizedPreviews": false,    "ReadingListsDeveloperMode": true },"resources/readinglist.scripts/utils.js":function(require,module,exports){/** * @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 ) => {	let titlePath = 'ReadingLists';	if ( ownerName ) {		titlePath += `/${ownerName}`;	}	if ( id ) {		titlePath += `/${id}`;	}	const titleWithName = title ? `${titlePath}/${encodeURIComponent( title )}` : titlePath; try { return (			new mw.Title( titleWithName, -1 )		).getUrl; } catch ( e ) { // Uncaught Error: Unable to parse title // e.g. Special:ReadingLists/1/ return (			new mw.Title( titlePath, -1 )		).getUrl; } };

module.exports = { getReadingListUrl }; },"resources/readinglist.scripts/api.js":function(require,module,exports){const DEFAULT_READING_LIST_NAME = mw.msg( 'readinglists-default-title' ); const DEFAULT_READING_LIST_DESCRIPTION = mw.msg( 'readinglists-default-description' ); const { getReadingListUrl } = require( './utils.js' ); const config = require( './config.json' ); const api = new mw.Api; const isBetaCluster = => mw.config.get( 'wgServer' ).indexOf( 'wikipedia.beta.wmflabs' ) > -1;

const WATCHLIST_ID = -1; const WATCHLIST_NAME = 'Watchlist'; const WATCHLIST_DESCRIPTION = 'Articles that you edited or follow.';

/** * @typedef ApiQueryResponseReadingListEntry * @property {string} title */

/** * @typedef {Object} ImportedList * @property {string} name * @property {string} description * @param {ProjectTitleMap|ProjectTitleMap} list */

/** * @typedef ApiQueryResponseReadingListEntryItem * @property {string} title * @property {number} id */

/** * @typedef ApiQueryResponseReadingListItem * @property {number} id * @property {string} name * @property {string} description * @property {boolean} default whether it is the default */

/** * @typedef ApiQueryResponsePage * @property {string} title */

/** * @typedef ApiQueryResponseReadingListsQuery * @property {ApiQueryResponseReadingListItem[]} readinglists */

/** * @typedef ApiQueryResponseReadingListQuery * @property {ApiQueryResponseReadingListEntryItem[]} readinglistentries */

/** * @typedef ApiQueryResponseTitlesQuery * @property {ApiQueryResponsePage[]} pages */

/** * @typedef ApiQueryResponseReadingListEntries * @property {ApiQueryResponseReadingListQuery} query */

/** * @typedef ApiQueryError * @property {number} code */

/** * @typedef ApiQueryResponseReadingLists * @property {ApiQueryResponseReadingListsQuery} query * @property {ApiQueryError} [error] */

/** * @typedef ApiQueryResponseTitles * @property {ApiQueryResponseTitlesQuery} query */

/** * @typedef Card * @property {number} [id] id relating to reading list * @property {number} [pageid] id relating to the page title * @property {string} url * @property {string} [ownerName] * @property {string} name * @property {string} [description] */

/** * 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} project * @return {boolean} */ const isLanguageCode = ( project ) => { const hasProtocol = project.indexOf( '//' ) > -1; return !hasProtocol && project.indexOf( '.' ) === -1 && project.indexOf( ':' ) === -1; };

/** * From a project identifier work out which API to use. * * @param {string} project * @return {string} */ function getProjectHost( project ) { const isLang = isLanguageCode( project ); const hasProtocol = project.indexOf( '//' ) > -1; if ( config.ReadingListsDeveloperMode ) { if ( project.indexOf( 'localhost' ) > -1 ) { return 'https://en.wikipedia.org'; } else if ( isLang ) { return `https://${project}.wikipedia.org`; } else { return hasProtocol ? project : `//${project}`; }	}	if ( isLang ) { return `https://${project}.${window.location.host.split( '.' ).slice( 1 ).join( '.' )}`; } else { return hasProtocol ? project : `//${project}`; } }

/** * @param {string} project * @return {function( ApiQueryResponsePage ): Card} */ const transformPage = ( project ) => { return ( page ) => { return Object.assign( page, {			project: getProjectHost( project ),			// T320293			url: `${getProjectHost( project )}${new mw.Title( page.title ).getUrl}`,			pageid: page.pageid,			thumbnail: page.thumbnail ? {				width: page.thumbnail.width,				height: page.thumbnail.height,				url: page.thumbnail.source			} : null		} ); }; };

/** * Sets up the reading list feature for new users who have never used it before. * * @return {jQuery.Promise } */ function setupCollections { return api.postWithToken( 'csrf', {		action: 'readinglists',		command: 'setup'	} ); }

/** * * @param {string} ownerName (username) * @param {number[]} marked a list of collection IDs which have a certain title * @return {Promise} */ 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 ); }		} );	} ); }

function getWatchlistMeta( ownerName ) { return Promise.resolve( watchlistCard( ownerName ) ); }

/** * Gets pages on a given reading list * * @param {string} ownerName * @param {number} id of collection * @return {jQuery.Promise} */ function getCollectionMeta( ownerName, id ) { if ( id === WATCHLIST_ID ) { return getWatchlistMeta( ownerName ); }	return api.get( { action: 'query', format: 'json', meta: 'readinglists', rllist: id, formatversion: 2 } ) .then( function ( /** @type {ApiQueryResponseReadingLists} */ data ) {			if ( data.error && data.error.code ) {				throw new Error( `Error: ${data.error.code}` );			}			return readingListToCard( data.query.readinglists[ 0 ], ownerName, []			);		} ); }

/** * @typedef {Object.} ProjectTitleMap * @typedef {Object.} ProjectIDMap */

/** * Gets pages on a given reading list * * @param {ProjectTitleMap|ProjectIDMap} projectMap * @return {jQuery.Promise } */ function getPagesFromProjectMap( projectMap ) { const projects = Object.keys( projectMap ); const promises = []; for ( let i = 0; i < projects.length; i++ ) { promises.push( getPagesFromPageIdentifiers( projects[ i ], projectMap[ projects[ i ] ] ) ); }	return Promise.all( promises ).then( ( args ) => {		return Array.prototype.concat.apply( [], args );	} ); }

/** * From a project identifier work out which API to use. * * @param {string} project * @return {string} */ function getProjectApiUrl( project ) { if ( config.ReadingListsDeveloperMode ) { return `${getProjectHost( project )}/w/api.php`; }	return `${getProjectHost( project )}${mw.config.get( 'wgScriptPath' )}/api.php`; }

/** * * @param {string} project e.g. 'http://localhost:8888' or language code e.g. 'en' * @param {number[]|string[]} pageidsOrPageTitles * @return {jQuery.Promise } */ function getThumbnailsAndDescriptions( project, pageidsOrPageTitles ) { const isPageIds = pageidsOrPageTitles[ 0 ] && typeof pageidsOrPageTitles[ 0 ] === 'number'; const pageids = isPageIds ? pageidsOrPageTitles : undefined; const titles = isPageIds ? undefined : pageidsOrPageTitles;

const ajaxOptions = { url: `${getProjectApiUrl( project )}` };

const filterOutMissingPagesIfIDsPassed = ( page ) => { return isPageIds ? !page.missing : true; };

return pageidsOrPageTitles.length ? api.get( {		action: 'query',		format: 'json',		origin: '*',		formatversion: 2,		prop: 'pageimages|description',		pageids,		titles,		piprop: 'thumbnail',		pithumbsize: 200	}, ajaxOptions ).then( function ( /** @type {ApiQueryResponseTitles} */ pageData ) {		return pageData && pageData.query ?			pageData.query.pages.filter( filterOutMissingPagesIfIDsPassed ).map( transformPage( project ) ) : [];	} ) : Promise.resolve( [] ); } /** * Gets pages from a given project and list of pageids * * @param {string} project * @param {number[]|string[]} pageids * @return {jQuery.Promise } */ function getPagesFromPageIdentifiers( project, pageids ) { const LIMIT = 50; if ( pageids.length > LIMIT ) { const promises = []; for ( let i = 0; i < pageids.length; i += LIMIT ) { promises.push( getPagesFromPageIdentifiers( project, pageids.slice( i, i + LIMIT ) ) ); }		return Promise.all( promises ).then( ( args ) => {			return Array.prototype.concat.apply( [], args );		} ); }	if ( pageids.length > 250 ) { return Promise.reject( 'readinglists-import-size-error' ); }	return getThumbnailsAndDescriptions( project, pageids ); }

/** * @param {ApiQueryResponsePage} pages * @return {ProjectTitleMap} */ function toProjectTitlesMap( pages ) { const /** @type {ProjectTitleMap} */projectTitleMap = {}; pages.forEach( ( page ) => {		if ( !projectTitleMap[ page.project ] ) {			projectTitleMap[ page.project ] = [];		}		projectTitleMap[ page.project ].push( page.title );	} ); return projectTitleMap; }

/** * @param {ApiQueryResponsePage} pages * @return {jQuery.Promise } */ function getPagesFromReadingListPages( pages ) { return getPagesFromProjectMap( toProjectTitlesMap( pages ) ); }

function getWatchlistPages( continueQuery = {} ) { return api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, wrnamespace: 0, list: 'watchlistraw', wrlimit: 100 }, continueQuery ) ).then( ( data ) => {		const pages = data.watchlistraw.map( ( page ) => Object.assign( {}, page, {			project: getCurrentProjectName		}));		if ( data.continue ) {			return getWatchlistPages( data.continue ).then( ( extraPages ) => pages.concat( extraPages ) );		}		return pages;	} ); }

function getReadingListPages( collectionId, continueQuery = {} ) { return api.get( Object.assign( { action: 'query', format: 'json', formatversion: 2, list: 'readinglistentries', rlelimit: 100, rlelists: collectionId }, continueQuery ) ).then( ( /** @type {ApiQueryResponseReadingListEntries} */data ) => {		const pages = data.query.readinglistentries;		if ( data.continue ) {			return getReadingListPages( collectionId, data.continue ).then( ( extraPages ) => pages.concat( extraPages ) );		}		return pages;	} ); }

/** * Gets pages on a given reading list * * @param {number} collectionId * @return {jQuery.Promise } */ function getPages( collectionId ) { const query = collectionId === WATCHLIST_ID ? getWatchlistPages : getReadingListPages( collectionId );

return query.then( ( readinglistpages ) => {		return getPagesFromReadingListPages( readinglistpages ).then( function ( /** @type {ApiQueryResponsePage} */ pages ) { // make sure project is passed down. return pages.map( ( page, /** @type {number} */ i ) =>				Object.assign( readinglistpages[ i ], page ) ).sort( ( a, b ) => a.title < b.title ? -1 : 1 ); }, => {			return Promise.reject( 'readinglistentries-error' ); } );	} ); }

/** * @param {string} name * @param {string} description * @param {ProjectTitleMap|ProjectTitleMap} list * @return {string} */ function toBase64( name, description, list ) { return btoa( JSON.stringify( { name, description, list } ) ); }

/** * @param {ImportedList} importedList * @return {ImportedList} */ function normalizeImportedData( importedList ) { Object.keys( importedList.list ).forEach( ( key ) => {		// If encounter a language code (no protocol or subdomain) assume Wikipedia		if ( isLanguageCode( key ) ) {			importedList.list[ `https://${key}.wikipedia.org` ] = importedList.list[ key ];			delete importedList.list[ key ];		}	} ); return importedList; }

/** * @param {string} data * @return {Object} */ function fromBase64( data ) { return normalizeImportedData( JSON.parse( atob( data ) ) ); }

/** * * @param {number} id of list * @return {JQuery.Promise } */ function deleteCollection( id ) { return api.postWithToken( 'csrf', {			action: 'readinglists',			list: id,			command: 'delete'	} ); }

/** * * @return {string} */ function getCurrentProjectName { // Use wgServer to avoid issues with ".m." domain const server = mw.config.get( 'wgServer' ); if ( config.ReadingListsDeveloperMode ) { return 'https://en.wikipedia.org'; }	if ( isBetaCluster ) { return server.replace( 'wikipedia.beta.wmflabs', 'wikipedia' ).replace( 'https://', '' ); }	return server.indexOf( '//' ) === 0 ? window.location.protocol + server : server; }

/** * @param {number} id of list * @param {string} title of page * @return {JQuery.Promise} */ 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 ];		}	} ); }

function addToWatchlist( title ) { return api.postWithToken( 'watch', {		action: 'watch',		titles: title	} ); }

function removeFromWatchlist( title ) { return api.postWithToken( 'watch', {		action: 'watch',		titles: title,		unwatch: '1'	} ); }

/** * Sets up the reading list feature for new users who have never used it before. * * @return {jQuery.Promise } * @param {string|null} id if an existing collection * @param {string} name of list * @param {string} description of list * @return {JQuery.Promise } */ 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 ); } }

// 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 } */ function removeFromList( id, title ) { if ( id === WATCHLIST_ID ) { return removeFromWatchlist( title ); }

return findItemInList( id, title ).then( function ( entry ) {		return api.postWithToken( 'csrf', { action: 'readinglists', entry: entry.id, command: 'deleteentry' } );	} ); }

/** * @param {number} id * @param {string|array} titleOrTitles * @param {string} [projectName] * @return {JQuery.Promise } */ 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;

if ( id === WATCHLIST_ID ) { return addToWatchlist( titleOrTitles ); }

return api.postWithToken( 'csrf', {		action: 'readinglists',		list: id,		project,		title,		batch,		command: 'createentry'	} ).then( ( result ) => ( { id } ) ); }

/** * 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 getWatchlistPages( ownerName ).then(( pages ) => { const titleNormal = title.replace(/_/g, ' ' ); if ( pages.filter( ( page ) => page.title === titleNormal ).length ) { return [ WATCHLIST_ID ]; } else { return []; }		} ).then( ( wl ) => { 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 ).concat( wl );				console.log('g', marked);				return cards.map( ( card ) => { return Object.assign( card, {						marked: marked.indexOf( card.id ) > -1					} ); } );			} );		} );	} ); }

/** * * @param {string} ownerName (username) * @param {number[]} marked a list of collection IDs which have a certain title * @return {Promise} */ 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 ) ).concat( watchlistCard( ownerName ) )			);		}, 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 ); }		} );	} ); }

const watchlistCard = ( ownerName ) => { const name = WATCHLIST_NAME; const description = WATCHLIST_DESCRIPTION; const id = WATCHLIST_ID; return readingListToCard( {		id,		name,		description	}, ownerName ) };

module.exports = { getReadingListUrl, deleteCollection, removeFromList, addToList, getCollectionsWithMembership, test: { readingListToCard, getProjectHost, getProjectApiUrl },	fromBase64, toBase64, getPages, saveReadingList, getCollectionMeta, getCollections, getPagesFromProjectMap }; },"resources/ext.gadget.readinglist/api.js":function(require,module,exports){module.exports = require( '../readinglist.scripts/api.js' ); },"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 = " \n\		 \n\			 \n\				 \n\					 \n\						 \n\					<\/template> \n\					 \n\						 \n\					<\/template> \n\					 \n\						<\/cdx-icon> \n\						<\/cdx-icon> \n\					<\/template> \n\				<\/cdx-card> \n\			<\/li> \n\		<\/ul> \n\		 \n\			Add to new list<\/cdx-button> \n\			<a :href=\"collectionsUrl\">Manage lists<\/a> \n\		<\/footer> \n\	<\/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\"> \n\		<div class=\"dialog-collection-editor-panel\"> \n\			<cdx-card class=\"dialog-collection-editor-panel-preview\"> \n\				<template #title=\"\"> \n\					 \n\				<\/template> \n\				<template #description=\"\"> \n\					 \n\				<\/template> \n\			<\/cdx-card> \n\			 Name<\/label> \n\			<cdx-text-input v-model=\"title\" placeholder=\"Name this list\" class=\"dialog-collection-editor-panel-input\"><\/cdx-text-input> \n\			 Description<\/label> \n\			<cdx-text-input v-model=\"description\" placeholder=\"Describe this list\" class=\"dialog-collection-input dialog-collection-editor-panel-input-description\"><\/cdx-text-input> \n\		<\/div> \n\		<template #footer=\"\"> \n\			<cdx-button :disabled=\"isSaveDisabled\" @click=\"save\"><\/cdx-button> \n\		<\/template> \n\	<\/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\"> \n\		<div class=\"wvui-dialog-shield\" @click=\"onCancel\"><\/div> \n\		<div class=\"wvui-dialog-container\" @click.stop=\"\"> \n\			<header class=\"wvui-dialog-container-heading\"> \n\				 <\/h2> \n\				<cdx-icon v-if=\"cancelMsg &amp;&amp; !simple\" class=\"wvui-dialog-container-heading-cancel\" :icon=\"closeIcon\" @click=\"onCancel\"><\/cdx-icon> \n\			<\/header> \n\			<div class=\"wvui-dialog-container-content\"> \n\				 <\/slot> \n\			<\/div> \n\			<nav class=\"wvui-dialog-container-footer\"> \n\				<slot name=\"footer\"><\/slot> \n\			<\/nav> \n\		<\/div> \n\	<\/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' ); const teleportTarget = require( 'mediawiki.page.ready' ).teleportTarget; const rlOverlayTarget = document.createElement( 'div' ); teleportTarget.appendChild(rlOverlayTarget); let vm;

function hideOverlay { if (vm ) { vm.unmount; } }

function showOverlay( promise ) { return promise.then((app) => {		app.mount( rlOverlayTarget );		vm = app;	}); }

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 }; // }}},{"css":},{"readinglists-default-description":"(readinglists-default-description)","readinglists-default-title":"(readinglists-default-title)"}];});