diff --git a/app/assets/javascripts/note.js b/app/assets/javascripts/note.js
deleted file mode 100644
index 79ab086bfa2cd7572b387f1dabb1995e84571140..0000000000000000000000000000000000000000
--- a/app/assets/javascripts/note.js
+++ /dev/null
@@ -1,182 +0,0 @@
-var NoteList = {
-
-  notes_path: null,
-  target_params: null,
-  target_id: 0,
-  target_type: null,
-  first_id: 0,
-  last_id: 0,
-  disable:false,
-
-  init:
-    function(tid, tt, path) {
-      this.notes_path = path + ".js";
-      this.target_id = tid;
-      this.target_type = tt;
-      this.target_params = "&target_type=" + this.target_type + "&target_id=" + this.target_id;
-
-      // get notes
-      this.getContent();
-
-      // get new notes every n seconds
-      this.initRefresh();
-
-      $('.delete-note').live('ajax:success', function() {
-        $(this).closest('li').fadeOut(); });
-
-      $(".note-form-holder").live("ajax:before", function(){
-        $(".submit_note").disable()
-      })
-
-      $(".note-form-holder").live("ajax:complete", function(){
-        $(".submit_note").enable()
-      })
-
-      disableButtonIfEmptyField(".note-text", ".submit_note");
-
-      $(".note-text").live("focus", function(){
-        $(this).css("height", "80px");
-        $('.note_advanced_opts').show();
-      });
-
-      $("#note_attachment").change(function(e){
-        var val = $('.input-file').val();
-        var filename = val.replace(/^.*[\\\/]/, '');
-        $(".file_name").text(filename);
-      });
-
-    },
-
-
-  /**
-   * Load new notes to fresh list called 'new_notes_list': 
-   * - Replace 'new_notes_list' with new list every n seconds
-   * - Append new notes to this list after submit
-   */
-
-  initRefresh:
-    function() {
-      // init timer
-      var intNew = setInterval("NoteList.getNew()", 10000);
-    },
-
-  replace:
-    function(html) {
-      $("#new_notes_list").html(html);
-    },
-
-  prepend:
-    function(id, html) {
-      if(id != this.last_id) {
-        $("#new_notes_list").prepend(html);
-      }
-    },
-
-  getNew:
-    function() {
-      // refersh notes list
-      $.ajax({
-        type: "GET",
-      url: this.notes_path,
-      data: "last_id=" + this.last_id + this.target_params,
-      dataType: "script"});
-    },
-
-  refresh:
-    function() {
-      // refersh notes list
-      $.ajax({
-        type: "GET",
-      url: this.notes_path,
-      data: "first_id=" + this.first_id + "&last_id=" + this.last_id + this.target_params,
-      dataType: "script"});
-    },
-
-
-  /**
-   * Init load of notes: 
-   * 1. Get content with ajax call
-   * 2. Set content of notes list with loaded one
-   */
-
-
-  getContent: 
-    function() { 
-      $.ajax({
-        type: "GET",
-      url: this.notes_path,
-      data: "?" + this.target_params,
-      complete: function(){ $('.status').removeClass("loading")},
-      beforeSend: function() { $('.status').addClass("loading") },
-      dataType: "script"});
-    },
-
-  setContent:
-    function(fid, lid, html) {
-      this.last_id = lid;
-      this.first_id = fid;
-      $("#notes-list").html(html);
-
-      // Init infinite scrolling
-      this.initLoadMore();
-    },
-
-
-  /**
-   * Paging for old notes when scroll to bottom: 
-   * 1. Init scroll events with 'initLoadMore'
-   * 2. Load onlder notes with 'getOld' method
-   * 3. append old notes to bottom of list with 'append'
-   *
-   */
-  getOld:
-    function() {
-      $('.loading').show();
-      $.ajax({
-        type: "GET",
-        url: this.notes_path,
-        data: "first_id=" + this.first_id + this.target_params,
-        complete: function(){ $('.status').removeClass("loading")},
-        beforeSend: function() { $('.status').addClass("loading") },
-        dataType: "script"});
-    },
-
-  append:
-    function(id, html) {
-      if(this.first_id == id) { 
-        this.disable = true;
-      } else { 
-        this.first_id = id;
-        $("#notes-list").append(html);
-      }
-    },
-
-  initLoadMore:
-    function() {
-      $(document).endlessScroll({
-        bottomPixels: 400,
-      fireDelay: 1000,
-      fireOnce:true,
-      ceaseFire: function() { 
-        return NoteList.disable;
-      },
-      callback: function(i) {
-        NoteList.getOld();
-      }
-      });
-    }
-};
-
-var PerLineNotes = { 
-  init:
-    function() {
-      $(".line_note_link, .line_note_reply_link").live("click", function(e) {
-        var form = $(".per_line_form");
-        $(this).closest("tr").after(form);
-        form.find("#note_line_code").val($(this).attr("line_code"));
-        form.show();
-        return false;
-      });
-      disableButtonIfEmptyField(".line-note-text", ".submit_inline_note");
-    }
-}
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
new file mode 100644
index 0000000000000000000000000000000000000000..81bb1d6d1b0f81cfd20a075c865ac3f375037042
--- /dev/null
+++ b/app/assets/javascripts/notes.js
@@ -0,0 +1,251 @@
+var NoteList = {
+
+  notes_path: null,
+  target_params: null,
+  target_id: 0,
+  target_type: null,
+  top_id: 0,
+  bottom_id: 0,
+  loading_more_disabled: false,
+  reversed: false,
+
+  init:
+    function(tid, tt, path) {
+      this.notes_path = path + ".js";
+      this.target_id = tid;
+      this.target_type = tt;
+      this.reversed = $("#notes-list").hasClass("reversed");
+      this.target_params = "&target_type=" + this.target_type + "&target_id=" + this.target_id;
+
+      // get initial set of notes
+      this.getContent();
+
+      $("#notes-list, #new-notes-list").on("ajax:success", ".delete-note", function() {
+        $(this).closest('li').fadeOut();
+      });
+
+      $(".note-form-holder").on("ajax:before", function(){
+        $(".submit_note").disable()
+      })
+
+      $(".note-form-holder").on("ajax:complete", function(){
+        $(".submit_note").enable()
+      })
+
+      disableButtonIfEmptyField(".note-text", ".submit_note");
+
+      $(".note-text").on("focus", function(){
+        $(this).css("height", "80px");
+        $('.note_advanced_opts').show();
+      });
+
+      $("#note_attachment").change(function(e){
+        var val = $('.input-file').val();
+        var filename = val.replace(/^.*[\\\/]/, '');
+        $(".file_name").text(filename);
+      });
+    },
+
+
+  /**
+   * Handle loading the initial set of notes.
+   * And set up loading more notes when scrolling to the bottom of the page.
+   */
+
+
+  /**
+   * Gets an inital set of notes.
+   */
+  getContent:
+    function() {
+      $.ajax({
+        type: "GET",
+      url: this.notes_path,
+      data: "?" + this.target_params,
+      complete: function(){ $('.notes-status').removeClass("loading")},
+      beforeSend: function() { $('.notes-status').addClass("loading") },
+      dataType: "script"});
+    },
+
+  /**
+   * Called in response to getContent().
+   * Replaces the content of #notes-list with the given html.
+   */
+  setContent:
+    function(first_id, last_id, html) {
+      this.top_id = first_id;
+      this.bottom_id = last_id;
+      $("#notes-list").html(html);
+
+      // init infinite scrolling
+      this.initLoadMore();
+
+      // init getting new notes
+      if (this.reversed) {
+        this.initRefreshNew();
+      }
+    },
+
+
+  /**
+   * Handle loading more notes when scrolling to the bottom of the page.
+   * The id of the last note in the list is in this.bottom_id.
+   *
+   * Set up refreshing only new notes after all notes have been loaded.
+   */
+
+
+  /**
+   * Initializes loading more notes when scrolling to the bottom of the page.
+   */
+  initLoadMore:
+    function() {
+      $(document).endlessScroll({
+        bottomPixels: 400,
+        fireDelay: 1000,
+        fireOnce:true,
+        ceaseFire: function() {
+          return NoteList.loading_more_disabled;
+        },
+        callback: function(i) {
+          NoteList.getMore();
+        }
+      });
+    },
+
+  /**
+   * Gets an additional set of notes.
+   */
+  getMore:
+    function() {
+      // only load more notes if there are no "new" notes
+      $('.loading').show();
+      $.ajax({
+        type: "GET",
+        url: this.notes_path,
+        data: "loading_more=1&" + (this.reversed ? "before_id" : "after_id") + "=" + this.bottom_id + this.target_params,
+        complete: function(){ $('.notes-status').removeClass("loading")},
+        beforeSend: function() { $('.notes-status').addClass("loading") },
+        dataType: "script"});
+    },
+
+  /**
+   * Called in response to getMore().
+   * Append notes to #notes-list.
+   */
+  appendMoreNotes:
+    function(id, html) {
+      if(id != this.bottom_id) {
+        this.bottom_id = id;
+        $("#notes-list").append(html);
+      }
+    },
+
+  /**
+   * Called in response to getMore().
+   * Disables loading more notes when scrolling to the bottom of the page.
+   * Initalizes refreshing new notes.
+   */
+  finishedLoadingMore:
+    function() {
+      this.loading_more_disabled = true;
+
+      // from now on only get new notes
+      if (!this.reversed) {
+        this.initRefreshNew();
+      }
+    },
+
+
+  /**
+   * Handle refreshing and adding of new notes.
+   *
+   * New notes are all notes that are created after the site has been loaded.
+   * The "old" notes are in #notes-list the "new" ones will be in #new-notes-list.
+   * The id of the last "old" note is in this.bottom_id.
+   */
+
+
+  /**
+   * Initializes getting new notes every n seconds.
+   */
+  initRefreshNew:
+    function() {
+      setInterval("NoteList.getNew()", 10000);
+    },
+
+  /**
+   * Gets the new set of notes.
+   */
+  getNew:
+    function() {
+      $.ajax({
+        type: "GET",
+      url: this.notes_path,
+      data: "loading_new=1&after_id=" + (this.reversed ? this.top_id : this.bottom_id) + this.target_params,
+      dataType: "script"});
+    },
+
+  /**
+   * Called in response to getNew().
+   * Replaces the content of #new-notes-list with the given html.
+   */
+  replaceNewNotes:
+    function(html) {
+      $("#new-notes-list").html(html);
+    },
+
+  /**
+   * Adds a single note to #new-notes-list.
+   */
+  appendNewNote:
+    function(id, html) {
+      if (this.reversed) {
+        $("#new-notes-list").prepend(html);
+      } else {
+        $("#new-notes-list").append(html);
+      }
+    }
+};
+
+var PerLineNotes = {
+  init:
+    function() {
+      /**
+       * Called when clicking on the "add note" or "reply" button for a diff line.
+       *
+       * Shows the note form below the line.
+       * Sets some hidden fields in the form.
+       */
+      $(".diff_file_content").on("click", ".line_note_link, .line_note_reply_link", function(e) {
+        var form = $(".per_line_form");
+        $(this).closest("tr").after(form);
+        form.find("#note_line_code").val($(this).data("lineCode"));
+        form.show();
+        return false;
+      });
+
+      disableButtonIfEmptyField(".line-note-text", ".submit_inline_note");
+
+      /**
+       * Called in response to successfully deleting a note on a diff line.
+       *
+       * Removes the actual note from view.
+       * Removes the reply button if the last note for that line has been removed.
+       */
+      $(".diff_file_content").on("ajax:success", ".delete-note", function() {
+        var trNote = $(this).closest("tr");
+        trNote.fadeOut(function() {
+          $(this).remove();
+        });
+
+        // check if this is the last note for this line
+        // elements must really be removed for this to work reliably
+        var trLine = trNote.prev();
+        var trRpl  = trNote.next();
+        if (trLine.hasClass("line_holder") && trRpl.hasClass("reply")) {
+          trRpl.fadeOut(function() { $(this).remove(); });
+        }
+      });
+    }
+}
diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss
index 6a965fa47b927a2d4d686817eb342094bfcab92e..2db20f166a683973300617ac4d80a1510832767d 100644
--- a/app/assets/stylesheets/sections/notes.scss
+++ b/app/assets/stylesheets/sections/notes.scss
@@ -3,14 +3,17 @@
  *
  */
 #notes-list,
