Index: app/helpers/application_helper.rb
===================================================================
--- app/helpers/application_helper.rb	(revision 272)
+++ app/helpers/application_helper.rb	(working copy)
@@ -94,4 +94,14 @@
     hash = "#{aggregator.to_s}_#{Digest::SHA1.hexdigest(url)}".to_sym
     controller.cache[hash] ||= aggregator.new(url)
   end  
+
+  def time_in_words(from_time, to_time)
+    if(from_time>to_time)
+      delta="ago"
+    else
+      delta="later"
+    end
+
+    to_time.strftime('%B %d, %Y at %H:%M %p')+" ("+distance_of_time_in_words(from_time,to_time)+" #{delta})"
+  end
 end
Index: app/helpers/admin/base_helper.rb
===================================================================
--- app/helpers/admin/base_helper.rb	(revision 272)
+++ app/helpers/admin/base_helper.rb	(working copy)
@@ -77,6 +77,8 @@
     text_filter_options << [ 'None', 'none' ]
     text_filter_options << [ 'Textile', 'textile' ] if defined?(RedCloth)
     text_filter_options << [ 'Markdown', 'markdown' ] if defined?(BlueCloth)
+    text_filter_options << [ 'SmartyPants', 'smartypants' ] if defined?(RubyPants)
+    text_filter_options << [ 'Markdown with SmartyPants', 'markdown smartypants' ] if defined?(RubyPants) and defined?(BlueCloth)
 
     text_filter_options
   end
Index: app/models/category.rb
===================================================================
--- app/models/category.rb	(revision 272)
+++ app/models/category.rb	(working copy)
@@ -1,5 +1,5 @@
 class Category < ActiveRecord::Base
-  acts_as_list
+#  acts_as_list
   has_and_belongs_to_many :articles, :order => "created_at DESC"
 end
 
Index: app/models/comment.rb
===================================================================
--- app/models/comment.rb	(revision 272)
+++ app/models/comment.rb	(working copy)
@@ -2,6 +2,7 @@
 
 class Comment < ActiveRecord::Base
   belongs_to :article
+  acts_as_tree :order => "id"
 
   validates_presence_of :author, :body
   validates_against_spamdb :body, :url, :ip
@@ -25,7 +26,8 @@
     end
 
     def transform_body
-      self.body_html = HtmlEngine.transform(body, config["text_filter"], [:filter_html]) # Escape HTML in comments
+      text_filter = config["comment_text_filter"]||config["text_filter"]
+      self.body_html = HtmlEngine.transform(body, text_filter, [:filter_html]) # Escape HTML in comments
     end
 
 end
Index: app/models/configuration.rb
===================================================================
--- app/models/configuration.rb	(revision 272)
+++ app/models/configuration.rb	(working copy)
@@ -6,6 +6,7 @@
   setting :sp_article_auto_close, :int, "Auto-close ability to comment/trackback articles after X days"
   setting :sp_url_limit, :int, "Limit for URLs in comments and trackbacks"
   setting :text_filter, :string, "Default HTML transformation style"
+  setting :comment_text_filter, :string, "Comment HTML transformation style"
 end
 
 def config
Index: app/controllers/admin/content_controller.rb
===================================================================
--- app/controllers/admin/content_controller.rb	(revision 272)
+++ app/controllers/admin/content_controller.rb	(working copy)
@@ -13,7 +13,7 @@
 
   def show
     @article = Article.find(@params['id'])
-    @categories = Category.find(:all)
+    @categories = Category.find(:all, :order => 'name')
   end
 
   def new
Index: app/controllers/admin/categories_controller.rb
===================================================================
--- app/controllers/admin/categories_controller.rb	(revision 272)
+++ app/controllers/admin/categories_controller.rb	(working copy)
@@ -6,7 +6,7 @@
   end
 
   def list
-    @categories = Category.find_all
+    @categories = Category.find(:all, :order => 'name')
   end
 
   def show
Index: app/controllers/articles_controller.rb
===================================================================
--- app/controllers/articles_controller.rb	(revision 272)
+++ app/controllers/articles_controller.rb	(working copy)
@@ -18,14 +18,14 @@
     @comment      = Comment.new
     @page_title   = @article.title
 
-    fill_from_cookies(@comment) 
+    populate_comment(@comment)
   end
     
   def permalink
     @article    = Article.find_by_permalink(@params["year"], @params["month"], @params["day"], @params["title"])
     @comment    = Comment.new
 
-    fill_from_cookies(@comment)    
+    populate_comment(@comment)
     
     if @article.nil?
       error("Post not found..")
