Diaspora reply button

A script to add 'reply' links to comments on default Diaspora* (desktop) website interfaces, which will insert the current comment's author's handle in the new comment textarea.

Terá de instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

Terá de instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Terá de instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Terá de instalar uma extensão como Tampermonkey para instalar este script.

Terá de instalar uma extensão de gestão de scripts de utilizador para instalar este script.

(Já tenho um gestor de scripts de utilizador, deixe-me instalá-lo!)

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão como Stylus para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

Terá de instalar uma extensão de gestão de estilos de utilizador para instalar este estilo.

(Já tenho um gestor de estilos de utilizador, deixe-me instalá-lo!)

// ==UserScript==
// @name Diaspora reply button
// @description A script to add 'reply' links to comments on default Diaspora* (desktop) website interfaces, which will insert the current comment's author's handle in the new comment textarea.
// @author Filip H.F. "FiXato" Slagter 
// @version  5
// @include  /^https:\/\/(www\.)?(plu|joindia)spora\.com/
// @run-at      document-idle
// @grant    none
// @namespace https://github.com/FiXato
// ==/UserScript==
// esversion: 6
// Latest version at: https://gist.github.com/FiXato/fed7eca2e044705a3c14253bf2184335

window.debugUserScript=false;
function consoleDebug() {
  if (window.debugUserScript) {
  	return console.log.apply(null, arguments);
  }
}

// Function by Dmitriy Kubyshkin and copied (with minor variable name modifications) from https://www.everythingfrontend.com/posts/insert-text-into-textarea-at-cursor-position.html
function insertAtCursor(textarea_element, textToInsert) {
  const isSuccess = document.execCommand("insertText", false, textToInsert);

  // Firefox (non-standard method)
  if (!isSuccess && typeof textarea_element.setRangeText === "function") {
    const start = textarea_element.selectionStart;
    textarea_element.setRangeText(textToInsert);
    // update cursor to be at the end of insertion
    textarea_element.selectionStart = textarea_element.selectionEnd = start + textToInsert.length;

    // Notify any possible listeners of the change
    const e = document.createEvent("UIEvent");
    e.initEvent("input", true, false);
    textarea_element.dispatchEvent(e);
  }
}

function get_hovercard_data(profile_url, link_element, callback_fn) {
  consoleDebug('getting hovercard data for profile link: ', profile_url);
  var request = new XMLHttpRequest();
  request.open('GET', profile_url + '/hovercard.json', true);

  request.onload = function() {
    if (request.status >= 200 && request.status < 400) {
      // Success!
      var data = JSON.parse(request.responseText);
      consoleDebug("hovercard JSON: ", request.responseText);
      consoleDebug("hovercard data: ", data);
      callback_fn(data['diaspora_id'], link_element);
    } else {
      consoleDebug("Unsupported HTTP status while retrieving hovercard data: ", request.status, request.responseText);
    }
  };

  request.onerror = function() {
    // There was a connection error of some sort
     consoleDebug("Request error while retrieving hovercard data: ", request.status, request.responseText);
  };

  request.send();
}


function insert_handle_in_comment(handle, link_element) {
  var new_comment_box = link_element.closest('.comment_stream').querySelector('.new-comment .comment-box');
  consoleDebug("Inserting handle at ", handle, new_comment_box);
  insertAtCursor(new_comment_box, '@{' + handle + '}');
  new_comment_box.focus();
}

function add_reply_link(comment_element) {
  consoleDebug("Comment: ", comment_element);
  consoleDebug(comment_element.dataset);
  if (comment_element.dataset.profile_link) {
    consoleDebug('Comment already has a profile link: ', comment_element.dataset.profile_link, comment_element);
    return;
  }
  else {
    consoleDebug("No profile link");
    comment_element.dataset.profile_link = comment_element.querySelector('.author-name')['href'];
    consoleDebug('Profile link added to comment: ', comment_element.dataset.profile_link, comment_element);
  }
	var reply_link = document.createElement('a');
  var new_comment_form = comment_element.closest('.comment_stream').querySelector('form.new-comment');
  consoleDebug("new comment form: ", comment_element, new_comment_form);
  reply_link['href'] = '#' + new_comment_form['id'];
  reply_link.appendChild(document.createTextNode("reply"));
  reply_link.dataset.profile_link = comment_element.dataset.profile_link;
  consoleDebug('Reply Link: ', reply_link);
  reply_link.addEventListener('click', function (e) {
    if (!this.dataset.handle) {
      consoleDebug('Could not find handle in dataset. Requesting from hovercard url', this);
      data = get_hovercard_data(this.dataset.profile_link, this, insert_handle_in_comment);
    }
    else {
      insert_handle_in_comment(this.dataset.handle, this);
    }
	});
  consoleDebug('Reply Link with event listener: ', reply_link);

  var control_icons = comment_element.querySelector('.control-icons');
  consoleDebug('control-icons: ', control_icons);
	control_icons.appendChild(reply_link);
}


window.mainContainerQueryPath = "#main-stream > div, #profile_container .row, #container";

function process_comments() {
 	var comments = document.querySelectorAll('.comment_stream .comments .comment');
  if (comments) {
    consoleDebug(comments.length);
    consoleDebug("Adding reply link to comments: ", comments);
    comments.forEach(comment_element => add_reply_link(comment_element));
  }
  else {
    consoleDebug("Could not find comments");
  }
}

(function() {
  'use strict';
  
  var stream = document.querySelector(window.mainContainerQueryPath);
  var callback = function(mutationRecords) {
    consoleDebug("mutationRecords callback");
    stream = document.querySelector(window.mainContainerQueryPath);

    mutationRecords.forEach(function(mutationRecord) {
      if (mutationRecord && mutationRecord.type && mutationRecord.type == "childList") {
        if (mutationRecord.target) {
          if (!mutationRecord.target.className.includes('timeago')) {
        		consoleDebug(mutationRecord);
            if(mutationRecord.target.className && mutationRecord.target.className.includes('comments')) {
            	process_comments();
          	}
          }
        }
        if (mutationRecord.previousSibling && mutationRecord.previousSibling.className && mutationRecord.previousSibling.className.includes('stream-element') && mutationRecord.previousSibling.className.includes('loaded')) {
          consoleDebug("Calling process_comments()");
          process_comments();
      	}
      }
		});
  }
  if( stream ) {
    new MutationObserver(callback).observe(stream, {
      attributes: true,
      attributeOldValue: true,
      childList: true,
      subtree: true
    })
  }
})();