-#new_notes_list {
+#new-notes-list {
   display:block;
   list-style:none;
   margin:0px;
   padding:0px;
 }
 
-#new_notes_list li:last-child{
+#new-notes-list:not(.reversed) {
+  border-top:1px solid #aaa;
+}
+#new-notes-list.reversed {
   border-bottom:1px solid #aaa;
 }
 
@@ -48,7 +51,6 @@
 
 .note {
   padding: 8px 0;
-  border-bottom: 1px solid #eee;
   overflow: hidden;
   display: block;
   img {float: left; margin-right: 10px;}
@@ -70,6 +72,18 @@
     .delete-note { display:block; }
   }
 }
+#notes-list:not(.reversed) .note,
+#new-notes-list:not(.reversed) .note {
+  border-bottom: 1px solid #eee;
+}
+#notes-list.reversed .note,
+#new-notes-list.reversed .note {
+  border-top: 1px solid #eee;
+}
+
+.notes-status {
+  margin: 18px;
+}
 
 
 p.notify_controls input{
diff --git a/app/contexts/notes/load_context.rb b/app/contexts/notes/load_context.rb
index c89a7d19761ba5118af4f9b3b22ec4e4c1303a24..6d26e16a56c331a46d8ed1e383857f380a634b68 100644
--- a/app/contexts/notes/load_context.rb
+++ b/app/contexts/notes/load_context.rb
@@ -3,30 +3,31 @@ module Notes
     def execute
       target_type = params[:target_type]
       target_id   = params[:target_id]
-      first_id    = params[:first_id]
-      last_id     = params[:last_id]
+      after_id    = params[:after_id]
+      before_id   = params[:before_id]
 
 
       @notes = case target_type
-               when "commit" 
-                 then project.commit_notes(project.commit(target_id)).fresh.limit(20)
-               when "snippet"
-                 then  project.snippets.find(target_id).notes
-               when "wall"
-                 then project.common_notes.order("created_at DESC").fresh.limit(50)
+               when "commit"
+                 project.commit_notes(project.commit(target_id)).fresh.limit(20)
                when "issue"
-                 then project.issues.find(target_id).notes.inc_author.order("created_at DESC").limit(20)
+                 project.issues.find(target_id).notes.inc_author.fresh.limit(20)
                when "merge_request"
-                 then project.merge_requests.find(target_id).notes.inc_author.order("created_at DESC").limit(20)
+                 project.merge_requests.find(target_id).notes.inc_author.fresh.limit(20)
+               when "snippet"
+                 project.snippets.find(target_id).notes.fresh
+               when "wall"
+                 # this is the only case, where the order is DESC
+                 project.common_notes.order("created_at DESC, id DESC").limit(50)
                when "wiki"
-                 then project.wikis.reverse.map {|w| w.notes.fresh }.flatten[0..20]
+                 project.wikis.reverse.map {|w| w.notes.fresh }.flatten[0..20]
                end
 
-      @notes = if last_id
-                 @notes.where("id > ?", last_id)
-               elsif first_id
-                 @notes.where("id < ?", first_id)
-               else 
+      @notes = if after_id
+                 @notes.where("id > ?", after_id)
+               elsif before_id
+                 @notes.where("id < ?", before_id)
+               else
                  @notes
                end
     end
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..28701661dc474c036e74a26f4c40c72ad051f63d
--- /dev/null
+++ b/app/helpers/notes_helper.rb
@@ -0,0 +1,9 @@
+module NotesHelper
+  def loading_more_notes?
+    params[:loading_more].present?
+  end
+
+  def loading_new_notes?
+    params[:loading_new].present?
+  end
+end
diff --git a/app/models/note.rb b/app/models/note.rb
index 4c46c7dfffa80215a34577f723791bfa920eeb99..34edb94edca5cf64fafd764fb5587a25b99792fd 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -36,7 +36,7 @@ class Note < ActiveRecord::Base
   scope :today, where("created_at >= :date", date: Date.today)
   scope :last_week, where("created_at  >= :date", date: (Date.today - 7.days))
   scope :since, lambda { |day| where("created_at  >= :date", date: (day)) }
-  scope :fresh, order("created_at DESC")
+  scope :fresh, order("created_at ASC, id ASC")
   scope :inc_author_project, includes(:project, :author)
   scope :inc_author, includes(:author)
 
diff --git a/app/views/commits/_text_file.html.haml b/app/views/commits/_text_file.html.haml
index 0f6210f2b5a0c094d542425ad87158f1d70df24e..9f5b5345d5f76a735b335a283c5d53d0f3910b97 100644
--- a/app/views/commits/_text_file.html.haml
+++ b/app/views/commits/_text_file.html.haml
@@ -13,14 +13,11 @@
         %td.old_line
           = link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
           - if @comments_allowed
-            = link_to "", "#", class: "line_note_link", "line_code" => line_code, title: "Add note for this line"
+            = render "notes/per_line_note_link", line_code: line_code
         %td.new_line= link_to raw(type == "old" ? "&nbsp;" : line_new) , "##{line_code}", id: line_code
         %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line} &nbsp;"
 
         - if @comments_allowed