@@ -78,20 +78,43 @@
     @comment.article = @article
     @comment.ip = request.remote_ip
 
-    if @request.post? and @comment.save      
-      @comment.body = ""
-      
+    if @article.allow_comments and @request.post? and @comment.save      
       cookies['author']  = { :value => @comment.author, :expires => 2.weeks.from_now } 
       cookies['url']     = { :value => @comment.url, :expires => 2.weeks.from_now } 
+      cookies['email']   = { :value => @comment.email, :expires => 2.weeks.from_now } 
 
       @headers["Content-Type"] = "text/html; charset=utf-8"
       
-      render_partial("comment", @comment)      
+      if(@comment.parent_id)
+        render_partial("comment", @comment.parent)     
+      else
+        render_partial_collection("commentset", @article.comments.reject { |c| c.parent_id })     
+      end
     else
       render_partial("comment_error", @comment)
     end
   end  
 
+  def commentbox
+    @article = Article.find(@params["id"])    
+    @comment      = Comment.new
+    @comment.article_id=@params['id']
+    @comment.parent_id = @params['parent_id']
+
+    populate_comment(@comment)
+    render_partial("commentbox", @comment)
+  end
+
+  def commenttitle
+    @article = Article.find(@params["id"])    
+    @comment      = Comment.new
+    @comment.article_id=@params['id']
+    @comment.parent_id = @params['parent_id']
+
+    populate_comment(@comment)
+    render_text(@comment.title)
+  end
+
   # Receive trackbacks linked to articles
   def trackback
     @result = true
@@ -138,10 +161,23 @@
     def fill_from_cookies(comment)      
       comment.author  ||= cookies['author']
       comment.url     ||= cookies['url']
+      comment.email   ||= cookies['email']
     end
     
     def rescue_action_in_public(exception)
       error(exception.message)
     end
 
+    def populate_comment(comment)
+      if(comment.parent_id)
+        comment.parent=Comment.find(comment.parent_id)
+        comment.title = comment.parent.title if comment.parent
+      end
+      
+      if(not comment.title)
+        comment.title=@article.title
+      end
+      
+      fill_from_cookies(comment) 
+    end
 end
Index: app/apis/movable_type_service.rb
===================================================================
--- app/apis/movable_type_service.rb	(revision 272)
+++ app/apis/movable_type_service.rb	(working copy)
@@ -119,6 +119,8 @@
   def supportedTextFilters()
     filters = []
     filters << MovableTypeStructs::TextFilter.new(:key => 'markdown', :label => 'Markdown') if defined?(BlueCloth)
+    filters << MovableTypeStructs::TextFilter.new(:key => 'smartypants', :label => 'SmartyPants') if defined?(RubyPants)
+    filters << MovableTypeStructs::TextFilter.new(:key => 'markdown smartypants', :label => 'Markdown with SmartyPants') if defined?(RubyPants) and defined?(BlueCloth)
     filters << MovableTypeStructs::TextFilter.new(:key => 'textile', :label => 'Textile') if defined?(RedCloth)
     filters
   end
@@ -145,4 +147,4 @@
   def pub_date(time)
     time.strftime "%a, %e %b %Y %H:%M:%S %Z"
   end
-end
\ No newline at end of file
+end
Index: app/views/xml/commentrss.rxml
===================================================================
--- app/views/xml/commentrss.rxml	(revision 272)
+++ app/views/xml/commentrss.rxml	(working copy)
@@ -9,7 +9,7 @@
 
     for comment in @comments
       xml.item do
-        xml.title "\"#{comment.article.title}\" by #{comment.author}"
+        xml.title "\"#{comment.article.title}\": #{comment.title} by #{comment.author}"
         xml.description comment.body_html
         xml.pubDate pub_date(comment.created_at)
         xml.guid comment_link(comment)
Index: app/views/articles/read.rhtml
===================================================================
--- app/views/articles/read.rhtml	(revision 272)
+++ app/views/articles/read.rhtml	(working copy)
@@ -1,16 +1,20 @@
 <% content_for("script") do %>
