Userscripts.org Selection Quoting

By sizzlemctwizzle Last update Sep 21, 2009 — Installed 119 times. Daily Installs: 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 1, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1

There are 13 previous versions of this script.

// ==UserScript==
// @name           Userscripts.org Selection Quoting
// @namespace      sizzlemctwizzle
// @version        1.0.9
// @description    Just select the text and click "Quote"
// @require        http://sizzlemctwizzle.com/updater.php?id=48059
// @include        http://userscripts.org/topics/*
// ==/UserScript==

// Smart XPath Function
function $x(x, t, r) {
    if (t && t.tagName) 
        var h = r, r = t, t = h;    
    var d = r ? r.ownerDocument || r : r = document, p;
    switch (t) {
    case 1:
        p = 'numberValue';
        break;
    case 2:
        p = 'stringValue';
        break;
    case 3:
        p = 'booleanValue';
        break;
    case 8: case 9:
        p = 'singleNodeValue';
        break;
    default:
        return d.evaluate(x, r, null, t || 6, null);
    }
    return d.evaluate(x, r, null, t, null)[p];
}
// Optional shortcut functions I like
function $x1(x, r) { return $x(x, 9, r) } 
function $xb(x, r) { return $x(x, 3, r) }
    
// A robust and universal forEach
function forEach(lst, cb) {
    if (lst.snapshotItem)
        for (var i = 0, len = lst.snapshotLength; i < len; ++i)
            cb(lst.snapshotItem(i), i, lst);
    else if (lst.iterateNext) {
        var item;
        while (item = lst.iterateNext()) 
            cb(item, lst);
    } else if (typeof lst.length != 'undefined') 
        for (var i = 0, len = lst.length; i < len; ++i)
            cb(lst[i], i, lst);
    else 
        for (var i in lst) 
            cb(lst[i], i, lst);
}
// Get element by id
function $(element) { return document.getElementById(element); }
// Get elements by classname
function $c(element, root, result) {
  if (result && result.tagName) 
    var temp = root, root = result, result = temp;
  var root = root || document;
  element = (root.getElementsByClassName) ? root.getElementsByClassName(element) : $x(".//*[contains(concat(' ', @class, ' '), ' " + element + " ')]", root);
  if (!result && result!=0)
    return element;
  else
    return (element.length) ? element[result] : element.snapshotItem(result);
}

// Insert an element after another
function insertAfter(node, after) { after.parentNode.insertBefore(node, after.nextSibling);}

// Element creation function by Avg and JoeSimmons
function create(A, B, C) {
	if (!B) 
		A = document.createTextNode(A);
	else {
		A = document.createElement(A);
		for (var b in B) {
			if (b.indexOf("on") == 0)
				A.addEventListener(b.substring(2), B[b], false);
			else if (b == "style")
				A.setAttribute(b, B[b]);
			else
				A[b] = B[b];
		}
		if (C) 
			for(var i = 0, len = C.length; i<len; i++)
				A.appendChild(C[i]);
	}
	return A;
}

// Format the quote and append the quote to the correct box
function quote_handle(e) {
 e.preventDefault();
 var post_hentry = $x1('./ancestor::tr[contains(@class, "post hentry")]', e.target),
     post_id = post_hentry.id.split('row-')[1],
     user = $c('fn', post_hentry, 0).getElementsByTagName('a')[0],
     userName = user.getAttribute('text'),
     userUrl = '/users/'+user.getAttribute('user_id'),
     body = $("post-body-"+post_id); 
  if ((select=window.getSelection())&&(select.focusNode)&&(isPost(select, body.id))) {
    quoted = selectHTML(select, body);
  } else {
    quoted = body.innerHTML;
  }
  if ($("reply").getAttribute('style').match("display: none;")&&($("edit").offsetHeight>0)&&$("edit_post_body")) {
    box = $("edit_post_body");
  } else if ($("reply").offsetHeight>0) {
    box = $("post_body");
  } else {
    box = $("post_body");
    $("reply").style.display = "block";
    $("post_body").value = '';
  }
  box.focus();
  quoted = '<blockquote><strong><a href="'+userUrl+'">'+userName+'</a></strong>&nbsp;<a href="'+
    location.pathname+location.search+'#posts-'+post_id+'">wrote</a>:\n'+
    quoted.replace(/^(<p>\s*<\/p>)/g,'').replace(/^(\s*<br>\s*)*\s*/,'').replace(/^\s*/,'').replace(/<pre>((?:.|\s)*?)<\/pre>/gmi,function(str,p1){return'<pre>'+p1.replace(/\n/g,'<br>')+'</pre>'}).replace(/\n/g, '').replace(/<!--((?:.|\n)*)-->/, '').replace(/<br>/g, '\n').replace(/<p>/g, '').replace(/<\/p>/g, "\n").replace(/^\s+|\s+$/g, '').replace(/ {2,}/g,' ') +
    '</blockquote>\n';
  if(box.value=='') {
  box.value = quoted;
  box.scrollTop = box.scrollHeight;
  } else {
    if ((y=box.selectionEnd)-(x=box.selectionStart)==0) {
      box.value = (box.value).substring(0,x)+quoted+'\n'+(box.value).substring(y,(box.value).length);
      length=((box.value).substring(0,x)+quoted).length
      box.setSelectionRange(length, length); 
    } else {
      if(/\n$/.test(box.value)) box.value = box.value.replace(/\n+$/,'');
      box.value += quoted;
      box.scrollTop = box.scrollHeight;
    }
  }
}