-          - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at).reverse
+          - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at)
           - unless comments.empty?
-            - comments.each_with_index do |note, i|
-              = render "notes/reply_button", line_code: line_code if i.zero?
-              = render "notes/per_line_show", note: note
-              - @line_notes.reject!{ |n| n == note }
+            = render "notes/per_line_notes_with_reply", notes: comments
diff --git a/app/views/commits/show.html.haml b/app/views/commits/show.html.haml
index e01f8ea587858007f5e2a54c35cb603f33676209..d12fff96835e5f77adba037c8ade5535d524bfb5 100644
--- a/app/views/commits/show.html.haml
+++ b/app/views/commits/show.html.haml
@@ -1,6 +1,6 @@
 = render "commits/commit_box"
 = render "commits/diffs", diffs: @commit.diffs
-= render "notes/notes", tid: @commit.id, tt: "commit"
+= render "notes/notes_with_form", tid: @commit.id, tt: "commit"
 = render "notes/per_line_form"
 
 
diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml
index 9b1c72a3a12c6e5331edfa59596b653f81c1586c..0b72a820bb4e2536a8b3f0f0e19f96dca0c30479 100644
--- a/app/views/issues/show.html.haml
+++ b/app/views/issues/show.html.haml
@@ -61,4 +61,4 @@
         = markdown @issue.description
 
 
