User:Colin M/scripts/JWB annotated.js
Appearance
< User:Colin M | scripts
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:Colin M/scripts/JWB annotated. |
// Copied from [[User:Joeytje50/JWB.js]]
/**<nowiki>
* Install this script by pasting the following in your personal JavaScript file:
mw.loader.load('//wiki.riteme.site/w/index.php?title=User:Joeytje50/JWB.js/load.js&action=raw&ctype=text/javascript');
* Or for users on wiki.riteme.site:
importScript( 'User:Joeytje50/JWB.js/load.js' ); // Backlink: [[User:Joeytje50/JWB.js/load.js]]
* Note that this script will only run on the 'Project:AutoWikiBrowser/Script' page.
* This script is based on the downloadable AutoWikiBrowser.
*
* @licence
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
* @version 3.1.0
* @author Joeytje50
*/
//TODO: Re-enable summary box (possibly more) when not busy submitting.
//TODO: more advanced pagelist-generating options
//TODO: generate page list based on images on a page
//TODO: Split up i18n to separate files per language (in the same way MediaWiki does it)
//TODO: Add feature to perform general cleanup (<table> to {|, fullurl-links to wikilinks, removing underscores from wikilinks)
window.JWB = {}; //The main global object for the script.
/***** User verification *****/
(function() {
if (mw.config.get('wgCanonicalNamespace')+':'+mw.config.get('wgTitle') !== 'Project:AutoWikiBrowser/Script Beta' || JWB.allowed === false || mw.config.get('wgUserName') === null) {
JWB.allowed = false;
return;
}
mw.loader.load('//wiki.riteme.site/w/index.php?title=User:Joeytje50/JWB.css&action=raw&ctype=text/css', 'text/css');
mw.loader.load('mediawiki.diff.styles');
$.getScript('//wiki.riteme.site/w/index.php?title=User:Joeytje50/JWB.js/i18n.js&action=raw&ctype=text/javascript', function() {
if (JWB.allowed === true) {
JWB.init(); //init if verification has already returned true
} else if (JWB.allowed === false) {
alert(JWB.msg('not-on-list'));
}
});
//RegEx Typo Fixing
$.getScript('//wiki.riteme.site/w/index.php?title=User:Joeytje50/RETF.js&action=raw&ctype=text/javascript', function() {
$('#refreshRETF').click(RETF.load);
});
(new mw.Api()).get({
action: 'query',
titles: 'Project:AutoWikiBrowser/CheckPage',
prop: 'revisions',
meta: 'userinfo|siteinfo',
rvprop: 'content',
rvlimit: 1,
uiprop: 'groups',
siprop: 'namespaces',
indexpageids: true,
format: 'json',
}).done(function(response) {
if (response.error) {
alert('API error: ' + response.error.info);
JWB = false; //preventing further access. No verification => no access.
return;
}
JWB.ns = response.query.namespaces; //saving for later
JWB.username = response.query.userinfo.name; //preventing any "hacks" that change wgUserName or mw.config.wgUserName
var groups = response.query.userinfo.groups;
var page = response.query.pages[response.query.pageids[0]];
var users, bots;
if (response.query.pageids[0] !== '-1' && /<!--\s*enabledusersbegins\s*-->/.test(page.revisions[0]['*'])) {
var cont = page.revisions[0]['*'];
users = cont.substring(
cont.search(/<!--\s*enabledusersbegins\s*-->/),
cont.search(/<!--\s*enabledusersends\s*-->/)
).split('\n');
if (/<!--\s*enabledbots\s*-->/.test(cont)) {
bots = cont.substring(
cont.search(/<!--\s*enabledbots\s*-->/),
cont.search(/<!--\s*enabledbotsends\s*-->/)
).split('\n');
} else bots = [];
var i=0;
while (i<users.length) {
if (users[i].charAt(0) !== '*') {
users.splice(i,1);
} else {
users[i] = $.trim(users[i].substr(1));
i++;
}
}
i=0;
while (i<bots.length) {
if (bots[i].charAt(0) !== '*') {
bots.splice(i,1);
} else {
bots[i] = $.trim(bots[i].substr(1));
i++;
}
}
} else {
users = false; //fallback when page doesn't exist
}
// Temporary global debugging variables
JWB.debug = [groups.indexOf('bot'), users === false, bots && bots.indexOf(JWB.username)];
JWB.bot = groups.indexOf('bot') !== -1 && (users === false || bots.indexOf(JWB.username) !== -1);
JWB.sysop = groups.indexOf('sysop') !== -1;
if (JWB.username === "Joeytje50" && response.query.userinfo.id === 13299994) {//TEMP: Dev full access to entire interface.
JWB.bot = true;
users.push("Joeytje50");
}
if (JWB.sysop || response.query.pageids[0] === '-1' || users.indexOf(JWB.username) !== -1 || users === false) {
JWB.allowed = true;
if (JWB.messages.en) JWB.init(); //init if messages have already loaded
} else {
if (JWB.messages.en) {
//run this after messages have loaded, so the message that shows is in the user's language
alert(JWB.msg('not-on-list'));
}
JWB = false; //prevent further access
}
}).fail(function(xhr, error) {
alert(JWB.msg('verify-error') + '\n' + error);
JWB = false; //preventing further access. No verification => no access.
});
})();
/***** Global object/variables *****/
var objs = ['page', 'api', 'fn', 'pl', 'messages', 'setup', 'settings', 'ns'];
for (var i=0;i<objs.length;i++) {
JWB[objs[i]] = {};
}
JWB.summarySuffix = ' (via JWB)';
if (document.location.hostname == 'wiki.riteme.site') JWB.summarySuffix = ' (via [[WP:JWB]])'
JWB.lang = mw.config.get('wgUserLanguage');
JWB.index_php = mw.config.get('wgScript');
JWB.isStopped = true;
JWB.tooltip = window.tooltipAccessKeyPrefix || '';
JWB.configext = 'js';
if (document.location.hostname.split('.').slice(-2).join('.') == 'wikia.com' || document.location.hostname.split('.').slice(-2).join('.') == 'fandom.com') {
//it makes no sense, but Wikia does not allow users to create .js subpages of their userpage.
//Because the settings should REALLY be protected from vandalism automatically, the backup is .css
//even though this has nothing to do with CSS.
JWB.configext = 'css';
}
JWB.settingspage = 'JWB';
if (window.hasOwnProperty('JWBSETTINGS')) {
JWB.settingspage = JWBSETTINGS;
delete window.JWBSETTINGS; //clean up the global variable
}
/***** Google Analytics *****/
JWB.loadAnalytics = function() {
$.getScript('https://www.googletagmanager.com/gtag/js?id=UA-149754182-1', function() {
window.dataLayer = window.dataLayer || [];
function gtag(){
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-149754182-1', { 'anonymize_ip': true }); // anonymize_ip to comply with GDPR
});
}
/***** API functions *****/
//Main template for API calls
JWB.api.call = function(data, callback, onerror) {
data.format = 'json';
if (data.action !== 'query') data.bot = true;
$.ajax({
data: data,
dataType: 'json',
url: mw.config.get('wgScriptPath') + '/api.php',
type: 'POST',
success: function(response) {
if (response.error) {
alert('API error: ' + response.error.info);
JWB.stop();
} else {
callback(response);
}
},
error: function(xhr, error) {
alert('AJAX error: ' + error);
JWB.stop();
if (onerror) onerror();
}
});
};
//Get page diff, and process it for more interactivity
JWB.api.diff = function(callback) {
JWB.status('diff');
var editBoxInput = $('#editBoxArea').val();
var redirects = $('input.redirects:checked').val()==='follow'?'1':'0';
var data = {
'action': 'query',
'prop': 'info|revisions',
'indexpageids': true,
'titles': JWB.page.name,
'rvlimit': '1',
'rvdifftotext': editBoxInput,
'redirects': redirects
};
JWB.api.call(data, function(response) {
var pageExists = response.query.pageids[0] !== '-1';
var diff;
if (pageExists) {
var diffpage = response.query.pages[response.query.pageids[0]];
diff = diffpage.revisions[0].diff['*'];
if (diff === '') {
diff = '<h2>'+JWB.msg('no-changes-made')+'</h2>';
} else {
diff = '<table class="diff">'+
'<colgroup>'+
'<col class="diff-marker">'+
'<col class="diff-content">'+
'<col class="diff-marker">'+
'<col class="diff-content">'+
'</colgroup>'+
'<tbody>'+diff+'</tbody></table>';
}
} else {
diff = '<span style="font-weight:bold;color:red;">'+JWB.msg('page-not-exists')+'</span>';
}
$('#resultWindow').html(diff);
$('.diff-lineno').each(function() {
$(this).parent().attr('data-line',parseInt($(this).html().match(/\d+/)[0])-1).addClass('lineheader');
});
$('table.diff tr').each(function() { //add data-line attribute to every line, relative to the previous one. Used for click event.
if (!$(this).next().is('[data-line]') && !$(this).next().has('td.diff-deletedline + td.diff-empty')) {
$(this).next().attr('data-line',parseInt($(this).data('line'))+1);
} else if ($(this).next().has('td.diff-deletedline + td.diff-empty')) {
$(this).next().attr('data-line',$(this).data('line')); //copy over current data-line for deleted lines to prevent them from messing up counting.
}
});
JWB.status('done', true);
if (typeof(callback) === 'function') {
callback();
}
});
};
//Retrieve page contents/info, process them, and store information in JWB.page object.
JWB.api.get = function(pagename) {
JWB.pageCount();
if (!JWB.list[0] || JWB.isStopped) {
return JWB.stop();
}
if (pagename === '#PRE-PARSE-STOP') {
var curval = $('#articleList').val();
$('#articleList').val(curval.substr(curval.indexOf('\n') + 1));
$('#preparse').prop('checked', false);
JWB.stop();
return;
}
var redirect = $('input.redirects:checked').val();
var data = {
'action': 'query',
'prop': 'info|revisions',
'inprop': 'watched',
'intoken': 'edit|delete|protect|move|watch',
'titles': pagename,
'rvprop': 'content|timestamp|ids',
'rvlimit': '1',
'indexpageids': true,
'meta': 'userinfo',
'uiprop': 'hasmsg'
};
if (redirect=='follow'||redirect=='skip') data.redirects = true;
if (JWB.sysop) {
data.list = 'deletedrevs';
data.drprop = 'token';
}
JWB.status('load-page');
JWB.api.call(data, function(response) {
if (response.query.userinfo.hasOwnProperty('messages')) {
var view = mw.config.get('wgScriptPath') + '?title=Special:MyTalk';
var viewNew = view + '&diff=cur';
JWB.status(
'<span style="color:red;font-weight:bold;">'+
JWB.msg('status-newmsg',
'<a href="'+view+'" target="_blank">'+JWB.msg('status-talklink')+'</a>',
'<a href="'+viewNew+'" target="_blank">'+JWB.msg('status-difflink')+'</a>')+
'</span>', true);
alert(JWB.msg('new-message'));
JWB.stop();
return;
}
JWB.page = response.query.pages[response.query.pageids[0]];
JWB.page.name = JWB.list[0].split('|')[0];
var varOffset = JWB.list[0].indexOf('|') !== -1 ? JWB.list[0].indexOf('|') + 1 : 0;
JWB.page.pagevar = JWB.list[0].substr(varOffset);
JWB.page.content = JWB.page.revisions ? JWB.page.revisions[0]['*'] : '';
JWB.page.exists = !response.query.pages["-1"];
JWB.page.deletedrevs = response.query.deletedrevs;
JWB.page.watched = JWB.page.hasOwnProperty('watched');
if (response.query.redirects) {
JWB.page.name = response.query.redirects[0].to;
}
var newContent = JWB.replace(JWB.page.content);
if (JWB.stopped === true) return;
JWB.status('done', true);
var containRegex = $('#containRegex').prop('checked'), containFlags = $('#containFlags').val();
var skipContains = containRegex ? new RegExp($('#skipContains').val(), containFlags) : $('#skipContains').val();
var skipNotContains = containRegex ? new RegExp($('#skipNotContains').val(), containFlags) : $('#skipContains').val();
if (
($('#skipNoChange').prop('checked') && JWB.page.content === newContent) || //skip if no changes are made
($('#skipContains').val() && JWB.page.content.match(skipContains)) ||
($('#skipNotContains').val() && !JWB.page.content.match(skipNotContains)) ||
($('#exists-no').prop('checked') && !JWB.page.exists) ||
($('#exists-yes').prop('checked') && JWB.page.exists) ||
(redirect==='skip' && response.query.redirects) // variable redirect is defined outside this callback function.
) {
JWB.log('skip', JWB.page.name);
return JWB.next();
} else {
$('#editBoxArea').val(newContent);
$('#currentpage').html(JWB.msg('editbox-currentpage', JWB.page.name, encodeURIComponent(JWB.page.name)));
if ($('#preparse').prop('checked')) {
$('#articleList').val($.trim($('#articleList').val()) + '\n' + JWB.list[0]); //move current page to the bottom
JWB.next();
return;
} else if (JWB.bot && $('#autosave').prop('checked')) {
JWB.api.diff(function() {
//timeout will take #throttle's value * 1000, if it's a number above 0. Currently defaults to 0.
setTimeout(JWB.api.submit, Math.max(+$('#throttle').val() || 0, 0) * 1000, JWB.page.name);
});
} else {
JWB.api.diff();
}
}
JWB.updateButtons();
});
};
//Some functions with self-explanatory names:
JWB.api.submit = function(page) {
JWB.status('submit');
var summary = $('#summary').val();
if ($('#summary').parent('label').hasClass('viaJWB')) summary += JWB.summarySuffix;
if ((typeof page === 'text' && page !== JWB.page.name) || $('#currentpage a').html().replace(/&/g, '&') !== JWB.page.name) {
console.log(page, JWB.page.name, $('#currentpage a').html())
JWB.stop();
alert(JWB.msg('autosave-error', JWB.msg('tab-log')));
$('#currentpage').html(JWB.msg('editbox-currentpage', ' ', ' '));
return;
}
var data = {
'title': JWB.page.name,
'summary': summary,
'action': 'edit',
'basetimestamp': JWB.page.revisions ? JWB.page.revisions[0].timestamp : '',
'token': JWB.page.edittoken,
'text': $('#editBoxArea').val(),
'watchlist': $('#watchPage').val()
};
if ($('#minorEdit').prop('checked')) data.minor = true;
JWB.api.call(data, function(response) {
JWB.log('edit', response.edit.title, response.edit.newrevid);
JWB.status('done', true);
JWB.next();
});
};
JWB.api.preview = function() {
JWB.status('preview');
JWB.api.call({
'title': JWB.page.name,
'action': 'parse',
'text': $('#editBoxArea').val()
}, function(response) {
$('#resultWindow').html(response.parse.text['*']);
$('#resultWindow div.previewnote').remove();
JWB.status('done', true);
});
};
JWB.api.move = function() {
JWB.status('move');
var topage = $('#moveTo').val().replace(/\$x/gi, JWB.page.pagevar);
var summary = $('#summary').val();
if ($('#summary').parent('label').hasClass('viaJWB')) summary += JWB.summarySuffix;
var data = {
'action':'move',
'from': JWB.page.name,
'to': topage,
'token': JWB.page.movetoken,
'reason': summary,
'ignorewarnings': 'yes'
};
if ($('#moveTalk').prop('checked')) data.movetalk = true;
if ($('#moveSubpage').prop('checked')) data.movesubpages = true;
if ($('#suppressRedir').prop('checked')) data.noredirect = true;
JWB.api.call(data, function(response) {
JWB.log('move', response.move.from, reponse.move.to);
JWB.status('done', true);
if (!$('#moveTo').val().match(/\$x/i)) $('#moveTo').val('')[0].focus(); //clear entered move-to pagename if it's not based on the pagevar
JWB.next(topage);
});
};
JWB.api.delete = function() {
JWB.status(($('#deletePage').is('.undelete') ? 'un' : '') + 'delete');
var summary = $('#summary').val();
if ($('#summary').parent('label').hasClass('viaJWB')) summary += JWB.summarySuffix;
var undeltoken = JWB.page.deletedrevs && JWB.page.deletedrevs[0] ? JWB.page.deletedrevs[0].token : '';
JWB.api.call({
'action': (!JWB.page.exists ? 'un' : '') + 'delete',
'title': JWB.page.name,
'token': JWB.page.exists ? JWB.page.deletetoken : undeltoken,
'reason': summary
}, function(response) {
JWB.log((!JWB.page.exists ? 'un' : '') + 'delete', (response.delete||response.undelete).title);
JWB.status('done', true);
JWB.next(response.undelete && response.undelete.title);
});
};
JWB.api.protect = function() {
JWB.status('protect');
var summary = $('#summary').val();
if ($('#summary').parent('label').hasClass('viaJWB')) summary += JWB.summarySuffix;
var editprot = $('#editProt').val();
var moveprot = $('#moveProt').val();
JWB.api.call({
'action':'protect',
'title': JWB.page.name,
'token': JWB.page.protecttoken,
'reason': summary,
'expiry': $('#protectExpiry').val()!==''?$('#protectExpiry').val():'infinite',
'protections': (JWB.page.exists?'edit='+editprot+'|move='+moveprot:'create='+editprot)
}, function(response) {
var protactions = '';
var prots = response.protect.protections;
for (var i=0;i<prots.length;i++) {
if (typeof prots[i].edit == 'string') {
protactions += ' edit: '+(prots[i].edit?prots[i].edit:'all');
} else if (typeof prots[i].move == 'string') {
protactions += ' move: '+(prots[i].move?prots[i].move:'all');
} else if (typeof prots[i].create == 'string') {
protactions += ' create: '+(prots[i].create?prots[i].create:'all');
}
}
protactions += ' expires: '+prots[0].expiry;
JWB.log('protect', response.protect.title, protactions);
JWB.status('done', false);
JWB.next(response.protect.title);
});
};
JWB.api.watch = function() {
JWB.status('watch');
var data = {
'action':'watch',
'title':JWB.page.name,
'token':JWB.page.watchtoken
};
if (JWB.page.watched) data.unwatch = true;
JWB.api.call(data, function(response) {
JWB.status('<span style="color:green;">'+
JWB.msg('status-watch-'+(JWB.page.watched ? 'removed' : 'added'), "'"+JWB.page.name+"'")+
'</span>', true);
JWB.page.watched = !JWB.page.watched;
$('#watchNow').html( JWB.msg('watch-' + (JWB.page.watched ? 'remove' : 'add')) );
});
};
/***** Pagelist functions *****/
JWB.pl.list = [];
JWB.pl.iterations = 0;
JWB.pl.stop = function() {
JWB.pl.iterations = 0;
$('#pagelistPopup [disabled]:not(fieldset [disabled]), #pagelistPopup legend input').prop('disabled', false);
$('#pagelistPopup legend input').trigger('change');
$('#pagelistPopup button img').remove();
}
JWB.pl.getNSpaces = function() {
var list = $('#pagelistPopup [name="namespace"]')[0];
if (list.selectedOptions.length == list.options.length) {
return ''; //return empty string if every namespace is selected; this will make the request default to having no filter
} else {
return $('#pagelistPopup [name="namespace"]').val().join('|'); //.val() returns an array of selected options.
}
};
/**
* abbrs: array of abbreviations for each page generator enabled.
* ps = prefix search, pl = links on page, etc.
* note that the "Links to page" section may generate multiple generators (each
* of wikilinks, transclusions, and file usage are done separately)
* lists: as above, but full names. Possible elements include:
* prefixsearch, links, watchlistraw
* These correspond to possible "list" values for https://www.mediawiki.org/wiki/API:Query
* Er, except when they don't. "links" is not a valid list value (but this just causes a benign warning)
* data: object with keys for each value in lists, plus "continue". For each generator,
* the corresponding value will be the user input related to that generator
* (e.g. the category name, the title prefix). If there are multiple values for
* a generator, they'll be pipe-delimited within the string.
* More keys will be added to this obj during this function's execution.
* (This is ultimately used as POST data for API calls)
**/
JWB.pl.getList = function(abbrs, lists, data) {
$('#pagelistPopup button, #pagelistPopup input, #pagelistPopup select').prop('disabled', true);
JWB.pl.iterations++;
// cf. https://www.mediawiki.org/wiki/API:Query
data.action = 'query';
var nspaces = JWB.pl.getNSpaces();
for (var i=0;i<abbrs.length;i++) {
if (nspaces) data[abbrs[i]+'namespace'] = nspaces;
data[abbrs[i]+'limit'] = 'max';
}
if (lists.indexOf('links') !== -1) {
// If using the "Links on page" feature, then ask the API to give us the links
// on each returned page
data.prop = 'links';
}
data.list = lists.join('|');
JWB.api.call(data, function(response) {
var maxiterate = 100; //allow up to 100 consecutive requests at a time to avoid overloading the server.
if (!response.query) response.query = {};
if (response.watchlistraw) response.query.watchlistraw = response.watchlistraw; //adding some consistency
var plist = [];
if (response.query.pages) {
var links;
for (var id in response.query.pages) {
links = response.query.pages[id].links;
for (var i=0;i<links.length;i++) {
plist.push(links[i].title);
}
}
}
for (var l in response.query) {
if (l === 'pages') continue;
for (var i=0;i<response.query[l].length;i++) {
plist.push(response.query[l][i].title);
}
}
//add the result to the pagelist immediately, as opposed to saving it all up and adding in 1 go like AWB does
$('#articleList').val($.trim($('#articleList').val()) + '\n' + plist.join('\n'));
JWB.pageCount();
var cont = response.continue;
console.log("Continue",JWB.pl.iterations, cont);
if (cont && JWB.pl.iterations <= maxiterate) {
var lists = [];
if (response.query) { //compatibility with the code I wrote for the old query-continue. TODO: make this unnecessary?
for (var list in response.query) {
lists.push(list); //add to the new array of &list= values
}
}
var abbrs = [];
for (var abbr in cont) {
data[abbr] = cont[abbr]; //add the &xxcontinue= value to the data
if (abbr != 'continue') {
abbrs.push(abbr.replace('continue','')); //find out what xx is and add it to the list of abbrs
}
}
JWB.pl.getList(abbrs, lists, data); //recursive function to get every page of a list
} else {
if (JWB.pl.iterations > maxiterate) {
JWB.status('pl-over-lim', true);
} else {
JWB.status('done', true);
}
JWB.pl.stop();
}
}, function() { //on error, simply reset and let the user work with what he has
JWB.status('done', true);
JWB.pl.stop();
});
};
//JWB.pl.getList(['wr'], ['watchlistraw'], {}) for watchlists
JWB.pl.generate = function() {
var $fields = $('#pagelistPopup fieldset').not('[disabled]');
var spinner = '<img src="//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif" width="15" height="15" alt="'+JWB.msg('status-alt')+'"/>';
$('#pagelistPopup').find('button[type="submit"]').append(spinner);
var abbrs = [], lists = [], data = {'continue': ''};
$fields.each(function() {
var list = $(this).find('legend input').attr('name');
var abbr;
if (list === 'linksto') { //Special case since this fieldset features 3 merged lists in 1 fieldset
if (!$('[name="title"]').val()) return;
$('[name="backlinks"], [name="embeddedin"], [name="imageusage"]').filter(':checked').each(function() {
var val = this.value;
abbrs.push(val);
lists.push(this.name);
data[val+'title'] = $('[name="title"]').val();
data[val+'filterredir'] = $('[name="filterredir"]:checked').val();
if ($('[name="redirect"]').prop('checked')) data[val+'redirect'] = true;
});
} else { //default input system
abbr = $(this).find('legend input').val();
lists.push(list);
abbrs.push(abbr);
// For each input in this fieldset other than the checkbox enabling it,
// add a key-value pair to data, where the key is the input's name and
// the value is the value (or pipe-separated string of values, if multiple)
// (These will correspond to params of the API query)
$(this).find('input').not('legend input').each(function() {
if ((this.type === 'checkbox' || this.type === 'radio') && this.checked === false) return;
if ($(this).is('[name="cmtitle"]')) {
//making sure every page has a Category: prefix, in case the user left it out
var newval = $(this).val().replace(new RegExp(JWB.ns[14]['*']+':', 'gi'), '').split('|')[0];
$(this).val(JWB.ns[14]['*']+':'+newval);
}
var name = this.name;
var val = this.value;
if (data.hasOwnProperty(name)) {
data[name] += '|'+val;
} else {
data[name] = val;
}
});
console.log("abbrs, lists, data:", abbrs, lists, data);
}
});
if (abbrs.length) JWB.pl.getList(abbrs, lists, data);
};
/***** Setup functions *****/
JWB.setup.save = function(name) {
name = name || prompt(JWB.msg('setup-prompt', JWB.msg('setup-prompt-store')), $('#loadSettings').val());
if (name === null) return;
var self = JWB.settings[name] = {
string: {},
bool: {},
replaces: []
};
//inputs with a text value
$('textarea, input[type="text"], input[type="number"], select').not('.replaces input, #editBoxArea, #settings *, #google-analytics').each(function() {
if (typeof $(this).val() == 'string') {
self.string[this.id] = this.value.replace(/\n{2,}/g,'\n');
} else {
self.string[this.id] = $(this).val();
}
});
self.replaces = [];
$('.replaces').each(function() {
if ($(this).find('.replaceText').val() || $(this).find('.replaceWith').val()) {
self.replaces.push({
replaceText: $(this).find('.replaceText').val(),
replaceWith: $(this).find('.replaceWith').val(),
useRegex: $(this).find('.useRegex').prop('checked'),
regexFlags: $(this).find('.regexFlags').val(),
ignoreNowiki: $(this).find('.ignoreNowiki').prop('checked')
});
}
});
$('input[type="radio"], input[type="checkbox"]').not('.replaces input').each(function() {
self.bool[this.id] = this.checked;
});
if (!$('#loadSettings option[value="'+name+'"]').length) {
$('#loadSettings').append('<option value="'+name+'">'+name+'</option>');
}
$('#loadSettings').val(name);
console.log(self);
};
JWB.setup.apply = function(name) {
name = name && JWB.settings[name] ? name : 'default';
var self = JWB.settings[name];
$('#loadSettings').val(name);
$('.replaces + .replaces').remove(); //reset find&replace inputs
$('.replaces input[type="text"]').val('');
$('.useRegex').each(function() {this.checked = false;});
$('#pagelistPopup legend input').trigger('change'); //fix checked state of pagelist generating inputs
for (var a in self.string) {
$('#'+a).val(self.string[a]);
}
for (var b in self.bool) {
$('#'+b).prop('checked', self.bool[b]);
}
var cur;
for (var c=0;c<self.replaces.length;c++) {
if ($('.replaces').length <= c) $('#moreReplaces')[0].click();
cur = self.replaces[c];
for (var d in cur) {
if (cur[d] === true || cur[d] === false) {
$('.replaces').eq(c).find('.'+d).prop('checked', cur[d]);
} else {
$('.replaces').eq(c).find('.'+d).val(cur[d]);
}
}
}
$('.useRegex, #containRegex,'+
'#pagelistPopup legend input,'+
'#viaJWB').trigger('change'); //reset disabled inputs
};
JWB.setup.getObj = function() {
// google analytics preferences do not depend on which settings profile, so should be stored outside that.
JWB.settings['_google_analytics'] = $('#allow-analytics').prop('checked');
var settings = [];
for (var i in JWB.settings) {
if (i != '_blank') {
settings.push('"' + i + '": ' + JSON.stringify(JWB.settings[i]));
}
}
return '{\n\t' + settings.join(',\n\t') + '\n}';
};
JWB.setup.submit = function() {
var name = prompt(JWB.msg('setup-prompt', JWB.msg('setup-prompt-save')), $('#loadSettings').val());
if (name === null) return;
if ($.trim(name) === '') name = 'default';
JWB.setup.save(name);
JWB.status('setup-submit');
JWB.api.call({
'title': 'User:'+JWB.username+'/'+JWB.settingspage+'-settings.'+JWB.configext,
'summary': JWB.msg(['setup-summary', mw.config.get('wgContentLanguage')]),
'action': 'edit',
'token': JWB.setup.edittoken,
'text': JWB.setup.getObj(),
'minor': true
}, function(response) {
JWB.status('done', true);
});
};
//TODO: use blob uri
JWB.setup.download = function() {
var name = prompt(JWB.msg('setup-prompt', JWB.msg('setup-prompt-save')), $('#loadSettings').val());
if (name === null) return;
if ($.trim(name) === '') name = 'default';
JWB.setup.save(name);
JWB.status('setup-dload');
var url = 'data:application/json;base64,' + btoa(JWB.setup.getObj());
var elem = $('#download-anchor')[0];
if (elem.hasOwnProperty('download')) { //use download attribute when possible, for its ability to specify a filename
elem.href = url;
elem.click();
setTimeout(function() {elem.removeAttribute('href');}, 2000);
} else { //fallback to iframes for browsers with no support for download="" attributes
elem = $('#download-iframe')[0];
elem.src = url.replace('application/json', 'application/octet-stream');
setTimeout(function() {elem.removeAttribute('src');}, 2000);
}
JWB.status('done', true);
};
JWB.setup.import = function(e) {
e.preventDefault();
file = (e.dataTransfer||this).files[0];
if ($(this).is('#import')) { //reset input
this.outerHTML = this.outerHTML;
$('#import').change(JWB.setup.import);
}
if (!window.hasOwnProperty('FileReader')) {
alert(JWB.msg('old-browser'));
JWB.status('old-browser', '<a target="_blank" href="'+JWB.index_php+'?title=Special:MyPage/'+JWB.settingspage+'-settings.'+JWB.configext+'">/'+JWB.settingspage+'-settings.'+JWB.configext+'</a>');
return;
}
if (file.name.split('.').pop().toLowerCase() !== 'json') {
alert(JWB.msg('not-json'));
return;
}
JWB.status('Processing file');
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(e) {
JWB.status('done', true);
try {
//Exclusion regex based on http://stackoverflow.com/a/23589204/1256925
//Removes all JS comments from the file, except when they're between quotes.
var data = JSON.parse(reader.result.replace(/("[^"]*")|(\/\*[\w\W]*\*\/|\/\/[^\n]*)/g, function(match, g1, g2) {
if (g1) return g1;
}));
} catch(e) {
alert(JWB.msg('json-err', e.message, JWB.msg('json-err-upload')));
console.log(e); //also log the error for further info
return;
}
JWB.setup.extend(data);
};
JWB.status('Processing file');
};
JWB.setup.load = function() {
JWB.status('setup-load');
JWB.api.call({
'action': 'query',
'titles': 'User:' + (JWB.username||mw.config.get('wgUserName')) + '/'+JWB.settingspage+'-settings.'+JWB.configext,
'prop': 'info|revisions',
'intoken': 'edit',
'rvprop': 'content',
'indexpageids': true
}, function(response) {
JWB.status('done', true);
if (JWB === false) return; //user is not allowed to use JWB
var firstrun = JWB.setup.edittoken ? false : true;
var page = response.query.pages[response.query.pageids[0]];
JWB.setup.edittoken = page.edittoken;
if (response.query.pageids[0] === '-1') {
if (JWB.allowed && firstrun) JWB.setup.save('default'); //this runs when this callback returns after the init has loaded.
JWB.loadAnalytics(); // load Analytics, because user did not opt-out (no settings page)
return;
}
var data = page.revisions[0]['*'];
if (!data) {
if (JWB.allowed && firstrun) JWB.setup.save('default'); //this runs when this callback returns after the init has loaded.
JWB.loadAnalytics(); // load Analytics, because user did not opt-out (empty settings page)
return;
}
try {
data = JSON.parse(data);
} catch(e) {
alert(JWB.msg('json-err', e.message, JWB.msg('json-err-page', JWB.settingspage)) || 'JSON error:\n'+e.message);
JWB.setup.save('default');
JWB.loadAnalytics(); // load Analytics, because user did not opt-out (no valid settings page)
return;
}
if (data["_google_analytics"] === false) {
$('#allow-analytics').prop('checked', false); // user opts out of analytics
} else {
JWB.loadAnalytics(); // if user's settings exist, and the google analytics preference is not false, load GA.
delete data["_google_analytics"]; // prevent "_google_analytics" from appearing as a profile
}
JWB.setup.extend(data);
});
};
JWB.setup.extend = function(obj) {
$.extend(JWB.settings, obj);
if (!JWB.settings.hasOwnProperty('default')) {
JWB.setup.save('default');
}
for (var i in JWB.settings) {
if ($('#loadSettings').find('option[value="'+i+'"]').length) continue;
$('#loadSettings').append('<option value="'+i+'">'+i+'</option>');
}
JWB.setup.apply($('#loadSettings').val());
};
JWB.setup.delete = function() {
var name = $('#loadSettings').val();
if (name === '_blank') return alert(JWB.msg('setup-delete-blank'));
var temp = {};
temp[name] = JWB.settings[name];
JWB.setup.temp = $.extend({}, temp);
delete JWB.settings[name];
$('#loadSettings').val('default');
if (name === 'default') {
JWB.setup.apply('_blank');
JWB.setup.save('default');
JWB.status(JWB.msg('status-del-default', '<a href="javascript:JWB.setup.undelete();">'+JWB.msg('status-del-undo')+'</a>'), true);
} else {
$('#loadSettings').find('[value="'+name+'"]').remove();
JWB.setup.apply();
JWB.status(JWB.msg('status-del-setup', name, '<a href="javascript:JWB.setup.undelete();">'+JWB.msg('status-del-undo')+'</a>'), true);
}
};
JWB.setup.undelete = function() {
JWB.setup.extend(JWB.setup.temp);
JWB.status('done', true);
};
/***** Main other functions *****/
//Show status message
JWB.status = function(action, done) {
$('#summary, .editbutton').prop('disabled', !done); //Disable box when not done (so busy loading). re-enable when done loading.
var status = JWB.msg('status-'+action);
if (status === false) return;
var spinImg = '<img src="//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif" width="15" height="15" alt="'+JWB.msg('status-alt')+'"/>';
if (status) {
if (!done) { //spinner if not done
status += ' ' + spinImg;
}
} else {
status = action;
}
$('#status').html(status);
JWB.pageCount();
return action=='done';
};
JWB.pageCount = function() {
if (JWB.allowed === false||!$('#articleList').length) return;
$('#articleList').val(($('#articleList').val()||'').replace(/(^[ \t]*$\n)*/gm, ''));
JWB.list = $('#articleList').val().split('\n');
var count = JWB.list.length;
if (count === 1 && JWB.list[0] === '') count = 0;
$('#totPages').html(count);
};
//Perform all specified find&replace actions
JWB.replace = function(input) {
JWB.pageCount();
var varOffset = JWB.list[0].indexOf('|') !== -1 ? JWB.list[0].indexOf('|') + 1 : 0;
JWB.page.pagevar = JWB.list[0].substr(varOffset);
$('.replaces').each(function() {
var $this = $(this);
var regexFlags = $this.find('.regexFlags').val();
var replace = $this.find('.replaceText').val().replace(/\$x/gi, JWB.page.pagevar).replace(/\\{2}/g, '\\').replace(/\\n/g,'\n') || '$';
var useRegex = replace === '$' || $this.find('.useRegex').prop('checked');
if (useRegex && regexFlags.indexOf('_') !== -1) {
replace = replace.replace(/[ _]/g, '[ _]'); //replaces any of [Space OR underscore] with a match for spaces or underscores.
replace = replace.replace(/(\[[^\]]*)\[ _\]/g, '$1 _'); //in case a [ _] was placed inside another [] match, remove the [].
regexFlags = regexFlags.replace('_', '');
}
//apply replaces where \n and \\ work in both regular text and regex mode.
var rWith = $this.find('.replaceWith').val().replace(/\$x/gi, JWB.page.pagevar).replace(/\\{2}/g, '\\').replace(/\\n/g,'\n');
try {
if ($this.find('.ignoreNowiki').prop('checked')) {
if (!useRegex) {
replace = replace.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
regexFlags = 'g';
}
input = JWB.replaceParsed(input, replace, regexFlags, rWith);
} else if (useRegex) {
replace = new RegExp(replace, regexFlags);
input = input.replace(replace, rWith);
} else {
input = input.split(replace).join(rWith); //global replacement without having to escape all special chars.
}
} catch(e) {
JWB.stop();
return JWB.status('regex-err', false);
}
});
if ($('#enableRETF').prop('checked')) {
input = RETF.replace(input);
}
return input;
};
//function to *only* replace the parsed wikitext
//It excludes comments (<!-- -->), and nowiki, math, source, syntaxhighlight, pre, code, gallery and timeline tags)
//Based on http://stackoverflow.com/a/23589204/1256925
JWB.replaceParsed = function(str, replace, flags, rwith) {
var exclude = '(<!--[\\s\\S]*?-->|<(nowiki|math|source|syntaxhighlight|pre|gallery|timeline)[^>]*?>[\\s\\S]*?<\\/\\2>)';
//add /i flag, to exclude the correct tags regardless of casing.
//This won't matter for the actual replacing, as the specified flags are used there.
var re = new RegExp(exclude + '|(' + replace + ')', flags.replace(/i|$/, 'i'));
return str.replace(re, function(match, g1, g2, g3) {
if (g3 !== undefined) { //continue to perform replacement if the match is the group that's supposed to be the match
return match.replace(new RegExp(replace, flags), rwith);
} else { //do nothing if the match is one of the excluded groups
return match;
}
});
};
//Adds a line to the logs tab.
JWB.log = function(action, page, info) {
var d = new Date();
var pagee = encodeURIComponent(page);
var extraInfo = '', actionStat = '';
switch (action) {
case 'edit':
if (typeof info === 'undefined') {
action = 'null-edit';
actionStat = 'nullEdits';
extraInfo = '';
} else {
extraInfo = ' (<a target="_blank" href="'+JWB.index_php+'?title='+pagee+'&diff='+info+'">diff</a>)';
actionStat = 'pagesSaved';
}
break;
case 'skip':
actionStat = 'pagesSkipped';
break;
case 'move':
extraInfo = ' to <a target="_blank" href="/wiki/'+encodeURIComponent(info)+'" title="'+info+'">'+info+'</a>';
break;
case 'protect':
extraInfo = info;
break;
}
actionStat = '#' + (actionStat || 'otherActions');
$(actionStat).html(+$(actionStat).html() + 1);
$('#actionlog tbody')
.append('<tr>'+
'<td>'+(JWB.fn.pad0(d.getHours())+':'+JWB.fn.pad0(d.getMinutes())+':'+JWB.fn.pad0(d.getSeconds()))+'</td>'+
'<th>'+action+'</th>'+
'<td><a target="_blank" href="/wiki/'+pagee+'" title="'+page+'">'+page+'</a>'+ extraInfo +'</td>'+
'</tr>')
.parents('.JWBtabc').scrollTop($('#actionlog tbody').parents('.JWBtabc')[0].scrollHeight);
};
//Move to the next page in the list
JWB.next = function(nextPage) {
if ($.trim(nextPage) && !$('#skipAfterAction').prop('checked')) {
nextPage = $.trim(nextPage) + '\n';
} else {
nextPage = '';
}
$('#articleList').val($('#articleList').val().replace(/^.*\n?/, nextPage));
JWB.list.splice(0,1);
JWB.pageCount();
JWB.api.get(JWB.list[0].split('|')[0]);
};
//Stop everything, reset inputs and editor
JWB.stop = function() {
$('#stopbutton,'+
'.editbutton,'+
'#watchNow,'+
'.JWBtabc[data-tab="2"] .editbutton,'+
'#watchNow'+
'.JWBtabc[data-tab="4"] button').prop('disabled', true);
$('#startbutton, #articleList,'+
'.JWBtabc[data-tab="1"] button,'+
'#replacesPopup button,'+
'#replacesPopup input,'+
'.JWBtabc input, select').prop('disabled', false);
$('#resultWindow').html('');
$('#editBoxArea').val('');
$('#currentpage').html(JWB.msg('editbox-currentpage', ' ', ' '));
JWB.pl.stop();
JWB.status('done', true);
JWB.isStopped = true;
};
//Start AutoWikiBrowsing
JWB.start = function() {
JWB.pageCount();
if (JWB.list.length === 0 || (JWB.list.length === 1 && !JWB.list[0])) {
alert(JWB.msg('no-pages-listed'));
} else if ($('#skipNoChange').prop('checked') && !$('.replaceText').val() && !$('.replaceWith').val() && !$('#enableRETF').prop('checked')) {
alert(JWB.msg('infinite-skip-notice'));
} else {
JWB.isStopped = false;
if ($('#preparse').prop('checked') && !$('#articleList').val().match('#PRE-PARSE-STOP')) {
$('#articleList').val($.trim($('#articleList').val()) + '\n#PRE-PARSE-STOP'); //mark where to stop pre-parsing
} else {
$('#preparse-reset').click();
}
$('#stopbutton, .editbutton, #watchNow, .JWBtabc[data-tab="2"] button, .JWBtabc[data-tab="4"] button').prop('disabled', false);
$('#startbutton, #articleList, .JWBtabc[data-tab="1"] button, #replacesPopup button, #replacesPopup input, .JWBtabc input, select').prop('disabled', true);
JWB.api.get(JWB.list[0].split('|')[0]);
}
};
JWB.updateButtons = function() {
if (!JWB.page.exists && $('#deletePage').is('.delete')) {
$('#deletePage').removeClass('delete').addClass('undelete').html('Undelete');
JWB.fn.blink('#deletePage'); //Indicate the button has changed
} else if (JWB.page.exists && $('#deletePage').is('.undelete')) {
$('#deletePage').removeClass('undelete').addClass('delete').html('Delete');
JWB.fn.blink('#deletePage'); //Indicate the button has changed
}
if (!JWB.page.exists) {
$('#movePage').prop('disabled', true);
} else {
$('#movePage').prop('disabled', false);
}
$('#watchNow').html( JWB.msg('watch-' + (JWB.page.watched ? 'remove' : 'add')) );
};
/***** General functions *****/
//Clear all existing timers to prevent them from getting errors
JWB.fn.clearAllTimeouts = function() {
var i = setTimeout(function() {
return void(0);
}, 1000);
for (var n=0;n<=i;n++) {
clearTimeout(n);
clearInterval(i);
}
console.log('Cleared all running intervals up to index',i);
};
//Filter an array to only contain unique values.
JWB.fn.uniques = function(arr) {
var a = [];
for (var i=0, l=arr.length; i<l; i++) {
if (a.indexOf(arr[i]) === -1 && arr[i] !== '') {
a.push(arr[i]);
}
}
return a;
};
//Prepends zeroes until the number has the desired length of len (default 2)
JWB.fn.pad0 = function(n, len) {
n = n.toString();
len = len||2;
return n.length < len ? Array(len-n.length).join('0')+n : n;
};
JWB.fn.blink = function(el,t) {
t=t?t:500;
$(el).prop('disabled', true)
.children().animate({opacity:'0.1'},t-100)
.animate({opacity:'1'},t)
.animate({opacity:'0.1'},t-100)
.animate({opacity:'1'},t);
setTimeout("$('"+el+"').prop('disabled', false)",t*4-400);
};
JWB.fn.setSelection = function(el, start, end, dir) {
dir = dir||'none'; //Default value
end = end||start; //If no end is specified, assume the caret is placed without creating text selection.
if (el.setSelectionRange) {
el.focus();
el.setSelectionRange(start, end, dir);
} else if (el.createTextRange) {
var rng = el.createTextRange();
rng.collapse(true);
rng.moveStart('character', start);
rng.moveEnd('character', end);
rng.select();
}
};
JWB.fn.scrollSelection = function(el, index) { //function to fix scrolling to selection - doesn't do that automatically.
var newEl = document.createElement('textarea'); //create a new textarea to simulate the same conditions
var elStyle = getComputedStyle(el);
newEl.style.height = elStyle.height; //copy over size-influencing styles
newEl.style.width = elStyle.width;
newEl.style.lineHeight = elStyle.lineHeight;
newEl.style.fontSize = elStyle.fontSize;
newEl.value = el.value.substr(0,index);
document.body.appendChild(newEl); //needs to be added to the HTML for the scrollHeight and clientHeight to work.
if (newEl.scrollHeight != newEl.clientHeight) {
el.scrollTop = newEl.scrollHeight - 2;
} else {
el.scrollTop = 0;
}
newEl.remove(); //clean up the mess I've made
};
//i18n function
JWB.msg = function(message) {
var args = arguments;
var lang = JWB.lang;
if (typeof message === 'object') {
lang = message[1];
message = message[0];
}
if (lang == 'qqx') return message;
if (!JWB.messages || !JWB.messages.en) return message;
var msg;
if (JWB.messages.hasOwnProperty(lang) && JWB.messages[lang].hasOwnProperty(message)) {
msg = JWB.messages[lang][message];
} else {
msg = (JWB.messages.en.hasOwnProperty(message)) ? JWB.messages.en[message] : '';
}
msg = msg.replace(/\$(\d+)/g, function(match, num) {
return args[+num] || match;
});
return msg;
};
/***** Init *****/
JWB.init = function() {
console.log(JWB.messages.en, !!JWB.messages.en);
JWB.setup.load();
JWB.fn.clearAllTimeouts();
if (!JWB.messages[JWB.lang] && JWB.lang != 'qqx') JWB.lang = 'en';
var findreplace = '<div class="replaces">'+
'<label style="display:block;">'+JWB.msg('label-replace')+' <input type="text" class="replaceText"/></label>'+
'<label style="display:block;">'+JWB.msg('label-rwith')+' <input type="text" class="replaceWith"/></label>'+
'<div class="regexswitch">'+
'<label><input type="checkbox" class="useRegex"> '+JWB.msg('label-useregex')+'</label>'+
'<a class="re101" href="http://regex101.com/#javascript" target="_blank">?</a>'+
'<label class="divisor" title="'+JWB.msg('tip-regex-flags')+'" style="display:none;">'+
JWB.msg('label-regex-flags')+' <input type="text" class="regexFlags" value="g"/>'+ //default: global replacement
'</label>'+
'<br/>'+
'</div>'+
'<label title="'+JWB.msg('tip-ignore-comment')+'">'+
'<input type="checkbox" class="ignoreNowiki"> '+JWB.msg('label-ignore-comment')+
'</label>'+
'</div>';
var NSList = '<select multiple name="namespace" id="namespacelist">';
for (var i in JWB.ns) {
if (parseInt(i) < 0) continue; //No Special: or Media: in the list
NSList += '<option value="'+JWB.ns[i].id+'" selected>'+(JWB.ns[i]['*'] || '('+JWB.msg('namespace-main')+')')+'</option>';
}
NSList += '</select>';
/***** Interface *****/
document.title = 'AWB+search Script'+(document.title.split('-')[1] ? ' -'+document.title.split('-')[1] : '');
$('body').html(
'<article id="resultWindow"></article>'+
'<main id="inputsWindow">'+
'<div id="inputsBox">'+
'<aside id="articleBox">'+
'<b>'+JWB.msg('pagelist-caption')+'</b>'+
'<textarea id="articleList"></textarea>'+
'</aside>'+
'<section id="tabs">'+
'<nav class="tabholder">'+
'<span class="JWBtab" data-tab="1">'+JWB.msg('tab-setup')+'</span> '+
'<span class="JWBtab active" data-tab="2">'+JWB.msg('tab-editing')+'</span> '+
'<span class="JWBtab" data-tab="3">'+JWB.msg('tab-skip')+'</span> '+
(JWB.sysop?'<span class="JWBtab" data-tab="4">'+JWB.msg('tab-other')+'</span> ':'')+
' <span class="JWBtab log" data-tab="5">'+JWB.msg('tab-log')+'</span> '+
'</nav>'+
'<section class="JWBtabc" data-tab="1"></section>'+
'<section class="JWBtabc active" data-tab="2"></section>'+
'<section class="JWBtabc" data-tab="3"></section>'+
(JWB.sysop?'<section class="JWBtabc" data-tab="4"></section>':'')+
'<section class="JWBtabc log" data-tab="5"></section>'+
'<footer id="status">done</footer>'+
'</section>'+
'<aside id="editBox">'+
'<b>'+JWB.msg('editbox-caption')+' - <span id="currentpage">'+JWB.msg('editbox-currentpage', ' ', ' ')+'</span></b>'+
'<textarea id="editBoxArea"></textarea>'+
'</aside>'+
'</div>'+
'</main>'+
'<footer id="stats">'+
JWB.msg('stat-pages')+' <span id="totPages">0</span>; '+
JWB.msg('stat-save')+' <span id="pagesSaved">0</span>; '+
JWB.msg('stat-null')+' <span id="nullEdits">0</span>; '+
JWB.msg('stat-skip')+' <span id="pagesSkipped">0</span>; '+
JWB.msg('stat-other')+' <span id="otherActions">0</span>; '+
'</footer>'+
'<div id="overlay" style="display:none;"></div>'+
'<section class="JWBpopup" id="replacesPopup" style="display:none;">'+
'<button id="moreReplaces">'+JWB.msg('button-more-fields')+'</button>'+
'<br>'+findreplace+
'</section>'+
'<section class="JWBpopup" id="pagelistPopup" style="display:none;">'+
'<form action="#" onsubmit="JWB.pl.generate();event.preventDefault();"></form>'+
'</section>'
);
$('.JWBtabc[data-tab="1"]').html(
'<fieldset id="pagelist">'+
'<legend>'+JWB.msg('label-pagelist')+'</legend>'+
'<button id="removeDupes">'+JWB.msg('button-remove-dupes')+'</button> '+
'<button id="sortArticles">'+JWB.msg('button-sort')+'</button>'+
'<br>'+
'<label title="'+JWB.msg('tip-preparse')+'">'+
'<input type="checkbox" id="preparse"> '+JWB.msg('preparse')+
'</label>'+
'<span class="divisor"></span>'+
'<button id="preparse-reset" title="'+JWB.msg('tip-preparse-reset')+'">'+JWB.msg('preparse-reset')+'</button>'+
'<br>'+
'<button id="pagelistButton">'+JWB.msg('pagelist-generate')+'</button>'+
'</fieldset>'+
'<fieldset id="settings">'+
'<legend>'+JWB.msg('label-settings')+'</legend>'+
'<button id="saveAs" title="'+JWB.msg('tip-store-setup')+'">'+JWB.msg('store-setup')+'</button>'+
'<br>'+
'<label>'+
JWB.msg('load-settings') + ' '+
'<select id="loadSettings">'+
'<option value="default" selected>default</option>'+
'<option value="_blank">'+JWB.msg('blank-setup')+'</option>'+
'</select>'+
'</label>'+
'<span class="divisor"></span>'+
'<button id="deleteSetup" title="'+JWB.msg('tip-delete-setup')+'">'+JWB.msg('delete-setup')+'</button>'+
'<hr>'+
'<button id="saveToWiki">'+JWB.msg('save-setup')+'</button>'+
'<span class="divisor"></span>'+
'<button id="download">'+JWB.msg('download-setup')+'</button>'+
'<hr>'+
'<label class="button" id="importLabel" title="'+JWB.msg('tip-import-setup')+'">'+
'<input type="file" id="import" accept=".json">'+
JWB.msg('import-setup')+
'</label>'+
'<span class="divisor"></span>'+
'<button id="updateSetups" title="'+JWB.msg('tip-update-setup', JWB.settingspage)+'">'+JWB.msg('update-setup')+'</button>'+
'<div id="downloads">'+
'<a download="JWB-settings.json" target="_blank" id="download-anchor"></a>'+
'<iframe id="download-iframe"></iframe>'+
'</div>'+
'</fieldset>'+
'<fieldset id="google-analytics">'+
'<legend>'+JWB.msg('label-analytics')+'</legend>'+
'<label title="'+JWB.msg('tip-analytics')+'">'+
'<input id="allow-analytics" type="checkbox" checked> '+
JWB.msg('allow-analytics')+
'</label>'+
'<span class="divisor">'+
'<a target="_blank" href="https://wiki.riteme.site/wiki/User:Joeytje50/JWB#Analytics" title="'+JWB.msg('tip-analytics-link')+'"><i>'+
JWB.msg('analytics-link')+
'</i></a>'+
'</fieldset>'
);
$('.JWBtabc[data-tab="2"]').html(
'<label class="minorEdit"><input type="checkbox" id="minorEdit" checked> '+JWB.msg('minor-edit')+'</label>'+
'<label class="editSummary viaJWB">'+JWB.msg('edit-summary')+' <input class="fullwidth" type="text" id="summary" maxlength="250"></label>'+
' <input type="checkbox" id="viaJWB" checked title="'+JWB.msg('tip-via-JWB')+'">'+
'<select id="watchPage">'+
'<option value="watch">'+JWB.msg('watch-watch')+'</option>'+
'<option value="unwatch">'+JWB.msg('watch-unwatch')+'</option>'+
'<option value="nochange" selected>'+JWB.msg('watch-nochange')+'</option>'+
'<option value="preferences">'+JWB.msg('watch-preferences')+'</option>'+
'</select>'+
'<span class="divisor"></span>'+
'<button id="watchNow" disabled accesskey="w" title="['+JWB.tooltip+'w]">'+
JWB.msg('watch-add')+
'</button>'+
'<br>'+
(JWB.bot?
'<label><input type="checkbox" id="autosave"> '+JWB.msg('auto-save')+'</label>'+
'<label title="'+JWB.msg('tip-save-interval')+'" class="divisor">'+
JWB.msg('save-interval', '<input type="number" min="0" value="0" style="width:50px" id="throttle" disabled>')+
'</label>'+
'<br>'
:'')+
'<span id="startstop">'+
'<button id="startbutton" accesskey="a" title="['+JWB.tooltip+'a]">'+JWB.msg('editbutton-start')+'</button>'+
'<br>'+
'<button id="stopbutton" disabled accesskey="q" title="['+JWB.tooltip+'q]">'+JWB.msg('editbutton-stop')+'</button> '+
'</span>'+
'<button class="editbutton" id="skipButton" disabled accesskey="n" title="['+JWB.tooltip+'n]">'+JWB.msg('editbutton-skip')+'</button>'+
'<button class="editbutton" id="submitButton" disabled accesskey="s" title="['+JWB.tooltip+'s]">'+JWB.msg('editbutton-save')+'</button>'+
'<br>'+
'<button class="editbutton" id="previewButton" disabled accesskey="p" title="['+JWB.tooltip+'p]">'+JWB.msg('editbutton-preview')+'</button>'+
'<button class="editbutton" id="diffButton" disabled accesskey="d" title="['+JWB.tooltip+'d]">'+JWB.msg('editbutton-diff')+'</button>'+
'<button id="replacesButton">'+JWB.msg('button-open-popup')+'</button>'+
findreplace+
'<hr>'+
'<label><input type="checkbox" id="enableRETF">'+
JWB.msg('label-enable-RETF',
'<a href="/wiki/Project:AutoWikiBrowser/Typos" target="_blank">'+
JWB.msg('label-RETF')+
'</a>')+
'</label>'+
' <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/2a/Gnome-view-refresh.svg/20px-Gnome-view-refresh.svg.png"'+
'id="refreshRETF" title="'+JWB.msg('tip-refresh-RETF')+'">'
);
$('.JWBtabc[data-tab="3"]').html(
'<fieldset>'+
'<legend>'+JWB.msg('label-redirects')+'</legend>'+
'<label title="'+JWB.msg('tip-redirects-follow')+'">'+
'<input type="radio" class="redirects" value="follow" name="redir" id="redir-follow"> '+JWB.msg('redirects-follow')+' '+
'</label>'+
'<label title="'+JWB.msg('tip-redirects-skip')+'">'+
'<input type="radio" class="redirects" value="skip" name="redir" id="redir-skip"> '+JWB.msg('redirects-skip')+' '+
'</label>'+
'<label title="'+JWB.msg('tip-redirects-edit')+'">'+
'<input type="radio" class="redirects" value="edit" name="redir" id="redir-edit" checked> '+JWB.msg('redirects-edit')+''+
'</label>'+
'</fieldset>'+
'<fieldset>'+
'<legend>'+JWB.msg('label-skip-when')+'</legend>'+
'<label><input type="checkbox" id="skipNoChange"> '+JWB.msg('skip-no-change')+'</label>'+
'<br>'+
'<label><input type="radio" id="exists-yes" name="exists" value="yes"> '+JWB.msg('skip-exists-yes')+'</label>'+
'<label><input type="radio" id="exists-no" name="exists" value="no" checked> '+JWB.msg('skip-exists-no')+'</label>'+
'<label><input type="radio" id="exists-neither" name="exists" value="neither">'+JWB.msg('skip-exists-neither')+'</label>'+
'<br>'+
(JWB.sysop?'<label><input type="checkbox" id="skipAfterAction" checked> '+JWB.msg('skip-after-action')+'</label>':'')+
'</fieldset>'+
'<label>'+JWB.msg('skip-contains')+' <input class="fullwidth" type="text" id="skipContains"></label>'+
'<label>'+JWB.msg('skip-not-contains')+' <input class="fullwidth" type="text" id="skipNotContains"></label>'+
'<div class="regexswitch">'+
'<label><input type="checkbox" id="containRegex"> '+JWB.msg('label-useregex')+'</label>'+
'<a class="re101" href="http://regex101.com/#javascript" target="_blank">?</a>'+
'<label class="divisor" title="'+JWB.msg('tip-regex-flags')+'" style="display:none;">'+
JWB.msg('label-regex-flags')+' <input type="text" id="containFlags"/>'+
'</label>'+
'</div>'
);
if (JWB.sysop) $('.JWBtabc[data-tab="4"]').html(
'<fieldset>'+
'<legend>'+JWB.msg('move-header')+'</legend>'+
'<label><input type="checkbox" id="suppressRedir"> '+JWB.msg('move-redir-suppress')+'</label>'+
'<br>'+
JWB.msg('move-also')+' '+
'<label><input type="checkbox" id="movetalk"> '+JWB.msg('move-talk-page')+'</label> '+
'<label><input type="checkbox" id="movesubpage"> '+JWB.msg('move-subpage')+'</label>'+
'<br>'+
'<label>'+JWB.msg('move-new-name')+' <input type="text" id="moveTo"></label>'+
'</fieldset>'+
'<fieldset>'+
'<legend>'+JWB.msg('protect-header')+'</legend>'+
JWB.msg('protect-edit')+
'<select id="editProt">'+
'<option value="all" selected>'+JWB.msg('protect-none')+'</option>'+
'<option value="autoconfirmed">'+JWB.msg('protect-autoconf')+'</option>'+
'<option value="sysop">'+JWB.msg('protect-sysop')+'</option>'+
'</select> '+
'<br>'+
JWB.msg('protect-move')+
'<select id="moveProt">'+
'<option value="all" selected>'+JWB.msg('protect-none')+'</option>'+
'<option value="autoconfirmed">'+JWB.msg('protect-autoconf')+'</option>'+
'<option value="sysop">'+JWB.msg('protect-sysop')+'</option>'+
'</select> '+
'<br>'+
'<label>'+JWB.msg('protect-expiry')+' <input type="text" id="protectExpiry"/></label>'+
'</fieldset>'+
'<button id="movePage" disabled accesskey="m" title="['+JWB.tooltip+'m]">'+JWB.msg('editbutton-move')+'</button> '+
'<button id="deletePage" disabled accesskey="x" title="['+JWB.tooltip+'x]">'+JWB.msg('editbutton-delete')+'</button> '+
'<button id="protectPage" disabled accesskey="z" title="['+JWB.tooltip+'z]">'+JWB.msg('editbutton-protect')+'</button> '+
'<button id="skipPage" disabled title="['+JWB.tooltip+'n]">'+JWB.msg('editbutton-skip')+'</button>'
);
$('.JWBtabc[data-tab="5"]').html('<table id="actionlog"><tbody></tbody></table>');
$('#pagelistPopup form').html(
'<div id="ns-filter" title="'+JWB.msg('tip-ns-select')+'">' + JWB.msg('label-ns-select') + NSList + '</div>'+
'<fieldset>'+
'<legend><label><input type="checkbox" id="categorymembers" name="categorymembers" value="cm"> '+JWB.msg('legend-cm')+'</label></legend>'+
'<label title="Namespace prefix not required.">'+JWB.msg('label-cm')+' <input type="text" name="cmtitle" id="cmtitle"></label>'+
'<div>'+JWB.msg('cm-include')+' '+
'<label><input type="checkbox" id="cmtype-page" name="cmtype" value="page" checked> '+JWB.msg('cm-include-pages')+'</label>'+
'<label><input type="checkbox" id="cmtype-subcg" name="cmtype" value="subcat" checked> '+JWB.msg('cm-include-subcgs')+'</label>'+
'<label><input type="checkbox" id="cmtype-file" name="cmtype" value="file" checked> '+JWB.msg('cm-include-files')+'</label>'+
'</div>'+
'</fieldset>'+
'<fieldset>'+
'<legend><label><input type="checkbox" name="linksto" id="linksto"> '+JWB.msg('legend-linksto')+'</label></legend>'+
'<label>'+JWB.msg('label-linksto')+' <input type="text" name="title" id="linksto-title"></label>'+
'<div>'+JWB.msg('links-include')+' '+
'<label><input type="checkbox" id="backlinks" name="backlinks" value="bl" checked> '+JWB.msg('links-include-links')+'</label>'+
'<label><input type="checkbox" id="embeddedin" name="embeddedin" value="ei"> '+JWB.msg('links-include-templ')+'</label>'+
'<label><input type="checkbox" id="imageusage" name="imageusage" value="iu"> '+JWB.msg('links-include-files')+'</label>'+
'</div>'+
'<div>'+JWB.msg('links-redir')+' '+
'<label><input type="radio" id="rfilter-redir" name="filterredir" value="redirects"> '+JWB.msg('links-redir-redirs')+'</label>'+
'<label><input type="radio" id="rfilter-nonredir" name="filterredir" value="nonredirects"> '+JWB.msg('links-redir-noredirs')+'</label>'+
'<label><input type="radio" id="rfilter-all" name="filterredir" value="all" checked> '+JWB.msg('links-redir-all')+'</label>'+
'</div>'+
'<label title="'+JWB.msg('tip-link-redir')+'">'+
'<input type="checkbox" name="redirect" value="true" checked id="linksto-redir"> '+JWB.msg('label-link-redir')+
'</label>'+
'</fieldset>'+
'<fieldset>'+
'<legend><label><input type="checkbox" id="prefixsearch" name="prefixsearch" value="ps"> '+JWB.msg('legend-ps')+'</label></legend>'+
'<label>'+JWB.msg('label-ps')+' <input type="text" name="pssearch" id="pssearch"></label>'+
'</fieldset>'+
'<fieldset>'+
'<legend><label><input type="checkbox" id="watchlistraw" name="watchlistraw" value="wr"> '+JWB.msg('legend-wr')+'</label></legend>'+
JWB.msg('label-wr')+
'</fieldset>'+
'<fieldset>'+
'<legend><label><input type="checkbox" id="proplinks" name="links" value="pl"> '+JWB.msg('legend-pl')+'</label></legend>'+
'<label title="'+JWB.msg('tip-pl')+'">'+JWB.msg('label-pl')+' <input type="text" id="pltitles" name="titles"></label>'+
'</fieldset>'+
'<fieldset>'+
'<legend><label><input type="checkbox" name="search" value="sr">Search</label></legend>'+
'<label title="Query">Query<input type="text" name="srsearch"></label>'+
'</fieldset>'+
'<button type="submit">'+JWB.msg('pagelist-generate')+'</button>'
);
$('body').addClass('AutoWikiBrowser'); //allow easier custom styling of JWB.
/***** Setup *****/
JWB.setup.save('_blank'); //default setup
if (JWB.settings.hasOwnProperty('default')) {
JWB.setup.apply();
} else if (JWB.setup.hasOwnProperty('edittoken')) {
JWB.setup.save('default');
}
JWB.setup.extend({});
/***** Event handlers *****/
//Alert user when leaving the tab, to prevent accidental closing.
onbeforeunload = function() {
return "Closing this tab will cause you to lose all progress.";
};
ondragover = function(e) {
e.preventDefault();
};
$('.JWBtab').click(function() {
$('.active').removeClass('active');
$(this).addClass('active');
$('.JWBtabc[data-tab="'+$(this).attr('data-tab')+'"]').addClass('active');
});
function showRegexFlags() {
// >>this<< is the element that's triggered
$(this).parent().nextAll('label').toggle(this.checked);
}
$('body').on('change', '#useRegex, #containRegex, .useRegex', showRegexFlags);
$('#preparse-reset').click(function() {
$('#articleList').val($('#articleList').val().replace(/#PRE-PARSE-STOP/g,'').replace(/\n\n/g, '\n'));
});
$('#saveAs').click(function() {
JWB.setup.save();
});
$('#loadSettings').change(function() {
JWB.setup.apply(this.value);
});
$('#download').click(JWB.setup.download);
$('#saveToWiki').click(JWB.setup.submit);
$('#import').change(JWB.setup.import);
ondrop = JWB.setup.import;
$('#updateSetups').click(JWB.setup.load);
$('#deleteSetup').click(JWB.setup.delete);
if (window.RETF) $('#refreshRETF').click(RETF.load);
$('#replacesButton, #pagelistButton').click(function() {
var popup = this.id.slice(0, -6); //omits the 'Button' in the id by cutting off the last 6 characters
$('#'+popup+'Popup, #overlay').show();
});
$('#overlay').click(function() {
$('#replacesPopup, #pagelistPopup, #overlay').hide();
});
$('#moreReplaces').click(function() {
$('#replacesPopup').append(findreplace);
});
$('#replacesPopup').on('keydown', '.replaces:last', function(e) {
if (e.which === 9) $('#moreReplaces')[0].click();
});
$('#pagelistPopup legend input').change(function() {
//remove disabled attr when checked, add when not.
$(this).parents('fieldset').find('input').not('legend input').prop('disabled', !this.checked);
$(this).parents('fieldset').prop('disabled', !this.checked);
}).trigger('change');
$('#resultWindow').on('click', 'tr[data-line]:not(.lineheader) *', function(e) {
var line = +$(e.target).closest('tr[data-line]').data('line');
var index = $('#editBoxArea').val().split('\n').slice(0, line-1).join('\n').length;
$('#editBoxArea')[0].focus();
JWB.fn.setSelection($('#editBoxArea')[0], index+1);
JWB.fn.scrollSelection($('#editBoxArea')[0], index);
});
$('#removeDupes').click(function() {
$('#articleList').val(JWB.fn.uniques($('#articleList').val().split('\n')).join('\n'));
JWB.pageCount();
});
$('#sortArticles').click(function() {
$('#articleList').val($('#articleList').val().split('\n').sort().join('\n'));
JWB.pageCount();
});
$('#watchNow').click(JWB.api.watch);
$('#autosave').change(function() {
$('#throttle').prop('disabled', !this.checked);
});
$('#viaJWB').change(function() {
$('#summary').parent('label')
.toggleClass('viaJWB', this.checked)
.attr('maxlength', 250 - this.checked*JWB.summarySuffix.length); // Change the max size of the allowed summary according to having a suffix or not.
});
$('#startbutton').click(JWB.start);
$('#stopbutton').click(JWB.stop);
$('#submitButton').click(JWB.api.submit);
$('#previewButton').click(JWB.api.preview);
$('#diffButton').click(JWB.api.diff);
$('#skipButton, #skipPage').click(function() {
JWB.log('skip', JWB.list[0].split('|')[0]);
JWB.next();
});
if (JWB.sysop) {
$('#movePage').click(function() {
if ($('#moveTo').val().length === 0) {
return alert(JWB.msg('alert-no-move'));
}
JWB.api.move();
});
$('#protectPage').click(JWB.api.protect);
$('#deletePage').click(JWB.api.delete);
}
};
//Disable JWB altogether when it's loaded on a page other than Project:AutoWikiBrowser/Script. This script shouldn't be loaded on any other page in the first place.
if (JWB.allowed === false) JWB = false;