Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/*

                   |                   
                  /#\                  
                  ###                  
      \-         /###\         -/      
       \##\-    /#####\    -/##/       
        \######\#######/######/        
         \###################/         
          \#################/          
         -/#################\-         
   /############       ############\   
 <############# Sparkle #############>  
   \############       ############/   
         -\#################/-         
          /#################\          
         /###################\         
        /######/#######\######\        
       /##/-    \#####/    -\##\       
      /-         \###/         -\      
                  ###                  
                  \#/                  
                   |                   

Version 0.1.0.1: Rewriting the whole thing (better, hopefully)

This work (all content on this page) is licensed under:
* The Creative Commons Attribution-ShareAlike 3.0 [https://creativecommons.org/licenses/by-sa/3.0/] (CC BY-SA 3.0) license;
* The Creative Commons Attribution-ShareAlike 4.0 [https://creativecommons.org/licenses/by-sa/4.0/] (CC BY-SA 4.0) license;
* The GNU Lesser General Public License [https://www.gnu.org/copyleft/lgpl.html] (LGPL), version 3 or any later version.
* The GNU Free Documentation License [http://www.gnu.org/licenses/fdl-1.3.html] (GFDL), version 1.3 or any later version, with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.

<syntaxhighlight lang="js">

*/

// Basic functions

function api_templates( title ) {
	var params = {
		action: 'query',
		prop: 'templates',
		titles: title.getPrefixedDb(),
		tllimit: 'max',
		format: 'json',
		formatversion: '2'
	};
	return new Promise( function( resolve, reject ) {
		mw.loader.using( 'mediawiki.api', function() {
			api = new mw.Api();
			api.get( params ).done( function( data ) {
				resolve( data.query.pages[0].templates );
			} );
		} );
	} );
}

function api_edit_pre ( title, text, summary ) {
	var params = {
		action: 'edit',
		title: title.getPrefixedDb(),
		prependtext: text,
		summary: summary,
		format: 'json'
	};
	mw.loader.using( 'mediawiki.api', function () {
		api = new mw.Api();
		return api.postWithToken( 'csrf', params );
	} );
}

function api_edit_app( title, text, summary ) {
	var params = {
		action: 'edit',
		title: title.getPrefixedDb(),
		appendtext: text,
		summary: summary,
		format: 'json'
	};
	mw.loader.using( 'mediawiki.api', function () {
		api = new mw.Api();
		return api.postWithToken( 'csrf', params );
	} );
}

function api_creator( title ) {
	var params = {
		action: 'query',
		prop: 'revisions',
		titles: title.getPrefixedDb(),
		rvprop: 'user',
		rvlimit: '1',
		rvdir: 'newer',
		format: 'json',
		formatversion: '2'
	};
	return new Promise( function( resolve, reject ) {
		mw.loader.using( 'mediawiki.api', function() {
			api = new mw.Api();
			api.get( params ).done( function( data ) {
				resolve( data.query.pages[0].revisions[0].user );
			} );
		} );
	} );
}

