WPorg-Dev

A userscript to help developers access certain WordPress.org plugin information and pages a lot easier.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        WPorg-Dev
// @namespace   wordpress
// @description A userscript to help developers access certain WordPress.org plugin information and pages a lot easier.
// @include     https://wordpress.org/plugins/*
// @include     https://*.wordpress.org/plugins/*
// @version     2.0
// @copyright   2017 Armando Lüscher
// @author      Armando Lüscher
// @oujs:author noplanman
// @grant       GM_addStyle
// @require     https://code.jquery.com/jquery-1.11.3.min.js
// @homepageURL https://github.com/noplanman/WPorg-Dev-Userscript
// @supportURL  https://github.com/noplanman/WPorg-Dev-Userscript/issues
// ==/UserScript==

// The base URL of the WordPress plugins pages.
var wpBaseURL = location.protocol + '//' + location.host;
var pluginsBaseURL = wpBaseURL + '/plugins/';

var wodu = {};

/**
 * Extract the plugin slug from a URL.
 * @param {string} url URL to extract the plugin slug from.
 * @return {string} The extracted plugin slug.
 */
wodu.getSlug = function (url) {
  var p = url.indexOf(pluginsBaseURL);
  if (p >= 0) {
    return url.substring(p + pluginsBaseURL.length).split('/')[0];
  }
  return '';
};

/**
 * Generate the failed message and the retry button.
 *
 * @param {jQuery}   $el The error message panel gets appended to this jQuery object.
 * @param {Function} cb  Callback function when clicking the "Retry" button.
 * @return {jQuery} The jQuery object containing the message and button.
 */
wodu.getFailedRetryButton = function ($el, cb) {
  $el.removeClass('wodu-loaded');
  return $('<div class="wodu-failed error">Failed.</div>')
    .append($('<div class="alignright button button-primary button-small">Retry</div>').click(cb))
    .appendTo($el);
};

/**
 * Generate a dropdown menu with all the downloadable versions.
 *
 * When the selection changes, the selected version download begins.
 *
 * @param {jQuery} $devPage The page containing the links.
 * @return {jQuery} Dropdown menu with all the links.
 */
wodu.getDLLinkDropdown = function ($devPage) {
  var $select = $('<select class="wodu-dllink-dropdown"/>')
    .append('<option value="">' + $('#download-previous-link', $devPage).text() + '</option>')
    .click(function () {
      this.value = '';
    })
    .change(function () {
      if (this.value) location.href = this.value;
    });

  // Force width of first entry. (https://stackoverflow.com/a/27442394)
  var $selectTmp = $select.clone().hide();
  $selectTmp.appendTo($('body'));
  $select.width($selectTmp.width() + 10);
  $selectTmp.remove();

  $('.previous-versions option', $devPage).each(function () {
    $select.append('<option value="' + $(this).attr('value') + '">' + $(this).text() + '</option>');
  });

  return $select;
};

/**
 * Get the repository (and admin) links from the developers page.
 *
 * @param {string} slug     Plugin slug.
 * @param {jQuery} $devPage The page containing the links.
 * @return {array} An array of the developer links.
 */
wodu.getDevLinks = function (slug, $devPage) {
  return [
    '<a href="' + pluginsBaseURL + slug + '#developers">Developers</a>',
    '<a href="http://plugins.svn.wordpress.org/' + slug + '">SVN</a>',
    '<a href="http://plugins.trac.wordpress.org/browser/' + slug + '">Trac</a>',
    '<a href="http://plugins.trac.wordpress.org/log/' + slug + '">Log</a>'
  ];
};

/**
 * Load the extra plugin infos for a certain plugin.
 *
 * @param {jQuery} $card The plugin cart to load the infos for.
 */
wodu.loadPluginCardExtra = function ($card) {
  if ($card.hasClass('wodu-loaded')) {
    return;
  }
  $card.addClass('wodu-loaded');

  var slug = wodu.getSlug($('.entry-title a', $card).attr('href'));

  // Get rid of any error message that may be there.
  var $panelInfo = $('.wodu-panel-info', $card).empty();
  var $spinner = $('.wodu-spinner', $card).show();

  $.get(pluginsBaseURL + slug + '/advanced', function (response) {
    // Get rid of all images first, no need to load those.
    var $devPage = $(response.replace(/<img[^>]*>/g, ''));

    var $meta = $('.plugin-meta', $devPage);

    // Remove languages section, no need for it.
    $('.languages', $meta).parent().remove();

    $panelInfo.append($meta);

    var $panelDev = $('.wodu-panel-dev', $card)
      .append(wodu.getDevLinks(slug, $devPage).join('&nbsp;-&nbsp;'))
      .append(wodu.getDLLinkDropdown($devPage));
  })
    .fail(function () {
      wodu.getFailedRetryButton($panelInfo, function () {
        wodu.loadPluginCardExtra($card);
      });
    })
    .always(function () {
      $spinner.hide();
    });
};

