User:Euphoria/common.js: Difference between revisions

From Test Wiki
Content deleted Content added
fix
OOUI buttons
Line 1: Line 1:
//<nowiki>
// <nowiki>
mw.loader.using(['mediawiki.util', 'mediawiki.api'], function () {
mw.loader.using(['mediawiki.util', 'mediawiki.api', 'oojs-ui-core', 'oojs-ui-widgets'], function () {
const pagePrefix = 'User:Euphoria/Test VfD';
const pagePrefix = 'User:Euphoria/Test VfD';


// Only run on relevant pages in view mode
// Only run on relevant pages in view mode
const currentPage = mw.config.get('wgPageName').replace(/_/g, ' ');
const currentPage = mw.config.get('wgPageName').replace(/_/g, ' ');
if (
if (!currentPage.startsWith(pagePrefix) || mw.config.get('wgAction') !== 'view') return;
!currentPage.startsWith(pagePrefix + '/') ||
mw.config.get('wgAction') !== 'view'
) return;


$(function() {
$(function () {
$('#mw-content-text').find('h2, h3').each(function() {
$('#mw-content-text').find('h2, h3').each(function () {
const heading = this;
const heading = this;


// Skip headings already inside a .vfd container or without links
// Skip headings already inside a .vfd container or without links
if ($(heading).closest('.vfd').length) return;
if ($(heading).closest('.vfd').length) return;
const link = $(heading).find('a').first();
if (!$(heading).find('a').length) return;
if (!link.length) return;

// Extract full page name from link href
const href = link.attr('href');
if (!href) return;
const targetPage = decodeURIComponent(href.replace('/wiki/', '').replace(/_/g, ' '));


const container = document.createElement('span');
const container = document.createElement('span');
Line 25: Line 22:


const actions = [
const actions = [
{name: 'delete', color: '#e74c3c'},
{ name: 'delete', color: '#e74c3c', flag: 'destructive' },
{name: 'keep', color: '#27ae60'},
{ name: 'keep', color: '#27ae60', flag: 'progressive' },
{name: 'no consensus', color: '#f1c40f'}
{ name: 'no consensus', color: '#f1c40f', flag: '' }
];
];


actions.forEach(function(actionObj) {
actions.forEach(function (actionObj) {
const btn = document.createElement('button');
// Create OOUI button
btn.textContent = actionObj.name.charAt(0).toUpperCase();
const btn = new OO.ui.ButtonWidget({
btn.title = 'Close as ' + actionObj.name;
label: actionObj.name.charAt(0).toUpperCase(),
title: 'Close as ' + actionObj.name,
flags: actionObj.flag ? [actionObj.flag] : [],
framed: false
});


// Button styling
// Mouse hover brightness handled automatically by OOUI
btn.style.width = '16px';
btn.on('click', function (e) {
btn.style.height = '16px';
btn.style.fontSize = '65%';
btn.style.padding = '0';
btn.style.marginRight = '3px';
btn.style.border = 'none';
btn.style.borderRadius = '2px';
btn.style.backgroundColor = actionObj.color;
btn.style.color = '#fff';
btn.style.cursor = 'pointer';
btn.style.verticalAlign = 'middle';
btn.style.transition = '0.15s';
btn.addEventListener('mouseenter', () => btn.style.filter='brightness(1.3)');
btn.addEventListener('mouseleave', () => btn.style.filter='brightness(1)');

btn.addEventListener('click', function(e) {
e.preventDefault();
e.preventDefault();
if (!confirm('Are you sure you want to close as ' + actionObj.name + '?')) return;
if (!confirm('Are you sure you want to close as ' + actionObj.name + '?')) return;


btn.disabled = true;
btn.setDisabled(true);
const api = new mw.Api();
const api = new mw.Api();


// Fetch discussion page content of the target page
// Fetch discussion page content
api.get({
api.get({
action: 'query',
action: 'query',
prop: 'revisions',
prop: 'revisions',
titles: targetPage,
titles: currentPage,
rvslots: 'main',
rvslots: 'main',
rvprop: 'content',
rvprop: 'content',
format: 'json'
format: 'json'
}).done(function(data) {
}).done(function (data) {
const pages = data.query.pages;
const pages = data.query.pages;
const pageId = Object.keys(pages)[0];
const pageId = Object.keys(pages)[0];
let content = pages[pageId].revisions[0].slots.main['*'] || '';
let content = pages[pageId].revisions[0].slots.main['*'];


const discussionNewContent = '{{subst:vt|' + actionObj.name + '. --~~~~}}\n' + content.trim() + '\n{{subst:vb}}';
const discussionNewContent =
'{{subst:vt|' + actionObj.name + '. --~~~~}}\n' +
content.trim() +
'\n{{subst:vb}}';


// Edit discussion page
// Edit discussion page
api.postWithToken('csrf', {
api.postWithToken('csrf', {
action: 'edit',
action: 'edit',
title: targetPage, // always targetPage
title: currentPage,
text: discussionNewContent,
text: discussionNewContent,
summary: 'Closed as ' + actionObj.name,
summary: 'Closed as ' + actionObj.name,
minor: true
minor: true
}).done(function() {
}).done(function () {
const link = $(heading).find('a').first();
if (!link.length) {
mw.notify('Cannot find target page link!', { title: 'VfDcloser', type: 'error', timeout: 1500 });
setTimeout(() => location.reload(), 1500);
return;
}

const targetPage = link.attr('title') || link.text().trim();
if (!targetPage) {
mw.notify('Cannot determine target page title!', { title: 'VfDcloser', type: 'error', timeout: 1500 });
setTimeout(() => location.reload(), 1500);
return;
}

if (actionObj.name === 'delete') {
if (actionObj.name === 'delete') {
// Delete target page
// Delete target page
Line 86: Line 89:
action: 'delete',
action: 'delete',
title: targetPage,
title: targetPage,
reason: '[[' + targetPage + ']]' // deletion reason references subpage
reason: '[[' + currentPage + ']]'
}).done(() => {
}).done(() => {
const talkPage = 'Talk:' + targetPage;
const talkPage = 'Talk:' + targetPage;
api.get({action: 'query', titles: talkPage, format: 'json'}).done(data => {
api.get({
action: 'query',
titles: talkPage,
format: 'json'
}).done(data => {
const talkPages = data.query.pages;
const talkPages = data.query.pages;
const talkId = Object.keys(talkPages)[0];
const talkId = Object.keys(talkPages)[0];
Line 100: Line 107:
reason: 'Parent page deleted via VfD'
reason: 'Parent page deleted via VfD'
}).done(() => {
}).done(() => {
mw.notify('Discussion closed. Page and talk page deleted.', {title: 'VfDcloser', type: 'success', timeout: 1500});
mw.notify('Discussion closed. Page and talk page deleted.', { title: 'VfDcloser', type: 'success', timeout: 1500 });
setTimeout(() => location.reload(), 1500);
setTimeout(() => location.reload(), 1500);
}).fail(err => mw.notify('Error deleting talk page: ' + JSON.stringify(err), {title: 'VfDcloser', type: 'error', timeout: 1500}));
}).fail(err =>
mw.notify('Error deleting talk page: ' + JSON.stringify(err), { title: 'VfDcloser', type: 'error', timeout: 1500 })
);
} else {
} else {
mw.notify('Discussion closed. Page deleted.', {title: 'VfDcloser', type: 'success', timeout: 1500});
mw.notify('Discussion closed. Page deleted.', { title: 'VfDcloser', type: 'success', timeout: 1500 });
setTimeout(() => location.reload(), 1500);
setTimeout(() => location.reload(), 1500);
}
}
});
});
}).fail(err => mw.notify('Error deleting page: ' + JSON.stringify(err), {title: 'VfDcloser', type: 'error', timeout: 1500}));
}).fail(err =>
mw.notify('Error deleting page: ' + JSON.stringify(err), { title: 'VfDcloser', type: 'error', timeout: 1500 })
);
} else {
} else {
// Keep / No consensus: update page and talk page
// Keep / No consensus: update page and talk page
Line 118: Line 129:
rvprop: 'content',
rvprop: 'content',
format: 'json'
format: 'json'
}).done(function(articleData) {
}).done(function (articleData) {
const articlePages = articleData.query.pages;
const articlePages = articleData.query.pages;
const articleId = Object.keys(articlePages)[0];
const articleId = Object.keys(articlePages)[0];
let articleContent = articlePages[articleId].revisions[0].slots.main['*'] || '';
let articleContent = articlePages[articleId].revisions[0].slots.main['*'];
articleContent = articleContent.replace(/\{\{vfd-new\}\}/gi, '').trim();
articleContent = articleContent.replace(/\{\{vfd-new\}\}/gi, '').trim();


Line 139: Line 150:
rvprop: 'content',
rvprop: 'content',
format: 'json'
format: 'json'
}).done(function(talkData) {
}).done(function (talkData) {
const talkPages = talkData.query.pages;
const talkPages = talkData.query.pages;
const talkId = Object.keys(talkPages)[0];
const talkId = Object.keys(talkPages)[0];
Line 157: Line 168:
minor: true
minor: true
}).done(() => {
}).done(() => {
mw.notify('Discussion closed. Page and talk page updated.', {title: 'VfDcloser', type: 'success', timeout: 1500});
mw.notify('Discussion closed. Page and talk page updated.', { title: 'VfDcloser', type: 'success', timeout: 1500 });
setTimeout(() => location.reload(), 1500);
setTimeout(() => location.reload(), 1500);
}).fail(err => mw.notify('Error editing talk page: ' + JSON.stringify(err), {title: 'VfDcloser', type: 'error', timeout: 1500}));
}).fail(err =>
mw.notify('Error editing talk page: ' + JSON.stringify(err), { title: 'VfDcloser', type: 'error', timeout: 1500 })
);
});
});
}).fail(err => mw.notify('Error editing page: ' + JSON.stringify(err), {title: 'VfDcloser', type: 'error', timeout: 1500}));
}).fail(err =>
mw.notify('Error editing page: ' + JSON.stringify(err), { title: 'VfDcloser', type: 'error', timeout: 1500 })
);
});
});
}
}
}).fail(err => mw.notify('Error editing discussion page: ' + JSON.stringify(err), {title: 'VfDcloser', type: 'error', timeout: 1500}));
}).fail(err =>
mw.notify('Error editing discussion page: ' + JSON.stringify(err), { title: 'VfDcloser', type: 'error', timeout: 1500 })
);
});
});
});
});


container.appendChild(btn);
// Append OOUI button element to container
container.appendChild(btn.$element[0]);
});
});


Line 175: Line 193:
});
});
});
});
//</nowiki>
// </nowiki>