##// END OF EJS Templates
frontend: remove unused chat code and convert it to topics
ergo -
r372:21804de5 default
parent child Browse files
Show More
@@ -0,0 +1,3 b''
1 /plugins/__REGISTER__ - launched after the onDomReady() code from rhodecode.js is executed
2 /ui/plugins/code/anchor_focus - launched when rc starts to scroll on load to anchor on PR/Codeview
3 /ui/plugins/code/comment_form_built - launched when injectInlineForm() is executed and the form object is created
@@ -1,413 +1,401 b''
1 1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 2 // #
3 3 // # This program is free software: you can redistribute it and/or modify
4 4 // # it under the terms of the GNU Affero General Public License, version 3
5 5 // # (only), as published by the Free Software Foundation.
6 6 // #
7 7 // # This program is distributed in the hope that it will be useful,
8 8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 // # GNU General Public License for more details.
11 11 // #
12 12 // # You should have received a copy of the GNU Affero General Public License
13 13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 // #
15 15 // # This program is dual-licensed. If you wish to learn more about the
16 16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 /**
20 20 RhodeCode JS Files
21 21 **/
22 22
23 23 if (typeof console == "undefined" || typeof console.log == "undefined"){
24 24 console = { log: function() {} }
25 25 }
26 26
27 27 // TODO: move the following function to submodules
28 28
29 29 /**
30 30 * show more
31 31 */
32 32 var show_more_event = function(){
33 33 $('table .show_more').click(function(e) {
34 34 var cid = e.target.id.substring(1);
35 35 var button = $(this);
36 36 if (button.hasClass('open')) {
37 37 $('#'+cid).hide();
38 38 button.removeClass('open');
39 39 } else {
40 40 $('#'+cid).show();
41 41 button.addClass('open one');
42 42 }
43 43 });
44 44 };
45 45
46 46 var compare_radio_buttons = function(repo_name, compare_ref_type){
47 47 $('#compare_action').on('click', function(e){
48 48 e.preventDefault();
49 49
50 50 var source = $('input[name=compare_source]:checked').val();
51 51 var target = $('input[name=compare_target]:checked').val();
52 52 if(source && target){
53 53 var url_data = {
54 54 repo_name: repo_name,
55 55 source_ref: source,
56 56 source_ref_type: compare_ref_type,
57 57 target_ref: target,
58 58 target_ref_type: compare_ref_type,
59 59 merge: 1
60 60 };
61 61 window.location = pyroutes.url('compare_url', url_data);
62 62 }
63 63 });
64 64 $('.compare-radio-button').on('click', function(e){
65 65 var source = $('input[name=compare_source]:checked').val();
66 66 var target = $('input[name=compare_target]:checked').val();
67 67 if(source && target){
68 68 $('#compare_action').removeAttr("disabled");
69 69 $('#compare_action').removeClass("disabled");
70 70 }
71 71 })
72 72 };
73 73
74 74 var showRepoSize = function(target, repo_name, commit_id, callback) {
75 75 var container = $('#' + target);
76 76 var url = pyroutes.url('repo_stats',
77 77 {"repo_name": repo_name, "commit_id": commit_id});
78 78
79 79 if (!container.hasClass('loaded')) {
80 80 $.ajax({url: url})
81 81 .complete(function (data) {
82 82 var responseJSON = data.responseJSON;
83 83 container.addClass('loaded');
84 84 container.html(responseJSON.size);
85 85 callback(responseJSON.code_stats)
86 86 })
87 87 .fail(function (data) {
88 88 console.log('failed to load repo stats');
89 89 });
90 90 }
91 91
92 92 };
93 93
94 94 var showRepoStats = function(target, data){
95 95 var container = $('#' + target);
96 96
97 97 if (container.hasClass('loaded')) {
98 98 return
99 99 }
100 100
101 101 var total = 0;
102 102 var no_data = true;
103 103 var tbl = document.createElement('table');
104 104 tbl.setAttribute('class', 'trending_language_tbl');
105 105
106 106 $.each(data, function(key, val){
107 107 total += val.count;
108 108 });
109 109
110 110 var sortedStats = [];
111 111 for (var obj in data){
112 112 sortedStats.push([obj, data[obj]])
113 113 }
114 114 var sortedData = sortedStats.sort(function (a, b) {
115 115 return b[1].count - a[1].count
116 116 });
117 117 var cnt = 0;
118 118 $.each(sortedData, function(idx, val){
119 119 cnt += 1;
120 120 no_data = false;
121 121
122 122 var hide = cnt > 2;
123 123 var tr = document.createElement('tr');
124 124 if (hide) {
125 125 tr.setAttribute('style', 'display:none');
126 126 tr.setAttribute('class', 'stats_hidden');
127 127 }
128 128
129 129 var key = val[0];
130 130 var obj = {"desc": val[1].desc, "count": val[1].count};
131 131
132 132 var percentage = Math.round((obj.count / total * 100), 2);
133 133
134 134 var td1 = document.createElement('td');
135 135 td1.width = 300;
136 136 var trending_language_label = document.createElement('div');
137 137 trending_language_label.innerHTML = obj.desc + " (.{0})".format(key);
138 138 td1.appendChild(trending_language_label);
139 139
140 140 var td2 = document.createElement('td');
141 141 var trending_language = document.createElement('div');
142 142 var nr_files = obj.count +" "+ _ngettext('file', 'files', obj.count);
143 143
144 144 trending_language.title = key + " " + nr_files;
145 145
146 146 trending_language.innerHTML = "<span>" + percentage + "% " + nr_files
147 147 + "</span><b>" + percentage + "% " + nr_files + "</b>";
148 148
149 149 trending_language.setAttribute("class", 'trending_language');
150 150 $('b', trending_language)[0].style.width = percentage + "%";
151 151 td2.appendChild(trending_language);
152 152
153 153 tr.appendChild(td1);
154 154 tr.appendChild(td2);
155 155 tbl.appendChild(tr);
156 156 if (cnt == 3) {
157 157 var show_more = document.createElement('tr');
158 158 var td = document.createElement('td');
159 159 lnk = document.createElement('a');
160 160
161 161 lnk.href = '#';
162 162 lnk.innerHTML = _ngettext('Show more');
163 163 lnk.id = 'code_stats_show_more';
164 164 td.appendChild(lnk);
165 165
166 166 show_more.appendChild(td);
167 167 show_more.appendChild(document.createElement('td'));
168 168 tbl.appendChild(show_more);
169 169 }
170 170 });
171 171
172 172 $(container).html(tbl);
173 173 $(container).addClass('loaded');
174 174
175 175 $('#code_stats_show_more').on('click', function (e) {
176 176 e.preventDefault();
177 177 $('.stats_hidden').each(function (idx) {
178 178 $(this).css("display", "");
179 179 });
180 180 $('#code_stats_show_more').hide();
181 181 });
182 182
183 183 };
184 184
185 185
186 186 // Toggle Collapsable Content
187 187 function collapsableContent() {
188 188
189 189 $('.collapsable-content').not('.no-hide').hide();
190 190
191 191 $('.btn-collapse').unbind(); //in case we've been here before
192 192 $('.btn-collapse').click(function() {
193 193 var button = $(this);
194 194 var togglename = $(this).data("toggle");
195 195 $('.collapsable-content[data-toggle='+togglename+']').toggle();
196 196 if ($(this).html()=="Show Less")
197 197 $(this).html("Show More");
198 198 else
199 199 $(this).html("Show Less");
200 200 });
201 201 };
202 202
203 203 var timeagoActivate = function() {
204 204 $("time.timeago").timeago();
205 205 };
206 206
207 207 // Formatting values in a Select2 dropdown of commit references
208 208 var formatSelect2SelectionRefs = function(commit_ref){
209 209 var tmpl = '';
210 210 if (!commit_ref.text || commit_ref.type === 'sha'){
211 211 return commit_ref.text;
212 212 }
213 213 if (commit_ref.type === 'branch'){
214 214 tmpl = tmpl.concat('<i class="icon-branch"></i> ');
215 215 } else if (commit_ref.type === 'tag'){
216 216 tmpl = tmpl.concat('<i class="icon-tag"></i> ');
217 217 } else if (commit_ref.type === 'book'){
218 218 tmpl = tmpl.concat('<i class="icon-bookmark"></i> ');
219 219 }
220 220 return tmpl.concat(commit_ref.text);
221 221 };
222 222
223 223 // takes a given html element and scrolls it down offset pixels
224 224 function offsetScroll(element, offset){
225 225 setTimeout(function(){
226 226 console.log(element);
227 227 var location = element.offset().top;
228 228 // some browsers use body, some use html
229 229 $('html, body').animate({ scrollTop: (location - offset) });
230 230 }, 100);
231 231 }
232 232
233 233 /**
234 234 * global hooks after DOM is loaded
235 235 */
236 236 $(document).ready(function() {
237 237 firefoxAnchorFix();
238 238
239 239 $('.navigation a.menulink').on('click', function(e){
240 240 var menuitem = $(this).parent('li');
241 241 if (menuitem.hasClass('open')) {
242 242 menuitem.removeClass('open');
243 243 } else {
244 244 menuitem.addClass('open');
245 245 $(document).on('click', function(event) {
246 246 if (!$(event.target).closest(menuitem).length) {
247 247 menuitem.removeClass('open');
248 248 }
249 249 });
250 250 }
251 251 });
252 252 // Add tooltips
253 253 $('tr.line .lineno a').attr("title","Click to select line").addClass('tooltip');
254 254 $('tr.line .add-comment-line a').attr("title","Click to comment").addClass('tooltip');
255 255
256 256 // Set colors and styles
257 257 $('tr.line .lineno a').hover(
258 258 function(){
259 259 $(this).parents('tr.line').addClass('hover');
260 260 }, function(){
261 261 $(this).parents('tr.line').removeClass('hover');
262 262 }
263 263 );
264 264
265 265 $('tr.line .lineno a').click(
266 266 function(){
267 267 if ($(this).text() != ""){
268 268 $('tr.line').removeClass('selected');
269 269 $(this).parents("tr.line").addClass('selected');
270 270
271 271 // Replace URL without jumping to it if browser supports.
272 272 // Default otherwise
273 273 if (history.pushState) {
274 274 var new_location = location.href
275 275 if (location.hash){
276 276 new_location = new_location.replace(location.hash, "");
277 277 }
278 278
279 279 // Make new anchor url
280 280 var new_location = new_location+$(this).attr('href');
281 281 history.pushState(true, document.title, new_location);
282 282
283 283 return false;
284 284 }
285 285 }
286 286 }
287 287 );
288 288
289 289 $('tr.line .add-comment-line a').hover(
290 290 function(){
291 291 $(this).parents('tr.line').addClass('commenting');
292 292 }, function(){
293 293 $(this).parents('tr.line').removeClass('commenting');
294 294 }
295 295 );
296 296
297 297 $('tr.line .add-comment-line a').on('click', function(e){
298 298 var tr = $(e.currentTarget).parents('tr.line')[0];
299 299 injectInlineForm(tr);
300 300 return false;
301 301 });
302 302
303 303
304 304 $('.collapse_file').on('click', function(e) {
305 305 e.stopPropagation();
306 306 if ($(e.target).is('a')) { return; }
307 307 var node = $(e.delegateTarget).first();
308 308 var icon = $($(node.children().first()).children().first());
309 309 var id = node.attr('fid');
310 310 var target = $('#'+id);
311 311 var tr = $('#tr_'+id);
312 312 var diff = $('#diff_'+id);
313 313 if(node.hasClass('expand_file')){
314 314 node.removeClass('expand_file');
315 315 icon.removeClass('expand_file_icon');
316 316 node.addClass('collapse_file');
317 317 icon.addClass('collapse_file_icon');
318 318 diff.show();
319 319 tr.show();
320 320 target.show();
321 321 } else {
322 322 node.removeClass('collapse_file');
323 323 icon.removeClass('collapse_file_icon');
324 324 node.addClass('expand_file');
325 325 icon.addClass('expand_file_icon');
326 326 diff.hide();
327 327 tr.hide();
328 328 target.hide();
329 329 }
330 330 });
331 331
332 332 $('#expand_all_files').click(function() {
333 333 $('.expand_file').each(function() {
334 334 var node = $(this);
335 335 var icon = $($(node.children().first()).children().first());
336 336 var id = $(this).attr('fid');
337 337 var target = $('#'+id);
338 338 var tr = $('#tr_'+id);
339 339 var diff = $('#diff_'+id);
340 340 node.removeClass('expand_file');
341 341 icon.removeClass('expand_file_icon');
342 342 node.addClass('collapse_file');
343 343 icon.addClass('collapse_file_icon');
344 344 diff.show();
345 345 tr.show();
346 346 target.show();
347 347 });
348 348 });
349 349
350 350 $('#collapse_all_files').click(function() {
351 351 $('.collapse_file').each(function() {
352 352 var node = $(this);
353 353 var icon = $($(node.children().first()).children().first());
354 354 var id = $(this).attr('fid');
355 355 var target = $('#'+id);
356 356 var tr = $('#tr_'+id);
357 357 var diff = $('#diff_'+id);
358 358 node.removeClass('collapse_file');
359 359 icon.removeClass('collapse_file_icon');
360 360 node.addClass('expand_file');
361 361 icon.addClass('expand_file_icon');
362 362 diff.hide();
363 363 tr.hide();
364 364 target.hide();
365 365 });
366 366 });
367 367
368 368 // Mouse over behavior for comments and line selection
369 369
370 370 // Select the line that comes from the url anchor
371 371 // At the time of development, Chrome didn't seem to support jquery's :target
372 372 // element, so I had to scroll manually
373 373 if (location.hash) {
374 374 var splitIx = location.hash.indexOf('/?/');
375 375 if (splitIx !== -1){
376 376 var loc = location.hash.slice(0, splitIx);
377 377 var remainder = location.hash.slice(splitIx + 2);
378 378 }
379 379 else{
380 380 var loc = location.hash;
381 381 var remainder = null;
382 382 }
383 383 if (loc.length > 1){
384 384 var lineno = $(loc+'.lineno');
385 385 if (lineno.length > 0){
386 386 var tr = lineno.parents('tr.line');
387 387 tr.addClass('selected');
388 $.Topic('/ui/plugins/code/anchor_focus').prepare({
389 tr:tr,
390 remainder:remainder});
388 391
389 // once we scrolled into our line, trigger chat app
390 if (remainder){
391 tr.find('.add-comment-line a').trigger( "click" );
392 setTimeout(function(){
393 var nextNode = $(tr).next();
394 if(nextNode.hasClass('inline-comments')){
395 nextNode.next().find('.switch-to-chat').trigger( "click" );
396 }
397 else{
398 nextNode.find('.switch-to-chat').trigger( "click" );
399 }
400 // trigger scroll into, later so all elements are already loaded
401 tr[0].scrollIntoView();
402 }, 250);
403
404 }
405 else{
392 if (!remainder){
406 393 tr[0].scrollIntoView();
407 394 }
408 395 }
409 396 }
410 };
397 }
411 398
412 399 collapsableContent();
400 $.Topic('/plugins/__REGISTER__').prepare({});
413 401 });
@@ -1,664 +1,655 b''
1 1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 2 // #
3 3 // # This program is free software: you can redistribute it and/or modify
4 4 // # it under the terms of the GNU Affero General Public License, version 3
5 5 // # (only), as published by the Free Software Foundation.
6 6 // #
7 7 // # This program is distributed in the hope that it will be useful,
8 8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 // # GNU General Public License for more details.
11 11 // #
12 12 // # You should have received a copy of the GNU Affero General Public License
13 13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 // #
15 15 // # This program is dual-licensed. If you wish to learn more about the
16 16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 var firefoxAnchorFix = function() {
20 20 // hack to make anchor links behave properly on firefox, in our inline
21 21 // comments generation when comments are injected firefox is misbehaving
22 22 // when jumping to anchor links
23 23 if (location.href.indexOf('#') > -1) {
24 24 location.href += '';
25 25 }
26 26 };
27 27
28 28 // returns a node from given html;
29 29 var fromHTML = function(html){
30 30 var _html = document.createElement('element');
31 31 _html.innerHTML = html;
32 32 return _html;
33 33 };
34 34
35 35 var tableTr = function(cls, body){
36 36 var _el = document.createElement('div');
37 37 var _body = $(body).attr('id');
38 38 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
39 39 var id = 'comment-tr-{0}'.format(comment_id);
40 40 var _html = ('<table><tbody><tr id="{0}" class="{1}">'+
41 41 '<td class="add-comment-line"><span class="add-comment-content"></span></td>'+
42 42 '<td></td>'+
43 43 '<td></td>'+
44 44 '<td>{2}</td>'+
45 45 '</tr></tbody></table>').format(id, cls, body);
46 46 $(_el).html(_html);
47 47 return _el.children[0].children[0].children[0];
48 48 };
49 49
50 50 var removeInlineForm = function(form) {
51 51 form.parentNode.removeChild(form);
52 52 };
53 53
54 54 var createInlineForm = function(parent_tr, f_path, line) {
55 55 var tmpl = $('#comment-inline-form-template').html();
56 56 tmpl = tmpl.format(f_path, line);
57 57 var form = tableTr('comment-form-inline', tmpl);
58 58 var form_hide_button = $(form).find('.hide-inline-form');
59 59
60 60 $(form_hide_button).click(function(e) {
61 61 $('.inline-comments').removeClass('hide-comment-button');
62 62 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
63 63 if ($(newtr.nextElementSibling).hasClass('inline-comments-button')) {
64 64 $(newtr.nextElementSibling).show();
65 65 }
66 66 $(newtr).parents('.comment-form-inline').remove();
67 67 $(parent_tr).removeClass('form-open');
68 68 $(parent_tr).removeClass('hl-comment');
69 69 });
70 70
71 71 return form;
72 72 };
73 73
74 74 var getLineNo = function(tr) {
75 75 var line;
76 76 // Try to get the id and return "" (empty string) if it doesn't exist
77 77 var o = ($(tr).find('.lineno.old').attr('id')||"").split('_');
78 78 var n = ($(tr).find('.lineno.new').attr('id')||"").split('_');
79 79 if (n.length >= 2) {
80 80 line = n[n.length-1];
81 81 } else if (o.length >= 2) {
82 82 line = o[o.length-1];
83 83 }
84 84 return line;
85 85 };
86 86
87 87 /**
88 88 * make a single inline comment and place it inside
89 89 */
90 90 var renderInlineComment = function(json_data, show_add_button) {
91 91 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
92 92 try {
93 93 var html = json_data.rendered_text;
94 94 var lineno = json_data.line_no;
95 95 var target_id = json_data.target_id;
96 96 placeInline(target_id, lineno, html, show_add_button);
97 97 } catch (e) {
98 98 console.error(e);
99 99 }
100 100 };
101 101
102 102 function bindDeleteCommentButtons() {
103 103 $('.delete-comment').one('click', function() {
104 104 var comment_id = $(this).data("comment-id");
105 105
106 106 if (comment_id){
107 107 deleteComment(comment_id);
108 108 }
109 109 });
110 110 }
111 111
112 112 /**
113 113 * Inject inline comment for on given TR this tr should be always an .line
114 114 * tr containing the line. Code will detect comment, and always put the comment
115 115 * block at the very bottom
116 116 */
117 117 var injectInlineForm = function(tr){
118 118 if (!$(tr).hasClass('line')) {
119 119 return;
120 120 }
121 121
122 122 var _td = $(tr).find('.code').get(0);
123 123 if ($(tr).hasClass('form-open') ||
124 124 $(tr).hasClass('context') ||
125 125 $(_td).hasClass('no-comment')) {
126 126 return;
127 127 }
128 128 $(tr).addClass('form-open');
129 129 $(tr).addClass('hl-comment');
130 130 var node = $(tr.parentNode.parentNode.parentNode).find('.full_f_path').get(0);
131 131 var f_path = $(node).attr('path');
132 132 var lineno = getLineNo(tr);
133 133 var form = createInlineForm(tr, f_path, lineno);
134 134
135 135 var parent = tr;
136 136 while (1) {
137 137 var n = parent.nextElementSibling;
138 138 // next element are comments !
139 139 if ($(n).hasClass('inline-comments')) {
140 140 parent = n;
141 141 }
142 142 else {
143 143 break;
144 144 }
145 145 }
146 146 var _parent = $(parent).get(0);
147 147 $(_parent).after(form);
148 148 $('.comment-form-inline').prev('.inline-comments').addClass('hide-comment-button');
149 149 var f = $(form).get(0);
150 150
151 151 var _form = $(f).find('.inline-form').get(0);
152 152
153 $('.switch-to-chat', _form).on('click', function(evt){
154 var fParent = $(_parent).closest('.injected_diff').parent().prev('*[fid]');
155 var fid = fParent.attr('fid');
156
157 // activate chat and trigger subscription to channels
158 $.Topic('/chat_controller').publish({
159 action:'subscribe_to_channels',
160 data: ['/chat${0}$/fid/{1}/{2}'.format(templateContext.repo_name, fid, lineno)]
161 });
162 $(_form).closest('td').find('.comment-inline-form').addClass('hidden');
163 $(_form).closest('td').find('.chat-holder').removeClass('hidden');
164 });
165
166 153 var pullRequestId = templateContext.pull_request_data.pull_request_id;
167 154 var commitId = templateContext.commit_data.commit_id;
168 155
169 156 var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false);
170 157 var cm = commentForm.getCmInstance();
171 158
172 159 // set a CUSTOM submit handler for inline comments.
173 160 commentForm.setHandleFormSubmit(function(o) {
174 161 var text = commentForm.cm.getValue();
175 162
176 163 if (text === "") {
177 164 return;
178 165 }
179 166
180 167 if (lineno === undefined) {
181 168 alert('missing line !');
182 169 return;
183 170 }
184 171 if (f_path === undefined) {
185 172 alert('missing file path !');
186 173 return;
187 174 }
188 175
189 176 var excludeCancelBtn = false;
190 177 var submitEvent = true;
191 178 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
192 179 commentForm.cm.setOption("readOnly", true);
193 180 var postData = {
194 181 'text': text,
195 182 'f_path': f_path,
196 183 'line': lineno,
197 184 'csrf_token': CSRF_TOKEN
198 185 };
199 186 var submitSuccessCallback = function(o) {
200 187 $(tr).removeClass('form-open');
201 188 removeInlineForm(f);
202 189 renderInlineComment(o);
203 190 $('.inline-comments').removeClass('hide-comment-button');
204 191
205 192 // re trigger the linkification of next/prev navigation
206 193 linkifyComments($('.inline-comment-injected'));
207 194 timeagoActivate();
208 195 tooltip_activate();
209 196 bindDeleteCommentButtons();
210 197 commentForm.setActionButtonsDisabled(false);
211 198
212 199 };
213 200 var submitFailCallback = function(){
214 201 commentForm.resetCommentFormState(text)
215 202 };
216 203 commentForm.submitAjaxPOST(
217 204 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
218 205 });
219 206
220 207 setTimeout(function() {
221 208 // callbacks
222 209 if (cm !== undefined) {
223 210 cm.focus();
224 211 }
225 212 }, 10);
226 213
214 $.Topic('/ui/plugins/code/comment_form_built').prepare({
215 form:_form,
216 parent:_parent}
217 );
227 218 };
228 219
229 220 var deleteComment = function(comment_id) {
230 221 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
231 222 var postData = {
232 223 '_method': 'delete',
233 224 'csrf_token': CSRF_TOKEN
234 225 };
235 226
236 227 var success = function(o) {
237 228 window.location.reload();
238 229 };
239 230 ajaxPOST(url, postData, success);
240 231 };
241 232
242 233 var createInlineAddButton = function(tr){
243 234 var label = _gettext('Add another comment');
244 235 var html_el = document.createElement('div');
245 236 $(html_el).addClass('add-comment');
246 237 html_el.innerHTML = '<span class="btn btn-secondary">{0}</span>'.format(label);
247 238 var add = new $(html_el);
248 239 add.on('click', function(e) {
249 240 injectInlineForm(tr);
250 241 });
251 242 return add;
252 243 };
253 244
254 245 var placeAddButton = function(target_tr){
255 246 if(!target_tr){
256 247 return;
257 248 }
258 249 var last_node = target_tr;
259 250 // scan
260 251 while (1){
261 252 var n = last_node.nextElementSibling;
262 253 // next element are comments !
263 254 if($(n).hasClass('inline-comments')){
264 255 last_node = n;
265 256 // also remove the comment button from previous
266 257 var comment_add_buttons = $(last_node).find('.add-comment');
267 258 for(var i=0; i<comment_add_buttons.length; i++){
268 259 var b = comment_add_buttons[i];
269 260 b.parentNode.removeChild(b);
270 261 }
271 262 }
272 263 else{
273 264 break;
274 265 }
275 266 }
276 267 var add = createInlineAddButton(target_tr);
277 268 // get the comment div
278 269 var comment_block = $(last_node).find('.comment')[0];
279 270 // attach add button
280 271 $(add).insertAfter(comment_block);
281 272 };
282 273
283 274 /**
284 275 * Places the inline comment into the changeset block in proper line position
285 276 */
286 277 var placeInline = function(target_container, lineno, html, show_add_button) {
287 278 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
288 279
289 280 var lineid = "{0}_{1}".format(target_container, lineno);
290 281 var target_line = $('#' + lineid).get(0);
291 282 var comment = new $(tableTr('inline-comments', html));
292 283 // check if there are comments already !
293 284 var parent_node = target_line.parentNode;
294 285 var root_parent = parent_node;
295 286 while (1) {
296 287 var n = parent_node.nextElementSibling;
297 288 // next element are comments !
298 289 if ($(n).hasClass('inline-comments')) {
299 290 parent_node = n;
300 291 }
301 292 else {
302 293 break;
303 294 }
304 295 }
305 296 // put in the comment at the bottom
306 297 $(comment).insertAfter(parent_node);
307 298 $(comment).find('.comment-inline').addClass('inline-comment-injected');
308 299 // scan nodes, and attach add button to last one
309 300 if (show_add_button) {
310 301 placeAddButton(root_parent);
311 302 }
312 303
313 304 return target_line;
314 305 };
315 306
316 307 var linkifyComments = function(comments) {
317 308
318 309 for (var i = 0; i < comments.length; i++) {
319 310 var comment_id = $(comments[i]).data('comment-id');
320 311 var prev_comment_id = $(comments[i - 1]).data('comment-id');
321 312 var next_comment_id = $(comments[i + 1]).data('comment-id');
322 313
323 314 // place next/prev links
324 315 if (prev_comment_id) {
325 316 $('#prev_c_' + comment_id).show();
326 317 $('#prev_c_' + comment_id + " a.arrow_comment_link").attr(
327 318 'href', '#comment-' + prev_comment_id).removeClass('disabled');
328 319 }
329 320 if (next_comment_id) {
330 321 $('#next_c_' + comment_id).show();
331 322 $('#next_c_' + comment_id + " a.arrow_comment_link").attr(
332 323 'href', '#comment-' + next_comment_id).removeClass('disabled');
333 324 }
334 325 // place a first link to the total counter
335 326 if (i === 0) {
336 327 $('#inline-comments-counter').attr('href', '#comment-' + comment_id);
337 328 }
338 329 }
339 330
340 331 };
341 332
342 333 /**
343 334 * Iterates over all the inlines, and places them inside proper blocks of data
344 335 */
345 336 var renderInlineComments = function(file_comments, show_add_button) {
346 337 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
347 338
348 339 for (var i = 0; i < file_comments.length; i++) {
349 340 var box = file_comments[i];
350 341
351 342 var target_id = $(box).attr('target_id');
352 343
353 344 // actually comments with line numbers
354 345 var comments = box.children;
355 346
356 347 for (var j = 0; j < comments.length; j++) {
357 348 var data = {
358 349 'rendered_text': comments[j].outerHTML,
359 350 'line_no': $(comments[j]).attr('line'),
360 351 'target_id': target_id
361 352 };
362 353 renderInlineComment(data, show_add_button);
363 354 }
364 355 }
365 356
366 357 // since order of injection is random, we're now re-iterating
367 358 // from correct order and filling in links
368 359 linkifyComments($('.inline-comment-injected'));
369 360 bindDeleteCommentButtons();
370 361 firefoxAnchorFix();
371 362 };
372 363
373 364
374 365 /* Comment form for main and inline comments */
375 366 var CommentForm = (function() {
376 367 "use strict";
377 368
378 369 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions) {
379 370
380 371 this.withLineNo = function(selector) {
381 372 var lineNo = this.lineNo;
382 373 if (lineNo === undefined) {
383 374 return selector
384 375 } else {
385 376 return selector + '_' + lineNo;
386 377 }
387 378 };
388 379
389 380 this.commitId = commitId;
390 381 this.pullRequestId = pullRequestId;
391 382 this.lineNo = lineNo;
392 383 this.initAutocompleteActions = initAutocompleteActions;
393 384
394 385 this.previewButton = this.withLineNo('#preview-btn');
395 386 this.previewContainer = this.withLineNo('#preview-container');
396 387
397 388 this.previewBoxSelector = this.withLineNo('#preview-box');
398 389
399 390 this.editButton = this.withLineNo('#edit-btn');
400 391 this.editContainer = this.withLineNo('#edit-container');
401 392
402 393 this.cancelButton = this.withLineNo('#cancel-btn');
403 394
404 395 this.statusChange = '#change_status';
405 396 this.cmBox = this.withLineNo('#text');
406 397 this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions);
407 398
408 399 this.submitForm = formElement;
409 400 this.submitButton = $(this.submitForm).find('input[type="submit"]');
410 401 this.submitButtonText = this.submitButton.val();
411 402
412 403 this.previewUrl = pyroutes.url('changeset_comment_preview',
413 404 {'repo_name': templateContext.repo_name});
414 405
415 406 // based on commitId, or pullReuqestId decide where do we submit
416 407 // out data
417 408 if (this.commitId){
418 409 this.submitUrl = pyroutes.url('changeset_comment',
419 410 {'repo_name': templateContext.repo_name,
420 411 'revision': this.commitId});
421 412
422 413 } else if (this.pullRequestId) {
423 414 this.submitUrl = pyroutes.url('pullrequest_comment',
424 415 {'repo_name': templateContext.repo_name,
425 416 'pull_request_id': this.pullRequestId});
426 417
427 418 } else {
428 419 throw new Error(
429 420 'CommentForm requires pullRequestId, or commitId to be specified.')
430 421 }
431 422
432 423 this.getCmInstance = function(){
433 424 return this.cm
434 425 };
435 426
436 427 var self = this;
437 428
438 429 this.getCommentStatus = function() {
439 430 return $(this.submitForm).find(this.statusChange).val();
440 431 };
441 432
442 433 this.isAllowedToSubmit = function() {
443 434 return !$(this.submitButton).prop('disabled');
444 435 };
445 436
446 437 this.initStatusChangeSelector = function(){
447 438 var formatChangeStatus = function(state, escapeMarkup) {
448 439 var originalOption = state.element;
449 440 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
450 441 '<span>' + escapeMarkup(state.text) + '</span>';
451 442 };
452 443 var formatResult = function(result, container, query, escapeMarkup) {
453 444 return formatChangeStatus(result, escapeMarkup);
454 445 };
455 446
456 447 var formatSelection = function(data, container, escapeMarkup) {
457 448 return formatChangeStatus(data, escapeMarkup);
458 449 };
459 450
460 451 $(this.submitForm).find(this.statusChange).select2({
461 452 placeholder: _gettext('Status Review'),
462 453 formatResult: formatResult,
463 454 formatSelection: formatSelection,
464 455 containerCssClass: "drop-menu status_box_menu",
465 456 dropdownCssClass: "drop-menu-dropdown",
466 457 dropdownAutoWidth: true,
467 458 minimumResultsForSearch: -1
468 459 });
469 460 $(this.submitForm).find(this.statusChange).on('change', function() {
470 461 var status = self.getCommentStatus();
471 462 if (status && !self.lineNo) {
472 463 $(self.submitButton).prop('disabled', false);
473 464 }
474 465 //todo, fix this name
475 466 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
476 467 self.cm.setOption('placeholder', placeholderText);
477 468 })
478 469 };
479 470
480 471 // reset the comment form into it's original state
481 472 this.resetCommentFormState = function(content) {
482 473 content = content || '';
483 474
484 475 $(this.editContainer).show();
485 476 $(this.editButton).hide();
486 477
487 478 $(this.previewContainer).hide();
488 479 $(this.previewButton).show();
489 480
490 481 this.setActionButtonsDisabled(true);
491 482 self.cm.setValue(content);
492 483 self.cm.setOption("readOnly", false);
493 484 };
494 485
495 486 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
496 487 failHandler = failHandler || function() {};
497 488 var postData = toQueryString(postData);
498 489 var request = $.ajax({
499 490 url: url,
500 491 type: 'POST',
501 492 data: postData,
502 493 headers: {'X-PARTIAL-XHR': true}
503 494 })
504 495 .done(function(data) {
505 496 successHandler(data);
506 497 })
507 498 .fail(function(data, textStatus, errorThrown){
508 499 alert(
509 500 "Error while submitting comment.\n" +
510 501 "Error code {0} ({1}).".format(data.status, data.statusText));
511 502 failHandler()
512 503 });
513 504 return request;
514 505 };
515 506
516 507 // overwrite a submitHandler, we need to do it for inline comments
517 508 this.setHandleFormSubmit = function(callback) {
518 509 this.handleFormSubmit = callback;
519 510 };
520 511
521 512 // default handler for for submit for main comments
522 513 this.handleFormSubmit = function() {
523 514 var text = self.cm.getValue();
524 515 var status = self.getCommentStatus();
525 516
526 517 if (text === "" && !status) {
527 518 return;
528 519 }
529 520
530 521 var excludeCancelBtn = false;
531 522 var submitEvent = true;
532 523 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
533 524 self.cm.setOption("readOnly", true);
534 525 var postData = {
535 526 'text': text,
536 527 'changeset_status': status,
537 528 'csrf_token': CSRF_TOKEN
538 529 };
539 530
540 531 var submitSuccessCallback = function(o) {
541 532 if (status) {
542 533 location.reload(true);
543 534 } else {
544 535 $('#injected_page_comments').append(o.rendered_text);
545 536 self.resetCommentFormState();
546 537 bindDeleteCommentButtons();
547 538 timeagoActivate();
548 539 tooltip_activate();
549 540 }
550 541 };
551 542 var submitFailCallback = function(){
552 543 self.resetCommentFormState(text)
553 544 };
554 545 self.submitAjaxPOST(
555 546 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
556 547 };
557 548
558 549 this.previewSuccessCallback = function(o) {
559 550 $(self.previewBoxSelector).html(o);
560 551 $(self.previewBoxSelector).removeClass('unloaded');
561 552
562 553 // swap buttons
563 554 $(self.previewButton).hide();
564 555 $(self.editButton).show();
565 556
566 557 // unlock buttons
567 558 self.setActionButtonsDisabled(false);
568 559 };
569 560
570 561 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
571 562 excludeCancelBtn = excludeCancelBtn || false;
572 563 submitEvent = submitEvent || false;
573 564
574 565 $(this.editButton).prop('disabled', state);
575 566 $(this.previewButton).prop('disabled', state);
576 567
577 568 if (!excludeCancelBtn) {
578 569 $(this.cancelButton).prop('disabled', state);
579 570 }
580 571
581 572 var submitState = state;
582 573 if (!submitEvent && this.getCommentStatus() && !this.lineNo) {
583 574 // if the value of commit review status is set, we allow
584 575 // submit button, but only on Main form, lineNo means inline
585 576 submitState = false
586 577 }
587 578 $(this.submitButton).prop('disabled', submitState);
588 579 if (submitEvent) {
589 580 $(this.submitButton).val(_gettext('Submitting...'));
590 581 } else {
591 582 $(this.submitButton).val(this.submitButtonText);
592 583 }
593 584
594 585 };
595 586
596 587 // lock preview/edit/submit buttons on load, but exclude cancel button
597 588 var excludeCancelBtn = true;
598 589 this.setActionButtonsDisabled(true, excludeCancelBtn);
599 590
600 591 // anonymous users don't have access to initialized CM instance
601 592 if (this.cm !== undefined){
602 593 this.cm.on('change', function(cMirror) {
603 594 if (cMirror.getValue() === "") {
604 595 self.setActionButtonsDisabled(true, excludeCancelBtn)
605 596 } else {
606 597 self.setActionButtonsDisabled(false, excludeCancelBtn)
607 598 }
608 599 });
609 600 }
610 601
611 602 $(this.editButton).on('click', function(e) {
612 603 e.preventDefault();
613 604
614 605 $(self.previewButton).show();
615 606 $(self.previewContainer).hide();
616 607 $(self.editButton).hide();
617 608 $(self.editContainer).show();
618 609
619 610 });
620 611
621 612 $(this.previewButton).on('click', function(e) {
622 613 e.preventDefault();
623 614 var text = self.cm.getValue();
624 615
625 616 if (text === "") {
626 617 return;
627 618 }
628 619
629 620 var postData = {
630 621 'text': text,
631 622 'renderer': DEFAULT_RENDERER,
632 623 'csrf_token': CSRF_TOKEN
633 624 };
634 625
635 626 // lock ALL buttons on preview
636 627 self.setActionButtonsDisabled(true);
637 628
638 629 $(self.previewBoxSelector).addClass('unloaded');
639 630 $(self.previewBoxSelector).html(_gettext('Loading ...'));
640 631 $(self.editContainer).hide();
641 632 $(self.previewContainer).show();
642 633
643 634 // by default we reset state of comment preserving the text
644 635 var previewFailCallback = function(){
645 636 self.resetCommentFormState(text)
646 637 };
647 638 self.submitAjaxPOST(
648 639 self.previewUrl, postData, self.previewSuccessCallback, previewFailCallback);
649 640
650 641 });
651 642
652 643 $(this.submitForm).submit(function(e) {
653 644 e.preventDefault();
654 645 var allowedToSubmit = self.isAllowedToSubmit();
655 646 if (!allowedToSubmit){
656 647 return false;
657 648 }
658 649 self.handleFormSubmit();
659 650 });
660 651
661 652 }
662 653
663 654 return CommentForm;
664 655 })();
General Comments 0
You need to be logged in to leave comments. Login now