-function item_added() {
-  var li = $('commentList').getElementsByTagName('LI');
-  new Effect.Appear(li[li.length-1]);
+var old_commentclick='';
+
+function item_added(c_id) {
   Element.hide('comment_loading');
-  Element.show('commentform');
-  $('commentform').elements[2].value = '';
-  $('commentform').elements[2].focus();
   $('form-submit-button').disabled = false;
+  $('comment_body').value='';
+
+  var li = $('commentsub-'+c_id).getElementsByTagName('LI');
+  var newcommentid=li[li.length-1];
+  //$(newcommentid).style.display='none';
+  new Effect.Appear(newcommentid);
 }
-function item_loading() {
+function item_loading(a_id) {
   $('form-submit-button').disabled = true;
   Element.show('comment_loading');
+  reply_to(a_id,'');
 }
 
 window.onload = function() {
@@ -18,6 +22,32 @@
     Element.show('guest_url');
   }
 }
+function reply_to(a_id,c_id) {
+  commentbox=document.getElementById('commentbox');
+  commentblank=document.getElementById('commentbox-'+c_id);
+  oldcommentclick=document.getElementById('commentclick-'+old_commentclick);
+  newcommentclick=document.getElementById('commentclick-'+c_id);
+  oldcommentclick.style.display="block";
+  newcommentclick.style.display="none";
+
+  old_commentclick=c_id;
+
+  commentbox.parentNode.removeChild(commentbox);
+  commentblank.appendChild(commentbox);
+
+  document.getElementById('comment_parent_id').value=c_id;
+  commentform=document.getElementById('commentform');
+  commentform.onsubmit=function(){new Ajax.Updater('comment-'+c_id, '/articles/comment/'+a_id, {onLoading:function(request){item_loading(a_id)}, onComplete:function(request){item_added(c_id)}, parameters:Form.serialize(this), asynchronous:true}); return false;};
+
+  new Effect.Appear('commentbox');
+
+  new Ajax.Updater('comment_title', '/articles/commenttitle/'+a_id+'?parent_id='+c_id, 
+                   {asynchronous:true,
+                    insertion:function(container,text){container.value=text;}
+                   }); 
+
+  return false;
+}
 <% end %>
 <div class="post">
 <%= render_partial "article", @article %>
@@ -27,8 +57,8 @@
   <p class="postmetadata alt">
     <small><a href="#respond">Leave a response</a></small>
   </p>
-  <ol id="commentList" class="comment-list">
-    <%= render_collection_of_partials "comment", @article.comments.sort_by {|c| c.id} %>
+  <ol id="comment-" class="comment-list">
+    <%= render_collection_of_partials "commentset", @article.comments.sort_by {|c| c.id}.reject { |c| c.parent_id } %>
   </ol>
 <% end -%>
 
@@ -46,37 +76,6 @@
 <% end -%>
 
 <% if @article.allow_comments > 0 -%>
-  <%= form_remote_tag :url => {:action => "comment", :id => @article.id}, 
-                      :update => "commentList", :position=> "bottom", 
-                      :loading => "item_loading()", 
-                      :complete => "item_added()",
-                      :html => {:id=>"commentform",:class=>"commentform"} %>
-
-
-  <div class="comment-box">
-    <a name="respond"></a>
-    <table cellpadding="4" cellspacing="0" class="frm-tbl">
-      <tr>
-        <td><p><label for="comment_author">Your name</p></td>
-        <td> <%= text_field "comment", "author", :size => 20 %> <small><a href="javascript:Element.toggle('guest_url')">(leave url &#187;)</a></small></td>
-      </tr>
-      <tr id="guest_url" style="display:none;">
-        <td><p><label for="comment_url">Your blog</a></p></td>
-        <td> <%= text_field "comment", "url" %></td>
-      </tr>
-      <tr>
-        <td><p><label for="comment_body">Your message</label></p></td>
-        <td valign="top" colspan="2">
-          <%= text_area "comment", "body" %>
-        </td>
-      </tr>
-      <tr>
-        <td colspan="2" id="frm-btns">
-        <span id="comment_loading" style="display:none;"><%= image_tag "spinner.gif" %></span>&nbsp;&nbsp;
-          <input type="submit" name="submit" id="form-submit-button" value="submit" class="button" />
-        </td>
-      </tr>
-    </table>
-  </div>
-  <%= end_form_tag %>
-<% end -%>
+  <div id="commentbox-"><%= render_partial "commentbox" %></div>
+  <a class="commentclick" onclick="reply_to(<%=@article.id%>,'')" id="commentclick-" style="display:none;">(Reply to this post)</a>
+<% end -%>
\ No newline at end of file
Index: app/views/articles/_comment.rhtml
===================================================================
--- app/views/articles/_comment.rhtml	(revision 272)
+++ app/views/articles/_comment.rhtml	(working copy)
@@ -1,5 +1,23 @@
-<li class="graybox" id="comment-<%= comment.id %>" <%= 'style="display:none"' if @request.xml_http_request? %>>
+<div class="comment">
   <a name="comment-<%= comment.id %>"></a>
-  <cite><%= (comment.url.blank?) ? comment.author : link_to(comment.author, comment.url) %> </cite> said <%= distance_of_time_in_words @article.created_at, comment.created_at %> later:<br />
+  <div class="commentheader">
+    <% if comment.title %><h3><%=h comment.title %></h3><% end %>
+    <cite>
+      <% if comment.url.blank? %>
+        <%=h comment.author %>
+      <% else %>
+        <%= link_to(comment.author, comment.url) %> 
+      <% end %>
+    </cite> said on <%=h time_in_words(@article.created_at, comment.created_at) %>:
+  </div>
   <%= comment.body_html %>
-</li>
+  <a class="commentclick" onclick="reply_to(<%=comment.article_id%>,<%= comment.id %>)" id="commentclick-<%= comment.id %>">(Reply to this comment)</a>
+  <div class="commentclickbox" id="commentbox-<%= comment.id %>" />
+  </div>
+</div>
+<% if comment.children.size>0 %>
+  <ol id="commentsub-<%= comment.id %>" class="comment-sublist">
+    <%= render_collection_of_partials "commentset", comment.children.sort_by {|c| c.id} %>   
+  </ol>
+<% end %>
+    
\ No newline at end of file
Index: app/views/articles/_article.rhtml
===================================================================
--- app/views/articles/_article.rhtml	(revision 272)
+++ app/views/articles/_article.rhtml	(working copy)
@@ -7,7 +7,7 @@
       trackback:ping="<%= server_url_for :controller=>"articles", :action=>"trackback", :id=>article.id %>"
       dc:title="<%=h article.title %>"
       dc:identifier="<%= server_url_for :controller=>"articles", :action=>"read", :id=>article.id %>"
-      dc:description="<%=h strip_html(article.body_html[0..255]) %>"
+      dc:description="<%=h strip_html(article.body_html[0..255]).gsub(/--/,'-') %>"
       dc:creator="<%= h article.author %>"
       dc:date="<%= article.updated_at.xmlschema %>" />
   </rdf:RDF>
@@ -15,5 +15,5 @@
   <h2>
     <%= article_link article.title, article %>
   </h2>
-  <p class="auth">Posted by <%=h article.author %> <%= distance_of_time_in_words_to_now article.created_at %> ago</p>
+  <p class="auth">Posted by <%=h article.author %> on <%= time_in_words Time.now,article.created_at %></p>
   <%= article.body_html %>
Index: app/views/admin/content/_form.rhtml
===================================================================
--- app/views/admin/content/_form.rhtml	(revision 272)
+++ app/views/admin/content/_form.rhtml	(working copy)
@@ -1,15 +1,15 @@
 <%= error_messages_for 'article' %>
 <!--[form:articles]-->
 
-<label for="articles_title">Title:</label><%= text_field 'article', 'title'  %><br/>
+<label for="article_title">Title:</label><%= text_field 'article', 'title'  %><br/>
 
-<label for="articles_created_at">Created at:</label><%= datetime_select 'article', 'created_at'  %><br/>
+<label for="article_created_at">Created at:</label><%= datetime_select 'article', 'created_at'  %><br/>
 
-<label for="articles_body">Body:</label><%= text_area 'article', 'body'  %><br/>
+<label for="article_body">Body:</label><%= text_area 'article', 'body'  %><br/>
 
-<label for="articles_allow_comments">Allow comments:</label><%= check_box 'article', 'allow_comments'  %><br/>
-<label for="articles_allow_pings">Allow pings:</label><%= check_box 'article', 'allow_pings'  %><br/>
-<label for="articles_text_filter">Textfilter:</label><%= select 'article', 'text_filter', text_filter_options %><br/>
+<label for="article_allow_comments">Allow comments:</label><%= check_box 'article', 'allow_comments'  %><br/>
+<label for="article_allow_pings">Allow pings:</label><%= check_box 'article', 'allow_pings'  %><br/>
+<label for="article_text_filter">Textfilter:</label><%= select 'article', 'text_filter', text_filter_options %><br/>
 
 <br/>
 <br/>
Index: app/views/admin/general/index.rhtml
===================================================================
--- app/views/admin/general/index.rhtml	(revision 272)
+++ app/views/admin/general/index.rhtml	(working copy)
@@ -13,10 +13,10 @@
     <h2>General settings</h2>
     <% for field in @fields -%>
       <%= field.desc %><br/>
-      <% if field.name == :text_filter %>	
-     	   <select name="fields[text_filter]">
-            <%= options_for_select( text_filter_options, config["text_filter"] ) -%>
-         </select>
+      <% if field.name.to_s =~ /text_filter/  %>	
+     	   <select name="fields[<%= field.name.to_s %>]">
+            <%= options_for_select( text_filter_options, config[field.name.to_s] ) -%>
+         </select><br />
       <% else %>
          <%= settings_field field %><br/>
       <% end %>
Index: config/environment.rb
===================================================================
--- config/environment.rb	(revision 272)
+++ config/environment.rb	(working copy)
@@ -77,4 +77,6 @@
 require_dependency 'spam_protection'
 require_dependency 'xmlrpc_fix'
 
-ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.update(:database_manager => CGI::Session::ActiveRecordStore)                                                              
\ No newline at end of file
+require 'rubypants' rescue nil
+
+ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.update(:database_manager => CGI::Session::ActiveRecordStore)
Index: db/schema.mysql.sql
===================================================================
--- db/schema.mysql.sql	(revision 272)
+++ db/schema.mysql.sql	(working copy)
@@ -47,6 +47,7 @@
 CREATE TABLE `comments` (
   `id` int(11) NOT NULL auto_increment,
   `article_id` int(11) default NULL,
+  `parent_id` int(11) default NULL,
   `author` varchar(255) default NULL,
   `email` varchar(255) default NULL,
   `url` varchar(255) default NULL,
Index: db/schema.psql.sql
===================================================================
--- db/schema.psql.sql	(revision 272)
+++ db/schema.psql.sql	(working copy)
@@ -50,6 +50,7 @@
 CREATE TABLE comments (
   id SERIAL PRIMARY KEY NOT NULL,
   article_id int NOT NULL REFERENCES articles,
+  parent_id int default NULL,
   title varchar(255) default NULL,
   author varchar(255) default NULL,
   email varchar(255) default NULL,
Index: db/schema.sqlite.sql
===================================================================
--- db/schema.sqlite.sql	(revision 272)
+++ db/schema.sqlite.sql	(working copy)
@@ -42,6 +42,7 @@
 CREATE TABLE 'comments' (
   'id'          INTEGER PRIMARY KEY NOT NULL,
   'article_id'  INTEGER DEFAULT NULL,
+  'parent_id'   INTEGER DEFAULT NULL,
   'author'      VARCHAR(255) DEFAULT NULL,
   'email'       VARCHAR(255) DEFAULT NULL,
   'url'         VARCHAR(255) DEFAULT NULL,
Index: lib/html_engine.rb
===================================================================
--- lib/html_engine.rb	(revision 272)
+++ lib/html_engine.rb	(working copy)
@@ -2,14 +2,23 @@
   
   def self.transform(txt, text_filter = 'textile', restrictions = [])
     return "" if txt.to_s.empty?  
-    
-    case text_filter
-      when "markdown": BlueCloth.new(txt, restrictions).to_html
+
+    text_filter='filterhtml '+text_filter if restrictions.include?(:filter_html)
+
+    text_filter.split(/ /).each do |filter|
+      case filter
+      when "markdown": 
+        txt = BlueCloth.new(txt, restrictions).to_html
       when "textile": 
-        txt = self.encode_html(txt) if restrictions.include?(:filter_html)
         RedCloth.new(txt, restrictions).to_html(:textile)
-      else txt
+      when "smartypants":
+        txt = RubyPants.new(txt).to_html
+      when "filterhtml":
+        txt = self.encode_html(txt)
+      end
     end
+
+    return txt
   end
 
   # Taken from BlueCloth since RedCloth's filter_html is broken
@@ -19,4 +28,4 @@
     str
 	end 
 
-end
\ No newline at end of file
+end
Index: public/stylesheets/azure.css
===================================================================
--- public/stylesheets/azure.css	(revision 272)
+++ public/stylesheets/azure.css	(working copy)
@@ -167,12 +167,31 @@
  /*+-------------------------------------------+
   |   			      COMMENTS						         |
   +-------------------------------------------+*/
-  .comment-list li {
+  .comment {  
   	background: #d3e0ea;
   	padding: 5px;
   	margin-bottom: 8px;
   	color: #555;
   	}
+
+  .commentheader { 
+        font-weight: bold;
+        }
+
+  .comment-list, .comment-sublist { 
+        list-style-type: none;
+        margin: 0em;
+        padding: 0;
+        }
+
+  .comment-sublist { 
+        margin-left: 2em;
+        }
+
+  .commentclick { 
+        font-size: 0.8em;
+        font-style: italic;
+        }
   	
  /*+-------------------------------------------+
   |   			SIDEBAR 						               |
