User:Lucas Werkmeister/addStatementCountsToCategories.js

From Meta, a Wikimedia project coordination wiki

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/**
 * User script that shows the number of statements of the linked item for all pages and subcategories in a category.
 *
 * To use the script on a wiki, add the following to [[Special:MyPage/common.js|your common.js]] on that wiki:
 * 
 *     mw.loader.load( '//meta.wikimedia.org/w/index.php?title=User:Lucas_Werkmeister/addStatementCountsToCategories.js&action=raw&ctype=text/javascript' );
 * 
 * To use the script on all wikis, add that line to [[m:Special:MyPage/global.js|your global.js]] on Meta-Wiki instead.
 * It should work on any Wikimedia wiki.
 *
 * Compare also:
 * * [[User:Nikki/StatementCounts.js]]
 * * [[User:CamelCaseNick/statementCountsInCategories.js]]
 */
(function() {
  if (mw.config.get('wgNamespaceNumber') !== 14
    || mw.config.get('wgAction') !== 'view'
    || mw.config.exists('wgDiffOldId')
  ) {
  	return $.when();
  }
  return mw.loader.using(['mw.config.values.wbRepo', 'mediawiki.ForeignApi']).then(function() {
    var wbRepo = mw.config.get('wbRepo');
    var clientApi = new mw.Api();
    var repoApi = new mw.ForeignApi(wbRepo.url + wbRepo.scriptPath + '/api.php');
    
    // collect links of category pages
    var hrefPrefix = document.location.origin + mw.config.get('wgArticlePath').replace('$1', '');
    var linksByTitle = {};
    // (`#mw-content-text > .mw-category-generated` bypasses any fake `<div id=mw-pages>` in the wikitext part)
    document.querySelectorAll(
      // TODO use :is(#mw-pages, #mw-subcategories) once MW drops support for browsers that don’t support :is()
      '#mw-content-text > .mw-category-generated #mw-pages li a, ' +
      '#mw-content-text > .mw-category-generated #mw-subcategories li a'
    ).forEach(function (link) {
      var title = decodeURIComponent(link.href.replace(hrefPrefix, '')).replace(/_/g, ' ');
      linksByTitle[title] = link;
    });
    var titles = Object.keys(linksByTitle);

    /**
     * Process a batch of up to 50 titles, then tail-call self with the next batch.
     */
    function processTitlesBatch(titlesBatch) {
      if (titlesBatch.length === 0) {
        return;
      }
      // get linked items from client API
      return clientApi.get({
        action: 'query',
        titles: titlesBatch,
        prop: ['pageprops'],
        ppprop: ['wikibase_item'],
        formatversion: 2,
        maxlag: 5,
      }).then(function(clientResponse) {
      	// associate returned items with pages
        var titlesByItem = {};
        clientResponse.query.pages.forEach(function (page) {
          var item = page.pageprops && page.pageprops.wikibase_item;
          if (item) {
            titlesByItem[item] = page.title;
          }
        });
        var items = Object.keys(titlesByItem);
        if (items.length === 0) {
          return;
        }
        // get statement counts of those items from repo API
        return repoApi.get({
          action: 'query',
          titles: items,
          prop: ['pageprops'],
          ppprop: ['wb-claims'],
          formatversion: 2,
          maxlag: 5,
        }).then(function(repoResponse) {
          repoResponse.query.pages.forEach(function (itemPage) {
          	// get statement count and associated client page / link
            var item = itemPage.title;
            var statements = itemPage.pageprops && itemPage.pageprops['wb-claims'];
            if (!statements) {
              console.warn('No statements count for item ' + item + ' of ' + titlesByItem[item]);
              return;
            }
            var title = titlesByItem[item] || {};
            var link = linksByTitle[title];
            if (link === undefined) {
              console.warn('No link for entity sitelink title ' + title);
              return;
            }

            // create and attach note with number of statements, linking to item
            var note = document.createElement('span');
            note.append(' (');
            var repoLink = document.createElement('a');
            repoLink.href = wbRepo.url + wbRepo.articlePath.replace('$1', item);
            repoLink.textContent = statements + ' st.';
            note.append(repoLink);
            note.append(')');
            link.insertAdjacentElement('afterend', note);
          });

          // process remaining titles
          return processTitlesBatch(titles.splice(0, 50));
        });
      });
    }
    return processTitlesBatch(titles.splice(0, 50));
  });
})().catch(console.error);