User:Alexis Jazz/Kill-It-With-Fire.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. |
![]() | This user script seems to have a documentation page at User:Alexis Jazz/Kill-It-With-Fire. |
/*
* Kill-It-With-Fire
* Forked from Restore-a-lot which was in turn forked from Cat-a-lot
* Undo multiple edits from Special:Contributions
* See ? for documentation (that inspires confidence)
*
* @rev 00:13, 10 February 2018 (UTC)
* @author Originally by Magnus Manske (2007)
* @author RegExes by Ilmari Karonen (2010)
* @author Completely rewritten by DieBuche (2010-2012)
* @author Rillke (2012-2014)
* @author Perhelion (2017)
* @author Alexis Jazz is a forking idiot (Restore-a-lot, 2020)
* @author Various fixes by Zhuyifei1999 (Restore-a-lot, 2020)
* @author Alexis Jazz is screwing around again (Kill-It-With-Fire, 2022)
* <nowiki>
*/
/* global jQuery, mediaWiki */
/* eslint one-var:0, vars-on-top:0, no-underscore-dangle:0, valid-jsdoc:0,
curly:0, camelcase:0, no-useless-escape:0, no-alert:0 */ // extends: wikimedia
/* jshint unused:true, forin:false, smarttabs:true, loopfunc:true, browser:true */
( function ( $, mw ) {
'use strict';
var ns = mw.config.get( 'wgNamespaceNumber' );
var msgs = {
// Preferences
// new: added 2012-09-19. Please translate.
// Use user language for i18n
'restore-a-lot-comment-label': 'Custom undo reason',
'restore-a-lot-edit-question': 'Why is this undo necessary?',
// Progress
// 'restore-a-lot-loading': 'Loading …',
'restore-a-lot-editing': 'Undoing edit',
'restore-a-lot-of': 'of ',
'restore-a-lot-skipped-server': 'The following {{PLURAL:$1|1=page|$1 pages}} couldn’t be changed, since there were problems connecting to the server:',
'restore-a-lot-all-done': 'Selected revisions have been undone.',
'restore-a-lot-done': 'Done!', // mw.msg("Feedback-close")
'restore-a-lot-undelete': 'Revision undone',
// as in 17 files selected
'restore-a-lot-files-selected': '{{PLURAL:$1|1=One revision|$1 revisions}} selected.',
// Actions
'restore-a-lot-undeletelink': 'Undo',
'restore-a-lot-instructions': 'Select revisions, hit undo.',
'restore-a-lot-select': 'Select',
'restore-a-lot-all': 'all',
'restore-a-lot-none': 'none',
// 'restore-a-lot-none-selected': 'No files selected!', 'Ooui-selectfile-placeholder'
// Summaries (project language):
'restore-a-lot-summary': 'Undo revision [[Special:Diff/REVID|REVID]] by [[User:USER|USER]] [Undone using [[w:en:WP:Kill-It-With-Fire|Kill-It-With-Fire]]]',
};
mw.messages.set( msgs );
function msg( /* params */ ) {
var args = Array.prototype.slice.call( arguments, 0 );
args[ 0 ] = 'restore-a-lot-' + args[ 0 ];
return ( args.length === 1 ) ?
mw.message( args[ 0 ] ).plain() :
mw.message.apply( mw.message, args ).parse();
}
mw.util.addCSS(
'#killitwithfire {bottom: 0;display: block;position: fixed;left: 0;z-index: 100;padding: 5px;box-shadow: 0 2px 4px rgba(0,0,0,0.5); background-color: #FEF6E7;}'+
'#killitwithfire.ui-resizable {min-width:15em;}'+
'#killitwithfire_data, #killitwithfire_mark_counter {display: none;}'+
'#killitwithfire_data ul {list-style-image: none;list-style-type: none;margin: 0 0 0 5px;}'+
//'#killitwithfire_selections, #killitwithfire_mark_counter, #killitwithfire_settings {background: url(/w/skins/Vector/images/portal-break.png) no-repeat;padding: 5px;}'+
'#killitwithfire_remove {font-weight: bold;display: block;}'+
'a {cursor:pointer;}'+
'.killitwithfire_move, .killitwithfire_action {font-weight: bold;}'+
'.killitwithfire_feedback {border: 1px #A9DE16 solid !important;background: #EAF2CB /*url(//up.wiki.x.io/wikipedia/commons/d/de/Ajax-loader.gif)*/ no-repeat 8px 14px !important;padding-left: 2.85em !important;padding-top: 10px !important;font-size: 1.1em !important;}'+
'.killitwithfire_done {background-image: url(//up.wiki.x.io/wikipedia/commons/thumb/0/0e/Dialog-apply.svg/50px-Dialog-apply.svg.png) !important;background-position: 8px 50% !important;padding-top: 0 !important;}'+
'#killitwithfire_searchcatname,#killitwithfire_comment {font-size: 112%;margin: -5px 0 5px -5px;width: 100%;}'+
'.skin-vector #killitwithfire {font-size: .75em;}'+
'.killitwithfire_markAsDone {background-color: #BBB !important;}'+
'.killitwithfire_selected {background-color: #DF6 !important;}'+
'#killitwithfire_no_found, #killitwithfire_last_selected, #killitwithfire_settings {font-weight:bold;}'+
'#killitwithfire_last_selected {outline:1px dotted #999;}'+
'#killitwithfire_category_list table {border-collapse: collapse;}'+
'#killitwithfire_category_list tr:hover {background-color: #fc3;}'+
'#killitwithfire_category_list {overflow: auto;}');
// There is only one Restore-a-lot on one page
var $body, $container, $dataContainer, $markCounter, $instructions, $selections,
$selectFiles, $selectPages, $selectNone, $selectInvert, $head, $link;
var KIWF = mw.libs.restoreALot = {
apiUrl: mw.util.wikiScript( 'api' ),
origin: '',
searchmode: false,
version: '4.77',
setHeight: 450,
settings: '',
init: function () {
$body = $( document.body );
$container = $( '<div>' )
.attr( 'id', 'killitwithfire' )
.appendTo( $body );
$dataContainer = $( '<div>' )
.attr( 'id', 'killitwithfire_data' )
.appendTo( $container );
$markCounter = $( '<div>' )
.attr( 'id', 'killitwithfire_mark_counter' )
.appendTo( $dataContainer );
$instructions = $( '<div>' )
.attr( 'id', 'killitwithfire_selections' )
.text( msg( 'instructions' ) )
.appendTo( $dataContainer );
$selections = $( '<div>' )
.attr( 'id', 'killitwithfire_selections' )
.text( msg( 'select' ) + ':' )
.appendTo( $dataContainer );
$head = $( '<div>' )
.attr( 'id', 'killitwithfire_head' )
.appendTo( $container );
$link = $( '<a>' )
.attr( 'id', 'killitwithfire_toggle' )
.text( 'Kill-It-With-Fire' )
.appendTo( $head );
$container.one( 'mouseover', function () { // Try load on demand earliest as possible
mw.loader.load( [ 'jquery.ui'] );
} );
$( '<a>' )
// .attr( 'id', 'killitwithfire_select_all' )
.text( msg( 'all' ) )
.on( 'click', function () {
KIWF.toggleAll( true );
} )
.appendTo( $selections.append( ' ' ) );
if ( this.settings.editpages ) {
$selectFiles = $( '<a>' )
.on( 'click', function () {
KIWF.toggleAll( 'files' );
} );
$selectPages = $( '<a>' )
.on( 'click', function () {
KIWF.toggleAll( 'pages' );
} );
$selections.append( $( '<span>' ).hide().append( [ ' / ', $selectFiles, ' / ', $selectPages ] ) );
}
$selectNone = $( '<a>' )
// .attr( 'id', 'killitwithfire_select_none' )
.text( msg( 'none' ) )
.on( 'click', function () {
KIWF.toggleAll( false );
} );
$selectInvert = $( '<a>' )
.on( 'click', function () {
KIWF.toggleAll( null );
} );
$selections.append( [ ' • ', $selectNone, ' • ', $selectInvert] );
$('#killitwithfire_data').append(
$( '<div>' ).append( [
$( '<input>' )
.attr( {
id: 'killitwithfire_comment',
tabindex: -1,
type: 'text',
placeholder: msg( 'comment-label' )
} )
] )
);
$selections.append( $( '<a>' )
.text( msg( 'undeletelink' ) )
.on( 'click', function () {
KIWF.doSomething();
} )
.addClass( 'killitwithfire_action ui-button' )
.css( { margin: '5px', padding: '2px' } )
);
$link
.on( 'click', function () {
$( this ).toggleClass( 'killitwithfire_enabled' );
// Load autocomplete on demand
mw.loader.using( 'jquery.ui' );
if ( !KIWF.executed ) {
$.when( mw.loader.using( [
'jquery.ui',
'jquery.ui',
'jquery.ui',
'mediawiki.api',
'mediawiki.jqueryMsg'
] ), $.ready )
.then( function () {
return new mw.Api().loadMessagesIfMissing( [
'Cancel',
'Mobile-frontend-return-to-page',
'Ooui-selectfile-placeholder',
// 'Visualeditor-clipboard-copy',
'Prefs-files',
'Categories',
'Checkbox-invert',
//'Apifeatureusage-warnings'
] );
} ).then( function () {
KIWF.run();
} );
} else { KIWF.run(); }
} );
mw.loader.using( 'mediawiki.cookie', function () { // Let catAlot stay open
var val = mw.cookie.get( 'catAlotO' );
if ( val && Number( val ) === ns ) { $link.click(); }
}
);
},
findAllLabels: function ( searchmode ) {
// It's possible to allow any kind of pages as well but what happens if you click on "select all" and don't expect it
switch ( searchmode ) {
case 'contribs':
this.labels = this.labels.add( $( 'div.mw-body-content li' ) );
break;
}
},
/**
* @brief Get rev from selected pages
* @return [array] touple of page rev and $object
*/
getMarkedLabels: function () {
this.selectedLabels = this.labels.filter( '.killitwithfire_selected:visible' );
return this.selectedLabels.map( function () {
// this might select too much in cases currently unknown, does it even matter?
// it was 'a.new[class$="title"]' previously but this doesn't work on DRs
var label = $( this ), revHref = label[0].querySelectorAll('.mw-changeslist-diff')[0].attributes.href;
return [ [ revHref, label ] ];
} );
},
updateSelectionCounter: function () {
this.selectedLabels = this.labels.filter( '.killitwithfire_selected:visible' );
var first = $markCounter.is( ':hidden' );
$markCounter
.html( msg( 'files-selected', this.selectedLabels.length ) )
.show();
if ( first && !$dataContainer.is( ':hidden' ) ) { // Workaround to fix position glitch
first = $markCounter.innerHeight();
$container
.offset( { top: $container.offset().top - first } )
.height( $container.height() + first );
$( window ).on( 'beforeunload', function () {
if ( KIWF.labels.filter( '.killitwithfire_selected:visible' )[ 0 ] ) { return 'You have pages selected!'; } // There is a default message in the browser
} );
}
},
makeClickable: function () {
this.labels = $();
this.pageLabels = $(); // only for distinct all selections
this.findAllLabels( this.searchmode );
this.labels.KIWFShiftClick( function () {
KIWF.updateSelectionCounter();
} )
.addClass( 'killitwithfire_label' );
},
toggleAll: function ( select ) {
if ( typeof select === 'string' && this.pageLabels[ 0 ] ) {
this.pageLabels.toggleClass( 'killitwithfire_selected', true );
if ( select === 'files' ) // pages get deselected
{ this.labels.toggleClass( 'killitwithfire_selected' ); }
} else {
// invert / none / all
this.labels.toggleClass( 'killitwithfire_selected', select );
}
this.updateSelectionCounter();
},
undeleteFile: function ( rev ) {
var revIDToUndo;
revIDToUndo = new mw.Uri(window.location.protocol+'//'+window.location.host+rev[0].nodeValue).query.oldid;
var sumCmt; // summary comment
sumCmt = msg( 'summary' ).replace(/USER/g,mw.config.get('wgRelevantUserName')).replace(/REVID/g,revIDToUndo);
sumCmt += this.summary ? ' ' + this.summary : '';
var data = {
action: 'edit',
summary: sumCmt,
title: new mw.Uri(window.location.protocol+'//'+window.location.host+rev[0].nodeValue).query.title.replace(/_/g,' '),
undo: revIDToUndo,
token: this.edittoken
};
this.doAPICall( data, function ( r ) {
delete KIWF.XHR[ rev[ 0 ] ];
return KIWF.updateCounter( r );
} );
this.markAsDone( rev[ 1 ] );
},
markAsDone: function ( label ) {
label.addClass( 'killitwithfire_markAsDone' ).append( '<br>' + msg( 'undelete' ) );
},
updateCounter: function () {
this.counterCurrent++;
if ( this.counterCurrent > this.counterNeeded ) { this.displayResult(); } else { this.domCounter.text( this.counterCurrent ); }
},
displayResult: function () {
document.body.style.cursor = 'auto';
this.progressDialog.parent()
.addClass( 'killitwithfire_done' )
.find( '.ui-dialog-buttonpane button span' ).eq( 0 )
.text( mw.msg( 'Mobile-frontend-return-to-page' ) );
var rep = this.domCounter.parent()
.height( 'auto' )
.html( '<h3>' + msg( 'done' ) + '</h3>' )
.append( msg( 'all-done' ) + '<br>' );
if ( this.connectionError.length ) {
rep.append( '<h5>' + msg( 'skipped-server', this.connectionError.length ) + '</h5>' )
.append( this.connectionError.join( '<br>' ) );
}
},
doSomething: function () {
var pages = this.getMarkedLabels();
if ( !pages.length ) { return alert( mw.msg( 'Ooui-selectfile-placeholder' ) ); }
this.connectionError = [];
this.counterCurrent = 1;
this.counterNeeded = pages.length;
this.XHR = {};
this.cancelled = 0;
this.summary = '';
if ( $( '#killitwithfire_comment' )[0].value != '' ) { this.summary = $( '#killitwithfire_comment' )[0].value; } // TODO custom pre-value
if ( this.summary !== null ) {
mw.loader.using( [ 'jquery.ui', 'mediawiki.util' ], function () {
KIWF.showProgress();
if ( !KIWF.cancelled ) {
KIWF.doAPICall( {
meta: 'tokens',
}, function ( result ) {
if ( !result || !result.query ) { return; }
KIWF.edittoken = result.query.tokens.csrftoken;
pages = Array.from( pages ).map( function ( page ) {
return function () {
var defer = $.Deferred();
setTimeout( function timer() {
KIWF.undeleteFile( page );
defer.resolve();
}, 1500 );
return defer;
};
} );
var defer = pages.shift()();
pages.map( function ( pagefunc ) {
defer = defer.then( pagefunc );
} );
} );
}
} );
}
},
doAPICall: function ( params, callback ) {
params = $.extend( {
action: 'query',
format: 'json'
}, params );
var i = 0,
apiUrl = this.apiUrl,
doCall,
handleError = function ( jqXHR, textStatus, errorThrown ) {
mw.log( 'Error: ', jqXHR, textStatus, errorThrown );
if ( i < 4 ) {
window.setTimeout( doCall, 300 );
i++;
} else if ( params.undo ) {
this.connectionError.push( params.undo );
this.updateCounter();
return;
}
};
doCall = function () {
var xhr = $.ajax( {
url: apiUrl,
cache: false,
dataType: 'json',
data: params,
type: 'POST',
success: callback,
error: handleError
} );
if ( params.action === 'undelete' && !KIWF.cancelled ) { KIWF.XHR[ params.undo ] = xhr; }
};
doCall();
},
doAbort: function () {
for ( var t in this.XHR ) { this.XHR[ t ].abort(); }
if ( this.cancelled ) { // still not for undo
this.progressDialog.remove();
this.toggleAll( false );
$head.last().show();
}
this.cancelled = 1;
},
showProgress: function () {
document.body.style.cursor = 'wait';
this.progressDialog = $( '<div>' )
.html( ' ' + msg( 'editing' ) + ' <span id="killitwithfire_current">' + KIWF.counterCurrent + '</span> ' + msg( 'of' ) + KIWF.counterNeeded )
.dialog( {
width: 450,
height: 180,
minHeight: 90,
modal: true,
resizable: false,
draggable: false,
// closeOnEscape: true,
dialogClass: 'killitwithfire_feedback',
buttons: [ {
text: mw.msg( 'Cancel' ), // Stops all actions
click: function () {
$( this ).dialog( 'close' );
}
} ],
close: function () {
KIWF.cancelled = 1;
KIWF.doAbort();
$( this ).remove();
},
open: function ( event, ui ) { // Workaround modify
ui = $( this ).parent();
ui.find( '.ui-dialog-titlebar' ).hide();
ui.find( '.ui-dialog-buttonpane.ui-widget-content' )
.removeClass( 'ui-widget-content' );
/* .find( 'span' ).css( { fontSize: '90%' } )*/
}
} );
if ( $head.children().length < 3 ) {
$( '<span>' )
.css( {
'float': 'right',
fontSize: '75%'
} );
}
this.domCounter = $( '#killitwithfire_current' );
},
minimize: function ( e ) {
KIWF.top = Math.max( 0, $container.position().top );
KIWF.height = $container.height();
$dataContainer.hide();
$container.animate( {
height: $head.height(),
top: $( window ).height() - $head.height() * 1.4
}, function () {
$( e.target ).one( 'click', KIWF.maximize );
} );
},
maximize: function ( e ) {
$dataContainer.show();
$container.animate( {
top: KIWF.top,
height: KIWF.height
}, function () {
$( e.target ).one( 'click', KIWF.minimize );
} );
},
run: function () {
if ( $( '.killitwithfire_enabled' )[ 0 ] ) {
this.makeClickable();
if ( !this.executed ) { // only once
$selectInvert.text( mw.msg( 'Checkbox-invert' ) );
if ( this.settings.editpages && this.pageLabels[ 0 ] ) {
$selectFiles.text( mw.msg( 'Prefs-files' ) );
$selectPages.text( mw.msg( 'Categories' ) ).parent().show();
}
//$link.after( $( '<a>' )
// .text( '–' )
// .css( { fontWeight: 'bold', marginLeft: '.7em' } )
// .one( 'click', this.minimize ),
$link.after( $( '<a href="http://commons.wikimedia.org/wiki/Special:MyLanguage/Help:Gadget-Restore-a-lot">' )
.text( 'Kill-It-With-Fire 0.1' )
.css( { float: 'right' } )
);
}
$dataContainer.show();
$container.one( 'mouseover', function () {
$( this )
.resizable( {
handles: 'n',
alsoResize: '#killitwithfire_category_list',
start: function ( e, ui ) { // Otherwise box get static if sametime resize with draggable
ui.helper.css( {
top: ui.helper.offset().top - $( window ).scrollTop(),
position: 'fixed'
} );
},
} )
.draggable( {
cursor: 'move',
start: function ( e, ui ) {
ui.helper.on( 'click.prevent',
function ( e ) { e.preventDefault(); }
);
ui.helper.css( 'height', ui.helper.height() );
},
stop: function ( e, ui ) {
setTimeout(
function () {
ui.helper.off( 'click.prevent' );
}, 300
);
}
} )
.one( 'mousedown', function () {
$container.height( $container.height() ); // Workaround to calculate
} );
} );
$link.html( $( '<span>' )
.text( '×' )
.css( { font: 'bold 2em monospace', lineHeight: '.75em' } )
);
$link.next().show();
if ( this.cancelled ) { $head.last().show(); }
mw.cookie.set( 'catAlotO', ns ); // Let stay open on new window
} else { // Reset
$dataContainer.hide();
$container
.draggable( 'destroy' )
.resizable( 'destroy' )
.removeAttr( 'style' );
// Unbind click handlers
this.labels.off( 'click.KIWF' );
this.setHeight = 450;
$link.text( 'Kill-It-With-Fire' )
.nextAll().hide();
this.executed = 1;
mw.cookie.set( 'catAlotO', null );
}
},
};
// The gadget is not immediately needed, so let the page load normally
window.setTimeout( function () {
switch ( ns ) {
case -1:
KIWF.searchmode = {
'Contributions': 'contribs',
}[ mw.config.get( 'wgCanonicalSpecialPageName' ) ];
break;
}
if ( KIWF.searchmode ) {
var maybeLaunch = function () {
function init() {
$( function () {
KIWF.init();
} );
}
mw.loader.using( [ 'user' ], init, init );
};
maybeLaunch();
}
}, 400 );
/**
* When clicking a restore-a-lot label with Shift pressed, select all labels between the current and last-clicked one.
*/
$.fn.KIWFShiftClick = function ( cb ) {
var prevCheckbox = null,
$box = this;
// When our boxes are clicked..
$box.on( 'click.KIWF', function ( e ) {
// Prevent following the link and text selection
if ( !e.ctrlKey ) { e.preventDefault(); }
// Highlight last selected
$( '#killitwithfire_last_selected' )
.removeAttr( 'id' );
var $thisControl = $( e.target ),
method;
if ( !$thisControl.hasClass( 'killitwithfire_label' ) ) { $thisControl = $thisControl.parents( '.killitwithfire_label' ); }
$thisControl.attr( 'id', 'killitwithfire_last_selected' )
.toggleClass( 'killitwithfire_selected' );
// And one has been clicked before…
if ( prevCheckbox !== null && e.shiftKey ) {
method = $thisControl.hasClass( 'killitwithfire_selected' ) ? 'addClass' : 'removeClass';
// Check or uncheck this one and all in-between checkboxes
$box.slice(
Math.min( $box.index( prevCheckbox ), $box.index( $thisControl ) ),
Math.max( $box.index( prevCheckbox ), $box.index( $thisControl ) ) + 1
)[ method ]( 'killitwithfire_selected' );
}
// Either way, update the prevCheckbox variable to the one clicked now
prevCheckbox = $thisControl;
if ( $.isFunction( cb ) ) { cb(); }
} );
return $box;
};
}( jQuery, mediaWiki ) );
// </nowiki>