-.issue_notes#notes= render "notes/notes", tid: @issue.id, tt: "issue"
+.issue_notes#notes= render "notes/notes_with_form", tid: @issue.id, tt: "issue"
diff --git a/app/views/merge_requests/_show.html.haml b/app/views/merge_requests/_show.html.haml
index f1b3fa9fe984458532ce63c95cda14108cfa3282..40b7219019967e6b30c7416f76109d4d132e4ece 100644
--- a/app/views/merge_requests/_show.html.haml
+++ b/app/views/merge_requests/_show.html.haml
@@ -16,7 +16,7 @@
         Diff
 
 .merge_request_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" }
-  = render("notes/notes", tid: @merge_request.id, tt: "merge_request")
+  = render("notes/notes_with_form", tid: @merge_request.id, tt: "merge_request")
 .merge-request-diffs
   = render "merge_requests/show/diffs" if @diffs
 .status
diff --git a/app/views/merge_requests/show.js.haml b/app/views/merge_requests/show.js.haml
index 7a27b1668499571c454383d9f85b88da627911c2..f44ccbb5127a52c7ea8031de48b588b19dc66c07 100644
--- a/app/views/merge_requests/show.js.haml
+++ b/app/views/merge_requests/show.js.haml
@@ -1,2 +1,2 @@
 :plain