// Get the innerHTML of the selection
// Wrap the selection in its parent's tag
// If the parent is a blockquote then append the correct attribution
function selectHTML(sel, body) {
  var html = "";
  for (var i=0;i<sel.rangeCount;i++){
    var d = create('span', {});
    var r = sel.getRangeAt(i);
    var parent_element = r.commonAncestorContainer;
    if(parent_element.parentNode.tagName!="TD"&&parent_element.parentNode.tagName!="TR") tag=parent_element.parentNode.tagName.toLowerCase();
    else if (parent_element.tagName&&parent_element.tagName!="TD") tag=parent_element.tagName.toLowerCase();
    else tag = '';
    var prev_html = parent_element.innerHTML;
    try {
      r.surroundContents(d);
    } catch(e) {
      GM_log("Selection quoting failed due to a bug in Firefox 3.5.x");
      return body.innerHTML;
    }
    if(quote_frag=getParentQuote(parent_element,body.id)) {
      if((link1=quote_frag.getElementsByTagName('a')[0])&&(/http:\/\/userscripts\.org\/users\/[^\/]+/.test(link1.href))) { 
	author_name = link1.textContent;
	author_link = link1.pathname;
	if((link2=quote_frag.getElementsByTagName('a')[1])&&(/http:\/\/userscripts\.org\/topics\/\d+.*#posts-\d+/.test(link2.href))) {
	  quote_link = link2.pathname;
	} else {
	  quote_link = '';
	}
	quoted = '<blockquote><strong><a href="'+author_link+'">'+author_name+'</a></strong>&nbsp;'+((quote_link=='')?'wrote:':'<a href="'+quote_link+'">wrote</a>:')+'<br>';
	if (tag=="ul"||tag=="li") quoted+='<ul><li>'+d.innerHTML+'</li></ul>';
	else if(tag!=''&&tag!='blockquote') quoted+='<'+tag+'>'+d.innerHTML+'</'+tag+'>';
	else quoted+=d.innerHTML;
	quoted += '</blockquote>';
      }
    } else {
      quoted='';
      }
    if (quoted!='') html +=quoted;
    else if(tag=="ul"||tag=="li") html += '<ul><li>'+d.innerHTML+'</li></ul>';
    else if(tag!=''&&tag!=='a') html += '<'+tag+'>'+d.innerHTML+'</'+tag+'>';
    else html +=d.innerHTML;
    parent_element.innerHTML = prev_html;
    }
  return html;
}

// Determine if the selected portion is in a post
function isPost(sel, id) {
  if(sel.getRangeAt(0).commonAncestorContainer.tagName=="TBODY") return false;
  node = sel.focusNode;
  while (node.parentNode!=null) {
    if(node.id&&node.id==id) return true;
    node = node.parentNode;
  }
  return false;
}

// Determine if the selection portion is in a blockquote
// and return that node
function getParentQuote(node,id) {
  while (node.parentNode&&node.id!=id) {
    node = node.parentNode;
    if (node.tagName=="BLOCKQUOTE") return node;
  }
  return false;
}

if($xb("//a[@class='utility']/child::text()[.='Reply to topic']"))
  forEach($x('//td[@class="author vcard"]//span[@class="role"]'),
     function(role){
       insertAfter(create('a', {href: '#', 
	       className: 'utility', 
	       textContent: 'Quote', 
	       style: 'display: block; clear: both; padding-top: 3px;', 
	       onclick: function(e) { quote_handle(e); }}), role);
     });