User:Gary/automatic article lead image.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// AUTOMATIC ARTICLE LEAD IMAGE
//
// Description: Adds a lead image to biographies that don't have lead images.
$(() => {
  // Check if ran.
  if (window.autoLeadImage) {
    return;
  }

  // Set running.
  window.autoLeadImage = true;

  // This API key is IP-restricted, so this script won't work for other people.
  const googleApiKey = 'AIzaSyBuXFJn7vNI1JYqe1GQx2i-JhH4sI7JGKk';

  // eslint-disable-next-line complexity
  function init() {
    const enableAutomaticLeadImage = () => {
      if (
        window.mw.config.get('wgCanonicalNamespace') === '' &&
        window.mw.config.get('wgAction') === 'view' &&
        window.mw.util.getParamValue('disable') !== 'leadimage'
      ) {
        return true;
      }

      return false;
    };

    const isPrintable = Boolean(window.location.href.match(/&printable=yes/));

    if (!enableAutomaticLeadImage() || isPrintable) {
      return false;
    }

    let isAPerson = false;

    // eslint-disable-next-line no-restricted-syntax
    for (const category of window.mw.config.get('wgCategories')) {
      const cat = category.replace(/_/g, ' ');
      if (
        cat.indexOf('Living people') === 0 ||
        cat.indexOf(' births') !== -1 ||
        cat.indexOf(' deaths') !== -1
      ) {
        isAPerson = true;
        break;
      }
    }

    if (!isAPerson || findLeadImages() === 1) {
      return false;
    }

    const personName = window.mw.config.get('wgTitle');
    const url =
      `https://www.googleapis.com/customsearch/v1?q=${personName}` +
      '&cx=004227430873773175363:j7_jwozfw80&imgType=face&num=1&' +
      `searchType=image&key=${googleApiKey}`;

    // Get the search results.
    return $.get(url, (json) => {
      // Get the first item's info.
      const imageTitle = json.items[0].title;
      const imageLink = json.items[0].link;
      const imageThumbnail = json.items[0].image.thumbnailLink;

      // If image does not exist, then show the Google-cached thumbnail instead.
      return $.ajax({
        url: imageLink,
        error: () => imageResults([imageTitle, imageThumbnail]),
        success: () => imageResults([imageTitle, imageLink]),
      });

      // We failed to get an image, so just show a placeholder.
    }).fail(() =>
      imageResults([
        'Example',
        'https://upload.wikimedia.org/wikipedia/commons/7/70/Example.png',
      ]),
    );
  }

  // eslint-disable-next-line complexity, max-statements
  function imageResults(results) {
    let addAfter;
    let colSpan;
    let jumpToNav;
    let newNode;
    let newNodeDiv;
    let parentNode;

    const defaultLeadImageSize = 200;

    // The title of the infobox, if there is one.
    const fn = $('.fn').first();
    const infobox = findLeadImages();
    const imageUrl = decodeURI(results[1]);

    // there is no infobox and no lead image
    if (infobox === -1) {
      // insert an image after the "difference" box
      if ($('#difference').length) {
        jumpToNav = $('#diffHeading').next();
        // insert an image into the lead
      } else {
        jumpToNav = $('.mw-content-ltr:eq(0)')
          .children()
          .first();

        // eslint-disable-next-line max-depth
        if (jumpToNav[0].nodeName !== 'P') {
          jumpToNav = jumpToNav.nextAll('p').first();
        }
      }
      // everything else after here has parentNode as a TR
      // in the infobox, there is no name formatted with microformats
    } else if (!fn.length) {
      parentNode = infobox
        .children()
        .first()
        .children()
        .first();
      colSpan = parentNode
        .children()
        .first()
        .attr('colspan');
      addAfter = parentNode;
      // there is a name formatted with microformats, in either TH or TD
    } else if (fn[0].nodeName === 'TH' || fn[0].nodeName === 'TD') {
      parentNode = fn.parent();
      colSpan = fn.attr('colspan');
      addAfter = parentNode;
      // there is a name formatted with microformats, in a CAPTION
    } else if (fn[0].nodeName === 'CAPTION') {
      parentNode = fn
        .siblings()
        .last()
        .children()
        .first();
      colSpan = parentNode
        .children()
        .first()
        .attr('colspan');
      addAfter = parentNode.prev();
      if (colSpan <= 1) {
        colSpan = 2;
      }
      // similar to the above, but it is the parent node
    } else if (fn.parent()[0].nodeName === 'CAPTION') {
      const table = fn.closest('table');
      parentNode = table.find('tr').first();
      colSpan = table
        .find('th')
        .first()
        .attr('colspan');
      addAfter = $();
      // standard infobox with no microformats
    } else {
      parentNode = fn.parent().parent();

      if (parentNode[0].nodeName !== 'TR') {
        parentNode = parentNode.parent();
      }

      colSpan = fn.parent().attr('colspan');
      addAfter = parentNode;
    }

    const newNodeImage = $(
      `<a class="image" href="${imageUrl}"><img alt="${
        results[0]
      }" src="${imageUrl}" style="width: ${defaultLeadImageSize}px;" /></a>`,
    );

    const newNodePreLink = $('<span>From </span>');
    const newNodeLink = $(
      `<a href="http://images.google.com/images?q=${window.mw.config.get(
        'wgTitle',
      )}">Google Images</a>`,
    );

    // there is no infobox and no lead image
    if (infobox === -1) {
      const imagePath =
        'http://bits.wikimedia.org/skins-1.5/common/images/magnify-clip.png';
      const magnify = $(
        '<div class="magnify"><a title="" class="internal" ' +
          `href="${imageUrl}"><img width="15" height="11" alt="" ` +
          `src="${imagePath}"></a></div>`,
      );

      const newNodeCaption = $('<div class="thumbcaption"></div>')
        .append(magnify)
        .append(newNodePreLink)
        .append(newNodeLink);

      newNodeImage.addClass('thumbimage');
      newNodeDiv = $(
        `<div class="thumbinner" style="width: ${defaultLeadImageSize +
          2}px;"></div>`,
      )
        .append(newNodeImage)
        .append(newNodeCaption);
      newNode = $('<div class="thumb tright"></div>').append(newNodeDiv);
      return jumpToNav.before(newNode);
      // there is an infobox with no image
    }

    const newNodeSpan = $('<span style="font-size: 95%;"></span>')
      .append(newNodePreLink)
      .append(newNodeLink);
    newNodeDiv = $('<div></div>')
      .append(newNodeImage)
      .append('<br />')
      .append(newNodeSpan);
    const newNodeChild = $(
      `<td colspan="${colSpan}" style="text-align: center;"></td>`,
    ).append(newNodeDiv);
    newNode = $('<tr></tr>').append(newNodeChild);

    if (addAfter.length) {
      return addAfter.after(newNode);
    }

    return parentNode.before(newNode);
  }

  // eslint-disable-next-line complexity
  function findLeadImages() {
    const defaultImageNames = [
      'File:Replace_this_image_male.svg',
      'File:Replace_this_image_female.svg',
    ];

    const infoboxes = $('.infobox');
    const tocColors = $('.toccolours');
    let infoboxImages = false;

    // get images in infoboxes
    if (infoboxes.length) {
      const $images = $('.image', infoboxes.first());

      // Remove default images.
      $images.each((index, element) => {
        const $image = $(element);

        if (defaultImageNames.indexOf($image.attr('href')) > -1) {
          $image.remove();
        }
      });

      // Running this again because we may have removed some images.
      infoboxImages = $('.image', infoboxes.first());
    } else if (tocColors.length) {
      infoboxImages = $('.image', tocColors.first());
    }

    // there is more than one image in infoboxes
    if (infoboxImages.length) {
      return 1;
    }

    // there is an infobox with no image
    if (infoboxImages.length === 0) {
      return infoboxes.first();
    }

    // There are no infoboxes. Check if we are viewing a diff first
    let node;
    if ($('table.diff').length) {
      node = $('h2.diff-currentversion-title:eq(0)').next();
    } else {
      node = $('.mw-content-ltr:eq(0)')
        .children()
        .first();
    }

    while (
      node.length &&
      node.attr('id') !== 'section-1' &&
      !node.hasClass('printfooter')
    ) {
      // there is a lead image
      if (
        (node[0].nodeName === 'P' || node[0].nodeName === 'DIV') &&
        (node
          .children()
          .first()
          .hasClass('image') ||
          (node.children().eq(1) &&
            node
              .children()
              .first()
              .hasClass('thumbinner')))
      ) {
        return 1;
      }

      node = node.next();
    }

    // there are no lead images
    return -1;
  }

  init();
});