Commit 4a6596af274c01036aaf9f49a5b38cd678716873

Authored by randx
1 parent 92137b7b

Fixed bunch of js bugs with comments. Also added development tips

README.md
... ... @@ -39,5 +39,6 @@ Email
39 39  
40 40 ## Contribute
41 41  
  42 +[Development Tips](https://github.com/gitlabhq/gitlabhq/blob/master/doc/development.md)
42 43 Want to help - send a pull request.
43 44 We'll accept good pull requests.
... ...
app/assets/javascripts/application.js
... ... @@ -128,3 +128,23 @@ function showDiff(link) {
128 128 function ajaxGet(url) {
129 129 $.ajax({type: "GET", url: url, dataType: "script"});
130 130 }
  131 +
  132 +/**
  133 + * Disable button if text field is empty
  134 + */
  135 +function disableButtonIfEmtpyField(field_selector, button_selector) {
  136 + field = $(field_selector);
  137 + if(field.val() == "") {
  138 + field.closest("form").find(button_selector).attr("disabled", "disabled").addClass("disabled");
  139 + }
  140 +
  141 + field.on('keyup', function(){
  142 + var field = $(this);
  143 + var closest_submit = field.closest("form").find(button_selector);
  144 + if(field.val() == "") {
  145 + closest_submit.attr("disabled", "disabled").addClass("disabled");
  146 + } else {
  147 + closest_submit.removeAttr("disabled").removeClass("disabled");
  148 + }
  149 + })
  150 +}
... ...
app/assets/javascripts/note.js
1 1 var NoteList = {
2 2  
3   -notes_path: null,
4   -target_params: null,
5   -target_id: 0,
6   -target_type: null,
7   -first_id: 0,
8   -last_id: 0,
9   -disable:false,
10   -
11   -init:
12   - function(tid, tt, path) {
13   - this.notes_path = path + ".js";
14   - this.target_id = tid;
15   - this.target_type = tt;
16   - this.target_params = "&target_type=" + this.target_type + "&target_id=" + this.target_id;
17   -
18   - // get notes
19   - this.getContent();
20   -
21   - // get new notes every n seconds
22   - this.initRefresh();
23   -
24   - $('.delete-note').live('ajax:success', function() {
25   - $(this).closest('li').fadeOut(); });
26   -
27   - $('#note_note').on('keyup', function(){
28   - var field = $(this);
29   - var closest_submit = field.closest("form").find(".submit_note");
30   - if(field.val() == "") {
31   - closest_submit.attr("disabled", "disabled").addClass("disabled");
32   - } else {
33   - closest_submit.removeAttr("disabled").removeClass("disabled");
34   - }
35   - })
36   -
37   - $("#new_note").live("ajax:before", function(){
38   - $(".submit_note").attr("disabled", "disabled");
39   - })
40   -
41   - $("#new_note").live("ajax:complete", function(){
42   - $(".submit_note").removeAttr("disabled");
43   - })
44   -
45   - $("#note_note").live("focus", function(){
46   - $(this).css("height", "80px");
47   - $('.note_advanced_opts').show();
48   - if($(this).val() == "") {
49   - $(this).closest("form").find(".submit_note").attr("disabled", "disabled").addClass("disabled");
50   - }
51   - });
52   -
53   - $("#note_attachment").change(function(e){
  3 + notes_path: null,
  4 + target_params: null,
  5 + target_id: 0,
  6 + target_type: null,
  7 + first_id: 0,
  8 + last_id: 0,
  9 + disable:false,
  10 +
  11 + init:
  12 + function(tid, tt, path) {
  13 + this.notes_path = path + ".js";
  14 + this.target_id = tid;
  15 + this.target_type = tt;
  16 + this.target_params = "&target_type=" + this.target_type + "&target_id=" + this.target_id;
  17 +
  18 + // get notes
  19 + this.getContent();
  20 +
  21 + // get new notes every n seconds
  22 + this.initRefresh();
  23 +
  24 + $('.delete-note').live('ajax:success', function() {
  25 + $(this).closest('li').fadeOut(); });
  26 +
  27 + $(".note-form-holder").live("ajax:before", function(){
  28 + $(".submit_note").attr("disabled", "disabled");
  29 + })
  30 +
  31 + $(".note-form-holder").live("ajax:complete", function(){
  32 + $(".submit_note").removeAttr("disabled");
  33 + })
  34 +
  35 + disableButtonIfEmtpyField(".note-text", ".submit_note");
  36 +
  37 + $(".note-text").live("focus", function(){
  38 + $(this).css("height", "80px");
  39 + $('.note_advanced_opts').show();
  40 + });
  41 +
  42 + $("#note_attachment").change(function(e){
54 43 var val = $('.input-file').val();
55 44 var filename = val.replace(/^.*[\\\/]/, '');
56 45 $(".file_name").text(filename);
57   - });
  46 + });
58 47  
59   - },
  48 + },
60 49  
61 50  
62   -/**
63   - * Load new notes to fresh list called 'new_notes_list':
64   - * - Replace 'new_notes_list' with new list every n seconds
65   - * - Append new notes to this list after submit
66   - */
  51 + /**
  52 + * Load new notes to fresh list called 'new_notes_list':
  53 + * - Replace 'new_notes_list' with new list every n seconds
  54 + * - Append new notes to this list after submit
  55 + */
67 56  
68   -initRefresh:
69   - function() {
70   - // init timer
71   - var intNew = setInterval("NoteList.getNew()", 10000);
72   - },
  57 + initRefresh:
  58 + function() {
  59 + // init timer
  60 + var intNew = setInterval("NoteList.getNew()", 10000);
  61 + },
73 62  
74   -replace:
75   - function(html) {
76   - $("#new_notes_list").html(html);
77   - },
  63 + replace:
  64 + function(html) {
  65 + $("#new_notes_list").html(html);
  66 + },
78 67  
79   -prepend:
80   - function(id, html) {
81   - if(id != this.last_id) {
82   - $("#new_notes_list").prepend(html);
83   - }
84   - },
  68 + prepend:
  69 + function(id, html) {
  70 + if(id != this.last_id) {
  71 + $("#new_notes_list").prepend(html);
  72 + }
  73 + },
85 74  
86   -getNew:
87   - function() {
88   - // refersh notes list
89   - $.ajax({
90   - type: "GET",
  75 + getNew:
  76 + function() {
  77 + // refersh notes list
  78 + $.ajax({
  79 + type: "GET",
91 80 url: this.notes_path,
92 81 data: "last_id=" + this.last_id + this.target_params,
93 82 dataType: "script"});
94   - },
  83 + },
95 84  
96   -refresh:
97   - function() {
98   - // refersh notes list
99   - $.ajax({
100   - type: "GET",
  85 + refresh:
  86 + function() {
  87 + // refersh notes list
  88 + $.ajax({
  89 + type: "GET",
101 90 url: this.notes_path,
102 91 data: "first_id=" + this.first_id + "&last_id=" + this.last_id + this.target_params,
103 92 dataType: "script"});
104   - },
  93 + },
105 94  
106 95  
107   -/**
108   - * Init load of notes:
109   - * 1. Get content with ajax call
110   - * 2. Set content of notes list with loaded one
111   - */
  96 + /**
  97 + * Init load of notes:
  98 + * 1. Get content with ajax call
  99 + * 2. Set content of notes list with loaded one
  100 + */
112 101  
113 102  
114   -getContent:
115   - function() {
116   - $.ajax({
117   - type: "GET",
  103 + getContent:
  104 + function() {
  105 + $.ajax({
  106 + type: "GET",
118 107 url: this.notes_path,
119 108 data: "?" + this.target_params,
120 109 complete: function(){ $('.status').removeClass("loading")},
121 110 beforeSend: function() { $('.status').addClass("loading") },
122 111 dataType: "script"});
123   - },
  112 + },
124 113  
125   -setContent:
126   - function(fid, lid, html) {
  114 + setContent:
  115 + function(fid, lid, html) {
127 116 this.last_id = lid;
128 117 this.first_id = fid;
129 118 $("#notes-list").html(html);
130 119  
131 120 // Init infinite scrolling
132 121 this.initLoadMore();
133   - },
134   -
135   -
136   -/**
137   - * Paging for old notes when scroll to bottom:
138   - * 1. Init scroll events with 'initLoadMore'
139   - * 2. Load onlder notes with 'getOld' method
140   - * 3. append old notes to bottom of list with 'append'
141   - *
142   - */
143   -
144   -
145   -getOld:
146   - function() {
147   - $('.loading').show();
148   - $.ajax({
149   - type: "GET",
150   - url: this.notes_path,
151   - data: "first_id=" + this.first_id + this.target_params,
152   - complete: function(){ $('.status').removeClass("loading")},
153   - beforeSend: function() { $('.status').addClass("loading") },
154   - dataType: "script"});
155   - },
156   -
157   -append:
158   - function(id, html) {
159   - if(this.first_id == id) {
160   - this.disable = true;
161   - } else {
162   - this.first_id = id;
163   - $("#notes-list").append(html);
164   - }
165   - },
166   -
  122 + },
  123 +
  124 +
  125 + /**
  126 + * Paging for old notes when scroll to bottom:
  127 + * 1. Init scroll events with 'initLoadMore'
  128 + * 2. Load onlder notes with 'getOld' method
  129 + * 3. append old notes to bottom of list with 'append'
  130 + *
  131 + */
  132 + getOld:
  133 + function() {
  134 + $('.loading').show();
  135 + $.ajax({
  136 + type: "GET",
  137 + url: this.notes_path,
  138 + data: "first_id=" + this.first_id + this.target_params,
  139 + complete: function(){ $('.status').removeClass("loading")},
  140 + beforeSend: function() { $('.status').addClass("loading") },
  141 + dataType: "script"});
  142 + },
  143 +
  144 + append:
  145 + function(id, html) {
  146 + if(this.first_id == id) {
  147 + this.disable = true;
  148 + } else {
  149 + this.first_id = id;
  150 + $("#notes-list").append(html);
  151 + }
  152 + },
167 153  
168   -initLoadMore:
169   - function() {
170   - $(document).endlessScroll({
171   - bottomPixels: 400,
  154 + initLoadMore:
  155 + function() {
  156 + $(document).endlessScroll({
  157 + bottomPixels: 400,
172 158 fireDelay: 1000,
173 159 fireOnce:true,
174 160 ceaseFire: function() {
... ... @@ -177,6 +163,20 @@ initLoadMore:
177 163 callback: function(i) {
178 164 NoteList.getOld();
179 165 }
180   - });
181   - }
  166 + });
  167 + }
  168 +};
  169 +
  170 +var PerLineNotes = {
  171 + init:
  172 + function() {
  173 + $(".line_note_link, .line_note_reply_link").live("click", function(e) {
  174 + var form = $(".per_line_form");
  175 + $(this).closest("tr").after(form);
  176 + form.find("#note_line_code").val($(this).attr("line_code"));
  177 + form.show();
  178 + return false;
  179 + });
  180 + disableButtonIfEmtpyField(".line-note-text", ".submit_inline_note");
  181 + }
182 182 }
... ...
app/assets/stylesheets/sections/notes.scss
... ... @@ -30,7 +30,7 @@
30 30 }
31 31  
32 32 #new_note {
33   - #note_note {
  33 + .note-text {
34 34 height:25px;
35 35 }
36 36 .attach_holder {
... ...
app/views/commits/show.html.haml
... ... @@ -5,12 +5,6 @@
5 5  
6 6  
7 7 :javascript
8   - $(document).ready(function(){
9   - $(".line_note_link, .line_note_reply_link").live("click", function(e) {
10   - var form = $(".per_line_form");
11   - $(this).parent().parent().after(form);
12   - form.find("#note_line_code").val($(this).attr("line_code"));
13   - form.show();
14   - return false;
15   - });
  8 + $(function(){
  9 + PerLineNotes.init();
16 10 });
... ...
app/views/notes/_create_common.js.haml
1 1 - if note.valid?
2 2 :plain
3   - $("#new_note .error").remove();
4   - $('#new_note textarea').val("");
5   - $('#preview-link').text('Preview');
6   - $('#preview-note').hide(); $('#note_note').show();
  3 + $(".note-form-holder .error").remove();
  4 + $('.note-form-holder textarea').val("");
  5 + $('.note-form-holder #preview-link').text('Preview');
  6 + $('.note-form-holder #preview-note').hide();
  7 + $('.note-form-holder').show();
7 8 NoteList.prepend(#{note.id}, "#{escape_javascript(render partial: "notes/show", locals: {note: note})}");
8 9 - else
9 10 :plain
10   - $("#new_note").replaceWith("#{escape_javascript(render('form'))}");
  11 + $(".note-form-holder").replaceWith("#{escape_javascript(render('form'))}");
11 12  
... ...
app/views/notes/_create_line.js.haml
1 1 - if note.valid?
2 2 :plain
3 3 $(".per_line_form").hide();
4   - $('#new_note textarea').val("");
  4 + $('.line-note-form-holder textarea').val("");
5 5 $("a.line_note_reply_link[line_code='#{note.line_code}']").closest("tr").remove();
6 6 var trEl = $(".#{note.line_code}").parent();
7 7 trEl.after("#{escape_javascript(render partial: "notes/per_line_show", locals: {note: note})}");
... ...
app/views/notes/_form.html.haml
1   -= form_for [@project, @note], remote: "true", multipart: true do |f|
2   - %h3.page_title Leave a comment
3   - -if @note.errors.any?
4   - .alert-message.block-message.error
5   - - @note.errors.full_messages.each do |msg|
6   - %div= msg
  1 +.note-form-holder
  2 + = form_for [@project, @note], remote: "true", multipart: true do |f|
  3 + %h3.page_title Leave a comment
  4 + -if @note.errors.any?
  5 + .alert-message.block-message.error
  6 + - @note.errors.full_messages.each do |msg|
  7 + %div= msg
7 8  
8   - = f.hidden_field :noteable_id
9   - = f.hidden_field :noteable_type
10   - = f.text_area :note, size: 255
11   - #preview-note.preview_note.hide
12   - .hint
13   - .right Comments are parsed with #{link_to "Gitlab Flavored Markdown", help_markdown_path, target: '_blank'}.
14   - .clearfix
  9 + = f.hidden_field :noteable_id
  10 + = f.hidden_field :noteable_type
  11 + = f.text_area :note, size: 255, class: 'note-text'
  12 + #preview-note.preview_note.hide
  13 + .hint
  14 + .right Comments are parsed with #{link_to "Gitlab Flavored Markdown", help_markdown_path, target: '_blank'}.
  15 + .clearfix
15 16  
16   - .row.note_advanced_opts.hide
17   - .span3
18   - = f.submit 'Add Comment', class: "btn success submit_note grouped", id: "submit_note"
19   - = link_to 'Preview', preview_project_notes_path(@project), class: 'btn grouped', id: 'preview-link'
20   - .span4.notify_opts
21   - %h6.left Notify via email:
22   - = label_tag :notify do
23   - = check_box_tag :notify, 1, @note.noteable_type != "Commit"
24   - %span Project team
  17 + .row.note_advanced_opts.hide
  18 + .span3
  19 + = f.submit 'Add Comment', class: "btn success submit_note grouped", id: "submit_note"
  20 + = link_to 'Preview', preview_project_notes_path(@project), class: 'btn grouped', id: 'preview-link'
  21 + .span4.notify_opts
  22 + %h6.left Notify via email:
  23 + = label_tag :notify do
  24 + = check_box_tag :notify, 1, @note.noteable_type != "Commit"
  25 + %span Project team
25 26  
26   - - if @note.notify_only_author?(current_user)
27   - = label_tag :notify_author do
28   - = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
29   - %span Commit author
30   - .span5.attachments
31   - %h6.left Attachment:
32   - %span.file_name File name...
  27 + - if @note.notify_only_author?(current_user)
  28 + = label_tag :notify_author do
  29 + = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
  30 + %span Commit author
  31 + .span5.attachments
  32 + %h6.left Attachment:
  33 + %span.file_name File name...
33 34  
34   - .input.input_file
35   - %a.file_upload.btn.small Upload File
36   - = f.file_field :attachment, class: "input-file"
37   - %span.hint Any file less than 10 MB
  35 + .input.input_file
  36 + %a.file_upload.btn.small Upload File
  37 + = f.file_field :attachment, class: "input-file"
  38 + %span.hint Any file less than 10 MB
38 39  
... ...
app/views/notes/_per_line_form.html.haml
1 1 %table{style: "display:none;"}
2 2 %tr.per_line_form
3 3 %td{colspan: 3 }
4   - = form_for [@project, @note], remote: "true", multipart: true do |f|
5   - %h3.page_title Leave a note
6   - %div.span10
7   - -if @note.errors.any?
8   - .alert-message.block-message.error
9   - - @note.errors.full_messages.each do |msg|
10   - %div= msg
  4 + .line-note-form-holder
  5 + = form_for [@project, @note], remote: "true", multipart: true do |f|
  6 + %h3.page_title Leave a note
  7 + %div.span10
  8 + -if @note.errors.any?
  9 + .alert-message.block-message.error
  10 + - @note.errors.full_messages.each do |msg|
  11 + %div= msg
11 12  
12   - = f.hidden_field :noteable_id
13   - = f.hidden_field :noteable_type
14   - = f.hidden_field :line_code
15   - = f.text_area :note, size: 255
16   - .note_actions
17   - .buttons
18   - = f.submit 'Add note', class: "btn primary submit_note", id: "submit_note"
19   - = link_to "Cancel", "#", class: "btn hide-button"
20   - .options
21   - %h6.left Notify via email:
22   - .labels
23   - = label_tag :notify do
24   - = check_box_tag :notify, 1, @note.noteable_type != "Commit"
25   - %span Project team
  13 + = f.hidden_field :noteable_id
  14 + = f.hidden_field :noteable_type
  15 + = f.hidden_field :line_code
  16 + = f.text_area :note, size: 255, class: 'line-note-text'
  17 + .note_actions
  18 + .buttons
  19 + = f.submit 'Add note', class: "btn primary submit_note submit_inline_note", id: "submit_note"
  20 + = link_to "Cancel", "#", class: "btn hide-button"
  21 + .options
  22 + %h6.left Notify via email:
  23 + .labels
  24 + = label_tag :notify do
  25 + = check_box_tag :notify, 1, @note.noteable_type != "Commit"
  26 + %span Project team
26 27  
27   - - if @note.notify_only_author?(current_user)
28   - = label_tag :notify_author do
29   - = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
30   - %span Commit author
  28 + - if @note.notify_only_author?(current_user)
  29 + = label_tag :notify_author do
  30 + = check_box_tag :notify_author, 1 , @note.noteable_type == "Commit"
  31 + %span Commit author
31 32  
32 33 :javascript
33 34 $(function(){
... ...
doc/development.md 0 → 100644
... ... @@ -0,0 +1,45 @@
  1 +## Development tips:
  2 +
  3 +### Start application in development mode
  4 +
  5 +#### 1. Via foreman
  6 +
  7 + bundle exec foreman -p 3000
  8 +
  9 +#### 2. Via gitlab cli
  10 +
  11 + ./gitlab start
  12 +
  13 +#### 3. Manually
  14 +
  15 + bundle exec rails s
  16 + bundle exec rake environment resque:work QUEUE=* VVERBOSE=1
  17 +
  18 +
  19 +### Run tests:
  20 +
  21 +#### 1. Packages
  22 +
  23 + # ubuntu
  24 + sudo apt-get install libqt4-dev libqtwebkit-dev
  25 + sudo apt-get install xvfb
  26 +
  27 + # Mac
  28 + brew install qt
  29 + brew install xvfb
  30 +
  31 +#### 2. DB & seeds
  32 +
  33 + bundle exec rake db:setup RAILS_ENV=test
  34 + bundle exec rake db:seed_fu RAILS_ENV=test
  35 +
  36 +### 3. Run Tests
  37 +
  38 + # All in one
  39 + bundle exec gitlab:test
  40 +
  41 + # Rspec
  42 + bundle exec rake spec
  43 +
  44 + # Cucumber
  45 + bundle exec rake cucumber
... ...
gitlab
... ... @@ -22,10 +22,11 @@ class GitlabCli
22 22 case @mode
23 23 when 'production';
24 24 system(unicorn_start_cmd)
  25 + system(resque_start_cmd)
25 26 else
26 27 system(rails_start_cmd)
  28 + system(resque_dev_start_cmd)
27 29 end
28   - system(resque_start_cmd)
29 30 end
30 31  
31 32 def stop
... ... @@ -57,6 +58,10 @@ class GitlabCli
57 58 "kill -QUIT `cat #{pid}`"
58 59 end
59 60  
  61 + def resque_dev_start_cmd
  62 + "./resque_dev.sh > /dev/null 2>&1"
  63 + end
  64 +
60 65 def resque_start_cmd
61 66 "./resque.sh > /dev/null 2>&1"
62 67 end
... ...
resque_dev.sh
1   -bundle exec rake environment resque:work QUEUE=post_receive,mailer,system_hook VVERBOSE=1
  1 +mkdir -p tmp/pids
  2 +bundle exec rake environment resque:work QUEUE=post_receive,mailer,system_hook VVERBOSE=1 PIDFILE=tmp/pids/resque_worker.pid RAILS_ENV=development BACKGROUND=yes
... ...