diff --git quote.admin.inc quote.admin.inc
index b1418e2..fda5800 100644
--- quote.admin.inc
+++ quote.admin.inc
@@ -51,6 +51,13 @@ function quote_settings_form() {
     '#default_value' => _quote_variable_get('subject_required')
   );
 
+  $form['quote']['use_javascript'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Use javascript'),
+    '#description' => t('If enabled, javascript will be used for quoting to provide a better user experience.'),
+    '#default_value' => _quote_variable_get('use_javascript')
+  );
+
   return system_settings_form($form);
 }
 
diff --git quote.js quote.js
new file mode 100644
index 0000000..fd90605
--- /dev/null
+++ quote.js
@@ -0,0 +1,56 @@
+// $Id $
+
+/**
+ * @file
+ * Allow javascript quoting of a document using javascript.
+ */
+
+/**
+ * Add the proper click behavior to all quote links.
+ */
+Drupal.behaviors.quote = function(context) {
+  $('a.quote-link:not(.quote-processed)', context)
+    .addClass('quote-processed')
+    .click(function(e) {
+      var settings = Drupal.settings.quote[this.id];
+      var textarea = settings.textarea;
+      // Don't use this behavior if there isn't an edit-body id.
+      if (!textarea || $(textarea).length == 0) {
+        return true;
+      }
+      e.preventDefault();
+      var text = '[quote=' + settings.author + ']' + jQuery.trim(settings.body) + '[/quote]\n';
+      insertAtCursor($(textarea).get(0), text);
+    });
+
+  /**
+   * Insert a piece of text at the cursor in a given textarea.
+   *
+   * @param object
+   *   A javascript (not jquery) object.
+   * @param text
+   *   The text to insert.
+   */
+  function insertAtCursor(object, text) {
+    if (document.selection) {
+      object.focus();
+      var sel = document.selection.createRange();
+      sel.text = text;
+      object.focus();
+    }
+    else if (object.selectionStart || object.selectionStart == '0') {
+      var startPos = object.selectionStart;
+      var endPos = object.selectionEnd;
+      var scrollTop = object.scrollTop;
+      object.value = object.value.substring(0, startPos)+text+object.value.substring(endPos,object.value.length);
+      object.focus();
+      object.selectionStart = startPos + text.length;
+      object.selectionEnd = startPos + text.length;
+      object.scrollTop = scrollTop;
+    }
+    else {
+      object.value += text;
+      object.focus();
+    }
+  };
+}
\ No newline at end of file
diff --git quote.module quote.module
index 438a522..6122f1a 100644
--- quote.module
+++ quote.module
@@ -53,29 +53,72 @@ function quote_init() {
 function quote_link($type, $post, $teaser = FALSE) {
   $links = array();
 
-  if (user_access('post comments')) {
+  $comment_type = '';
+  $can_quote = FALSE;
+
+  if ($type == 'node') {
+    // Check to see if this is a nodecomment on a node.
+    if (isset($post->comment_target_nid)) {
+      $parent_node = node_load($post->comment_target_nid);
+    }
+    else {
+      $parent_node = $post;
+    }
+
+    // See what the comment type on this node is.
+    $comment_type = module_invoke('nodecomment', 'get_comment_type', $parent_node->type);
+    if (!$comment_type) {
+      $can_quote = user_access('post comments') && $parent_node->comment == COMMENT_NODE_READ_WRITE;
+      $href = "comment/reply/$post->nid";
+      $textarea = '#edit-comment';
+    }
+    else {
+      $can_quote = user_access("create $comment_type content") && $parent_node->node_comment == COMMENT_NODE_READ_WRITE;
+      $href = "node/add/". str_replace('_', '-', $comment_type) ."/$parent_node->nid";
+      if ($post != $parent_node) {
+        $href .= "/$post->nid";
+      }
+      $textarea = '#edit-body';
+    }
+
+    // Get an unadulterated version so we can use the unfiltered node here.
+    $node = node_load($post->nid);
+    $author = $node->name;
+    $body = $node->body;
+    $id = 'quote-link-node-' . $post->nid;
+  }
+  else if ($type == 'comment') {
+    $parent_node = node_load($post->nid);
+    $can_quote = user_access('post comments') && $parent_node->comment == COMMENT_NODE_READ_WRITE;
+    $href = "comment/reply/$post->nid/$post->cid";
+    $author = $post->name;
+    $body = $post->comment;
+    $id = 'quote-link-comment-' . $post->cid;
+    $textarea = '#edit-comment';
+  }
+
+  if ($type == 'node' && $parent_node->nid == $post->nid) {
+    if (!_quote_variable_get('node_link_display')) {
+      $can_quote = FALSE;
+    }
+  }
+
+  if ($can_quote && in_array($parent_node->type, _quote_variable_get('node_types'))) {
     $link = array(
-      'title' => t('Quote'),
-      'attributes' => array('title' => t('Quote this post in your reply.')),
+      'title' => $parent_node == $post ? t('Quote') : t('quote'),
+      'href' => $href,
+      'attributes' => array('title' => t('Quote this post in your reply.'), 'class' => 'quote-link', 'id' => $id),
       'query' => 'quote=1',
       'fragment' => 'comment-form'
     );
-    // $post can be either a node or a comment.
-    if ($type == 'comment') {
-      // Display quote link for comments only if the parent node is accepting
-      // comments and has the quote filter enabled.
-      $node = menu_get_object();
-      if (in_array($node->type, _quote_variable_get('node_types')) && $node->comment == COMMENT_NODE_READ_WRITE) {
-        $link['href'] = "comment/reply/$post->nid/$post->cid";
-        $link['title'] = t('quote');
-        $links['quote'] = $link;
-      }
-    }
-    elseif ($type == 'node' && in_array($post->type, _quote_variable_get('node_types')) && $post->comment == COMMENT_NODE_READ_WRITE && _quote_variable_get('node_link_display')) {
-      // Display quote link for nodes only if the node is accepting comments,
-      // has the quote filter enabled and has quote_link_display set.
-      $link['href'] = "comment/reply/$post->nid";
-      $links['quote'] = $link;
+    $links['quote'] = $link;
+
+    if (_quote_variable_get('use_javascript')) {
+      $settings['quote'][$id]['author'] = $author;
+      $settings['quote'][$id]['body'] = $body;
+      $settings['quote'][$id]['textarea'] = $textarea;
+      drupal_add_js($settings, 'setting');
+      drupal_add_js(drupal_get_path('module', 'quote') . '/quote.js');
     }
   }
 
@@ -121,6 +164,28 @@ function quote_form_alter(&$form, &$form_state, $form_id) {
       $form['subject']['#required'] = TRUE;
     }
   }
+
+  if (!empty($_GET['quote']) && isset($form['type']) && isset($form['type']['#value'])) {
+    if ($form['type']['#value'] .'_node_form' == $form_id) {
+      $node = &$form['#node'];
+      $nid = arg(3);
+      $cid = arg(4);
+
+      if ($cid) {
+        $parent = node_load($cid);
+      }
+      elseif ($nid && _quote_variable_get('node_link_display')) {
+        $parent = node_load($nid);
+      }
+
+      if ($parent) {
+        $quote = $parent->body;
+        $author = !empty($parent->name) ? $parent->name : variable_get('anonymous', 'Anonymous');
+        // Add quoted text and preserve existing content (signature etc.).
+        $form['body_field']['body']['#default_value'] = '[quote='. $author .']'. trim($quote) ."[/quote]\n". $form['body_field']['body']['#default_value'];
+      }
+    }
+  }
 }
 
 /**
@@ -196,7 +261,8 @@ function _quote_variable_get($name = NULL) {
     $defaults = array(
       'node_types' => array('blog', 'story'),
       'node_link_display' => 1,
-      'subject_required' => 1
+      'subject_required' => 1,
+      'use_javascript' => 1,
     );
     $variables = variable_get('quote', array());
     $variables = array_merge($defaults, $variables);