// Module Del/Pr: Proposed deletion
function del_pr() {
	
	mw.loader.using( [
		'oojs-ui-core',
		'oojs-ui-widgets',
		'mediawiki.util'
	], function () {
		
		// Exit if already open; else add button
		if ( $( '#sparkle-container-del-pr' ).length > 0 ) {
			return;
		} else {
			$( '#content' ).prepend( `
				<div id="sparkle-container-del-pr">
					<h1 >
						Sparkle: Proposed Deletion
						<span id="sparkle-close-del-pr"
						style="float: right; font-size:50%">
						</span>
					</h1>
					<div id="sparkle-form-del-pr"></div>
					<hr />
				</div>
			` );
		}
		
		// Close button
		var button_close = new OO.ui.ButtonWidget( {
			framed: false,
			flags: [ 'destructive' ],
			icon: 'close'
		} );
		button_close.on( 'click', function () {
			$( '#sparkle-container-del-pr' ).remove();
		} );
		$( '#sparkle-close-del-pr' ).append( button_close.$element );
		
		// Index & Tabs
		var index = new OO.ui.IndexLayout( { expanded: false } );
		$( '#sparkle-form-del-pr' ).append( index.$element );
		var fieldset_nominate = new OO.ui.FieldsetLayout();
		var tab_nominate = new OO.ui.TabPanelLayout( 'nominate', {
			label: 'Nominate',
			content: [ fieldset_nominate ],
			expanded: false
		} );
		var fieldset_endorse = new OO.ui.FieldsetLayout();
		var tab_endorse = new OO.ui.TabPanelLayout( 'endorse', {
			label: 'Endorse',
			content: [ fieldset_endorse ],
			expanded: false
		} );
		var fieldset_blp = new OO.ui.FieldsetLayout();
		var tab_blp = new OO.ui.TabPanelLayout( 'blp', {
			label: 'BLP',
			content: [ fieldset_blp ],
			expanded: false
		} );
		index.addTabPanels( [ tab_nominate, tab_blp ] );
		
		// Tab "Nominate"
		var textinput_nominate_rationale = new OO.ui.TextInputWidget( {
			placeholder: 'Rationale'
		} );
		var button_nominate = new OO.ui.ButtonWidget( {
			label: 'Nominate',
			flags: [ 'primary', 'progressive' ]
		} );
		var field_nominate_rationale = new OO.ui.ActionFieldLayout(
			textinput_nominate_rationale,
			button_nominate
		);
		var checkbox_nominate_notify = new OO.ui.CheckboxInputWidget( {
			selected: true
		} );
		var field_nominate_notify = new OO.ui.FieldLayout(
			checkbox_nominate_notify,
			{
				label: 'Notify page creator?',
				align: 'inline'
			}
		);
		fieldset_nominate.addItems( [
			field_nominate_rationale,
			field_nominate_notify
		] );
		
		// Tab "Endorse"
		var textinput_endorse_rationale = new OO.ui.TextInputWidget( {
			placeholder: 'Rationale'
		} );
		var button_endorse = new OO.ui.ButtonWidget( {
			label: 'Endorse',
			flags: [ 'primary', 'progressive' ],
			disabled: true
		} );
		var field_endorse_rationale = new OO.ui.ActionFieldLayout(
			textinput_endorse_rationale,
			button_endorse
		);
		fieldset_endorse.addItems( [ field_endorse_rationale ] );
		
		// Tab "BLP"
		var textinput_blp_rationale = new OO.ui.TextInputWidget( {
			placeholder: 'Rationale'
		} );
		var button_blp = new OO.ui.ButtonWidget( {
			label: 'Nominate',
			flags: [ 'primary', 'progressive' ]
		} );
		var field_blp_rationale = new OO.ui.ActionFieldLayout(
			textinput_blp_rationale,
			button_blp
		);
		fieldset_blp.addItems( [ field_blp_rationale ] );
		
		// Progress bar & success message
		var progressbar = new OO.ui.ProgressBarWidget();
		var field_progressbar = new OO.ui.FieldLayout(
			progressbar,
			{
				align: 'top',
				label: 'Nominating...'
			}
		);
		var fieldset_progressbar = new OO.ui.FieldsetLayout();
		fieldset_progressbar.addItems( field_progressbar );
		var notice_success = new OO.ui.MessageWidget( {
			type: 'success',
			icon: 'trash',
			label: 'This page has been nominated for proposed deletion.'
				+ ' Reloading...'
		} );
		
		// Config variables
		var pagename = mw.config.get( 'wgPageName' );
		var title_page = new mw.Title( pagename );
		var title_talk = title_page.getTalkPage();
		var title_afd = new mw.Title(
			'Wikipedia:Articles for deletion/' + pagename
		);
		var title_mfd = new mw.Title(
			'Wikipedia:Miscellany for deletion/' + pagename
		);
		var username = mw.config.get( 'wgUserName' );
		
		// Check page for existing deletion templates
		var checkbox_speedy = new OO.ui.CheckboxInputWidget( {
			selected: false,
			disabled: true // Doesn't currently do anything
		} );
		var field_speedy = new OO.ui.FieldLayout(
			checkbox_speedy,
			{
				label: 'Remove speedy deletion tag(s)?',
				align: 'inline'
			}
		);
		var notice_discussion = new OO.ui.MessageWidget( {
			type: 'error',
			label: `
				This page is being discussed for deletion,
				so it may not be proposed for deletion.
			`
		} );
		api_templates( title_page )
		.then( function( templates ) {
			for ( let template of templates ) {
				// Speedy: Add checkbox to remove speedy
				if ( template.title == 'Template:Db-meta' ) {
					fieldset_nominate.addItems( [ field_speedy ] );
				}
				// Proposed: Add tab "Endorse", remove other tabs
				if (
					[
						'Template:Proposed deletion/dated',
						'Template:Proposed deletion/dated files',
						'Template:Prod blp/dated'
					].includes( template.title )
				) {
					index
					.addTabPanels( [
						tab_endorse
					] )
					.removeTabPanels( [
						tab_nominate,
						tab_blp
					] );
				}
				// Discussion: Disable module
				if (
					[
						'Template:Article for deletion/dated',
						'Template:Ffd',
						'Template:Mfd'
					].includes( template.title )
				) {
					$( '#' + index.getElementId() ).remove();
					$( '#sparkle-form-del-pr' ).append(
						notice_discussion.$element
					);
				}
			}
		} );
		
		// Core for "Nominate" tab
		function nominate() {
			
			// Replace index with progress bar
			$( '#' + index.getElementId() ).remove();
			$( '#sparkle-form-del-pr' ).append(
				fieldset_progressbar.$element
			);
			
			// Edits and summaries
			var page_edit = '{{subst:Prod|concern='
				+ textinput_nominate_rationale.value
				+ '|help=off}}\n';
			var page_summary = 'Nominate for [[WP:PRD|proposed deletion]] '
				+ '| [[WP:✨|✨]]';
			var talk_edit = '{{Old prod|nom='
				+ username
				+ '|nomdate={{subst:#time:Y-m-d}}|nomreason='
				+ textinput_nominate_rationale.value
				+ '}}\n';
			var talk_summary = 'Add notice of [[WP:PRD|proposed deletion]] '
				+ 'nomination | [[WP:✨|✨]]';
			var usertalk_edit = '\n\n{{subst:Prodnote|1='
				+ title_page.getPrefixedText()
				+ '|concern='
				+ textinput_nominate_rationale.value
				+ '}} ~~~~';
			var usertalk_summary = 'Add note of '
				+ '[[WP:PRD|proposed deletion]] nomination of [['
				+ title_page.getPrefixedText()
				+ ']] | [[WP:✨|✨]]';
			
			// Functions to do edits
			function edit_page() {
				// TODO: Add functionality for removing speedy tags
				return api_edit_pre( title_page, page_edit, page_summary );
			}
			function edit_talk() {
				return api_edit_pre( title_talk, talk_edit, talk_summary );
			}
			function edit_usertalk() {
				if ( checkbox_nominate_notify.selected ) {
					return new Promise( function( resolve, reject ) {
						api_creator( title_page )
						.then( function ( creator ) {
							var title_usertalk = new mw.Title(
								'User talk:' + creator
							);
							api_edit_app(
								title_usertalk,
								usertalk_edit,
								usertalk_summary
							);
						} )
						.then( function() {
							resolve();
						} );
					} );
				} else {
					return null;
				}
			}
			
			// Do edits
			Promise.all( [
				edit_page(),
				edit_talk(),
				edit_usertalk()
			] ).then( function() {
				$( '#' + fieldset_progressbar.getElementId() ).remove();
				$( '#sparkle-form-del-pr' ).append(
					notice_success.$element
				);
				setTimeout( function() {
					window.location.reload();
				}, 1000 );
			} );
			
		}
		
		// Core for "Endorse" tab
		function endorse() {
			alert('Endorse (this does not work yet)');
		}
		
		// Core for "BLP" tab
		function blp_nominate() {
			
			// Replace index with progress bar
			$( '#' + index.getElementId() ).remove();
			$( '#sparkle-form-del-pr' ).append(
				fieldset_progressbar.$element
			);
			
			// Edits and summaries
			var page_edit = '{{subst:Prod blp|concern='
				+ textinput_blp_rationale.value
				+ '|help=off}}\n';
			var page_summary = 'Nominate for [[WP:BLPPROD|proposed deletion '
				+ 'of unsourced biography of living person]] '
				+ '| [[WP:✨|✨]]';
			var usertalk_edit = '\n\n{{subst:ProdwarningBLP|1='
				+ title_page.getPrefixedText()
				+ '|concern='
				+ textinput_blp_rationale.value
				+ '}} ~~~~';
			var usertalk_summary = 'Add note of '
				+ '[[WP:BLPPROD|proposed deletion of unsourced '
				+ 'biography of living person]] nomination of [['
				+ title_page.getPrefixedText()
				+ ']] | [[WP:✨|✨]]';
			
			// Functions to do edits
			function edit_page() {
				return api_edit_pre( title_page, page_edit, page_summary );
			}
			function edit_usertalk() {
				return new Promise( function( resolve, reject ) {
					api_creator( title_page )
					.then( function ( creator ) {
						var title_usertalk = new mw.Title(
							'User talk:' + creator
						);
						api_edit_app(
							title_usertalk,
							usertalk_edit,
							usertalk_summary
						);
					} )
					.then( function() {
						resolve();
					} );
				} );
			}
			
			// Do edits
			Promise.all( [
				edit_page(),
				edit_usertalk()
			] ).then( function() {
				$( '#' + fieldset_progressbar.getElementId() ).remove();
				$( '#sparkle-form-del-pr' ).append(
					notice_success.$element
				);
				setTimeout( function() {
					window.location.reload();
				}, 1000 );
			} );
			
		}
		
		// Hooks/links
		textinput_nominate_rationale.focus();
		textinput_nominate_rationale.on( 'enter', function () {
			nominate();
		} );
		button_nominate.on( 'click', function() {
			nominate();
		} );
		textinput_endorse_rationale.on( 'enter', function() {
			endorse();
		} );
		button_endorse.on( 'click', function() {
			endorse();
		} );
		textinput_blp_rationale.on( 'enter', function() {
			blp_nominate();
		} );
		button_blp.on( 'click', function() {
			blp_nominate();
		} );
		
	} );
	
}

