User:Xover/EasyLinks.js

// 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 = $(' [ '); var closebracket = $(' ] '); var divider = $(' | ');

var wlspan = $('link').click(function(event) {       event.preventDefault;        doGetSectionWL($(headline).attr('id'));      }); var permaspan = $('permalink ').click(function(event) {       event.preventDefault;        doGetPermaSectionWL($(headline).attr('id'));      }); var containerspan = $(' '); $(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 += '* @'; string += '' + revtime + ': '; string += ' ' + summary + ' '; string += "\n"; return string; }

// Format the edit summary. function getSummary (apicomment) { let summary = ''; if (apicomment === '') { summary = ' [no edit summary provided] '; } else if (/^\/\*.*?\*\/$/.test(apicomment)) { summary = apicomment.replace(/^\/\*.*?\*\//, ' $&: '); summary += ' [no edit summary provided] '; summary = ' ' + summary + ' '; } else { summary = apicomment.replace(/^\/\*.*?\*\//, ' $&: '); }   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 =  + 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 =  + 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 =  + 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 =  + 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 = $(' ')        .addClass('mdiff-checkbox')        .css('margin-left', '1em')        .css('margin-right', '1em')        .css('vertical-aliign', 'middle')        .data('mdiff-revid', revid);      let cspan = $(' ')        .addClass('mdiff-span')        .css('background', '#663399')        .append(checkbox)        .fadeIn(1000);      return cspan;    });

var dialog = $(' Choose the revisions for which you want diffs. '); 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