User:Xover/GetDiffWL.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.loa

// 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.util', 'oojs-ui-core', 'oojs-ui-windows', 'jquery.ui', 'mediawiki.api'], 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;
    }

    // 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 diffPortlet = mw.util.addPortletLink(
      'p-cactions', '#', 'Get permalink', 'ca-getpermalink', 'Get a permanent wikilink to this page'
    ); 
    $(diffPortlet).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.util.getParamValue('diff');
    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()

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.