// Loads modules
function load() {
	
	// Dropdown
	$( '#p-cactions' ).after( `
		<nav class="mw-portlet mw-portlet-cactions vector-menu-dropdown-noicon vector-menu vector-menu-dropdown" id="sparkle-actions">
			<input type="checkbox" class="vector-menu-checkbox" />
			<h3>✨</h3>
			<div class="vector-menu-content">
				<ul id="sparkle-actions-list" class="vector-menu-content-list">
				</ul>
			</div>
		</nav>
	` );
	
	// Del/Pr
	if (
		mw.config.get( 'wgIsProbablyEditable' ) &&
		[ 0, 2, 6, 108 ].includes( mw.config.get( 'wgNamespaceNumber' ) )
	) {
		$( '#sparkle-actions-list' ).append( `
			<li>
				<a id="sparkle-action-del-pr"
				class="mw-list-item"
				title="Nominate for proposed deletion">
					Del/Pr
				</a>
			</li>
		` );
		$( '#sparkle-action-del-pr' ).click( function() { del_pr(); } );
	}
	
	// Load modules
	mw.loader.load( [
		'mediawiki.api',
		'mediawiki.util',
		'oojs-ui-core',
		'oojs-ui-widgets'
	] );
	
}

jQuery( load );

// </syntaxhighlight>