User:Xover/EasyLinks.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:Xover/EasyLinks. |
// Global to hold a reference to the OOUI.WindowManager.
var EasyLinks = {};
// Make sure the modules we need are loaded (will only load if not already)
mw.loader.using(['mediawiki.api', 'mediawiki.util', 'oojs-ui-core', 'oojs-ui-windows'], function () {
// Wait for the page to be parsed
$(document).ready(function () {
// TODO: Check for special pages and abort or…
// TODO: …only whitelist namespaces known to make sense.
// Let's at least disable Special:-pages by default to start.
if (mw.config.get('wgCanonicalNamespace') === 'Special') {
return;
}
// And on the Main Page
if (mw.config.get('wgIsMainPage')) {
return;
}
// Disable completely on mobile (and desktop users with Minerva)
if ($('body').hasClass('skin-minerva')) {
return;
}
// Create and append a window manager.
EasyLinks.windowManager = new OO.ui.WindowManager();
$('body').append(EasyLinks.windowManager.$element);
// Predefine a dialog that we'll open by name when needed.
// Specify common defaults here and we'll add the variable stuff when opening.
EasyLinks.windowManager.addWindows({
linkDialog: new OO.ui.MessageDialog({
actions: [
{
action: 'accept',
label: 'Dismiss',
flags: 'primary'
}
]
})
});
// If URL params include &diff= then this is a diff page, so add diffLink portlet
if (mw.util.getParamValue('diff')) {
var diffPortlet = mw.util.addPortletLink(
'p-cactions', '#', 'Get diff link', 'ca-getdiffwl', 'Get a wikilink to this diff'
);
$(diffPortlet).click(function (event) {
event.preventDefault();
doGetDiffWL();
});
}
// Add permalink portlet unconditionally; if &oldid= is missing we use the
// latest version of the page.
var permalinkPortlet = mw.util.addPortletLink(
'p-cactions', '#', 'Get permalink', 'ca-getpermalink', 'Get a permanent wikilink to this page'
);
$(permalinkPortlet).click(function (event) {
event.preventDefault();
doGetPermaWL();
});
// If wgAction is "history", add multidiff portlet
if (mw.config.get('wgAction') === "history") {
var multidiffPortlet = mw.util.addPortletLink(
'p-cactions', '#', 'Get multiple diff links', 'ca-getmdiffwl', 'Get wikilinks for multiple diffs'
);
$(multidiffPortlet).click(function (e) {
e.preventDefault();
setupMultiDiff();
});
}
// Add section link and permalink links to all headings (except page title)
$('h2, h3, h4, h5, h6').has('.mw-headline').each(function() {
var headline = $(this).children('span.mw-headline');
var editsection = $(this).children('span.mw-editsection');
var openbracket = $('<span>[ </span>');
var closebracket = $('<span> ]</span>');
var divider = $('<span> | </span>');
var wlspan = $('<a class="getwikilink" href="#">link</a>').click(function(event) {
event.preventDefault();
doGetSectionWL($(headline).attr('id'));
});
var permaspan = $('<a class="getpermalink" href="#">permalink</span>').click(function(event) {
event.preventDefault();
doGetPermaSectionWL($(headline).attr('id'));
});
var containerspan = $('<span class="mw-editsection getlinks"></span>');
$(containerspan).append(openbracket, wlspan, divider, permaspan, closebracket);
if ($(editsection).length > 0) {
$(containerspan).insertAfter(editsection);
} else {
$(containerspan).insertAfter(headline);
}
});
}); // END: $(document).ready()
// Format the timestamp.
function getTimestring (timestamp) {
const monthNames = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
var t = new Date(timestamp);
var Y = t.getUTCFullYear();
var M = monthNames[t.getUTCMonth()];
var D = t.getUTCDate();
var h = t.getUTCHours().padStart(2, '0');
var m = t.getUTCMinutes().padStart(2, '0');
var s = t.getUTCSeconds();
return `${h}:${m}, ${D} ${M} ${Y}`;
}
// Cobble together the wikitext string for mdiff output.
function formatMultiDiff(revid, user, revtime, summary) {
var string = '';
string += '*<small>{{noping|' + user + '}}@';
string += '[[Special:Diff/' + revid + '|' + revtime + ']]: </small>';
string += '<span class="easylinks-editsummary">' + summary + '</span>';
string += "\n";
return string;
}
// Format the edit summary.
function getSummary (apicomment) {
let summary = '';
if (apicomment === '') {
summary = '<small><span class="autocomment">[no edit summary provided]</span></small>';
} else if (/^\/\*.*?\*\/$/.test(apicomment)) {
summary = apicomment.replace(/^\/\*.*?\*\//, '<span class="autocomment">$&: </span>');
summary += '<span class="autocomment">[no edit summary provided]</span>';
summary = '<small>' + summary + '</small>';
} else {
summary = apicomment.replace(/^\/\*.*?\*\//, '<span class="autocomment">$&: </span>');
}
return summary;
}
// Common utility function to put the link(s) on the user's clipboard,
// and pop up a notification that it happened.
function copyAndNotify(title, description, data) {
// Create a textInputWidget to hold the link, and a FieldLayout to wrap it.
var textInput = new OO.ui.MultilineTextInputWidget({
value: data,
multiline: true,
autosize: true,
});
var textField = new OO.ui.FieldLayout(textInput, {align: 'top', label: null});
// Configure the message dialog when it is opened with the window manager's openWindow() method.
var instance = EasyLinks.windowManager.openWindow('linkDialog', {
title: title,
message: textField.$element
});
// Select the link in the textInput for easy copying.
// Has to happen after it's finished opening or the button will steal focus.
instance.opened.then(function () {
// Try to close dialog when the user hits Enter (since we steal focus from the button).
textInput.on('enter', function () {
EasyLinks.windowManager.getCurrentWindow().close({action: 'accept'});
});
// Try to close dialog on copy siince we no longer need it.
textInput.on('copy', function () {
EasyLinks.windowManager.getCurrentWindow().close({action: 'accept'});
});
// Select the link, copy it to the clipboard, notify the user, and close the dialog.
textInput.select();
console.log(window.getSelection().toString());
if (document.execCommand('copy')) {
mw.notify(
description + ' was automatically copied to your clipboard.',
{title: 'Link copied', type: 'info', tag: 'EasyLinksCopied'}
);
EasyLinks.windowManager.getCurrentWindow().close({action: 'accept'});
} else {
// Auto-copy failed; don't close the dialog to let the user copy manually.
}
});
} // END: copyAndNotify()
// Get the diff id from URL params and create a diff wikilink to that rev.
function doGetDiffWL() {
var diff = mw.config.get('wgDiffNewId');
var diffLink = '[[Special:Diff/' + diff + '|' + diff + ']]';
copyAndNotify(
'Internal wikilink to this diff',
'The wikilink for this diff',
diffLink
);
} // END: doGetDiffWL()
// Get the old rev id from URL params, if present, or the latest rev otherwise.
function doGetPermaWL() {
var oldid;
let kind = '';
if (mw.util.getParamValue('oldid')) {
oldid = mw.util.getParamValue('oldid');
kind = 'this';
} else {
oldid = mw.config.get('wgCurRevisionId');
kind = 'the latest';
}
var oldidLink = '[[Special:PermanentLink/' + oldid + '|' + oldid + ']]';
copyAndNotify(
'Internal wikilink to this version',
'A permanent wikilink to ' + kind + ' revision',
oldidLink
);
} // END: doGetPermaWL()
// Creare a wikilink to a section, irrespective of namespace.
function doGetSectionWL(headlineid) {
var h = $('#' + $.escapeSelector(headlineid));
var title = $(h).text();
var page = mw.config.get('wgPageName');
var sectionLink = '[[' + page + '#' + headlineid + '|' + title + ']]';
copyAndNotify(
'Wikilink to this section',
'A wikilink to this section',
sectionLink
);
} // END: doGetSectionWL()
// Create a permalink to a specific section.
function doGetPermaSectionWL(headlineid) {
var h = $('#' + $.escapeSelector(headlineid));
var title = $(h).text();
var oldid;
// If url contains &oldid= then this is a specific revision of the page,
// otherwise grab the latest revision and use that.
let kind = '';
if (mw.util.getParamValue('oldid')) {
oldid = mw.util.getParamValue('oldid');
kind = 'this';
} else {
oldid = mw.config.get('wgCurRevisionId');
kind = 'the latest';
}
var permaSectionLink = '[[Special:PermanentLink/' + oldid + '#' + headlineid + '|' + title + ']]';
copyAndNotify(
'Permanent wikilink to this section',
'A wikilink to this section at ' + kind + ' revision',
permaSectionLink
);
} // END: doGetPermaSectionWL()
// On history pages, let user pick multiple revisions and get a list of diffs
// for those revisions. Unlike the other functions, this one is "modal": when
// activated we pop up a dialog to prompt the user to pick revisions, and let
// them cancel or confirm the selection.
function setupMultiDiff() {
$('ul#pagehistory > li').prepend(function() {
let revid = $(this).data('mwRevid');
let checkbox = $('<input type="checkbox" />')
.addClass('mdiff-checkbox')
.css('margin-left', '1em')
.css('margin-right', '1em')
.css('vertical-aliign', 'middle')
.data('mdiff-revid', revid);
let cspan = $('<span/>')
.addClass('mdiff-span')
.css('background', '#663399')
.append(checkbox)
.fadeIn(1000);
return cspan;
});
var dialog = $('<div id="mdiff-dialog"><p>Choose the revisions for which you want diffs.</p></div>');
dialog.dialog({
autoOpen: false,
title: 'Choose revisions',
position: {my: 'right top', at: 'right top', of: '#mw-content-text'},
buttons: {
Cancel: function() {
$('.mdiff-checkbox').remove();
$(this).dialog('close');
},
Ok: function() {
$(this).dialog('close');
var selectedRevs = $('.mdiff-checkbox:checked');
if (selectedRevs.length === 0) {
// Nothing selected: remove checkboxes and abort.
$('.mdiff-checkbox').remove();
return;
}
let revisions = [];
selectedRevs.each(function() {
let rev = $(this).data('mdiff-revid');
revisions.push(rev);
});
var diffLinks = '';
var api = new mw.Api();
api.get({
action: 'query',
prop: 'revisions',
rvprop: ['ids', 'flags', 'timestamp', 'user', 'size', 'comment', 'tags'],
revids: revisions
}).done(function (apiResult) {
Object.values(apiResult.query.pages).forEach(function(page) {
page.revisions.forEach(function(p) {
var revid = p.revid;
var user = p.user;
var revsize = p.size;
var revtime = getTimestring(p.timestamp);
var summary = getSummary(p.comment);
diffLinks += formatMultiDiff(revid, user, revtime, summary);
});
});
$('.mdiff-checkbox').remove();
copyAndNotify(
'Wikilinks for these diffs',
'A list of wikilinks for these diffs',
diffLinks
);
}); // END: api.get().done()
} // END: Ok()
} // END: buttons: {}
}); // END: dialog.dialog()
dialog.dialog('open');
} // END: setupMultiDiff()
}); // END: mw.loader.using()