noplanman / Reddit Plus

// ==UserScript==
// @name        Reddit Plus
// @namespace   reddit
// @description Additional little features for Reddit.
// @include     https://www.reddit.com*
// @version     1.2
// @copyright   2015 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/Reddit-Plus
// @supportURL  https://github.com/noplanman/Reddit-Plus/issues
// ==/UserScript==

var RedditPlus = {};

/**
 * The MutationObserver to detect page changes.
 */
RedditPlus.Observer = {

  // The mutation observer object.
  observer : null,

  // The elements that we are observing.
  queryToObserve : '#siteTable',

  /**
   * Start observing for DOM changes.
   */
  init : function() {

    // Check if we can use the MutationObserver.
    if ('MutationObserver' in window) {
      var toObserve = document.querySelector(RedditPlus.Observer.queryToObserve);
      if (toObserve) {
        RedditPlus.Observer.observer = new MutationObserver(function() {
          RedditPlus.addCommentToggles();
        });

        // Observe child changes.
        RedditPlus.Observer.observer.observe(toObserve, {
          childList: true
        });
      }
    }
  }
};

/**
 * Fetch the comments and fill them into the appropriate div.
 * @param  {jQuery} $div DIV of the comment area.
 * @param  {string} url  URL of the entry to load the comments from.
 */
RedditPlus.loadComments = function($div, url) {
  // Let the user know that it's loading the comments.
  $div.html('loading...');

  // Get the comments page and extract the info we need.
  $.get(url, function(response) {
    // Get rid of all images first, no need to load those.
    var $post = $(response.replace(/<img[^>]*>/g, ''));

    // Also fetch the text telling us how many comments there are.
    var numOfCommentsText = $post.find('.flat-list .first .comments').html();
    $div.prevAll('.flat-list').find('.first .comments').html(numOfCommentsText);

    // Find the comments area.
    var $commentArea = $('.commentarea', $post);

    // Remove the title and comment filter menu.
    $commentArea.find('.panestack-title, .menuarea').remove();

    // Hide the input field to add a comment and add a button to show it.
    var $commentForm = $('.usertext.cloneable', $commentArea).hide();
    var $textArea = $('textarea', $commentForm);

    // Add a button at the top to add a new comment.
    $('<a/>', {
      class : 'rp-button',
      html  : '<button>add new comment</button><button style="display:none;">cancel</button>',
      click : function() {
        // Switch the "Add new comment" and "Cancel" buttons.
        $('button', this).toggle();

        // Show or Hide the comment form.
        $commentForm.toggle();

        // Set the focus on the comment input field.
        $textArea.focus();
      }
    }).prependTo($commentArea);

    $('button.save', $commentArea).click(function() { $textArea.focus(); });

    // Add a link at the bottom to close the comments.
    $('<a/>', {
      class : 'rp-link',
      html  : '<span>close comments [-]</span>',
      click : function() {
        // Scroll the window to the correct position.
        $(window).scrollTop($(window).scrollTop() - $div.height());

        // Hide the comments.
        RedditPlus.toggleComments($div, url);
      }
    }).appendTo($commentArea);


    // Add a button at the top to reload the comments.
    $('<a/>', {
      class : 'rp-button',
      html  : '<button>reload comments</button>',
      click : function() {
        // Reload all the comments.
        RedditPlus.loadComments($div, url);
      }
    }).prependTo($commentArea);

    // Add the freshly loaded comments to the DIV.
    $div.html($commentArea);
  });
};

/**
 * Toggle the comments area below the link.
 *
 * @param  {jQuery} $div DIV of the comment area.
 * @param  {string} url  URL of the entry to load the comments from.
 */
RedditPlus.toggleComments = function($div, url) {
  // Show / Hide the comments area div.
  $div.toggle();

  // Switch the "[+]" and "[-]" buttons.
  $div.closest('.entry').find('.rp-comments-toggle span').toggle();

  // If we aren't loading / haven't loaded the comments yet, do this now.
  if (!$div.attr('data-loading')) {
    $div.attr('data-loading', true);
    RedditPlus.loadComments($div, url);
  }
};

/**
 * Add the toggles next to the comment links.
 */
RedditPlus.addCommentToggles = function() {
  // Don't execute on comment pages.
  if ($('body.comments-page').length > 0) {
    return;
  }

  // Add toggles next to the comment links that haven't been handled yet.
  $('.comments').not('.rp-comments-toggle-added').each(function() {
    var $commentsLink = $(this);

    // Remember the url of the post page, cause that's where we load the comments from.
    var url = this.href;

    // The div that will contain the loaded comments.
    var $div = $('<div/>', {
      class : 'rp-comments-div',
      html  : 'loading...'
    })
    .hide()
    .appendTo($commentsLink.closest('.entry'));

    // Link to expand / reduce the comments.
    $('<a/>', {
      class : 'rp-comments-toggle',
      style : 'cursor: pointer;',
      html  : '<span title="show comments">[+]</span><span title="close comments" style="display:none">[-]</span>',
      click : function() {
        // Show or Hide the comments.
        RedditPlus.toggleComments($div, url);
      }
    }).insertAfter($commentsLink);

    // Add a class to remember which ones have already been added.
    $commentsLink.addClass('rp-comments-toggle-added');
  });
};

/**
 * Start the party.
 */
RedditPlus.init = function() {
  // Add the global CSS rules.
  GM_addStyle(
    '.rp-comments-div .rp-button { display: inline-block; margin: 4px; }' +
    '.rp-comments-div .rp-link { cursor: pointer; }'
  );

  // Start the observer.
  RedditPlus.Observer.init();

  // Initial load.
  RedditPlus.addCommentToggles();
};

// 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', RedditPlus.init, false);
} else {
  window.setTimeout(RedditPlus.init, 0);
}