/**
 * Add extra plugin information to plugin cards.
 *
 * @param {jQuery} $pluginCards All the plugin cards displayed on the current page.
 */
wodu.setupPluginCardExtras = function ($pluginCards) {
  // Add the CSS.
  GM_addStyle(
    '.wodu-close { position: absolute; top: 4px; right: 4px; }' +
    '.wodu-plugin-card { position: relative; }' +
    '.wodu-title { font-size: 1.1em; }' +
    '.wodu-dllink-dropdown { float: right; padding: 3px 5px; }' +
    '.wodu-plugin-card-extras { box-sizing: border-box; overflow: auto; display: none; position: absolute; width: 100%; height: 100%; margin: -15px; border: 1px solid #ccc; padding: 4px 10px; background-color: rgba(255, 255, 255, 0.9); z-index: 1; }' +
    // Little triangle.
    '.wodu-plugin-card:before { content: ""; position: absolute; top: 0px; right: 0px; border-width: 36px 0 0 36px; border-style: solid; border-color: #ddd transparent; }' +
    '.wodu-extras-button { position: absolute; top: 4px; right: 4px; height: 16px; width: 16px; cursor: pointer; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAjElEQVQ4jdWSUQ2AMAxEn4RKQMIkIAEJSEHCHIAEnIATcAA/HYGyhoUvuL8l17vXdPAXdUD1drgBNkCejEGbrAZgNDTBmgRYnIAVaE/vqN6DSIBJm6w8/FFnJCHOGVMO/1w6A30yHWkP+FnqS1oBflrhQi3aFgvwo3pvwfaMHn4kc0ar4s/jqXbaP6gdQPwjtYbeRCAAAAAASUVORK5CYII=); }'
  );

  $pluginCards.each(function () {
    var $card = $(this).addClass('wodu-plugin-card');

    var $extrasButton = $('<div class="wodu-extras-button"/>')
      .click(function () {
        $extrasButton.hide();
        $extras.show();
        wodu.loadPluginCardExtra($card);
      })
      .prependTo($card);

    // Prepare the extras.
    var $close = $('<div class="wodu-close"/>')
      .click(function () {
        $extras.hide();
        $extrasButton.show();
      });
    var $extras = $('<div/>', {class: 'wodu-plugin-card-extras'})
      .append($close)
      .append('<div class="wodu-spinner"/>')
      .append('<div class="wodu-title">' + $('.entry-title a', $card).parent().html() + '</div>')
      .append('<div class="wodu-panel-dev"/>')
      .append('<div class="wodu-panel-info"/>')
      .prependTo($card);
  });
};

/**
 * Start the party.
 */
wodu.init = function () {
  // Add the global CSS rules.
  GM_addStyle(
    '.wodu-spinner { position: absolute; left: 50%; top: 50%; height: 16px; width: 16px; background: no-repeat center center url(data:image/gif;base64,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) }' +
    '.wodu-close { height: 16px; width: 16px; cursor: pointer; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAhklEQVQ4jaWT0Q2AIAwFbyQ2kw3sCG6IG8AG+kFNSAUEIWnSlLyDPgrkdQABuAYjADuFeFRoQwDSAiCwIH7iVUiANzer1ZoAr944FSTN0b1PQCwEzuRxBGAhPXETkCqA1mt1xbaFaROjaWfYxM30XKt1PZgapJVRPiF/iL8AUW8Qpc2cLAA3TsPXvWkb2AIAAAAASUVORK5CYII=) }' +
    '.wodu-failed { margin: 0; }' +
    '.wodu-panel-info .plugin-meta { margin: 0; }' +
    '.wodu-panel-info .plugin-meta .tags { width: auto; }' +
    '.wodu-panel-info .plugin-meta li { padding: 0; }'
  );

  var $pluginCards = $('.plugin-card');
  if ($pluginCards.length) {
    wodu.setupPluginCardExtras($pluginCards);
  }
};

// source: https://muffinresearch.co.uk/does-settimeout-solve-the-domcontentloaded-problem/
if (/(?!.*?compatible|.*?webkit)^mozilla|opera/i.test(navigator.userAgent)) { // Feeling dirty yet?
  document.addEventListener('DOMContentLoaded', wodu.init, false);
} else {
  window.setTimeout(wodu.init, 0);
}