// <nowiki>
// Script to make closing discussions easier
// @author DannyS712
// based on a script by Abelmoschus Esculentus, but completely rewritten from scratch
/* jshint maxerr: 999 */
$(() => {
var DiscussionCloser = {};
window.DiscussionCloser = DiscussionCloser;
DiscussionCloser.config = {
version: 'DEV',
debug: true
};
DiscussionCloser.init = function () {
mw.loader.using(
[ 'mediawiki.util', 'mediawiki.api' ],
DiscussionCloser.run
);
};
DiscussionCloser.debug = function () {
if ( DiscussionCloser.config.debug ) {
console.log.apply( null, arguments );
}
};
DiscussionCloser.run = function () {
DiscussionCloser.debug( 'Starting DiscussionCloser' );
DiscussionCloser.addCSS();
DiscussionCloser.addFakeLinks();
};
DiscussionCloser.onErrHandler = function () {
// Shared error handler
alert( 'Something went wrong' );
console.log.apply( null, arguments );
};
DiscussionCloser.addCSS = function () {
mw.util.addCSS(`
#DiscussionCloser-wrapper {
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: rgba(0,0,0,0.4);
}
#DiscussionCloser-interface {
background-color: #d6d6d6;
margin: 10% auto;
padding: 2px 20px;
border: 1px solid #888;
width: 80%;
max-width: 60em;
font-size: 90%;
}
#DiscussionCloser-interface-body {
min-height: 7em;
width: 875px;
height: 400px;
overflow-y: scroll;
}
#DiscussionCloser-interface-footer {
min-height: 3em;
}
.DiscussionCloser-bottom {
margin-bottom: 0.5em;
}
#DiscussionCloser-preview-raw {
background-color: white;
}
#DiscussionCloser-preview-raw span.mw-editsection {
display: none;
}
input[name=DiscussionCloser-radios] {
margin-left: 7px;
margin-right: 2px;
}
`);
};
DiscussionCloser.addFakeLinks = function () {
var $sectionHeadings = $( '#mw-content-text' ).find( 'h1,h2,h3,h4,h5,h6' );
var SECTION_FROM_URL_REGEX = /§ion=(\d+)/;
$sectionHeadings.each( function ( idx, header ) {
var $editSection = $( header ).find( '.mw-editsection' );
// console.log( $editSection );
var editLinkURL = $editSection.children( 'a:first' ).attr( 'href' );
// console.log( editLinkURL );
var sectionNum = SECTION_FROM_URL_REGEX.exec( editLinkURL );
if ( sectionNum === null ) {
// Something went wrong
return;
}
sectionNum = parseInt( sectionNum[1] );
// console.log( sectionNum );
var $closeBracket = $editSection.children().last();
// console.log( $closeBracket );
$closeBracket.before(
"<span style='color: #54595d'> | </span>",
$( '<span>' ).append(
$( '<a>' )
.text( 'Close discussion' )
.click( function () {
console.log( 'Closing discussion for section number: ' + sectionNum );
console.log( $( this ) );
DiscussionCloser.getSectionContent( sectionNum ).then(
function ( currentContent ) {
console.log( currentContent );
DiscussionCloser.showForSection( sectionNum, currentContent );
}
);
} )
)
);
} );
};
DiscussionCloser.showForSection = function ( sectionNum, currentContent ) {
$( 'body' ).prepend( DiscussionCloser.addInterface( sectionNum, currentContent ) );
document.getElementById( 'DiscussionCloser-interface' ).addEventListener(
'keydown',
function ( key ) {
if ( key.ctrlKey && ( key.keyCode == 10 || key.keyCode == 13 ) ) {
document.getElementById( 'DiscussionCloser-submit' ).click();
} else if ( key.keyCode == 27 ) {
document.getElementById( 'DiscussionCloser-cancel' ).click();
}
}
);
};
DiscussionCloser.getTemplateInfo = function ( template ) {
// object with
// - `supportsStatus` (bool)
// - `topText` (string including %RESULT% and optionally %STATUS% )
// - `bottomText` (string)
// - `label` (string to show in the interface)
var templateInfo = {
ArchiveTop: {
supportsStatus: true,
topText: '{{Archive top |status=%STATUS% |result=%RESULT% }}',
bottomText: '{{Archive bottom}}',
label: 'Generic archive'
},
ArchiveTopRed: {
supportsStatus: true,
topText: '{{Archive top red |status=%STATUS% |result=%RESULT% }}',
bottomText: '{{Archive bottom}}',
label: 'Generic (red)'
},
ArchiveTopYellow: {
supportsStatus: true,
topText: '{{Archive top yellow |status=%STATUS% |result=%RESULT% }}',
bottomText: '{{Archive bottom}}',
label: 'Generic (yellow)'
},
ArchiveTopGreen: {
supportsStatus: true,
topText: '{{Archive top green |status=%STATUS% |result=%RESULT% }}',
bottomText: '{{Archive bottom}}',
label: 'Generic (green)'
},
ClosedRFCTop: {
supportsStatus: false,
topText: '{{Closed rfc top |result=%RESULT% }}',
bottomText: '{{closed rfc bottom}}',
label: 'Closed RFC'
},
RequestedMove: {
supportsStatus: false,
topText: '{{subst:RM top |result=%RESULT% }}',
bottomText: '{{subst:RM bottom}}',
label: 'Closed RM'
},
HiddenArchive: {
supportsStatus: false,
topText: '{{Hidden archive top |result=%RESULT% }}',
bottomText: '{{Hidden archive bottom}}',
label: 'Hidden archive'
}
};
if ( template === false ) {
return templateInfo;
}
return templateInfo[ template ];
};
DiscussionCloser.onTopTemplateChange = function ( newTemplate ) {
var $statusInput = $( '#DiscussionCloser-status-text' );
if ( DiscussionCloser.getTemplateInfo( newTemplate ).supportsStatus ) {
$statusInput.prop( 'disabled', false );
} else {
$statusInput.prop( 'disabled', true );
}
};
DiscussionCloser.addPreview = function ( sectionNum, currentContent ) {
var newText = DiscussionCloser.getNewSectionText( sectionNum, currentContent );
new mw.Api().get( {
action: 'parse',
title: mw.config.get( 'wgPageName' ),
text: newText,
prop: 'text',
pst: '1',
formatversion: 2
} ).then(
function ( result ) {
// console.log( result );
var parsedText = result.parse.text;
$( '#DiscussionCloser-preview-raw' ).html( parsedText );
$( '#DiscussionCloser-preview-wrapper' ).show();
},
DiscussionCloser.onErrHandler
);
};
DiscussionCloser.hidePreview = function () {
$( '#DiscussionCloser-preview-wrapper' ).hide();
};
DiscussionCloser.closeDiscussion = function ( sectionNum, currentContent ) {
console.log( 'Should try and close discussion now' );
var newText = DiscussionCloser.getNewSectionText( sectionNum, currentContent );
var summary = DiscussionCloser.getEditSummary( currentContent );
var editParams = {
action: 'edit',
title: mw.config.get( 'wgPageName' ),
section: sectionNum,
text: newText,
summary: summary,
notminor: true,
nocreate: true,
format: 'json',
formatversion: 2
};
// console.log( editParams );
new mw.Api().postWithEditToken( editParams ).then(
function ( response ) {
console.log( response );
DiscussionCloser.afterCloseEdit( response );
},
DiscussionCloser.onErrHandler
);
};
DiscussionCloser.afterCloseEdit = function ( response ) {
if ( response && response.edit && response.edit.result && response.edit.result === 'Success' ) {
// Edit succeeded
alert( 'Edit saved!' );
$( '#DiscussionCloser-wrapper' ).remove();
return;
}
alert( 'Something might have gone wrong' );
};
DiscussionCloser.getEditSummary = function ( currentContent ) {
// Either `/* sectionName */ ` or an empty string, for the start of the edit summary
var maybeMatch = currentContent.match( /^=+\s*(.*?)\s*=/ );
var sectionName = '';
if ( maybeMatch !== null ) {
sectionName = '/* ' + maybeMatch[1] + ' */ ';
}
var userInputSummary = $( '#DiscussionCloser-edit-summary' ).val();
if ( userInputSummary === '' ) {
// default
userInputSummary = 'Closing discussion';
}
var fullSummary = sectionName + userInputSummary + ' (with DiscussionCloser ' + DiscussionCloser.config.version + ')';
return fullSummary;
};
DiscussionCloser.getNewSectionText = function ( sectionNum, currentContent ) {
var closeTemplate = $( 'input[name="DiscussionCloser-radios"]:checked' ).attr( 'value' );
var closeTemplateInfo = DiscussionCloser.getTemplateInfo( closeTemplate );
var topText = closeTemplateInfo.topText;
if ( closeTemplateInfo.supportsStatus ) {
topText = topText.replace(
'%STATUS%',
$( '#DiscussionCloser-status-text' ).val()
);
}
topText = topText.replace(
'%RESULT%',
$( '#DiscussionCloser-result-text' ).val()
);
var newText = currentContent.replace(
/^(=.*?=\n)/,
'$1\n' + topText + '\n\n'
);
newText = newText + '\n\n' + closeTemplateInfo.bottomText;
// console.log( newText );
return newText;
};
DiscussionCloser.getSectionContent = function ( sectionNum ) {
return new Promise( function ( resolve ) {
new mw.Api().get( {
action: 'query',
prop: 'revisions',
rvprop: 'content',
rvslots: '*',
rvsection: sectionNum,
titles: mw.config.get( 'wgPageName' ),
format: 'json',
formatversion: 2
} ).then(
function ( response ) {
console.log( response );
var currentContent = response.query.pages[0].revisions[0].slots.main.content;
resolve( currentContent );
},
DiscussionCloser.onErrHandler
);
} );
};
DiscussionCloser.addInterface = function ( sectionNum, currentContent ) {
var $header = $( '<h4>' )
.attr( 'id', 'DiscussionCloser-interface-header' )
.text( 'DiscussionCloser' );
var $body = $( '<div>' )
.attr( 'id', 'DiscussionCloser-interface-body' );
var $footer = $( '<div>' )
.attr( 'id', 'DiscussionCloser-interface-footer' );
var $interface = $( '<div>' )
.attr( 'id', 'DiscussionCloser-interface' )
.append(
$header,
$( '<hr>' ),
$body,
$( '<hr>' ),
$footer
);
var $wrapper = $( '<div>' )
.attr( 'id', 'DiscussionCloser-wrapper' )
.append( $interface );
// Body content
var $templateOptions = $( '<div>' )
.addClass( 'DiscussionCloser-bottom' )
.append(
$( '<label>' ).text( 'Options: ' ),
$( '<input>' )
.attr( 'type', 'radio' )
.attr( 'name', 'DiscussionCloser-radios' )
.attr( 'value', 'ArchiveTop' )
.attr( 'checked', 'true' ),
$( '<label>' ).text( 'Generic' )
);
var allTemplates = DiscussionCloser.getTemplateInfo( false );
Object.keys( allTemplates ).forEach(
function ( templateName ) {
if ( templateName !== 'ArchiveTop' ) {
$templateOptions.append(
$( '<input>' )
.attr( 'type', 'radio' )
.attr( 'name', 'DiscussionCloser-radios' )
.attr( 'value', templateName ),
$( '<label>' ).text( allTemplates[templateName].label )
);
}
}
);
$templateOptions.children( 'input' ).on( 'change', function () {
// console.log( 'Selection changed' );
// console.log( this );
DiscussionCloser.onTopTemplateChange( $( this ).attr( 'value' ) );
} );
var $optionalStatus = $( '<div>' )
.addClass( 'DiscussionCloser-bottom' )
.append(
$( '<label>' ).text( 'Status (optional): ' ),
$( '<input>' )
.attr( 'id', 'DiscussionCloser-status-text' )
.attr( 'type', 'text' )
);
var $closingComment = $( '<div>' )
.addClass( 'DiscussionCloser-bottom' )
.append(
$( '<label>' ).text( 'Discussion result' ),
$( '<textarea>' )
.attr( 'id', 'DiscussionCloser-result-text' )
.attr( 'rows', 15 )
.css( 'resize', 'none' )
);
var $editSummary = $( '<div>' )
.addClass( 'DiscussionCloser-bottom' )
.append(
$( '<label>' ).text( 'Edit summary ("Closing discussion" if left blank): ' ),
$( '<input>' )
.attr( 'id', 'DiscussionCloser-edit-summary' )
.attr( 'type', 'text' )
);
var $hidePreview = $( '<button>' )
.attr( 'id', 'DiscussionCloser-preview-hide' )
.text( 'Hide preview' );
$hidePreview.click( DiscussionCloser.hidePreview );
var $previewDisplay = $( '<div>' )
.attr( 'id', 'DiscussionCloser-preview-wrapper' )
.addClass( 'DiscussionCloser-bottom' )
.append(
$( '<label>' ).text( 'Preview:' ),
$( '<div>' ).attr( 'id', 'DiscussionCloser-preview-raw' ),
$hidePreview
);
$previewDisplay.hide();
$body.append( $templateOptions, $optionalStatus, $closingComment, $editSummary, $previewDisplay );
// Footer buttons
var $submit = $( '<button>' )
.attr( 'id', 'DiscussionCloser-submit' )
.text( 'Close discussion' );
var $preview = $( '<button>' )
.attr( 'id', 'DiscussionCloser-preview' )
.text( 'View preview' );
var $cancel = $( '<button>' )
.attr( 'id', 'DiscussionCloser-cancel' )
.text( 'Cancel' );
$footer.append( $submit, $preview, $cancel );
// console.log( 'currentContent is: ' + currentContent );
$submit.click( function () {
// console.log( 'currentContent is: ' + currentContent );
DiscussionCloser.closeDiscussion( sectionNum, currentContent );
} );
$preview.click( function () {
// console.log( 'currentContent is: ' + currentContent );
DiscussionCloser.addPreview( sectionNum, currentContent );
} );
$cancel.click( function () {
// Can't just $wrapper.remove() since at this point it isn't added yet
$( this ).parents( '#DiscussionCloser-wrapper' ).remove();
} );
return $wrapper;
};
});
$( document ).ready( function () {
var mwConfig = mw.config.get( [ 'wgAction', 'wgPageName' ] );
if ( mwConfig.wgAction !== 'view' ) {
return;
}
if (
$( '#ca-addsection' ).length > 0 ||
mwConfig.wgPageName == 'Wikipedia:Administrators\'_noticeboard/Edit_warring' ||
mwConfig.wgPageName == 'Wikipedia:In_the_news/Candidates'
) {
DiscussionCloser.init();
}
} );
// </nowiki>