-  $(".merge-request-notes").html("#{escape_javascript(render("notes/notes", tid: @merge_request.id, tt: "merge_request"))}");
+  $(".merge-request-notes").html("#{escape_javascript(render notes/notes_with_form", tid: @merge_request.id, tt: "merge_request")}");
diff --git a/app/views/notes/_form.html.haml b/app/views/notes/_common_form.html.haml
similarity index 100%
rename from app/views/notes/_form.html.haml
rename to app/views/notes/_common_form.html.haml
diff --git a/app/views/notes/_create_common.js.haml b/app/views/notes/_create_common_note.js.haml
similarity index 72%
rename from app/views/notes/_create_common.js.haml
rename to app/views/notes/_create_common_note.js.haml
index e80eccb1b4c9fe4108cc2e581a64de17b73e90f9..bbebc2478c54f08d81f607cff864453d17f369e9 100644
--- a/app/views/notes/_create_common.js.haml
+++ b/app/views/notes/_create_common_note.js.haml
@@ -5,8 +5,9 @@
     $('.note-form-holder #preview-link').text('Preview');
     $('.note-form-holder #preview-note').hide();
     $('.note-form-holder').show();
-    NoteList.prepend(#{note.id}, "#{escape_javascript(render partial: "notes/show", locals: {note: note})}");
+    NoteList.appendNewNote(#{note.id}, "#{escape_javascript(render "notes/note", note: note)}");
+
 - else
   :plain
-    $(".note-form-holder").replaceWith("#{escape_javascript(render('form'))}");
+    $(".note-form-holder").replaceWith("#{escape_javascript(render 'form')}");
 
diff --git a/app/views/notes/_create_line.js.haml b/app/views/notes/_create_line.js.haml
deleted file mode 100644
index 662909f79679d4136986ea0963e4f415ea38e494..0000000000000000000000000000000000000000
--- a/app/views/notes/_create_line.js.haml
+++ /dev/null
@@ -1,8 +0,0 @@
-- if note.valid?
-  :plain
-    $(".per_line_form").hide();
-    $('.line-note-form-holder textarea').val("");
-    $("a.line_note_reply_link[line_code='#{note.line_code}']").closest("tr").remove();
-    var trEl = $(".#{note.line_code}").parent();
-    trEl.after("#{escape_javascript(render partial: "notes/per_line_show", locals: {note: note})}");
-    trEl.after("#{escape_javascript(render partial: "notes/reply_button", locals: {line_code: note.line_code})}");
diff --git a/app/views/notes/_create_per_line_note.js.haml b/app/views/notes/_create_per_line_note.js.haml
new file mode 100644
index 0000000000000000000000000000000000000000..180960e38e4e357afa0429b92f6c677309ae3aac
--- /dev/null
+++ b/app/views/notes/_create_per_line_note.js.haml
@@ -0,0 +1,19 @@
+- if note.valid?
+  :plain
+    // hide and reset the form
+    $(".per_line_form").hide();
+    $('.line-note-form-holder textarea').val("");
+
+    // find the reply button for this line
+    // (might not be there if this is the first note)
+    var trRpl = $("a.line_note_reply_link[data-line-code='#{note.line_code}']").closest("tr");
+    if (trRpl.size() == 0) {
+      // find the commented line ...
+      var trEl = $(".#{note.line_code}").parent();
+      // ... and insert the note and the reply button after it
+      trEl.after("#{escape_javascript(render "notes/per_line_reply_button", line_code: note.line_code)}");
+      trEl.after("#{escape_javascript(render "notes/per_line_note", note: note)}");
+    } else {
+      // instert new note before reply button
+      trRpl.before("#{escape_javascript(render "notes/per_line_note", note: note)}");
+    }
diff --git a/app/views/notes/_load.js.haml b/app/views/notes/_load.js.haml
deleted file mode 100644
index c16a699a1b7091d4e5aaaa4ebeb086b22b3dfe0a..0000000000000000000000000000000000000000
--- a/app/views/notes/_load.js.haml
+++ /dev/null
@@ -1,17 +0,0 @@
-- unless @notes.blank?
-  - if params[:last_id]
-    :plain
-      NoteList.replace("#{escape_javascript(render(partial: 'notes/notes_list'))}");
-
-  - elsif params[:first_id]
-    :plain
-      NoteList.append(#{@notes.last.id}, "#{escape_javascript(render(partial: 'notes/notes_list'))}");
-
-  - else
-    :plain
-      NoteList.setContent(#{@notes.last.id}, #{@notes.first.id}, "#{escape_javascript(render(partial: 'notes/notes_list'))}");
-
-- else
-  - if params[:first_id]
-    :plain
-      NoteList.append(#{params[:first_id]}, "");
diff --git a/app/views/notes/_show.html.haml b/app/views/notes/_note.html.haml
similarity index 100%
rename from app/views/notes/_show.html.haml
rename to app/views/notes/_note.html.haml
diff --git a/app/views/notes/_notes.html.haml b/app/views/notes/_notes.html.haml
index e692e7464658c60c524353cff4e3b0fa1d1bf158..adb5dfcbf18741de0e635f3958670f0d65a88be2 100644
--- a/app/views/notes/_notes.html.haml
+++ b/app/views/notes/_notes.html.haml
@@ -1,13 +1,4 @@
-- if can? current_user, :write_note, @project
-  = render "notes/form"
-.clear
-%hr
-%ul#new_notes_list
-%ul#notes-list
-.status
+- @notes.each do |note|
+  - next unless note.author
+  = render "note", note: note
 
-
-:javascript
-  $(function(){
-    NoteList.init("#{tid}", "#{tt}", "#{project_notes_path(@project)}");
-  });
diff --git a/app/views/notes/_notes_list.html.haml b/app/views/notes/_notes_list.html.haml
deleted file mode 100644
index 5673988d87d23b95923a1e389d4f0784472a8650..0000000000000000000000000000000000000000
--- a/app/views/notes/_notes_list.html.haml
+++ /dev/null
@@ -1,4 +0,0 @@
-- @notes.each do |note|
-  - next unless note.author
-  = render partial: "notes/show", locals: {note: note}
-
diff --git a/app/views/notes/_notes_with_form.html.haml b/app/views/notes/_notes_with_form.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..53716c1d3f4ddd3317bdcf63ad528e0db230a8c3
--- /dev/null
+++ b/app/views/notes/_notes_with_form.html.haml
@@ -0,0 +1,11 @@
+%ul#notes-list
+%ul#new-notes-list
+.notes-status
+
+- if can? current_user, :write_note, @project
+  = render "notes/common_form"
+
+:javascript
+  $(function(){
+    NoteList.init("#{tid}", "#{tt}", "#{project_notes_path(@project)}");
+  });
diff --git a/app/views/notes/_per_line_note.html.haml b/app/views/notes/_per_line_note.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..28bcd6e0c94f3e5779f1a8718340673a3671f1af
--- /dev/null
+++ b/app/views/notes/_per_line_note.html.haml
@@ -0,0 +1,5 @@
+%tr.line_notes_row
+  %td{colspan: 3}
+    %ul
+      = render "notes/note", note: note
+
diff --git a/app/views/notes/_per_line_note_link.html.haml b/app/views/notes/_per_line_note_link.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..72b59a596a352d02897948f85f4ddd3fd742991f
--- /dev/null
+++ b/app/views/notes/_per_line_note_link.html.haml
@@ -0,0 +1 @@
+= link_to "", "#", class: "line_note_link", data: { line_code: line_code }, title: "Add note for this line"
diff --git a/app/views/notes/_per_line_notes_with_reply.html.haml b/app/views/notes/_per_line_notes_with_reply.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..1bcfc41fe203b81162ef6447ee8c8bca8ab7f37e
--- /dev/null
+++ b/app/views/notes/_per_line_notes_with_reply.html.haml
@@ -0,0 +1,3 @@
+- notes.each do |note|
+  = render "notes/per_line_note", note: note
+= render "notes/per_line_reply_button", line_code: notes.first.line_code
diff --git a/app/views/notes/_per_line_reply_button.html.haml b/app/views/notes/_per_line_reply_button.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..42c737c75ae130e31ab518020ae427a4a0f7be79
--- /dev/null
+++ b/app/views/notes/_per_line_reply_button.html.haml
@@ -0,0 +1,4 @@
+%tr.line_notes_row.reply
+  %td{colspan: 3}
+    %i.icon-comment
+    = link_to "Reply", "#", class: "line_note_reply_link", data: { line_code: line_code }, title: "Add note for this line"
diff --git a/app/views/notes/_per_line_show.html.haml b/app/views/notes/_per_line_show.html.haml
deleted file mode 100644
index cf1769c0517d702eebc64d51a018c97e6a47d62a..0000000000000000000000000000000000000000
--- a/app/views/notes/_per_line_show.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-%tr.line_notes_row
-  %td{colspan: 3}
-    %ul
-      = render partial: "notes/show", locals: {note: note}
-
diff --git a/app/views/notes/_reply_button.html.haml b/app/views/notes/_reply_button.html.haml
deleted file mode 100644
index c981fb9fc726ade9557bc227941a4870aa85af43..0000000000000000000000000000000000000000
--- a/app/views/notes/_reply_button.html.haml
+++ /dev/null
@@ -1,4 +0,0 @@
-%tr.line_notes_row.reply
-  %td{colspan: 3}
-    %i.icon-comment
-    = link_to "Reply", "#", class: "line_note_reply_link", "line_code" => line_code, title: "Add note for this line"
diff --git a/app/views/notes/_reversed_notes_with_form.html.haml b/app/views/notes/_reversed_notes_with_form.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..05f01847da4fd9c9540dae2f9a4f54abb0e90ca5
--- /dev/null
+++ b/app/views/notes/_reversed_notes_with_form.html.haml
@@ -0,0 +1,11 @@
+- if can? current_user, :write_note, @project
+  = render "notes/common_form"
+
+%ul.reversed#new-notes-list
+%ul.reversed#notes-list
+.notes-status
+
+:javascript
+  $(function(){
+    NoteList.init("#{tid}", "#{tt}", "#{project_notes_path(@project)}");
+  });
\ No newline at end of file
diff --git a/app/views/notes/create.js.haml b/app/views/notes/create.js.haml
index 8f631f38f79bd9897eb460d0d499cabacc5c9f57..03866591c4fde9e97afb22daf5fc8fb75b3e1a6b 100644
--- a/app/views/notes/create.js.haml
+++ b/app/views/notes/create.js.haml
@@ -1,7 +1,7 @@
 - if @note.line_code
-  = render "create_line", note: @note
+  = render "create_per_line_note", note: @note
 - else
-  = render "create_common", note: @note
+  = render "create_common_note", note: @note
 
 -# Enable submit button
 :plain
diff --git a/app/views/notes/index.js.haml b/app/views/notes/index.js.haml
index ee31c0b8d3c1c8112471bbee240b14ef816f01ac..3814dbd46a2aa05a5eef17510e18d1ece396700a 100644
--- a/app/views/notes/index.js.haml
+++ b/app/views/notes/index.js.haml
@@ -1 +1,17 @@
-= render "notes/load"
+- unless @notes.blank?
+  - if loading_more_notes?
+    :plain
+      NoteList.appendMoreNotes(#{@notes.last.id}, "#{escape_javascript(render 'notes/notes')}");
+
+  - elsif loading_new_notes?
+    :plain
+      NoteList.replaceNewNotes("#{escape_javascript(render 'notes/notes')}");
+
+  - else
+    :plain
+      NoteList.setContent(#{@notes.first.id}, #{@notes.last.id}, "#{escape_javascript(render 'notes/notes')}");
+
+- else
+  - if loading_more_notes?
+    :plain
+      NoteList.finishedLoadingMore();
diff --git a/app/views/projects/wall.html.haml b/app/views/projects/wall.html.haml
index 97765d7ac887ad8fb86deb0684376b57eb88a2b1..591a8cd06d4b05a3241b707a84de0ae77d979e2c 100644
--- a/app/views/projects/wall.html.haml
+++ b/app/views/projects/wall.html.haml
@@ -1,2 +1,2 @@
 %div.wall_page
-  = render "notes/notes", tid: nil, tt: "wall"
+  = render "notes/reversed_notes_with_form", tid: nil, tt: "wall"
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index 0800b81d330d6fff31a0067a775e805067c49ffe..4188a9f198c976698a96f3e6a7176c71282a02fd 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -17,4 +17,4 @@
     %div{class: current_user.dark_scheme ? "black" : ""}
       = raw @snippet.colorize(options: { linenos: 'True'})
 
-= render "notes/notes", tid: @snippet.id, tt: "snippet"
+= render "notes/notes_with_form", tid: @snippet.id, tt: "snippet"
diff --git a/app/views/wikis/show.html.haml b/app/views/wikis/show.html.haml
index fc2352271d586758f21c802793ce41c7a0198eba..579ea1b3ad68719a54e336e3365ca3b334567796 100644
--- a/app/views/wikis/show.html.haml
+++ b/app/views/wikis/show.html.haml
@@ -21,4 +21,4 @@
     Delete this page
 
 %hr
-.wiki_notes#notes= render "notes/notes", tid: @wiki.id, tt: "wiki"
+.wiki_notes#notes= render "notes/notes_with_form", tid: @wiki.id, tt: "wiki"