##// END OF EJS Templates
i18n: replaced fragile extraction of JS translations from an _TM variable....
marcink -
r325:9f2e29d4 stable
parent child Browse files
Show More
@@ -1,34 +1,43 b''
1 // translate select2 components
1 // translate select2 components
2 select2Locales = {
2 select2Locales = {
3 formatLoadMore: function(pageNumber) {
3 formatLoadMore: function(pageNumber) {
4 return _TM["Loading more results..."];
4 return _gettext("Loading more results...");
5 },
5 },
6 formatSearching: function() {
6 formatSearching: function() {
7 return _TM["Searching..."];
7 return _gettext("Searching...");
8 },
8 },
9 formatNoMatches: function() {
9 formatNoMatches: function() {
10 return _TM["No matches found"];
10 return _gettext("No matches found");
11 },
11 },
12 formatAjaxError: function(jqXHR, textStatus, errorThrown) {
12 formatAjaxError: function(jqXHR, textStatus, errorThrown) {
13 return _TM["Loading failed"];
13 return _gettext("Loading failed");
14 },
14 },
15 formatMatches: function(matches) {
15 formatMatches: function(matches) {
16 if (matches === 1) {
16 if (matches === 1) {
17 return _TM["One result is available, press enter to select it."];
17 return _gettext("One result is available, press enter to select it.");
18 }
18 }
19 return _TM["{0} results are available, use up and down arrow keys to navigate."].format(matches);
19 return _gettext("{0} results are available, use up and down arrow keys to navigate.").format(matches);
20 },
20 },
21 formatInputTooShort: function(input, min) {
21 formatInputTooShort: function(input, min) {
22 var n = min - input.length;
22 var n = min - input.length;
23 return "Please enter {0} or more character".format(n) + (n === 1 ? "" : "s");
23 if (n === 1) {
24 return _gettext("Please enter {0} or more character").format(n);
25 }
26 return _gettext("Please enter {0} or more characters").format(n);
24 },
27 },
25 formatInputTooLong: function(input, max) {
28 formatInputTooLong: function(input, max) {
26 var n = input.length - max;
29 var n = input.length - max;
27 return "Please delete {0} character".format(n) + (n === 1 ? "" : "s");
30 if (n === 1) {
31 return _gettext("Please delete {0} character").format(n);
32 }
33 return _gettext("Please delete {0} characters").format(n);
28 },
34 },
29 formatSelectionTooBig: function(limit) {
35 formatSelectionTooBig: function(limit) {
30 return "You can only select {0} item".format(limit) + (limit === 1 ? "" : "s");
36 if (limit === 1) {
37 return _gettext("You can only select {0} item").format(limit);
38 }
39 return _gettext("You can only select {0} items").format(limit);
31 }
40 }
32 };
41 };
33
42
34 $.extend($.fn.select2.defaults, select2Locales);
43 $.extend($.fn.select2.defaults, select2Locales);
@@ -1,936 +1,936 b''
1 /**
1 /**
2 * Ajax Autocomplete for jQuery, version dev
2 * Ajax Autocomplete for jQuery, version dev
3 * RhodeCode additions
3 * RhodeCode additions
4 * (c) 2014 Tomas Kirda
4 * (c) 2014 Tomas Kirda
5 * (c) 2014 Marcin Kuzminski
5 * (c) 2014 Marcin Kuzminski
6 *
6 *
7 * Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
7 * Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
8 * For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete
8 * For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete
9 */
9 */
10 // Expose plugin as an AMD module if AMD loader is present:
10 // Expose plugin as an AMD module if AMD loader is present:
11 (function (factory) {
11 (function (factory) {
12 'use strict';
12 'use strict';
13 if (typeof define === 'function' && define.amd) {
13 if (typeof define === 'function' && define.amd) {
14 // AMD. Register as an anonymous module.
14 // AMD. Register as an anonymous module.
15 define(['jquery'], factory);
15 define(['jquery'], factory);
16 } else if (typeof exports === 'object' && typeof require === 'function') {
16 } else if (typeof exports === 'object' && typeof require === 'function') {
17 // Browserify
17 // Browserify
18 factory(require('jquery'));
18 factory(require('jquery'));
19 } else {
19 } else {
20 // Browser globals
20 // Browser globals
21 factory(jQuery);
21 factory(jQuery);
22 }
22 }
23 }(function ($) {
23 }(function ($) {
24 'use strict';
24 'use strict';
25
25
26 var
26 var
27 utils = (function () {
27 utils = (function () {
28 return {
28 return {
29 escapeRegExChars: function (value) {
29 escapeRegExChars: function (value) {
30 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
30 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
31 },
31 },
32 createNode: function (containerClass) {
32 createNode: function (containerClass) {
33 var div = document.createElement('div');
33 var div = document.createElement('div');
34 div.className = containerClass;
34 div.className = containerClass;
35 div.style.position = 'absolute';
35 div.style.position = 'absolute';
36 div.style.display = 'none';
36 div.style.display = 'none';
37 return div;
37 return div;
38 }
38 }
39 };
39 };
40 }()),
40 }()),
41
41
42 keys = {
42 keys = {
43 ESC: 27,
43 ESC: 27,
44 TAB: 9,
44 TAB: 9,
45 RETURN: 13,
45 RETURN: 13,
46 LEFT: 37,
46 LEFT: 37,
47 UP: 38,
47 UP: 38,
48 RIGHT: 39,
48 RIGHT: 39,
49 DOWN: 40
49 DOWN: 40
50 };
50 };
51
51
52 function Autocomplete(el, options) {
52 function Autocomplete(el, options) {
53 var noop = function () { },
53 var noop = function () { },
54 that = this,
54 that = this,
55 defaults = {
55 defaults = {
56 ajaxSettings: {},
56 ajaxSettings: {},
57 autoSelectFirst: false,
57 autoSelectFirst: false,
58 appendTo: document.body,
58 appendTo: document.body,
59 serviceUrl: null,
59 serviceUrl: null,
60 lookup: null,
60 lookup: null,
61 width: 'auto',
61 width: 'auto',
62 minChars: 1,
62 minChars: 1,
63 maxHeight: 300,
63 maxHeight: 300,
64 deferRequestBy: 0,
64 deferRequestBy: 0,
65 params: {},
65 params: {},
66 formatResult: Autocomplete.formatResult,
66 formatResult: Autocomplete.formatResult,
67 lookupFilter: Autocomplete.lookupFilter,
67 lookupFilter: Autocomplete.lookupFilter,
68 delimiter: null,
68 delimiter: null,
69 zIndex: 9999,
69 zIndex: 9999,
70 type: 'GET',
70 type: 'GET',
71 noCache: false,
71 noCache: false,
72 onSelect: noop,
72 onSelect: noop,
73 onSearchStart: noop,
73 onSearchStart: noop,
74 onSearchComplete: noop,
74 onSearchComplete: noop,
75 onSearchError: noop,
75 onSearchError: noop,
76 containerClass: 'autocomplete-suggestions',
76 containerClass: 'autocomplete-suggestions',
77 tabDisabled: false,
77 tabDisabled: false,
78 dataType: 'text',
78 dataType: 'text',
79 currentRequest: null,
79 currentRequest: null,
80 triggerSelectOnValidInput: false,
80 triggerSelectOnValidInput: false,
81 preventBadQueries: true,
81 preventBadQueries: true,
82 paramName: 'query',
82 paramName: 'query',
83 transformResult: function (response) {
83 transformResult: function (response) {
84 return typeof response === 'string' ? $.parseJSON(response) : response;
84 return typeof response === 'string' ? $.parseJSON(response) : response;
85 },
85 },
86 showNoSuggestionNotice: false,
86 showNoSuggestionNotice: false,
87 noSuggestionNotice: _TM['No results'],
87 noSuggestionNotice: _gettext('No results'),
88 orientation: 'bottom',
88 orientation: 'bottom',
89 forceFixPosition: false,
89 forceFixPosition: false,
90 replaceOnArrowKey: true
90 replaceOnArrowKey: true
91 };
91 };
92
92
93 // Shared variables:
93 // Shared variables:
94 that.element = el;
94 that.element = el;
95 that.el = $(el);
95 that.el = $(el);
96 that.suggestions = [];
96 that.suggestions = [];
97 that.badQueries = [];
97 that.badQueries = [];
98 that.selectedIndex = -1;
98 that.selectedIndex = -1;
99 that.currentValue = that.element.value;
99 that.currentValue = that.element.value;
100 that.intervalId = 0;
100 that.intervalId = 0;
101 that.cachedResponse = {};
101 that.cachedResponse = {};
102 that.onChangeInterval = null;
102 that.onChangeInterval = null;
103 that.onChange = null;
103 that.onChange = null;
104 that.isLocal = false;
104 that.isLocal = false;
105 that.suggestionsContainer = null;
105 that.suggestionsContainer = null;
106 that.noSuggestionsContainer = null;
106 that.noSuggestionsContainer = null;
107 that.options = $.extend({}, defaults, options);
107 that.options = $.extend({}, defaults, options);
108 that.classes = {
108 that.classes = {
109 selected: 'autocomplete-selected',
109 selected: 'autocomplete-selected',
110 suggestion: 'autocomplete-suggestion'
110 suggestion: 'autocomplete-suggestion'
111 };
111 };
112 that.hint = null;
112 that.hint = null;
113 that.hintValue = '';
113 that.hintValue = '';
114 that.selection = null;
114 that.selection = null;
115
115
116 // Initialize and set options:
116 // Initialize and set options:
117 that.initialize();
117 that.initialize();
118 that.setOptions(options);
118 that.setOptions(options);
119 }
119 }
120
120
121 Autocomplete.utils = utils;
121 Autocomplete.utils = utils;
122
122
123 $.Autocomplete = Autocomplete;
123 $.Autocomplete = Autocomplete;
124
124
125 Autocomplete.formatResult = function (suggestion, currentValue) {
125 Autocomplete.formatResult = function (suggestion, currentValue) {
126 var pattern = '(' + utils.escapeRegExChars(currentValue) + ')';
126 var pattern = '(' + utils.escapeRegExChars(currentValue) + ')';
127 return suggestion.value.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
127 return suggestion.value.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
128 };
128 };
129 Autocomplete.lookupFilter = function (suggestion, originalQuery, queryLowerCase) {
129 Autocomplete.lookupFilter = function (suggestion, originalQuery, queryLowerCase) {
130 return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1;
130 return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1;
131 };
131 };
132
132
133 Autocomplete.prototype = {
133 Autocomplete.prototype = {
134
134
135 killerFn: null,
135 killerFn: null,
136
136
137 initialize: function () {
137 initialize: function () {
138 var that = this,
138 var that = this,
139 suggestionSelector = '.' + that.classes.suggestion,
139 suggestionSelector = '.' + that.classes.suggestion,
140 selected = that.classes.selected,
140 selected = that.classes.selected,
141 options = that.options,
141 options = that.options,
142 container;
142 container;
143
143
144 // Remove autocomplete attribute to prevent native suggestions:
144 // Remove autocomplete attribute to prevent native suggestions:
145 that.element.setAttribute('autocomplete', 'off');
145 that.element.setAttribute('autocomplete', 'off');
146
146
147 that.killerFn = function (e) {
147 that.killerFn = function (e) {
148 if ($(e.target).closest('.' + that.options.containerClass).length === 0) {
148 if ($(e.target).closest('.' + that.options.containerClass).length === 0) {
149 that.killSuggestions();
149 that.killSuggestions();
150 that.disableKillerFn();
150 that.disableKillerFn();
151 }
151 }
152 };
152 };
153
153
154 // html() deals with many types: htmlString or Element or Array or jQuery
154 // html() deals with many types: htmlString or Element or Array or jQuery
155 that.noSuggestionsContainer = $('<div class="autocomplete-no-suggestion"></div>')
155 that.noSuggestionsContainer = $('<div class="autocomplete-no-suggestion"></div>')
156 .html(this.options.noSuggestionNotice).get(0);
156 .html(this.options.noSuggestionNotice).get(0);
157
157
158 that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass);
158 that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass);
159
159
160 container = $(that.suggestionsContainer);
160 container = $(that.suggestionsContainer);
161
161
162 container.appendTo(options.appendTo);
162 container.appendTo(options.appendTo);
163
163
164 // Only set width if it was provided:
164 // Only set width if it was provided:
165 if (options.width !== 'auto') {
165 if (options.width !== 'auto') {
166 container.width(options.width);
166 container.width(options.width);
167 }
167 }
168
168
169 // Listen for mouse over event on suggestions list:
169 // Listen for mouse over event on suggestions list:
170 container.on('mouseover.autocomplete', suggestionSelector, function () {
170 container.on('mouseover.autocomplete', suggestionSelector, function () {
171 that.activate($(this).data('index'));
171 that.activate($(this).data('index'));
172 });
172 });
173
173
174 // Deselect active element when mouse leaves suggestions container:
174 // Deselect active element when mouse leaves suggestions container:
175 container.on('mouseout.autocomplete', function () {
175 container.on('mouseout.autocomplete', function () {
176 that.selectedIndex = -1;
176 that.selectedIndex = -1;
177 container.children('.' + selected).removeClass(selected);
177 container.children('.' + selected).removeClass(selected);
178 });
178 });
179
179
180 // Listen for click event on suggestions list:
180 // Listen for click event on suggestions list:
181 container.on('click.autocomplete', suggestionSelector, function () {
181 container.on('click.autocomplete', suggestionSelector, function () {
182 that.select($(this).data('index'));
182 that.select($(this).data('index'));
183 });
183 });
184
184
185 that.fixPositionCapture = function () {
185 that.fixPositionCapture = function () {
186 if (that.visible) {
186 if (that.visible) {
187 that.fixPosition();
187 that.fixPosition();
188 }
188 }
189 };
189 };
190
190
191 $(window).on('resize.autocomplete', that.fixPositionCapture);
191 $(window).on('resize.autocomplete', that.fixPositionCapture);
192
192
193 that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); });
193 that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); });
194 that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); });
194 that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); });
195 that.el.on('blur.autocomplete', function () { that.onBlur(); });
195 that.el.on('blur.autocomplete', function () { that.onBlur(); });
196 that.el.on('focus.autocomplete', function () { that.onFocus(); });
196 that.el.on('focus.autocomplete', function () { that.onFocus(); });
197 that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); });
197 that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); });
198 },
198 },
199
199
200 onFocus: function () {
200 onFocus: function () {
201 var that = this;
201 var that = this;
202 that.fixPosition();
202 that.fixPosition();
203 if (that.options.minChars <= that.el.val().length) {
203 if (that.options.minChars <= that.el.val().length) {
204 that.onValueChange();
204 that.onValueChange();
205 }
205 }
206 },
206 },
207
207
208 onBlur: function () {
208 onBlur: function () {
209 this.enableKillerFn();
209 this.enableKillerFn();
210 },
210 },
211
211
212 setOptions: function (suppliedOptions) {
212 setOptions: function (suppliedOptions) {
213 var that = this,
213 var that = this,
214 options = that.options;
214 options = that.options;
215
215
216 $.extend(options, suppliedOptions);
216 $.extend(options, suppliedOptions);
217
217
218 that.isLocal = $.isArray(options.lookup);
218 that.isLocal = $.isArray(options.lookup);
219
219
220 if (that.isLocal) {
220 if (that.isLocal) {
221 options.lookup = that.verifySuggestionsFormat(options.lookup);
221 options.lookup = that.verifySuggestionsFormat(options.lookup);
222 }
222 }
223
223
224 options.orientation = that.validateOrientation(options.orientation, 'bottom');
224 options.orientation = that.validateOrientation(options.orientation, 'bottom');
225
225
226 // Adjust height, width and z-index:
226 // Adjust height, width and z-index:
227 $(that.suggestionsContainer).css({
227 $(that.suggestionsContainer).css({
228 'max-height': options.maxHeight + 'px',
228 'max-height': options.maxHeight + 'px',
229 'width': options.width + 'px',
229 'width': options.width + 'px',
230 'z-index': options.zIndex
230 'z-index': options.zIndex
231 });
231 });
232 },
232 },
233
233
234 clearCache: function () {
234 clearCache: function () {
235 this.cachedResponse = {};
235 this.cachedResponse = {};
236 this.badQueries = [];
236 this.badQueries = [];
237 },
237 },
238
238
239 clear: function () {
239 clear: function () {
240 this.clearCache();
240 this.clearCache();
241 this.currentValue = '';
241 this.currentValue = '';
242 this.suggestions = [];
242 this.suggestions = [];
243 },
243 },
244
244
245 disable: function () {
245 disable: function () {
246 var that = this;
246 var that = this;
247 that.disabled = true;
247 that.disabled = true;
248 if (that.currentRequest) {
248 if (that.currentRequest) {
249 that.currentRequest.abort();
249 that.currentRequest.abort();
250 }
250 }
251 },
251 },
252
252
253 enable: function () {
253 enable: function () {
254 this.disabled = false;
254 this.disabled = false;
255 },
255 },
256
256
257 fixPosition: function () {
257 fixPosition: function () {
258 // Use only when container has already its content
258 // Use only when container has already its content
259
259
260 var that = this,
260 var that = this,
261 $container = $(that.suggestionsContainer),
261 $container = $(that.suggestionsContainer),
262 containerParent = $container.parent().get(0);
262 containerParent = $container.parent().get(0);
263 // Fix position automatically when appended to body.
263 // Fix position automatically when appended to body.
264 // In other cases force parameter must be given.
264 // In other cases force parameter must be given.
265 if (containerParent !== document.body && !that.options.forceFixPosition)
265 if (containerParent !== document.body && !that.options.forceFixPosition)
266 return;
266 return;
267
267
268 // Choose orientation
268 // Choose orientation
269 var orientation = that.options.orientation,
269 var orientation = that.options.orientation,
270 containerHeight = $container.outerHeight(),
270 containerHeight = $container.outerHeight(),
271 height = that.el.outerHeight(),
271 height = that.el.outerHeight(),
272 offset = that.el.offset(),
272 offset = that.el.offset(),
273 styles = { 'top': offset.top, 'left': offset.left };
273 styles = { 'top': offset.top, 'left': offset.left };
274
274
275 if (orientation == 'auto') {
275 if (orientation == 'auto') {
276 var viewPortHeight = $(window).height(),
276 var viewPortHeight = $(window).height(),
277 scrollTop = $(window).scrollTop(),
277 scrollTop = $(window).scrollTop(),
278 topOverflow = -scrollTop + offset.top - containerHeight,
278 topOverflow = -scrollTop + offset.top - containerHeight,
279 bottomOverflow = scrollTop + viewPortHeight - (offset.top + height + containerHeight);
279 bottomOverflow = scrollTop + viewPortHeight - (offset.top + height + containerHeight);
280
280
281 orientation = (Math.max(topOverflow, bottomOverflow) === topOverflow)
281 orientation = (Math.max(topOverflow, bottomOverflow) === topOverflow)
282 ? 'top'
282 ? 'top'
283 : 'bottom';
283 : 'bottom';
284 }
284 }
285
285
286 if (orientation === 'top') {
286 if (orientation === 'top') {
287 styles.top += -containerHeight;
287 styles.top += -containerHeight;
288 } else {
288 } else {
289 styles.top += height;
289 styles.top += height;
290 }
290 }
291
291
292 // If container is not positioned to body,
292 // If container is not positioned to body,
293 // correct its position using offset parent offset
293 // correct its position using offset parent offset
294 if(containerParent !== document.body) {
294 if(containerParent !== document.body) {
295 var opacity = $container.css('opacity'),
295 var opacity = $container.css('opacity'),
296 parentOffsetDiff;
296 parentOffsetDiff;
297
297
298 if (!that.visible){
298 if (!that.visible){
299 $container.css('opacity', 0).show();
299 $container.css('opacity', 0).show();
300 }
300 }
301
301
302 parentOffsetDiff = $container.offsetParent().offset();
302 parentOffsetDiff = $container.offsetParent().offset();
303 styles.top -= parentOffsetDiff.top;
303 styles.top -= parentOffsetDiff.top;
304 styles.left -= parentOffsetDiff.left;
304 styles.left -= parentOffsetDiff.left;
305
305
306 if (!that.visible){
306 if (!that.visible){
307 $container.css('opacity', opacity).hide();
307 $container.css('opacity', opacity).hide();
308 }
308 }
309 }
309 }
310
310
311 // -2px to account for suggestions border.
311 // -2px to account for suggestions border.
312 if (that.options.width === 'auto') {
312 if (that.options.width === 'auto') {
313 styles.width = (that.el.outerWidth() - 2) + 'px';
313 styles.width = (that.el.outerWidth() - 2) + 'px';
314 }
314 }
315
315
316 $container.css(styles);
316 $container.css(styles);
317 },
317 },
318
318
319 enableKillerFn: function () {
319 enableKillerFn: function () {
320 var that = this;
320 var that = this;
321 $(document).on('click.autocomplete', that.killerFn);
321 $(document).on('click.autocomplete', that.killerFn);
322 },
322 },
323
323
324 disableKillerFn: function () {
324 disableKillerFn: function () {
325 var that = this;
325 var that = this;
326 $(document).off('click.autocomplete', that.killerFn);
326 $(document).off('click.autocomplete', that.killerFn);
327 },
327 },
328
328
329 killSuggestions: function () {
329 killSuggestions: function () {
330 var that = this;
330 var that = this;
331 that.stopKillSuggestions();
331 that.stopKillSuggestions();
332 that.intervalId = window.setInterval(function () {
332 that.intervalId = window.setInterval(function () {
333 that.hide();
333 that.hide();
334 that.stopKillSuggestions();
334 that.stopKillSuggestions();
335 }, 50);
335 }, 50);
336 },
336 },
337
337
338 stopKillSuggestions: function () {
338 stopKillSuggestions: function () {
339 window.clearInterval(this.intervalId);
339 window.clearInterval(this.intervalId);
340 },
340 },
341
341
342 isCursorAtEnd: function () {
342 isCursorAtEnd: function () {
343 var that = this,
343 var that = this,
344 valLength = that.el.val().length,
344 valLength = that.el.val().length,
345 selectionStart = that.element.selectionStart,
345 selectionStart = that.element.selectionStart,
346 range;
346 range;
347
347
348 if (typeof selectionStart === 'number') {
348 if (typeof selectionStart === 'number') {
349 return selectionStart === valLength;
349 return selectionStart === valLength;
350 }
350 }
351 if (document.selection) {
351 if (document.selection) {
352 range = document.selection.createRange();
352 range = document.selection.createRange();
353 range.moveStart('character', -valLength);
353 range.moveStart('character', -valLength);
354 return valLength === range.text.length;
354 return valLength === range.text.length;
355 }
355 }
356 return true;
356 return true;
357 },
357 },
358
358
359 onKeyPress: function (e) {
359 onKeyPress: function (e) {
360 var that = this;
360 var that = this;
361
361
362 // If suggestions are hidden and user presses arrow down, display suggestions:
362 // If suggestions are hidden and user presses arrow down, display suggestions:
363 if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) {
363 if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) {
364 that.suggest();
364 that.suggest();
365 return;
365 return;
366 }
366 }
367
367
368 if (that.disabled || !that.visible) {
368 if (that.disabled || !that.visible) {
369 return;
369 return;
370 }
370 }
371
371
372 switch (e.which) {
372 switch (e.which) {
373 case keys.ESC:
373 case keys.ESC:
374 that.el.val(that.currentValue);
374 that.el.val(that.currentValue);
375 that.hide();
375 that.hide();
376 break;
376 break;
377 case keys.RIGHT:
377 case keys.RIGHT:
378 if (that.hint && that.options.onHint && that.isCursorAtEnd()) {
378 if (that.hint && that.options.onHint && that.isCursorAtEnd()) {
379 that.selectHint();
379 that.selectHint();
380 break;
380 break;
381 }
381 }
382 return;
382 return;
383 case keys.TAB:
383 case keys.TAB:
384 if (that.hint && that.options.onHint) {
384 if (that.hint && that.options.onHint) {
385 that.selectHint();
385 that.selectHint();
386 return;
386 return;
387 }
387 }
388 // Fall through to RETURN
388 // Fall through to RETURN
389 case keys.RETURN:
389 case keys.RETURN:
390 if (that.selectedIndex === -1) {
390 if (that.selectedIndex === -1) {
391 that.hide();
391 that.hide();
392 return;
392 return;
393 }
393 }
394 that.select(that.selectedIndex);
394 that.select(that.selectedIndex);
395 if (e.which === keys.TAB && that.options.tabDisabled === false) {
395 if (e.which === keys.TAB && that.options.tabDisabled === false) {
396 return;
396 return;
397 }
397 }
398 break;
398 break;
399 case keys.UP:
399 case keys.UP:
400 that.moveUp();
400 that.moveUp();
401 break;
401 break;
402 case keys.DOWN:
402 case keys.DOWN:
403 that.moveDown();
403 that.moveDown();
404 break;
404 break;
405 default:
405 default:
406 return;
406 return;
407 }
407 }
408
408
409 // Cancel event if function did not return:
409 // Cancel event if function did not return:
410 e.stopImmediatePropagation();
410 e.stopImmediatePropagation();
411 e.preventDefault();
411 e.preventDefault();
412 },
412 },
413
413
414 onKeyUp: function (e) {
414 onKeyUp: function (e) {
415 var that = this;
415 var that = this;
416
416
417 if (that.disabled) {
417 if (that.disabled) {
418 return;
418 return;
419 }
419 }
420
420
421 switch (e.which) {
421 switch (e.which) {
422 case keys.UP:
422 case keys.UP:
423 case keys.DOWN:
423 case keys.DOWN:
424 return;
424 return;
425 }
425 }
426
426
427 clearInterval(that.onChangeInterval);
427 clearInterval(that.onChangeInterval);
428
428
429 if (that.currentValue !== that.el.val()) {
429 if (that.currentValue !== that.el.val()) {
430 that.findBestHint();
430 that.findBestHint();
431 if (that.options.deferRequestBy > 0) {
431 if (that.options.deferRequestBy > 0) {
432 // Defer lookup in case when value changes very quickly:
432 // Defer lookup in case when value changes very quickly:
433 that.onChangeInterval = setInterval(function () {
433 that.onChangeInterval = setInterval(function () {
434 that.onValueChange();
434 that.onValueChange();
435 }, that.options.deferRequestBy);
435 }, that.options.deferRequestBy);
436 } else {
436 } else {
437 that.onValueChange();
437 that.onValueChange();
438 }
438 }
439 }
439 }
440 },
440 },
441
441
442 onValueChange: function () {
442 onValueChange: function () {
443 var that = this,
443 var that = this,
444 options = that.options,
444 options = that.options,
445 value = that.el.val(),
445 value = that.el.val(),
446 query = that.getQuery(value),
446 query = that.getQuery(value),
447 index;
447 index;
448
448
449 if (that.selection && that.currentValue !== query) {
449 if (that.selection && that.currentValue !== query) {
450 that.selection = null;
450 that.selection = null;
451 (options.onInvalidateSelection || $.noop).call(that.element);
451 (options.onInvalidateSelection || $.noop).call(that.element);
452 }
452 }
453
453
454 clearInterval(that.onChangeInterval);
454 clearInterval(that.onChangeInterval);
455 that.currentValue = value;
455 that.currentValue = value;
456 that.selectedIndex = -1;
456 that.selectedIndex = -1;
457
457
458 // Check existing suggestion for the match before proceeding:
458 // Check existing suggestion for the match before proceeding:
459 if (options.triggerSelectOnValidInput) {
459 if (options.triggerSelectOnValidInput) {
460 index = that.findSuggestionIndex(query);
460 index = that.findSuggestionIndex(query);
461 if (index !== -1) {
461 if (index !== -1) {
462 that.select(index);
462 that.select(index);
463 return;
463 return;
464 }
464 }
465 }
465 }
466
466
467 if (query.length < options.minChars) {
467 if (query.length < options.minChars) {
468 that.hide();
468 that.hide();
469 } else {
469 } else {
470 that.getSuggestions(query);
470 that.getSuggestions(query);
471 }
471 }
472 },
472 },
473
473
474 findSuggestionIndex: function (query) {
474 findSuggestionIndex: function (query) {
475 var that = this,
475 var that = this,
476 index = -1,
476 index = -1,
477 queryLowerCase = query.toLowerCase();
477 queryLowerCase = query.toLowerCase();
478
478
479 $.each(that.suggestions, function (i, suggestion) {
479 $.each(that.suggestions, function (i, suggestion) {
480 if (suggestion.value.toLowerCase() === queryLowerCase) {
480 if (suggestion.value.toLowerCase() === queryLowerCase) {
481 index = i;
481 index = i;
482 return false;
482 return false;
483 }
483 }
484 });
484 });
485
485
486 return index;
486 return index;
487 },
487 },
488
488
489 getQuery: function (value) {
489 getQuery: function (value) {
490 var delimiter = this.options.delimiter,
490 var delimiter = this.options.delimiter,
491 parts;
491 parts;
492
492
493 if (!delimiter) {
493 if (!delimiter) {
494 return value;
494 return value;
495 }
495 }
496 parts = value.split(delimiter);
496 parts = value.split(delimiter);
497 return $.trim(parts[parts.length - 1]);
497 return $.trim(parts[parts.length - 1]);
498 },
498 },
499
499
500 getSuggestionsLocal: function (query) {
500 getSuggestionsLocal: function (query) {
501 var that = this,
501 var that = this,
502 options = that.options,
502 options = that.options,
503 queryLowerCase = query.toLowerCase(),
503 queryLowerCase = query.toLowerCase(),
504 data;
504 data;
505
505
506 // re-pack the data as it was comming from AJAX
506 // re-pack the data as it was comming from AJAX
507 data = {
507 data = {
508 suggestions: data
508 suggestions: data
509 };
509 };
510 return data;
510 return data;
511 },
511 },
512
512
513 getSuggestions: function (query) {
513 getSuggestions: function (query) {
514 var response,
514 var response,
515 that = this,
515 that = this,
516 options = that.options,
516 options = that.options,
517 serviceUrl = options.serviceUrl,
517 serviceUrl = options.serviceUrl,
518 params,
518 params,
519 cacheKey,
519 cacheKey,
520 ajaxSettings;
520 ajaxSettings;
521
521
522 options.params[options.paramName] = query;
522 options.params[options.paramName] = query;
523 params = options.ignoreParams ? null : options.params;
523 params = options.ignoreParams ? null : options.params;
524
524
525 if (that.isLocal) {
525 if (that.isLocal) {
526 response = that.getSuggestionsLocal(query);
526 response = that.getSuggestionsLocal(query);
527 } else {
527 } else {
528 if ($.isFunction(serviceUrl)) {
528 if ($.isFunction(serviceUrl)) {
529 serviceUrl = serviceUrl.call(that.element, query);
529 serviceUrl = serviceUrl.call(that.element, query);
530 }
530 }
531 cacheKey = serviceUrl + '?' + $.param(params || {});
531 cacheKey = serviceUrl + '?' + $.param(params || {});
532 response = that.cachedResponse[cacheKey];
532 response = that.cachedResponse[cacheKey];
533 }
533 }
534
534
535 if (response && $.isArray(response.suggestions)) {
535 if (response && $.isArray(response.suggestions)) {
536 that.suggestions = response.suggestions;
536 that.suggestions = response.suggestions;
537 that.suggest();
537 that.suggest();
538 } else if (!that.isBadQuery(query)) {
538 } else if (!that.isBadQuery(query)) {
539 if (options.onSearchStart.call(that.element, options.params) === false) {
539 if (options.onSearchStart.call(that.element, options.params) === false) {
540 return;
540 return;
541 }
541 }
542 if (that.currentRequest) {
542 if (that.currentRequest) {
543 that.currentRequest.abort();
543 that.currentRequest.abort();
544 }
544 }
545
545
546 ajaxSettings = {
546 ajaxSettings = {
547 url: serviceUrl,
547 url: serviceUrl,
548 data: params,
548 data: params,
549 type: options.type,
549 type: options.type,
550 dataType: options.dataType
550 dataType: options.dataType
551 };
551 };
552
552
553 $.extend(ajaxSettings, options.ajaxSettings);
553 $.extend(ajaxSettings, options.ajaxSettings);
554
554
555 that.currentRequest = $.ajax(ajaxSettings).done(function (data) {
555 that.currentRequest = $.ajax(ajaxSettings).done(function (data) {
556 var result;
556 var result;
557 that.currentRequest = null;
557 that.currentRequest = null;
558 result = options.transformResult(data);
558 result = options.transformResult(data);
559 that.processResponse(result, query, cacheKey);
559 that.processResponse(result, query, cacheKey);
560 options.onSearchComplete.call(that.element, query, result.suggestions);
560 options.onSearchComplete.call(that.element, query, result.suggestions);
561 }).fail(function (jqXHR, textStatus, errorThrown) {
561 }).fail(function (jqXHR, textStatus, errorThrown) {
562 options.onSearchError.call(that.element, query, jqXHR, textStatus, errorThrown);
562 options.onSearchError.call(that.element, query, jqXHR, textStatus, errorThrown);
563 });
563 });
564 }
564 }
565 },
565 },
566
566
567 isBadQuery: function (q) {
567 isBadQuery: function (q) {
568 if (!this.options.preventBadQueries){
568 if (!this.options.preventBadQueries){
569 return false;
569 return false;
570 }
570 }
571
571
572 var badQueries = this.badQueries,
572 var badQueries = this.badQueries,
573 i = badQueries.length;
573 i = badQueries.length;
574
574
575 while (i--) {
575 while (i--) {
576 if (q.indexOf(badQueries[i]) === 0) {
576 if (q.indexOf(badQueries[i]) === 0) {
577 return true;
577 return true;
578 }
578 }
579 }
579 }
580
580
581 return false;
581 return false;
582 },
582 },
583
583
584 hide: function () {
584 hide: function () {
585 var that = this;
585 var that = this;
586 that.visible = false;
586 that.visible = false;
587 that.selectedIndex = -1;
587 that.selectedIndex = -1;
588 $(that.suggestionsContainer).hide();
588 $(that.suggestionsContainer).hide();
589 that.signalHint(null);
589 that.signalHint(null);
590 },
590 },
591
591
592 suggest: function () {
592 suggest: function () {
593
593
594 var that = this,
594 var that = this,
595 options = that.options,
595 options = that.options,
596 formatResult = options.formatResult,
596 formatResult = options.formatResult,
597 filterResult = options.lookupFilter,
597 filterResult = options.lookupFilter,
598 value = that.getQuery(that.currentValue),
598 value = that.getQuery(that.currentValue),
599 className = that.classes.suggestion,
599 className = that.classes.suggestion,
600 classSelected = that.classes.selected,
600 classSelected = that.classes.selected,
601 container = $(that.suggestionsContainer),
601 container = $(that.suggestionsContainer),
602 noSuggestionsContainer = $(that.noSuggestionsContainer),
602 noSuggestionsContainer = $(that.noSuggestionsContainer),
603 beforeRender = options.beforeRender,
603 beforeRender = options.beforeRender,
604 limit = parseInt(that.options.lookupLimit, 10),
604 limit = parseInt(that.options.lookupLimit, 10),
605 html = '',
605 html = '',
606 index;
606 index;
607
607
608 // filter and limit given results
608 // filter and limit given results
609 var filtered_suggestions = $.grep(that.suggestions, function (suggestion) {
609 var filtered_suggestions = $.grep(that.suggestions, function (suggestion) {
610 return filterResult(suggestion, value, value.toLowerCase(), that.element);
610 return filterResult(suggestion, value, value.toLowerCase(), that.element);
611 });
611 });
612
612
613 if (limit && filtered_suggestions.length > limit) {
613 if (limit && filtered_suggestions.length > limit) {
614 filtered_suggestions = filtered_suggestions.slice(0, limit);
614 filtered_suggestions = filtered_suggestions.slice(0, limit);
615 }
615 }
616
616
617 if (filtered_suggestions.length === 0) {
617 if (filtered_suggestions.length === 0) {
618 this.options.showNoSuggestionNotice ? this.noSuggestions() : this.hide();
618 this.options.showNoSuggestionNotice ? this.noSuggestions() : this.hide();
619 return;
619 return;
620 }
620 }
621
621
622 if (options.triggerSelectOnValidInput) {
622 if (options.triggerSelectOnValidInput) {
623 index = that.findSuggestionIndex(value);
623 index = that.findSuggestionIndex(value);
624 if (index !== -1) {
624 if (index !== -1) {
625 that.select(index);
625 that.select(index);
626 return;
626 return;
627 }
627 }
628 }
628 }
629
629
630 // Build suggestions inner HTML:
630 // Build suggestions inner HTML:
631 $.each(filtered_suggestions, function (i, suggestion) {
631 $.each(filtered_suggestions, function (i, suggestion) {
632 html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value, Autocomplete.formatResult, that.element) + '</div>';
632 html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value, Autocomplete.formatResult, that.element) + '</div>';
633 });
633 });
634 // set internal suggestion for INDEX pick to work correctly
634 // set internal suggestion for INDEX pick to work correctly
635 that.suggestions = filtered_suggestions;
635 that.suggestions = filtered_suggestions;
636 this.adjustContainerWidth();
636 this.adjustContainerWidth();
637
637
638 noSuggestionsContainer.detach();
638 noSuggestionsContainer.detach();
639 container.html(html);
639 container.html(html);
640
640
641 // Select first value by default:
641 // Select first value by default:
642 if (options.autoSelectFirst) {
642 if (options.autoSelectFirst) {
643 that.selectedIndex = 0;
643 that.selectedIndex = 0;
644 container.children().first().addClass(classSelected);
644 container.children().first().addClass(classSelected);
645 }
645 }
646
646
647 if ($.isFunction(beforeRender)) {
647 if ($.isFunction(beforeRender)) {
648 beforeRender.call(that.element, container);
648 beforeRender.call(that.element, container);
649 }
649 }
650
650
651 that.fixPosition();
651 that.fixPosition();
652
652
653 container.show();
653 container.show();
654 that.visible = true;
654 that.visible = true;
655
655
656 that.findBestHint();
656 that.findBestHint();
657 },
657 },
658
658
659 noSuggestions: function() {
659 noSuggestions: function() {
660 var that = this,
660 var that = this,
661 container = $(that.suggestionsContainer),
661 container = $(that.suggestionsContainer),
662 noSuggestionsContainer = $(that.noSuggestionsContainer);
662 noSuggestionsContainer = $(that.noSuggestionsContainer);
663
663
664 this.adjustContainerWidth();
664 this.adjustContainerWidth();
665
665
666 // Some explicit steps. Be careful here as it easy to get
666 // Some explicit steps. Be careful here as it easy to get
667 // noSuggestionsContainer removed from DOM if not detached properly.
667 // noSuggestionsContainer removed from DOM if not detached properly.
668 noSuggestionsContainer.detach();
668 noSuggestionsContainer.detach();
669 container.empty(); // clean suggestions if any
669 container.empty(); // clean suggestions if any
670 container.append(noSuggestionsContainer);
670 container.append(noSuggestionsContainer);
671
671
672 that.fixPosition();
672 that.fixPosition();
673
673
674 container.show();
674 container.show();
675 that.visible = true;
675 that.visible = true;
676 },
676 },
677
677
678 adjustContainerWidth: function() {
678 adjustContainerWidth: function() {
679 var that = this,
679 var that = this,
680 options = that.options,
680 options = that.options,
681 width,
681 width,
682 container = $(that.suggestionsContainer);
682 container = $(that.suggestionsContainer);
683
683
684 // If width is auto, adjust width before displaying suggestions,
684 // If width is auto, adjust width before displaying suggestions,
685 // because if instance was created before input had width, it will be zero.
685 // because if instance was created before input had width, it will be zero.
686 // Also it adjusts if input width has changed.
686 // Also it adjusts if input width has changed.
687 // -2px to account for suggestions border.
687 // -2px to account for suggestions border.
688 if (options.width === 'auto') {
688 if (options.width === 'auto') {
689 width = that.el.outerWidth() - 2;
689 width = that.el.outerWidth() - 2;
690 container.width(width > 0 ? width : 300);
690 container.width(width > 0 ? width : 300);
691 }
691 }
692 },
692 },
693
693
694 findBestHint: function () {
694 findBestHint: function () {
695 var that = this,
695 var that = this,
696 value = that.el.val().toLowerCase(),
696 value = that.el.val().toLowerCase(),
697 bestMatch = null;
697 bestMatch = null;
698
698
699 if (!value) {
699 if (!value) {
700 return;
700 return;
701 }
701 }
702
702
703 $.each(that.suggestions, function (i, suggestion) {
703 $.each(that.suggestions, function (i, suggestion) {
704 var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0;
704 var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0;
705 if (foundMatch) {
705 if (foundMatch) {
706 bestMatch = suggestion;
706 bestMatch = suggestion;
707 }
707 }
708 return !foundMatch;
708 return !foundMatch;
709 });
709 });
710 that.signalHint(bestMatch);
710 that.signalHint(bestMatch);
711 },
711 },
712
712
713 signalHint: function (suggestion) {
713 signalHint: function (suggestion) {
714 var hintValue = '',
714 var hintValue = '',
715 that = this;
715 that = this;
716 if (suggestion) {
716 if (suggestion) {
717 hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length);
717 hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length);
718 }
718 }
719 if (that.hintValue !== hintValue) {
719 if (that.hintValue !== hintValue) {
720 that.hintValue = hintValue;
720 that.hintValue = hintValue;
721 that.hint = suggestion;
721 that.hint = suggestion;
722 (this.options.onHint || $.noop)(hintValue);
722 (this.options.onHint || $.noop)(hintValue);
723 }
723 }
724 },
724 },
725
725
726 verifySuggestionsFormat: function (suggestions) {
726 verifySuggestionsFormat: function (suggestions) {
727 // If suggestions is string array, convert them to supported format:
727 // If suggestions is string array, convert them to supported format:
728 if (suggestions.length && typeof suggestions[0] === 'string') {
728 if (suggestions.length && typeof suggestions[0] === 'string') {
729 return $.map(suggestions, function (value) {
729 return $.map(suggestions, function (value) {
730 return { value: value, data: null };
730 return { value: value, data: null };
731 });
731 });
732 }
732 }
733
733
734 return suggestions;
734 return suggestions;
735 },
735 },
736
736
737 validateOrientation: function(orientation, fallback) {
737 validateOrientation: function(orientation, fallback) {
738 orientation = $.trim(orientation || '').toLowerCase();
738 orientation = $.trim(orientation || '').toLowerCase();
739
739
740 if($.inArray(orientation, ['auto', 'bottom', 'top']) === -1){
740 if($.inArray(orientation, ['auto', 'bottom', 'top']) === -1){
741 orientation = fallback;
741 orientation = fallback;
742 }
742 }
743
743
744 return orientation;
744 return orientation;
745 },
745 },
746
746
747 processResponse: function (result, originalQuery, cacheKey) {
747 processResponse: function (result, originalQuery, cacheKey) {
748 var that = this,
748 var that = this,
749 options = that.options;
749 options = that.options;
750
750
751 result.suggestions = that.verifySuggestionsFormat(result.suggestions);
751 result.suggestions = that.verifySuggestionsFormat(result.suggestions);
752
752
753 // Cache results if cache is not disabled:
753 // Cache results if cache is not disabled:
754 if (!options.noCache) {
754 if (!options.noCache) {
755 that.cachedResponse[cacheKey] = result;
755 that.cachedResponse[cacheKey] = result;
756 if (options.preventBadQueries && result.suggestions.length === 0) {
756 if (options.preventBadQueries && result.suggestions.length === 0) {
757 that.badQueries.push(originalQuery);
757 that.badQueries.push(originalQuery);
758 }
758 }
759 }
759 }
760
760
761 // Return if originalQuery is not matching current query:
761 // Return if originalQuery is not matching current query:
762 if (originalQuery !== that.getQuery(that.currentValue)) {
762 if (originalQuery !== that.getQuery(that.currentValue)) {
763 return;
763 return;
764 }
764 }
765
765
766 that.suggestions = result.suggestions;
766 that.suggestions = result.suggestions;
767 that.suggest();
767 that.suggest();
768 },
768 },
769
769
770 activate: function (index) {
770 activate: function (index) {
771 var that = this,
771 var that = this,
772 activeItem,
772 activeItem,
773 selected = that.classes.selected,
773 selected = that.classes.selected,
774 container = $(that.suggestionsContainer),
774 container = $(that.suggestionsContainer),
775 children = container.find('.' + that.classes.suggestion);
775 children = container.find('.' + that.classes.suggestion);
776
776
777 container.find('.' + selected).removeClass(selected);
777 container.find('.' + selected).removeClass(selected);
778
778
779 that.selectedIndex = index;
779 that.selectedIndex = index;
780
780
781 if (that.selectedIndex !== -1 && children.length > that.selectedIndex) {
781 if (that.selectedIndex !== -1 && children.length > that.selectedIndex) {
782 activeItem = children.get(that.selectedIndex);
782 activeItem = children.get(that.selectedIndex);
783 $(activeItem).addClass(selected);
783 $(activeItem).addClass(selected);
784 return activeItem;
784 return activeItem;
785 }
785 }
786
786
787 return null;
787 return null;
788 },
788 },
789
789
790 selectHint: function () {
790 selectHint: function () {
791 var that = this,
791 var that = this,
792 i = $.inArray(that.hint, that.suggestions);
792 i = $.inArray(that.hint, that.suggestions);
793 that.select(i);
793 that.select(i);
794 },
794 },
795
795
796 select: function (index) {
796 select: function (index) {
797 var that = this;
797 var that = this;
798 that.hide();
798 that.hide();
799 that.onSelect(index);
799 that.onSelect(index);
800 },
800 },
801
801
802 moveUp: function () {
802 moveUp: function () {
803 var that = this;
803 var that = this;
804
804
805 if (that.selectedIndex === -1) {
805 if (that.selectedIndex === -1) {
806 return;
806 return;
807 }
807 }
808
808
809 if (that.selectedIndex === 0) {
809 if (that.selectedIndex === 0) {
810 $(that.suggestionsContainer).children().first().removeClass(that.classes.selected);
810 $(that.suggestionsContainer).children().first().removeClass(that.classes.selected);
811 that.selectedIndex = -1;
811 that.selectedIndex = -1;
812 that.el.val(that.currentValue);
812 that.el.val(that.currentValue);
813 that.findBestHint();
813 that.findBestHint();
814 return;
814 return;
815 }
815 }
816
816
817 that.adjustScroll(that.selectedIndex - 1);
817 that.adjustScroll(that.selectedIndex - 1);
818 },
818 },
819
819
820 moveDown: function () {
820 moveDown: function () {
821 var that = this;
821 var that = this;
822
822
823 if (that.selectedIndex === (that.suggestions.length - 1)) {
823 if (that.selectedIndex === (that.suggestions.length - 1)) {
824 return;
824 return;
825 }
825 }
826
826
827 that.adjustScroll(that.selectedIndex + 1);
827 that.adjustScroll(that.selectedIndex + 1);
828 },
828 },
829
829
830 adjustScroll: function (index) {
830 adjustScroll: function (index) {
831 var that = this,
831 var that = this,
832 activeItem = that.activate(index),
832 activeItem = that.activate(index),
833 offsetTop,
833 offsetTop,
834 upperBound,
834 upperBound,
835 lowerBound,
835 lowerBound,
836 heightDelta = 25;
836 heightDelta = 25;
837
837
838 if (!activeItem) {
838 if (!activeItem) {
839 return;
839 return;
840 }
840 }
841
841
842 offsetTop = activeItem.offsetTop;
842 offsetTop = activeItem.offsetTop;
843 upperBound = $(that.suggestionsContainer).scrollTop();
843 upperBound = $(that.suggestionsContainer).scrollTop();
844 lowerBound = upperBound + that.options.maxHeight - heightDelta;
844 lowerBound = upperBound + that.options.maxHeight - heightDelta;
845
845
846 if (offsetTop < upperBound) {
846 if (offsetTop < upperBound) {
847 $(that.suggestionsContainer).scrollTop(offsetTop);
847 $(that.suggestionsContainer).scrollTop(offsetTop);
848 } else if (offsetTop > lowerBound) {
848 } else if (offsetTop > lowerBound) {
849 $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta);
849 $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta);
850 }
850 }
851
851
852 if (that.options.replaceOnArrowKey) {
852 if (that.options.replaceOnArrowKey) {
853 that.el.val(that.getValue(that.suggestions[index].value));
853 that.el.val(that.getValue(that.suggestions[index].value));
854 }
854 }
855 that.signalHint(null);
855 that.signalHint(null);
856 },
856 },
857
857
858 onSelect: function (index) {
858 onSelect: function (index) {
859 var that = this,
859 var that = this,
860 onSelectCallback = that.options.onSelect,
860 onSelectCallback = that.options.onSelect,
861 suggestion = that.suggestions[index];
861 suggestion = that.suggestions[index];
862
862
863 that.currentValue = that.getValue(suggestion.value);
863 that.currentValue = that.getValue(suggestion.value);
864 var prevElem = {'value': that.el.val(),
864 var prevElem = {'value': that.el.val(),
865 'caret': that.element.selectionStart}
865 'caret': that.element.selectionStart}
866
866
867 if (that.currentValue !== that.el.val()) {
867 if (that.currentValue !== that.el.val()) {
868 that.el.val(that.currentValue);
868 that.el.val(that.currentValue);
869 }
869 }
870
870
871 that.signalHint(null);
871 that.signalHint(null);
872 that.suggestions = [];
872 that.suggestions = [];
873 that.selection = suggestion;
873 that.selection = suggestion;
874
874
875 if ($.isFunction(onSelectCallback)) {
875 if ($.isFunction(onSelectCallback)) {
876 onSelectCallback.call(this, that.element, suggestion, prevElem);
876 onSelectCallback.call(this, that.element, suggestion, prevElem);
877 }
877 }
878 },
878 },
879
879
880 getValue: function (value) {
880 getValue: function (value) {
881 var that = this,
881 var that = this,
882 delimiter = that.options.delimiter,
882 delimiter = that.options.delimiter,
883 currentValue,
883 currentValue,
884 parts;
884 parts;
885
885
886 if (!delimiter) {
886 if (!delimiter) {
887 return value;
887 return value;
888 }
888 }
889
889
890 currentValue = that.currentValue;
890 currentValue = that.currentValue;
891 parts = currentValue.split(delimiter);
891 parts = currentValue.split(delimiter);
892
892
893 if (parts.length === 1) {
893 if (parts.length === 1) {
894 return value;
894 return value;
895 }
895 }
896
896
897 return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value;
897 return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value;
898 },
898 },
899
899
900 dispose: function () {
900 dispose: function () {
901 var that = this;
901 var that = this;
902 that.el.off('.autocomplete').removeData('autocomplete');
902 that.el.off('.autocomplete').removeData('autocomplete');
903 that.disableKillerFn();
903 that.disableKillerFn();
904 $(window).off('resize.autocomplete', that.fixPositionCapture);
904 $(window).off('resize.autocomplete', that.fixPositionCapture);
905 $(that.suggestionsContainer).remove();
905 $(that.suggestionsContainer).remove();
906 }
906 }
907 };
907 };
908
908
909 // Create chainable jQuery plugin:
909 // Create chainable jQuery plugin:
910 $.fn.autocomplete = $.fn.devbridgeAutocomplete = function (options, args) {
910 $.fn.autocomplete = $.fn.devbridgeAutocomplete = function (options, args) {
911 var dataKey = 'autocomplete';
911 var dataKey = 'autocomplete';
912 // If function invoked without argument return
912 // If function invoked without argument return
913 // instance of the first matched element:
913 // instance of the first matched element:
914 if (arguments.length === 0) {
914 if (arguments.length === 0) {
915 return this.first().data(dataKey);
915 return this.first().data(dataKey);
916 }
916 }
917
917
918 return this.each(function () {
918 return this.each(function () {
919 var inputElement = $(this),
919 var inputElement = $(this),
920 instance = inputElement.data(dataKey);
920 instance = inputElement.data(dataKey);
921
921
922 if (typeof options === 'string') {
922 if (typeof options === 'string') {
923 if (instance && typeof instance[options] === 'function') {
923 if (instance && typeof instance[options] === 'function') {
924 instance[options](args);
924 instance[options](args);
925 }
925 }
926 } else {
926 } else {
927 // If instance already exists, destroy it:
927 // If instance already exists, destroy it:
928 if (instance && instance.dispose) {
928 if (instance && instance.dispose) {
929 instance.dispose();
929 instance.dispose();
930 }
930 }
931 instance = new Autocomplete(this, options);
931 instance = new Autocomplete(this, options);
932 inputElement.data(dataKey, instance);
932 inputElement.data(dataKey, instance);
933 }
933 }
934 });
934 });
935 };
935 };
936 }));
936 }));
@@ -1,205 +1,190 b''
1 // define module
1 // define module
2 var AgeModule = (function () {
2 var AgeModule = (function () {
3 return {
3 return {
4 age: function(prevdate, now, show_short_version, show_suffix, short_format) {
4 age: function(prevdate, now, show_short_version, show_suffix, short_format) {
5
5
6 var prevdate = moment(prevdate);
6 var prevdate = moment(prevdate);
7 var now = now || moment().utc();
7 var now = now || moment().utc();
8
8
9 var show_short_version = show_short_version || false;
9 var show_short_version = show_short_version || false;
10 var show_suffix = show_suffix || true;
10 var show_suffix = show_suffix || true;
11 var short_format = short_format || false;
11 var short_format = short_format || false;
12
12
13 // alias for backward compat
14 var _ = function(s) {
15 if (_TM.hasOwnProperty(s)) {
16 return _TM[s];
17 }
18 return s
19 };
20
21 var ungettext = function (singular, plural, n) {
22 if (n === 1){
23 return _(singular)
24 }
25 return _(plural)
26 };
27
28 var _get_relative_delta = function(now, prevdate) {
13 var _get_relative_delta = function(now, prevdate) {
29
14
30 var duration = moment.duration(moment(now).diff(prevdate));
15 var duration = moment.duration(moment(now).diff(prevdate));
31 return {
16 return {
32 'year': duration.years(),
17 'year': duration.years(),
33 'month': duration.months(),
18 'month': duration.months(),
34 'day': duration.days(),
19 'day': duration.days(),
35 'hour': duration.hours(),
20 'hour': duration.hours(),
36 'minute': duration.minutes(),
21 'minute': duration.minutes(),
37 'second': duration.seconds()
22 'second': duration.seconds()
38 };
23 };
39
24
40 };
25 };
41
26
42 var _is_leap_year = function(year){
27 var _is_leap_year = function(year){
43 return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
28 return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
44 };
29 };
45
30
46 var get_month = function(prevdate) {
31 var get_month = function(prevdate) {
47 return prevdate.getMonth()
32 return prevdate.getMonth()
48 };
33 };
49
34
50 var get_year = function(prevdate) {
35 var get_year = function(prevdate) {
51 return prevdate.getYear()
36 return prevdate.getYear()
52 };
37 };
53
38
54 var order = ['year', 'month', 'day', 'hour', 'minute', 'second'];
39 var order = ['year', 'month', 'day', 'hour', 'minute', 'second'];
55 var deltas = {};
40 var deltas = {};
56 var future = false;
41 var future = false;
57
42
58 if (prevdate > now) {
43 if (prevdate > now) {
59 var now_old = now;
44 var now_old = now;
60 now = prevdate;
45 now = prevdate;
61 prevdate = now_old;
46 prevdate = now_old;
62 future = true;
47 future = true;
63 }
48 }
64 if (future) {
49 if (future) {
65 // ? remove microseconds, we don't have it in JS
50 // ? remove microseconds, we don't have it in JS
66 }
51 }
67
52
68 // Get date parts deltas
53 // Get date parts deltas
69 for (part in order) {
54 for (part in order) {
70 var part = order[part];
55 var part = order[part];
71 var rel_delta = _get_relative_delta(now, prevdate);
56 var rel_delta = _get_relative_delta(now, prevdate);
72 deltas[part] = rel_delta[part]
57 deltas[part] = rel_delta[part]
73 }
58 }
74
59
75 //# Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
60 //# Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
76 //# not 1 hour, -59 minutes and -59 seconds)
61 //# not 1 hour, -59 minutes and -59 seconds)
77 var offsets = [[5, 60], [4, 60], [3, 24]];
62 var offsets = [[5, 60], [4, 60], [3, 24]];
78 for (element in offsets) { //# seconds, minutes, hours
63 for (element in offsets) { //# seconds, minutes, hours
79 var element = offsets[element];
64 var element = offsets[element];
80 var num = element[0];
65 var num = element[0];
81 var length = element[1];
66 var length = element[1];
82
67
83 var part = order[num];
68 var part = order[num];
84 var carry_part = order[num - 1];
69 var carry_part = order[num - 1];
85
70
86 if (deltas[part] < 0){
71 if (deltas[part] < 0){
87 deltas[part] += length;
72 deltas[part] += length;
88 deltas[carry_part] -= 1
73 deltas[carry_part] -= 1
89 }
74 }
90
75
91 }
76 }
92
77
93 // # Same thing for days except that the increment depends on the (variable)
78 // # Same thing for days except that the increment depends on the (variable)
94 // # number of days in the month
79 // # number of days in the month
95 var month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
80 var month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
96 if (deltas['day'] < 0) {
81 if (deltas['day'] < 0) {
97 if (get_month(prevdate) == 2 && _is_leap_year(get_year(prevdate))) {
82 if (get_month(prevdate) == 2 && _is_leap_year(get_year(prevdate))) {
98 deltas['day'] += 29;
83 deltas['day'] += 29;
99 } else {
84 } else {
100 deltas['day'] += month_lengths[get_month(prevdate) - 1];
85 deltas['day'] += month_lengths[get_month(prevdate) - 1];
101 }
86 }
102
87
103 deltas['month'] -= 1
88 deltas['month'] -= 1
104 }
89 }
105
90
106 if (deltas['month'] < 0) {
91 if (deltas['month'] < 0) {
107 deltas['month'] += 12;
92 deltas['month'] += 12;
108 deltas['year'] -= 1;
93 deltas['year'] -= 1;
109 }
94 }
110
95
111 //# Format the result
96 //# Format the result
112 if (short_format) {
97 if (short_format) {
113 var fmt_funcs = {
98 var fmt_funcs = {
114 'year': function(d) {return '{0}y'.format(d)},
99 'year': function(d) {return '{0}y'.format(d)},
115 'month': function(d) {return '{0}m'.format(d)},
100 'month': function(d) {return '{0}m'.format(d)},
116 'day': function(d) {return '{0}d'.format(d)},
101 'day': function(d) {return '{0}d'.format(d)},
117 'hour': function(d) {return '{0}h'.format(d)},
102 'hour': function(d) {return '{0}h'.format(d)},
118 'minute': function(d) {return '{0}min'.format(d)},
103 'minute': function(d) {return '{0}min'.format(d)},
119 'second': function(d) {return '{0}sec'.format(d)}
104 'second': function(d) {return '{0}sec'.format(d)}
120 }
105 }
121
106
122 } else {
107 } else {
123 var fmt_funcs = {
108 var fmt_funcs = {
124 'year': function(d) {return ungettext('{0} year', '{0} years', d).format(d)},
109 'year': function(d) {return _ngettext('{0} year', '{0} years', d).format(d)},
125 'month': function(d) {return ungettext('{0} month', '{0} months', d).format(d)},
110 'month': function(d) {return _ngettext('{0} month', '{0} months', d).format(d)},
126 'day': function(d) {return ungettext('{0} day', '{0} days', d).format(d)},
111 'day': function(d) {return _ngettext('{0} day', '{0} days', d).format(d)},
127 'hour': function(d) {return ungettext('{0} hour', '{0} hours', d).format(d)},
112 'hour': function(d) {return _ngettext('{0} hour', '{0} hours', d).format(d)},
128 'minute': function(d) {return ungettext('{0} min', '{0} min', d).format(d)},
113 'minute': function(d) {return _ngettext('{0} min', '{0} min', d).format(d)},
129 'second': function(d) {return ungettext('{0} sec', '{0} sec', d).format(d)}
114 'second': function(d) {return _ngettext('{0} sec', '{0} sec', d).format(d)}
130 }
115 }
131
116
132 }
117 }
133 var i = 0;
118 var i = 0;
134 for (part in order){
119 for (part in order){
135 var part = order[part];
120 var part = order[part];
136 var value = deltas[part];
121 var value = deltas[part];
137 if (value !== 0) {
122 if (value !== 0) {
138
123
139 if (i < 5) {
124 if (i < 5) {
140 var sub_part = order[i + 1];
125 var sub_part = order[i + 1];
141 var sub_value = deltas[sub_part]
126 var sub_value = deltas[sub_part]
142 } else {
127 } else {
143 var sub_value = 0
128 var sub_value = 0
144 }
129 }
145 if (sub_value == 0 || show_short_version) {
130 if (sub_value == 0 || show_short_version) {
146 var _val = fmt_funcs[part](value);
131 var _val = fmt_funcs[part](value);
147 if (future) {
132 if (future) {
148 if (show_suffix) {
133 if (show_suffix) {
149 return _('in {0}').format(_val)
134 return _gettext('in {0}').format(_val)
150 } else {
135 } else {
151 return _val
136 return _val
152 }
137 }
153
138
154 }
139 }
155 else {
140 else {
156 if (show_suffix) {
141 if (show_suffix) {
157 return _('{0} ago').format(_val)
142 return _gettext('{0} ago').format(_val)
158 } else {
143 } else {
159 return _val
144 return _val
160 }
145 }
161 }
146 }
162 }
147 }
163
148
164 var val = fmt_funcs[part](value);
149 var val = fmt_funcs[part](value);
165 var val_detail = fmt_funcs[sub_part](sub_value);
150 var val_detail = fmt_funcs[sub_part](sub_value);
166 if (short_format) {
151 if (short_format) {
167 var datetime_tmpl = '{0}, {1}';
152 var datetime_tmpl = '{0}, {1}';
168 if (show_suffix) {
153 if (show_suffix) {
169 datetime_tmpl = _('{0}, {1} ago');
154 datetime_tmpl = _gettext('{0}, {1} ago');
170 if (future) {
155 if (future) {
171 datetime_tmpl = _('in {0}, {1}');
156 datetime_tmpl = _gettext('in {0}, {1}');
172 }
157 }
173 }
158 }
174 } else {
159 } else {
175 var datetime_tmpl = _('{0} and {1}');
160 var datetime_tmpl = _gettext('{0} and {1}');
176 if (show_suffix) {
161 if (show_suffix) {
177 datetime_tmpl = _('{0} and {1} ago');
162 datetime_tmpl = _gettext('{0} and {1} ago');
178 if (future) {
163 if (future) {
179 datetime_tmpl = _('in {0} and {1}')
164 datetime_tmpl = _gettext('in {0} and {1}')
180 }
165 }
181 }
166 }
182 }
167 }
183
168
184 return datetime_tmpl.format(val, val_detail)
169 return datetime_tmpl.format(val, val_detail)
185 }
170 }
186 i += 1;
171 i += 1;
187 }
172 }
188
173
189 return _('just now')
174 return _gettext('just now')
190
175
191 },
176 },
192 createTimeComponent: function(dateTime, text) {
177 createTimeComponent: function(dateTime, text) {
193 return '<time class="timeago tooltip" title="{1}" datetime="{0}+0000">{1}</time>'.format(dateTime, text);
178 return '<time class="timeago tooltip" title="{1}" datetime="{0}+0000">{1}</time>'.format(dateTime, text);
194 }
179 }
195 }
180 }
196 })();
181 })();
197
182
198
183
199 jQuery.timeago.settings.localeTitle = false;
184 jQuery.timeago.settings.localeTitle = false;
200
185
201 // auto refresh the components every Ns
186 // auto refresh the components every Ns
202 jQuery.timeago.settings.refreshMillis = templateContext.timeago.refresh_time;
187 jQuery.timeago.settings.refreshMillis = templateContext.timeago.refresh_time;
203
188
204 // Display original dates older than N days
189 // Display original dates older than N days
205 jQuery.timeago.settings.cutoff = templateContext.timeago.cutoff_limit;
190 jQuery.timeago.settings.cutoff = templateContext.timeago.cutoff_limit;
@@ -1,422 +1,413 b''
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 /**
19 /**
20 RhodeCode JS Files
20 RhodeCode JS Files
21 **/
21 **/
22
22
23 if (typeof console == "undefined" || typeof console.log == "undefined"){
23 if (typeof console == "undefined" || typeof console.log == "undefined"){
24 console = { log: function() {} }
24 console = { log: function() {} }
25 }
25 }
26
26
27
28 // alias for backward compat
29 var _tm = function(s) {
30 if (_TM.hasOwnProperty(s)) {
31 return _TM[s];
32 }
33 return s
34 };
35
36 // TODO: move the following function to submodules
27 // TODO: move the following function to submodules
37
28
38 /**
29 /**
39 * show more
30 * show more
40 */
31 */
41 var show_more_event = function(){
32 var show_more_event = function(){
42 $('table .show_more').click(function(e) {
33 $('table .show_more').click(function(e) {
43 var cid = e.target.id.substring(1);
34 var cid = e.target.id.substring(1);
44 var button = $(this);
35 var button = $(this);
45 if (button.hasClass('open')) {
36 if (button.hasClass('open')) {
46 $('#'+cid).hide();
37 $('#'+cid).hide();
47 button.removeClass('open');
38 button.removeClass('open');
48 } else {
39 } else {
49 $('#'+cid).show();
40 $('#'+cid).show();
50 button.addClass('open one');
41 button.addClass('open one');
51 }
42 }
52 });
43 });
53 };
44 };
54
45
55 var compare_radio_buttons = function(repo_name, compare_ref_type){
46 var compare_radio_buttons = function(repo_name, compare_ref_type){
56 $('#compare_action').on('click', function(e){
47 $('#compare_action').on('click', function(e){
57 e.preventDefault();
48 e.preventDefault();
58
49
59 var source = $('input[name=compare_source]:checked').val();
50 var source = $('input[name=compare_source]:checked').val();
60 var target = $('input[name=compare_target]:checked').val();
51 var target = $('input[name=compare_target]:checked').val();
61 if(source && target){
52 if(source && target){
62 var url_data = {
53 var url_data = {
63 repo_name: repo_name,
54 repo_name: repo_name,
64 source_ref: source,
55 source_ref: source,
65 source_ref_type: compare_ref_type,
56 source_ref_type: compare_ref_type,
66 target_ref: target,
57 target_ref: target,
67 target_ref_type: compare_ref_type,
58 target_ref_type: compare_ref_type,
68 merge: 1
59 merge: 1
69 };
60 };
70 window.location = pyroutes.url('compare_url', url_data);
61 window.location = pyroutes.url('compare_url', url_data);
71 }
62 }
72 });
63 });
73 $('.compare-radio-button').on('click', function(e){
64 $('.compare-radio-button').on('click', function(e){
74 var source = $('input[name=compare_source]:checked').val();
65 var source = $('input[name=compare_source]:checked').val();
75 var target = $('input[name=compare_target]:checked').val();
66 var target = $('input[name=compare_target]:checked').val();
76 if(source && target){
67 if(source && target){
77 $('#compare_action').removeAttr("disabled");
68 $('#compare_action').removeAttr("disabled");
78 $('#compare_action').removeClass("disabled");
69 $('#compare_action').removeClass("disabled");
79 }
70 }
80 })
71 })
81 };
72 };
82
73
83 var showRepoSize = function(target, repo_name, commit_id, callback) {
74 var showRepoSize = function(target, repo_name, commit_id, callback) {
84 var container = $('#' + target);
75 var container = $('#' + target);
85 var url = pyroutes.url('repo_stats',
76 var url = pyroutes.url('repo_stats',
86 {"repo_name": repo_name, "commit_id": commit_id});
77 {"repo_name": repo_name, "commit_id": commit_id});
87
78
88 if (!container.hasClass('loaded')) {
79 if (!container.hasClass('loaded')) {
89 $.ajax({url: url})
80 $.ajax({url: url})
90 .complete(function (data) {
81 .complete(function (data) {
91 var responseJSON = data.responseJSON;
82 var responseJSON = data.responseJSON;
92 container.addClass('loaded');
83 container.addClass('loaded');
93 container.html(responseJSON.size);
84 container.html(responseJSON.size);
94 callback(responseJSON.code_stats)
85 callback(responseJSON.code_stats)
95 })
86 })
96 .fail(function (data) {
87 .fail(function (data) {
97 console.log('failed to load repo stats');
88 console.log('failed to load repo stats');
98 });
89 });
99 }
90 }
100
91
101 };
92 };
102
93
103 var showRepoStats = function(target, data){
94 var showRepoStats = function(target, data){
104 var container = $('#' + target);
95 var container = $('#' + target);
105
96
106 if (container.hasClass('loaded')) {
97 if (container.hasClass('loaded')) {
107 return
98 return
108 }
99 }
109
100
110 var total = 0;
101 var total = 0;
111 var no_data = true;
102 var no_data = true;
112 var tbl = document.createElement('table');
103 var tbl = document.createElement('table');
113 tbl.setAttribute('class', 'trending_language_tbl');
104 tbl.setAttribute('class', 'trending_language_tbl');
114
105
115 $.each(data, function(key, val){
106 $.each(data, function(key, val){
116 total += val.count;
107 total += val.count;
117 });
108 });
118
109
119 var sortedStats = [];
110 var sortedStats = [];
120 for (var obj in data){
111 for (var obj in data){
121 sortedStats.push([obj, data[obj]])
112 sortedStats.push([obj, data[obj]])
122 }
113 }
123 var sortedData = sortedStats.sort(function (a, b) {
114 var sortedData = sortedStats.sort(function (a, b) {
124 return b[1].count - a[1].count
115 return b[1].count - a[1].count
125 });
116 });
126 var cnt = 0;
117 var cnt = 0;
127 $.each(sortedData, function(idx, val){
118 $.each(sortedData, function(idx, val){
128 cnt += 1;
119 cnt += 1;
129 no_data = false;
120 no_data = false;
130
121
131 var hide = cnt > 2;
122 var hide = cnt > 2;
132 var tr = document.createElement('tr');
123 var tr = document.createElement('tr');
133 if (hide) {
124 if (hide) {
134 tr.setAttribute('style', 'display:none');
125 tr.setAttribute('style', 'display:none');
135 tr.setAttribute('class', 'stats_hidden');
126 tr.setAttribute('class', 'stats_hidden');
136 }
127 }
137
128
138 var key = val[0];
129 var key = val[0];
139 var obj = {"desc": val[1].desc, "count": val[1].count};
130 var obj = {"desc": val[1].desc, "count": val[1].count};
140
131
141 var percentage = Math.round((obj.count / total * 100), 2);
132 var percentage = Math.round((obj.count / total * 100), 2);
142
133
143 var td1 = document.createElement('td');
134 var td1 = document.createElement('td');
144 td1.width = 300;
135 td1.width = 300;
145 var trending_language_label = document.createElement('div');
136 var trending_language_label = document.createElement('div');
146 trending_language_label.innerHTML = obj.desc + " (.{0})".format(key);
137 trending_language_label.innerHTML = obj.desc + " (.{0})".format(key);
147 td1.appendChild(trending_language_label);
138 td1.appendChild(trending_language_label);
148
139
149 var td2 = document.createElement('td');
140 var td2 = document.createElement('td');
150 var trending_language = document.createElement('div');
141 var trending_language = document.createElement('div');
151 var nr_files = obj.count +" "+ (obj.count === 1 ? _tm('file'): _tm('files'));
142 var nr_files = obj.count +" "+ _ngettext('file', 'files', obj.count);
152
143
153 trending_language.title = key + " " + nr_files;
144 trending_language.title = key + " " + nr_files;
154
145
155 trending_language.innerHTML = "<span>" + percentage + "% " + nr_files
146 trending_language.innerHTML = "<span>" + percentage + "% " + nr_files
156 + "</span><b>" + percentage + "% " + nr_files + "</b>";
147 + "</span><b>" + percentage + "% " + nr_files + "</b>";
157
148
158 trending_language.setAttribute("class", 'trending_language');
149 trending_language.setAttribute("class", 'trending_language');
159 $('b', trending_language)[0].style.width = percentage + "%";
150 $('b', trending_language)[0].style.width = percentage + "%";
160 td2.appendChild(trending_language);
151 td2.appendChild(trending_language);
161
152
162 tr.appendChild(td1);
153 tr.appendChild(td1);
163 tr.appendChild(td2);
154 tr.appendChild(td2);
164 tbl.appendChild(tr);
155 tbl.appendChild(tr);
165 if (cnt == 3) {
156 if (cnt == 3) {
166 var show_more = document.createElement('tr');
157 var show_more = document.createElement('tr');
167 var td = document.createElement('td');
158 var td = document.createElement('td');
168 lnk = document.createElement('a');
159 lnk = document.createElement('a');
169
160
170 lnk.href = '#';
161 lnk.href = '#';
171 lnk.innerHTML = _tm('Show more');
162 lnk.innerHTML = _ngettext('Show more');
172 lnk.id = 'code_stats_show_more';
163 lnk.id = 'code_stats_show_more';
173 td.appendChild(lnk);
164 td.appendChild(lnk);
174
165
175 show_more.appendChild(td);
166 show_more.appendChild(td);
176 show_more.appendChild(document.createElement('td'));
167 show_more.appendChild(document.createElement('td'));
177 tbl.appendChild(show_more);
168 tbl.appendChild(show_more);
178 }
169 }
179 });
170 });
180
171
181 $(container).html(tbl);
172 $(container).html(tbl);
182 $(container).addClass('loaded');
173 $(container).addClass('loaded');
183
174
184 $('#code_stats_show_more').on('click', function (e) {
175 $('#code_stats_show_more').on('click', function (e) {
185 e.preventDefault();
176 e.preventDefault();
186 $('.stats_hidden').each(function (idx) {
177 $('.stats_hidden').each(function (idx) {
187 $(this).css("display", "");
178 $(this).css("display", "");
188 });
179 });
189 $('#code_stats_show_more').hide();
180 $('#code_stats_show_more').hide();
190 });
181 });
191
182
192 };
183 };
193
184
194
185
195 // Toggle Collapsable Content
186 // Toggle Collapsable Content
196 function collapsableContent() {
187 function collapsableContent() {
197
188
198 $('.collapsable-content').not('.no-hide').hide();
189 $('.collapsable-content').not('.no-hide').hide();
199
190
200 $('.btn-collapse').unbind(); //in case we've been here before
191 $('.btn-collapse').unbind(); //in case we've been here before
201 $('.btn-collapse').click(function() {
192 $('.btn-collapse').click(function() {
202 var button = $(this);
193 var button = $(this);
203 var togglename = $(this).data("toggle");
194 var togglename = $(this).data("toggle");
204 $('.collapsable-content[data-toggle='+togglename+']').toggle();
195 $('.collapsable-content[data-toggle='+togglename+']').toggle();
205 if ($(this).html()=="Show Less")
196 if ($(this).html()=="Show Less")
206 $(this).html("Show More");
197 $(this).html("Show More");
207 else
198 else
208 $(this).html("Show Less");
199 $(this).html("Show Less");
209 });
200 });
210 };
201 };
211
202
212 var timeagoActivate = function() {
203 var timeagoActivate = function() {
213 $("time.timeago").timeago();
204 $("time.timeago").timeago();
214 };
205 };
215
206
216 // Formatting values in a Select2 dropdown of commit references
207 // Formatting values in a Select2 dropdown of commit references
217 var formatSelect2SelectionRefs = function(commit_ref){
208 var formatSelect2SelectionRefs = function(commit_ref){
218 var tmpl = '';
209 var tmpl = '';
219 if (!commit_ref.text || commit_ref.type === 'sha'){
210 if (!commit_ref.text || commit_ref.type === 'sha'){
220 return commit_ref.text;
211 return commit_ref.text;
221 }
212 }
222 if (commit_ref.type === 'branch'){
213 if (commit_ref.type === 'branch'){
223 tmpl = tmpl.concat('<i class="icon-branch"></i> ');
214 tmpl = tmpl.concat('<i class="icon-branch"></i> ');
224 } else if (commit_ref.type === 'tag'){
215 } else if (commit_ref.type === 'tag'){
225 tmpl = tmpl.concat('<i class="icon-tag"></i> ');
216 tmpl = tmpl.concat('<i class="icon-tag"></i> ');
226 } else if (commit_ref.type === 'book'){
217 } else if (commit_ref.type === 'book'){
227 tmpl = tmpl.concat('<i class="icon-bookmark"></i> ');
218 tmpl = tmpl.concat('<i class="icon-bookmark"></i> ');
228 }
219 }
229 return tmpl.concat(commit_ref.text);
220 return tmpl.concat(commit_ref.text);
230 };
221 };
231
222
232 // takes a given html element and scrolls it down offset pixels
223 // takes a given html element and scrolls it down offset pixels
233 function offsetScroll(element, offset){
224 function offsetScroll(element, offset){
234 setTimeout(function(){
225 setTimeout(function(){
235 console.log(element);
226 console.log(element);
236 var location = element.offset().top;
227 var location = element.offset().top;
237 // some browsers use body, some use html
228 // some browsers use body, some use html
238 $('html, body').animate({ scrollTop: (location - offset) });
229 $('html, body').animate({ scrollTop: (location - offset) });
239 }, 100);
230 }, 100);
240 }
231 }
241
232
242 /**
233 /**
243 * global hooks after DOM is loaded
234 * global hooks after DOM is loaded
244 */
235 */
245 $(document).ready(function() {
236 $(document).ready(function() {
246 firefoxAnchorFix();
237 firefoxAnchorFix();
247
238
248 $('.navigation a.menulink').on('click', function(e){
239 $('.navigation a.menulink').on('click', function(e){
249 var menuitem = $(this).parent('li');
240 var menuitem = $(this).parent('li');
250 if (menuitem.hasClass('open')) {
241 if (menuitem.hasClass('open')) {
251 menuitem.removeClass('open');
242 menuitem.removeClass('open');
252 } else {
243 } else {
253 menuitem.addClass('open');
244 menuitem.addClass('open');
254 $(document).on('click', function(event) {
245 $(document).on('click', function(event) {
255 if (!$(event.target).closest(menuitem).length) {
246 if (!$(event.target).closest(menuitem).length) {
256 menuitem.removeClass('open');
247 menuitem.removeClass('open');
257 }
248 }
258 });
249 });
259 }
250 }
260 });
251 });
261 // Add tooltips
252 // Add tooltips
262 $('tr.line .lineno a').attr("title","Click to select line").addClass('tooltip');
253 $('tr.line .lineno a').attr("title","Click to select line").addClass('tooltip');
263 $('tr.line .add-comment-line a').attr("title","Click to comment").addClass('tooltip');
254 $('tr.line .add-comment-line a').attr("title","Click to comment").addClass('tooltip');
264
255
265 // Set colors and styles
256 // Set colors and styles
266 $('tr.line .lineno a').hover(
257 $('tr.line .lineno a').hover(
267 function(){
258 function(){
268 $(this).parents('tr.line').addClass('hover');
259 $(this).parents('tr.line').addClass('hover');
269 }, function(){
260 }, function(){
270 $(this).parents('tr.line').removeClass('hover');
261 $(this).parents('tr.line').removeClass('hover');
271 }
262 }
272 );
263 );
273
264
274 $('tr.line .lineno a').click(
265 $('tr.line .lineno a').click(
275 function(){
266 function(){
276 if ($(this).text() != ""){
267 if ($(this).text() != ""){
277 $('tr.line').removeClass('selected');
268 $('tr.line').removeClass('selected');
278 $(this).parents("tr.line").addClass('selected');
269 $(this).parents("tr.line").addClass('selected');
279
270
280 // Replace URL without jumping to it if browser supports.
271 // Replace URL without jumping to it if browser supports.
281 // Default otherwise
272 // Default otherwise
282 if (history.pushState) {
273 if (history.pushState) {
283 var new_location = location.href
274 var new_location = location.href
284 if (location.hash){
275 if (location.hash){
285 new_location = new_location.replace(location.hash, "");
276 new_location = new_location.replace(location.hash, "");
286 }
277 }
287
278
288 // Make new anchor url
279 // Make new anchor url
289 var new_location = new_location+$(this).attr('href');
280 var new_location = new_location+$(this).attr('href');
290 history.pushState(true, document.title, new_location);
281 history.pushState(true, document.title, new_location);
291
282
292 return false;
283 return false;
293 }
284 }
294 }
285 }
295 }
286 }
296 );
287 );
297
288
298 $('tr.line .add-comment-line a').hover(
289 $('tr.line .add-comment-line a').hover(
299 function(){
290 function(){
300 $(this).parents('tr.line').addClass('commenting');
291 $(this).parents('tr.line').addClass('commenting');
301 }, function(){
292 }, function(){
302 $(this).parents('tr.line').removeClass('commenting');
293 $(this).parents('tr.line').removeClass('commenting');
303 }
294 }
304 );
295 );
305
296
306 $('tr.line .add-comment-line a').on('click', function(e){
297 $('tr.line .add-comment-line a').on('click', function(e){
307 var tr = $(e.currentTarget).parents('tr.line')[0];
298 var tr = $(e.currentTarget).parents('tr.line')[0];
308 injectInlineForm(tr);
299 injectInlineForm(tr);
309 return false;
300 return false;
310 });
301 });
311
302
312
303
313 $('.collapse_file').on('click', function(e) {
304 $('.collapse_file').on('click', function(e) {
314 e.stopPropagation();
305 e.stopPropagation();
315 if ($(e.target).is('a')) { return; }
306 if ($(e.target).is('a')) { return; }
316 var node = $(e.delegateTarget).first();
307 var node = $(e.delegateTarget).first();
317 var icon = $($(node.children().first()).children().first());
308 var icon = $($(node.children().first()).children().first());
318 var id = node.attr('fid');
309 var id = node.attr('fid');
319 var target = $('#'+id);
310 var target = $('#'+id);
320 var tr = $('#tr_'+id);
311 var tr = $('#tr_'+id);
321 var diff = $('#diff_'+id);
312 var diff = $('#diff_'+id);
322 if(node.hasClass('expand_file')){
313 if(node.hasClass('expand_file')){
323 node.removeClass('expand_file');
314 node.removeClass('expand_file');
324 icon.removeClass('expand_file_icon');
315 icon.removeClass('expand_file_icon');
325 node.addClass('collapse_file');
316 node.addClass('collapse_file');
326 icon.addClass('collapse_file_icon');
317 icon.addClass('collapse_file_icon');
327 diff.show();
318 diff.show();
328 tr.show();
319 tr.show();
329 target.show();
320 target.show();
330 } else {
321 } else {
331 node.removeClass('collapse_file');
322 node.removeClass('collapse_file');
332 icon.removeClass('collapse_file_icon');
323 icon.removeClass('collapse_file_icon');
333 node.addClass('expand_file');
324 node.addClass('expand_file');
334 icon.addClass('expand_file_icon');
325 icon.addClass('expand_file_icon');
335 diff.hide();
326 diff.hide();
336 tr.hide();
327 tr.hide();
337 target.hide();
328 target.hide();
338 }
329 }
339 });
330 });
340
331
341 $('#expand_all_files').click(function() {
332 $('#expand_all_files').click(function() {
342 $('.expand_file').each(function() {
333 $('.expand_file').each(function() {
343 var node = $(this);
334 var node = $(this);
344 var icon = $($(node.children().first()).children().first());
335 var icon = $($(node.children().first()).children().first());
345 var id = $(this).attr('fid');
336 var id = $(this).attr('fid');
346 var target = $('#'+id);
337 var target = $('#'+id);
347 var tr = $('#tr_'+id);
338 var tr = $('#tr_'+id);
348 var diff = $('#diff_'+id);
339 var diff = $('#diff_'+id);
349 node.removeClass('expand_file');
340 node.removeClass('expand_file');
350 icon.removeClass('expand_file_icon');
341 icon.removeClass('expand_file_icon');
351 node.addClass('collapse_file');
342 node.addClass('collapse_file');
352 icon.addClass('collapse_file_icon');
343 icon.addClass('collapse_file_icon');
353 diff.show();
344 diff.show();
354 tr.show();
345 tr.show();
355 target.show();
346 target.show();
356 });
347 });
357 });
348 });
358
349
359 $('#collapse_all_files').click(function() {
350 $('#collapse_all_files').click(function() {
360 $('.collapse_file').each(function() {
351 $('.collapse_file').each(function() {
361 var node = $(this);
352 var node = $(this);
362 var icon = $($(node.children().first()).children().first());
353 var icon = $($(node.children().first()).children().first());
363 var id = $(this).attr('fid');
354 var id = $(this).attr('fid');
364 var target = $('#'+id);
355 var target = $('#'+id);
365 var tr = $('#tr_'+id);
356 var tr = $('#tr_'+id);
366 var diff = $('#diff_'+id);
357 var diff = $('#diff_'+id);
367 node.removeClass('collapse_file');
358 node.removeClass('collapse_file');
368 icon.removeClass('collapse_file_icon');
359 icon.removeClass('collapse_file_icon');
369 node.addClass('expand_file');
360 node.addClass('expand_file');
370 icon.addClass('expand_file_icon');
361 icon.addClass('expand_file_icon');
371 diff.hide();
362 diff.hide();
372 tr.hide();
363 tr.hide();
373 target.hide();
364 target.hide();
374 });
365 });
375 });
366 });
376
367
377 // Mouse over behavior for comments and line selection
368 // Mouse over behavior for comments and line selection
378
369
379 // Select the line that comes from the url anchor
370 // Select the line that comes from the url anchor
380 // At the time of development, Chrome didn't seem to support jquery's :target
371 // At the time of development, Chrome didn't seem to support jquery's :target
381 // element, so I had to scroll manually
372 // element, so I had to scroll manually
382 if (location.hash) {
373 if (location.hash) {
383 var splitIx = location.hash.indexOf('/?/');
374 var splitIx = location.hash.indexOf('/?/');
384 if (splitIx !== -1){
375 if (splitIx !== -1){
385 var loc = location.hash.slice(0, splitIx);
376 var loc = location.hash.slice(0, splitIx);
386 var remainder = location.hash.slice(splitIx + 2);
377 var remainder = location.hash.slice(splitIx + 2);
387 }
378 }
388 else{
379 else{
389 var loc = location.hash;
380 var loc = location.hash;
390 var remainder = null;
381 var remainder = null;
391 }
382 }
392 if (loc.length > 1){
383 if (loc.length > 1){
393 var lineno = $(loc+'.lineno');
384 var lineno = $(loc+'.lineno');
394 if (lineno.length > 0){
385 if (lineno.length > 0){
395 var tr = lineno.parents('tr.line');
386 var tr = lineno.parents('tr.line');
396 tr.addClass('selected');
387 tr.addClass('selected');
397
388
398 // once we scrolled into our line, trigger chat app
389 // once we scrolled into our line, trigger chat app
399 if (remainder){
390 if (remainder){
400 tr.find('.add-comment-line a').trigger( "click" );
391 tr.find('.add-comment-line a').trigger( "click" );
401 setTimeout(function(){
392 setTimeout(function(){
402 var nextNode = $(tr).next();
393 var nextNode = $(tr).next();
403 if(nextNode.hasClass('inline-comments')){
394 if(nextNode.hasClass('inline-comments')){
404 nextNode.next().find('.switch-to-chat').trigger( "click" );
395 nextNode.next().find('.switch-to-chat').trigger( "click" );
405 }
396 }
406 else{
397 else{
407 nextNode.find('.switch-to-chat').trigger( "click" );
398 nextNode.find('.switch-to-chat').trigger( "click" );
408 }
399 }
409 // trigger scroll into, later so all elements are already loaded
400 // trigger scroll into, later so all elements are already loaded
410 tr[0].scrollIntoView();
401 tr[0].scrollIntoView();
411 }, 250);
402 }, 250);
412
403
413 }
404 }
414 else{
405 else{
415 tr[0].scrollIntoView();
406 tr[0].scrollIntoView();
416 }
407 }
417 }
408 }
418 }
409 }
419 };
410 };
420
411
421 collapsableContent();
412 collapsableContent();
422 });
413 });
@@ -1,530 +1,530 b''
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 /**
19 /**
20 * Code Mirror
20 * Code Mirror
21 */
21 */
22 // global code-mirror logger;, to enable run
22 // global code-mirror logger;, to enable run
23 // Logger.get('CodeMirror').setLevel(Logger.DEBUG)
23 // Logger.get('CodeMirror').setLevel(Logger.DEBUG)
24
24
25 cmLog = Logger.get('CodeMirror');
25 cmLog = Logger.get('CodeMirror');
26 cmLog.setLevel(Logger.OFF);
26 cmLog.setLevel(Logger.OFF);
27
27
28
28
29 //global cache for inline forms
29 //global cache for inline forms
30 var userHintsCache = {};
30 var userHintsCache = {};
31
31
32
32
33 var initCodeMirror = function(textAreadId, resetUrl, focus, options) {
33 var initCodeMirror = function(textAreadId, resetUrl, focus, options) {
34 var ta = $('#' + textAreadId).get(0);
34 var ta = $('#' + textAreadId).get(0);
35 if (focus === undefined) {
35 if (focus === undefined) {
36 focus = true;
36 focus = true;
37 }
37 }
38
38
39 // default options
39 // default options
40 var codeMirrorOptions = {
40 var codeMirrorOptions = {
41 mode: "null",
41 mode: "null",
42 lineNumbers: true,
42 lineNumbers: true,
43 indentUnit: 4,
43 indentUnit: 4,
44 autofocus: focus
44 autofocus: focus
45 };
45 };
46
46
47 if (options !== undefined) {
47 if (options !== undefined) {
48 // extend with custom options
48 // extend with custom options
49 codeMirrorOptions = $.extend(true, codeMirrorOptions, options);
49 codeMirrorOptions = $.extend(true, codeMirrorOptions, options);
50 }
50 }
51
51
52 var myCodeMirror = CodeMirror.fromTextArea(ta, codeMirrorOptions);
52 var myCodeMirror = CodeMirror.fromTextArea(ta, codeMirrorOptions);
53
53
54 $('#reset').on('click', function(e) {
54 $('#reset').on('click', function(e) {
55 window.location = resetUrl;
55 window.location = resetUrl;
56 });
56 });
57
57
58 return myCodeMirror;
58 return myCodeMirror;
59 };
59 };
60
60
61 var initCommentBoxCodeMirror = function(textAreaId, triggerActions){
61 var initCommentBoxCodeMirror = function(textAreaId, triggerActions){
62 var initialHeight = 100;
62 var initialHeight = 100;
63
63
64 // global timer, used to cancel async loading
64 // global timer, used to cancel async loading
65 var loadUserHintTimer;
65 var loadUserHintTimer;
66
66
67 if (typeof userHintsCache === "undefined") {
67 if (typeof userHintsCache === "undefined") {
68 userHintsCache = {};
68 userHintsCache = {};
69 cmLog.debug('Init empty cache for mentions');
69 cmLog.debug('Init empty cache for mentions');
70 }
70 }
71 if (!$(textAreaId).get(0)) {
71 if (!$(textAreaId).get(0)) {
72 cmLog.debug('Element for textarea not found', textAreaId);
72 cmLog.debug('Element for textarea not found', textAreaId);
73 return;
73 return;
74 }
74 }
75 var escapeRegExChars = function(value) {
75 var escapeRegExChars = function(value) {
76 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
76 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
77 };
77 };
78 /**
78 /**
79 * Load hints from external source returns an array of objects in a format
79 * Load hints from external source returns an array of objects in a format
80 * that hinting lib requires
80 * that hinting lib requires
81 * @returns {Array}
81 * @returns {Array}
82 */
82 */
83 var loadUserHints = function(query, triggerHints) {
83 var loadUserHints = function(query, triggerHints) {
84 cmLog.debug('Loading mentions users via AJAX');
84 cmLog.debug('Loading mentions users via AJAX');
85 var _users = [];
85 var _users = [];
86 $.ajax({
86 $.ajax({
87 type: 'GET',
87 type: 'GET',
88 data: {query: query},
88 data: {query: query},
89 url: pyroutes.url('user_autocomplete_data'),
89 url: pyroutes.url('user_autocomplete_data'),
90 headers: {'X-PARTIAL-XHR': true},
90 headers: {'X-PARTIAL-XHR': true},
91 async: true
91 async: true
92 })
92 })
93 .done(function(data) {
93 .done(function(data) {
94 var tmpl = '<img class="gravatar" src="{0}"/>{1}';
94 var tmpl = '<img class="gravatar" src="{0}"/>{1}';
95 $.each(data.suggestions, function(i) {
95 $.each(data.suggestions, function(i) {
96 var userObj = data.suggestions[i];
96 var userObj = data.suggestions[i];
97
97
98 if (userObj.username !== "default") {
98 if (userObj.username !== "default") {
99 _users.push({
99 _users.push({
100 text: userObj.username + " ",
100 text: userObj.username + " ",
101 org_text: userObj.username,
101 org_text: userObj.username,
102 displayText: userObj.value_display, // search that field
102 displayText: userObj.value_display, // search that field
103 // internal caches
103 // internal caches
104 _icon_link: userObj.icon_link,
104 _icon_link: userObj.icon_link,
105 _text: userObj.value_display,
105 _text: userObj.value_display,
106
106
107 render: function(elt, data, completion) {
107 render: function(elt, data, completion) {
108 var el = document.createElement('div');
108 var el = document.createElement('div');
109 el.className = "CodeMirror-hint-entry";
109 el.className = "CodeMirror-hint-entry";
110 el.innerHTML = tmpl.format(
110 el.innerHTML = tmpl.format(
111 completion._icon_link, completion._text);
111 completion._icon_link, completion._text);
112 elt.appendChild(el);
112 elt.appendChild(el);
113 }
113 }
114 });
114 });
115 }
115 }
116 });
116 });
117 cmLog.debug('Mention users loaded');
117 cmLog.debug('Mention users loaded');
118 // set to global cache
118 // set to global cache
119 userHintsCache[query] = _users;
119 userHintsCache[query] = _users;
120 triggerHints(userHintsCache[query]);
120 triggerHints(userHintsCache[query]);
121 })
121 })
122 .fail(function(data, textStatus, xhr) {
122 .fail(function(data, textStatus, xhr) {
123 alert("error processing request: " + textStatus);
123 alert("error processing request: " + textStatus);
124 });
124 });
125 };
125 };
126
126
127 /**
127 /**
128 * filters the results based on the current context
128 * filters the results based on the current context
129 * @param users
129 * @param users
130 * @param context
130 * @param context
131 * @returns {Array}
131 * @returns {Array}
132 */
132 */
133 var filterUsers = function(users, context) {
133 var filterUsers = function(users, context) {
134 var MAX_LIMIT = 10;
134 var MAX_LIMIT = 10;
135 var filtered_users = [];
135 var filtered_users = [];
136 var curWord = context.string;
136 var curWord = context.string;
137
137
138 cmLog.debug('Filtering users based on query:', curWord);
138 cmLog.debug('Filtering users based on query:', curWord);
139 $.each(users, function(i) {
139 $.each(users, function(i) {
140 var match = users[i];
140 var match = users[i];
141 var searchText = match.displayText;
141 var searchText = match.displayText;
142
142
143 if (!curWord ||
143 if (!curWord ||
144 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
144 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
145 // reset state
145 // reset state
146 match._text = match.displayText;
146 match._text = match.displayText;
147 if (curWord) {
147 if (curWord) {
148 // do highlighting
148 // do highlighting
149 var pattern = '(' + escapeRegExChars(curWord) + ')';
149 var pattern = '(' + escapeRegExChars(curWord) + ')';
150 match._text = searchText.replace(
150 match._text = searchText.replace(
151 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
151 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
152 }
152 }
153
153
154 filtered_users.push(match);
154 filtered_users.push(match);
155 }
155 }
156 // to not return to many results, use limit of filtered results
156 // to not return to many results, use limit of filtered results
157 if (filtered_users.length > MAX_LIMIT) {
157 if (filtered_users.length > MAX_LIMIT) {
158 return false;
158 return false;
159 }
159 }
160 });
160 });
161
161
162 return filtered_users;
162 return filtered_users;
163 };
163 };
164
164
165 /**
165 /**
166 * Filter action based on typed in text
166 * Filter action based on typed in text
167 * @param actions
167 * @param actions
168 * @param context
168 * @param context
169 * @returns {Array}
169 * @returns {Array}
170 */
170 */
171
171
172 var filterActions = function(actions, context){
172 var filterActions = function(actions, context){
173 var MAX_LIMIT = 10;
173 var MAX_LIMIT = 10;
174 var filtered_actions= [];
174 var filtered_actions= [];
175 var curWord = context.string;
175 var curWord = context.string;
176
176
177 cmLog.debug('Filtering actions based on query:', curWord);
177 cmLog.debug('Filtering actions based on query:', curWord);
178 $.each(actions, function(i) {
178 $.each(actions, function(i) {
179 var match = actions[i];
179 var match = actions[i];
180 var searchText = match.displayText;
180 var searchText = match.displayText;
181
181
182 if (!curWord ||
182 if (!curWord ||
183 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
183 searchText.toLowerCase().lastIndexOf(curWord) !== -1) {
184 // reset state
184 // reset state
185 match._text = match.displayText;
185 match._text = match.displayText;
186 if (curWord) {
186 if (curWord) {
187 // do highlighting
187 // do highlighting
188 var pattern = '(' + escapeRegExChars(curWord) + ')';
188 var pattern = '(' + escapeRegExChars(curWord) + ')';
189 match._text = searchText.replace(
189 match._text = searchText.replace(
190 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
190 new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
191 }
191 }
192
192
193 filtered_actions.push(match);
193 filtered_actions.push(match);
194 }
194 }
195 // to not return to many results, use limit of filtered results
195 // to not return to many results, use limit of filtered results
196 if (filtered_actions.length > MAX_LIMIT) {
196 if (filtered_actions.length > MAX_LIMIT) {
197 return false;
197 return false;
198 }
198 }
199 });
199 });
200 return filtered_actions;
200 return filtered_actions;
201 };
201 };
202
202
203 var completeAfter = function(cm, pred) {
203 var completeAfter = function(cm, pred) {
204 var options = {
204 var options = {
205 completeSingle: false,
205 completeSingle: false,
206 async: true,
206 async: true,
207 closeOnUnfocus: true
207 closeOnUnfocus: true
208 };
208 };
209 var cur = cm.getCursor();
209 var cur = cm.getCursor();
210 setTimeout(function() {
210 setTimeout(function() {
211 if (!cm.state.completionActive) {
211 if (!cm.state.completionActive) {
212 cmLog.debug('Trigger mentions hinting');
212 cmLog.debug('Trigger mentions hinting');
213 CodeMirror.showHint(cm, CodeMirror.hint.mentions, options);
213 CodeMirror.showHint(cm, CodeMirror.hint.mentions, options);
214 }
214 }
215 }, 100);
215 }, 100);
216
216
217 // tell CodeMirror we didn't handle the key
217 // tell CodeMirror we didn't handle the key
218 // trick to trigger on a char but still complete it
218 // trick to trigger on a char but still complete it
219 return CodeMirror.Pass;
219 return CodeMirror.Pass;
220 };
220 };
221
221
222 var submitForm = function(cm, pred) {
222 var submitForm = function(cm, pred) {
223 $(cm.display.input.textarea.form).submit();
223 $(cm.display.input.textarea.form).submit();
224 return CodeMirror.Pass;
224 return CodeMirror.Pass;
225 };
225 };
226
226
227 var completeActions = function(cm, pred) {
227 var completeActions = function(cm, pred) {
228 var cur = cm.getCursor();
228 var cur = cm.getCursor();
229 var options = {
229 var options = {
230 closeOnUnfocus: true
230 closeOnUnfocus: true
231 };
231 };
232 setTimeout(function() {
232 setTimeout(function() {
233 if (!cm.state.completionActive) {
233 if (!cm.state.completionActive) {
234 cmLog.debug('Trigger actions hinting');
234 cmLog.debug('Trigger actions hinting');
235 CodeMirror.showHint(cm, CodeMirror.hint.actions, options);
235 CodeMirror.showHint(cm, CodeMirror.hint.actions, options);
236 }
236 }
237 }, 100);
237 }, 100);
238 };
238 };
239
239
240 var extraKeys = {
240 var extraKeys = {
241 "'@'": completeAfter,
241 "'@'": completeAfter,
242 Tab: function(cm) {
242 Tab: function(cm) {
243 // space indent instead of TABS
243 // space indent instead of TABS
244 var spaces = new Array(cm.getOption("indentUnit") + 1).join(" ");
244 var spaces = new Array(cm.getOption("indentUnit") + 1).join(" ");
245 cm.replaceSelection(spaces);
245 cm.replaceSelection(spaces);
246 }
246 }
247 };
247 };
248 // submit form on Meta-Enter
248 // submit form on Meta-Enter
249 if (OSType === "mac") {
249 if (OSType === "mac") {
250 extraKeys["Cmd-Enter"] = submitForm;
250 extraKeys["Cmd-Enter"] = submitForm;
251 }
251 }
252 else {
252 else {
253 extraKeys["Ctrl-Enter"] = submitForm;
253 extraKeys["Ctrl-Enter"] = submitForm;
254 }
254 }
255
255
256 if (triggerActions) {
256 if (triggerActions) {
257 extraKeys["Ctrl-Space"] = completeActions;
257 extraKeys["Ctrl-Space"] = completeActions;
258 }
258 }
259
259
260 var cm = CodeMirror.fromTextArea($(textAreaId).get(0), {
260 var cm = CodeMirror.fromTextArea($(textAreaId).get(0), {
261 lineNumbers: false,
261 lineNumbers: false,
262 indentUnit: 4,
262 indentUnit: 4,
263 viewportMargin: 30,
263 viewportMargin: 30,
264 // this is a trick to trigger some logic behind codemirror placeholder
264 // this is a trick to trigger some logic behind codemirror placeholder
265 // it influences styling and behaviour.
265 // it influences styling and behaviour.
266 placeholder: " ",
266 placeholder: " ",
267 extraKeys: extraKeys,
267 extraKeys: extraKeys,
268 lineWrapping: true
268 lineWrapping: true
269 });
269 });
270
270
271 cm.setSize(null, initialHeight);
271 cm.setSize(null, initialHeight);
272 cm.setOption("mode", DEFAULT_RENDERER);
272 cm.setOption("mode", DEFAULT_RENDERER);
273 CodeMirror.autoLoadMode(cm, DEFAULT_RENDERER); // load rst or markdown mode
273 CodeMirror.autoLoadMode(cm, DEFAULT_RENDERER); // load rst or markdown mode
274 cmLog.debug('Loading codemirror mode', DEFAULT_RENDERER);
274 cmLog.debug('Loading codemirror mode', DEFAULT_RENDERER);
275 // start listening on changes to make auto-expanded editor
275 // start listening on changes to make auto-expanded editor
276 cm.on("change", function(self) {
276 cm.on("change", function(self) {
277 var height = initialHeight;
277 var height = initialHeight;
278 var lines = self.lineCount();
278 var lines = self.lineCount();
279 if ( lines > 6 && lines < 20) {
279 if ( lines > 6 && lines < 20) {
280 height = "auto";
280 height = "auto";
281 }
281 }
282 else if (lines >= 20){
282 else if (lines >= 20){
283 zheight = 20*15;
283 zheight = 20*15;
284 }
284 }
285 self.setSize(null, height);
285 self.setSize(null, height);
286 });
286 });
287
287
288 var mentionHint = function(editor, callback, options) {
288 var mentionHint = function(editor, callback, options) {
289 var cur = editor.getCursor();
289 var cur = editor.getCursor();
290 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
290 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
291
291
292 // match on @ +1char
292 // match on @ +1char
293 var tokenMatch = new RegExp(
293 var tokenMatch = new RegExp(
294 '(^@| @)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*)$').exec(curLine);
294 '(^@| @)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*)$').exec(curLine);
295
295
296 var tokenStr = '';
296 var tokenStr = '';
297 if (tokenMatch !== null && tokenMatch.length > 0){
297 if (tokenMatch !== null && tokenMatch.length > 0){
298 tokenStr = tokenMatch[0].strip();
298 tokenStr = tokenMatch[0].strip();
299 } else {
299 } else {
300 // skip if we didn't match our token
300 // skip if we didn't match our token
301 return;
301 return;
302 }
302 }
303
303
304 var context = {
304 var context = {
305 start: (cur.ch - tokenStr.length) + 1,
305 start: (cur.ch - tokenStr.length) + 1,
306 end: cur.ch,
306 end: cur.ch,
307 string: tokenStr.slice(1),
307 string: tokenStr.slice(1),
308 type: null
308 type: null
309 };
309 };
310
310
311 // case when we put the @sign in fron of a string,
311 // case when we put the @sign in fron of a string,
312 // eg <@ we put it here>sometext then we need to prepend to text
312 // eg <@ we put it here>sometext then we need to prepend to text
313 if (context.end > cur.ch) {
313 if (context.end > cur.ch) {
314 context.start = context.start + 1; // we add to the @ sign
314 context.start = context.start + 1; // we add to the @ sign
315 context.end = cur.ch; // don't eat front part just append
315 context.end = cur.ch; // don't eat front part just append
316 context.string = context.string.slice(1, cur.ch - context.start);
316 context.string = context.string.slice(1, cur.ch - context.start);
317 }
317 }
318
318
319 cmLog.debug('Mention context', context);
319 cmLog.debug('Mention context', context);
320
320
321 var triggerHints = function(userHints){
321 var triggerHints = function(userHints){
322 return callback({
322 return callback({
323 list: filterUsers(userHints, context),
323 list: filterUsers(userHints, context),
324 from: CodeMirror.Pos(cur.line, context.start),
324 from: CodeMirror.Pos(cur.line, context.start),
325 to: CodeMirror.Pos(cur.line, context.end)
325 to: CodeMirror.Pos(cur.line, context.end)
326 });
326 });
327 };
327 };
328
328
329 var queryBasedHintsCache = undefined;
329 var queryBasedHintsCache = undefined;
330 // if we have something in the cache, try to fetch the query based cache
330 // if we have something in the cache, try to fetch the query based cache
331 if (userHintsCache !== {}){
331 if (userHintsCache !== {}){
332 queryBasedHintsCache = userHintsCache[context.string];
332 queryBasedHintsCache = userHintsCache[context.string];
333 }
333 }
334
334
335 if (queryBasedHintsCache !== undefined) {
335 if (queryBasedHintsCache !== undefined) {
336 cmLog.debug('Users loaded from cache');
336 cmLog.debug('Users loaded from cache');
337 triggerHints(queryBasedHintsCache);
337 triggerHints(queryBasedHintsCache);
338 } else {
338 } else {
339 // this takes care for async loading, and then displaying results
339 // this takes care for async loading, and then displaying results
340 // and also propagates the userHintsCache
340 // and also propagates the userHintsCache
341 window.clearTimeout(loadUserHintTimer);
341 window.clearTimeout(loadUserHintTimer);
342 loadUserHintTimer = setTimeout(function() {
342 loadUserHintTimer = setTimeout(function() {
343 loadUserHints(context.string, triggerHints);
343 loadUserHints(context.string, triggerHints);
344 }, 300);
344 }, 300);
345 }
345 }
346 };
346 };
347
347
348 var actionHint = function(editor, options) {
348 var actionHint = function(editor, options) {
349 var cur = editor.getCursor();
349 var cur = editor.getCursor();
350 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
350 var curLine = editor.getLine(cur.line).slice(0, cur.ch);
351
351
352 var tokenMatch = new RegExp('[a-zA-Z]{1}[a-zA-Z]*$').exec(curLine);
352 var tokenMatch = new RegExp('[a-zA-Z]{1}[a-zA-Z]*$').exec(curLine);
353
353
354 var tokenStr = '';
354 var tokenStr = '';
355 if (tokenMatch !== null && tokenMatch.length > 0){
355 if (tokenMatch !== null && tokenMatch.length > 0){
356 tokenStr = tokenMatch[0].strip();
356 tokenStr = tokenMatch[0].strip();
357 }
357 }
358
358
359 var context = {
359 var context = {
360 start: cur.ch - tokenStr.length,
360 start: cur.ch - tokenStr.length,
361 end: cur.ch,
361 end: cur.ch,
362 string: tokenStr,
362 string: tokenStr,
363 type: null
363 type: null
364 };
364 };
365
365
366 var actions = [
366 var actions = [
367 {
367 {
368 text: "approve",
368 text: "approve",
369 displayText: _TM['Set status to Approved'],
369 displayText: _gettext('Set status to Approved'),
370 hint: function(CodeMirror, data, completion) {
370 hint: function(CodeMirror, data, completion) {
371 CodeMirror.replaceRange("", completion.from || data.from,
371 CodeMirror.replaceRange("", completion.from || data.from,
372 completion.to || data.to, "complete");
372 completion.to || data.to, "complete");
373 $('#change_status').select2("val", 'approved').trigger('change');
373 $('#change_status').select2("val", 'approved').trigger('change');
374 },
374 },
375 render: function(elt, data, completion) {
375 render: function(elt, data, completion) {
376 var el = document.createElement('div');
376 var el = document.createElement('div');
377 el.className = "flag_status flag_status_comment_box approved pull-left";
377 el.className = "flag_status flag_status_comment_box approved pull-left";
378 elt.appendChild(el);
378 elt.appendChild(el);
379
379
380 el = document.createElement('span');
380 el = document.createElement('span');
381 el.innerHTML = completion.displayText;
381 el.innerHTML = completion.displayText;
382 elt.appendChild(el);
382 elt.appendChild(el);
383 }
383 }
384 },
384 },
385 {
385 {
386 text: "reject",
386 text: "reject",
387 displayText: _TM['Set status to Rejected'],
387 displayText: _gettext('Set status to Rejected'),
388 hint: function(CodeMirror, data, completion) {
388 hint: function(CodeMirror, data, completion) {
389 CodeMirror.replaceRange("", completion.from || data.from,
389 CodeMirror.replaceRange("", completion.from || data.from,
390 completion.to || data.to, "complete");
390 completion.to || data.to, "complete");
391 $('#change_status').select2("val", 'rejected').trigger('change');
391 $('#change_status').select2("val", 'rejected').trigger('change');
392 },
392 },
393 render: function(elt, data, completion) {
393 render: function(elt, data, completion) {
394 var el = document.createElement('div');
394 var el = document.createElement('div');
395 el.className = "flag_status flag_status_comment_box rejected pull-left";
395 el.className = "flag_status flag_status_comment_box rejected pull-left";
396 elt.appendChild(el);
396 elt.appendChild(el);
397
397
398 el = document.createElement('span');
398 el = document.createElement('span');
399 el.innerHTML = completion.displayText;
399 el.innerHTML = completion.displayText;
400 elt.appendChild(el);
400 elt.appendChild(el);
401 }
401 }
402 }
402 }
403 ];
403 ];
404
404
405 return {
405 return {
406 list: filterActions(actions, context),
406 list: filterActions(actions, context),
407 from: CodeMirror.Pos(cur.line, context.start),
407 from: CodeMirror.Pos(cur.line, context.start),
408 to: CodeMirror.Pos(cur.line, context.end)
408 to: CodeMirror.Pos(cur.line, context.end)
409 };
409 };
410 };
410 };
411 CodeMirror.registerHelper("hint", "mentions", mentionHint);
411 CodeMirror.registerHelper("hint", "mentions", mentionHint);
412 CodeMirror.registerHelper("hint", "actions", actionHint);
412 CodeMirror.registerHelper("hint", "actions", actionHint);
413 return cm;
413 return cm;
414 };
414 };
415
415
416 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
416 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
417 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
417 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
418 codeMirrorInstance.setOption("mode", mode);
418 codeMirrorInstance.setOption("mode", mode);
419 };
419 };
420
420
421 var setCodeMirrorLineWrap = function(codeMirrorInstance, line_wrap) {
421 var setCodeMirrorLineWrap = function(codeMirrorInstance, line_wrap) {
422 codeMirrorInstance.setOption("lineWrapping", line_wrap);
422 codeMirrorInstance.setOption("lineWrapping", line_wrap);
423 };
423 };
424
424
425 var setCodeMirrorModeFromSelect = function(
425 var setCodeMirrorModeFromSelect = function(
426 targetSelect, targetFileInput, codeMirrorInstance, callback){
426 targetSelect, targetFileInput, codeMirrorInstance, callback){
427
427
428 $(targetSelect).on('change', function(e) {
428 $(targetSelect).on('change', function(e) {
429 cmLog.debug('codemirror select2 mode change event !');
429 cmLog.debug('codemirror select2 mode change event !');
430 var selected = e.currentTarget;
430 var selected = e.currentTarget;
431 var node = selected.options[selected.selectedIndex];
431 var node = selected.options[selected.selectedIndex];
432 var mimetype = node.value;
432 var mimetype = node.value;
433 cmLog.debug('picked mimetype', mimetype);
433 cmLog.debug('picked mimetype', mimetype);
434 var new_mode = $(node).attr('mode');
434 var new_mode = $(node).attr('mode');
435 setCodeMirrorMode(codeMirrorInstance, new_mode);
435 setCodeMirrorMode(codeMirrorInstance, new_mode);
436 cmLog.debug('set new mode', new_mode);
436 cmLog.debug('set new mode', new_mode);
437
437
438 //propose filename from picked mode
438 //propose filename from picked mode
439 cmLog.debug('setting mimetype', mimetype);
439 cmLog.debug('setting mimetype', mimetype);
440 var proposed_ext = getExtFromMimeType(mimetype);
440 var proposed_ext = getExtFromMimeType(mimetype);
441 cmLog.debug('file input', $(targetFileInput).val());
441 cmLog.debug('file input', $(targetFileInput).val());
442 var file_data = getFilenameAndExt($(targetFileInput).val());
442 var file_data = getFilenameAndExt($(targetFileInput).val());
443 var filename = file_data.filename || 'filename1';
443 var filename = file_data.filename || 'filename1';
444 $(targetFileInput).val(filename + proposed_ext);
444 $(targetFileInput).val(filename + proposed_ext);
445 cmLog.debug('proposed file', filename + proposed_ext);
445 cmLog.debug('proposed file', filename + proposed_ext);
446
446
447
447
448 if (typeof(callback) === 'function') {
448 if (typeof(callback) === 'function') {
449 try {
449 try {
450 cmLog.debug('running callback', callback);
450 cmLog.debug('running callback', callback);
451 callback(filename, mimetype, new_mode);
451 callback(filename, mimetype, new_mode);
452 } catch (err) {
452 } catch (err) {
453 console.log('failed to run callback', callback, err);
453 console.log('failed to run callback', callback, err);
454 }
454 }
455 }
455 }
456 cmLog.debug('finish iteration...');
456 cmLog.debug('finish iteration...');
457 });
457 });
458 };
458 };
459
459
460 var setCodeMirrorModeFromInput = function(
460 var setCodeMirrorModeFromInput = function(
461 targetSelect, targetFileInput, codeMirrorInstance, callback) {
461 targetSelect, targetFileInput, codeMirrorInstance, callback) {
462
462
463 // on type the new filename set mode
463 // on type the new filename set mode
464 $(targetFileInput).on('keyup', function(e) {
464 $(targetFileInput).on('keyup', function(e) {
465 var file_data = getFilenameAndExt(this.value);
465 var file_data = getFilenameAndExt(this.value);
466 if (file_data.ext === null) {
466 if (file_data.ext === null) {
467 return;
467 return;
468 }
468 }
469
469
470 var mimetypes = getMimeTypeFromExt(file_data.ext, true);
470 var mimetypes = getMimeTypeFromExt(file_data.ext, true);
471 cmLog.debug('mimetype from file', file_data, mimetypes);
471 cmLog.debug('mimetype from file', file_data, mimetypes);
472 var detected_mode;
472 var detected_mode;
473 var detected_option;
473 var detected_option;
474 for (var i in mimetypes) {
474 for (var i in mimetypes) {
475 var mt = mimetypes[i];
475 var mt = mimetypes[i];
476 if (!detected_mode) {
476 if (!detected_mode) {
477 detected_mode = detectCodeMirrorMode(this.value, mt);
477 detected_mode = detectCodeMirrorMode(this.value, mt);
478 }
478 }
479
479
480 if (!detected_option) {
480 if (!detected_option) {
481 cmLog.debug('#mimetype option[value="{0}"]'.format(mt));
481 cmLog.debug('#mimetype option[value="{0}"]'.format(mt));
482 if ($(targetSelect).find('option[value="{0}"]'.format(mt)).length) {
482 if ($(targetSelect).find('option[value="{0}"]'.format(mt)).length) {
483 detected_option = mt;
483 detected_option = mt;
484 }
484 }
485 }
485 }
486 }
486 }
487
487
488 cmLog.debug('detected mode', detected_mode);
488 cmLog.debug('detected mode', detected_mode);
489 cmLog.debug('detected option', detected_option);
489 cmLog.debug('detected option', detected_option);
490 if (detected_mode && detected_option){
490 if (detected_mode && detected_option){
491
491
492 $(targetSelect).select2("val", detected_option);
492 $(targetSelect).select2("val", detected_option);
493 setCodeMirrorMode(codeMirrorInstance, detected_mode);
493 setCodeMirrorMode(codeMirrorInstance, detected_mode);
494
494
495 if(typeof(callback) === 'function'){
495 if(typeof(callback) === 'function'){
496 try{
496 try{
497 cmLog.debug('running callback', callback);
497 cmLog.debug('running callback', callback);
498 var filename = file_data.filename + "." + file_data.ext;
498 var filename = file_data.filename + "." + file_data.ext;
499 callback(filename, detected_option, detected_mode);
499 callback(filename, detected_option, detected_mode);
500 }catch (err){
500 }catch (err){
501 console.log('failed to run callback', callback, err);
501 console.log('failed to run callback', callback, err);
502 }
502 }
503 }
503 }
504 }
504 }
505
505
506 });
506 });
507 };
507 };
508
508
509 var fillCodeMirrorOptions = function(targetSelect) {
509 var fillCodeMirrorOptions = function(targetSelect) {
510 //inject new modes, based on codeMirrors modeInfo object
510 //inject new modes, based on codeMirrors modeInfo object
511 var modes_select = $(targetSelect);
511 var modes_select = $(targetSelect);
512 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
512 for (var i = 0; i < CodeMirror.modeInfo.length; i++) {
513 var m = CodeMirror.modeInfo[i];
513 var m = CodeMirror.modeInfo[i];
514 var opt = new Option(m.name, m.mime);
514 var opt = new Option(m.name, m.mime);
515 $(opt).attr('mode', m.mode);
515 $(opt).attr('mode', m.mode);
516 modes_select.append(opt);
516 modes_select.append(opt);
517 }
517 }
518 };
518 };
519
519
520 var CodeMirrorPreviewEnable = function(edit_mode) {
520 var CodeMirrorPreviewEnable = function(edit_mode) {
521 // in case it a preview enabled mode enable the button
521 // in case it a preview enabled mode enable the button
522 if (['markdown', 'rst', 'gfm'].indexOf(edit_mode) !== -1) {
522 if (['markdown', 'rst', 'gfm'].indexOf(edit_mode) !== -1) {
523 $('#render_preview').removeClass('hidden');
523 $('#render_preview').removeClass('hidden');
524 }
524 }
525 else {
525 else {
526 if (!$('#render_preview').hasClass('hidden')) {
526 if (!$('#render_preview').hasClass('hidden')) {
527 $('#render_preview').addClass('hidden');
527 $('#render_preview').addClass('hidden');
528 }
528 }
529 }
529 }
530 };
530 };
@@ -1,664 +1,664 b''
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 var firefoxAnchorFix = function() {
19 var firefoxAnchorFix = function() {
20 // hack to make anchor links behave properly on firefox, in our inline
20 // hack to make anchor links behave properly on firefox, in our inline
21 // comments generation when comments are injected firefox is misbehaving
21 // comments generation when comments are injected firefox is misbehaving
22 // when jumping to anchor links
22 // when jumping to anchor links
23 if (location.href.indexOf('#') > -1) {
23 if (location.href.indexOf('#') > -1) {
24 location.href += '';
24 location.href += '';
25 }
25 }
26 };
26 };
27
27
28 // returns a node from given html;
28 // returns a node from given html;
29 var fromHTML = function(html){
29 var fromHTML = function(html){
30 var _html = document.createElement('element');
30 var _html = document.createElement('element');
31 _html.innerHTML = html;
31 _html.innerHTML = html;
32 return _html;
32 return _html;
33 };
33 };
34
34
35 var tableTr = function(cls, body){
35 var tableTr = function(cls, body){
36 var _el = document.createElement('div');
36 var _el = document.createElement('div');
37 var _body = $(body).attr('id');
37 var _body = $(body).attr('id');
38 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
38 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
39 var id = 'comment-tr-{0}'.format(comment_id);
39 var id = 'comment-tr-{0}'.format(comment_id);
40 var _html = ('<table><tbody><tr id="{0}" class="{1}">'+
40 var _html = ('<table><tbody><tr id="{0}" class="{1}">'+
41 '<td class="add-comment-line"><span class="add-comment-content"></span></td>'+
41 '<td class="add-comment-line"><span class="add-comment-content"></span></td>'+
42 '<td></td>'+
42 '<td></td>'+
43 '<td></td>'+
43 '<td></td>'+
44 '<td>{2}</td>'+
44 '<td>{2}</td>'+
45 '</tr></tbody></table>').format(id, cls, body);
45 '</tr></tbody></table>').format(id, cls, body);
46 $(_el).html(_html);
46 $(_el).html(_html);
47 return _el.children[0].children[0].children[0];
47 return _el.children[0].children[0].children[0];
48 };
48 };
49
49
50 var removeInlineForm = function(form) {
50 var removeInlineForm = function(form) {
51 form.parentNode.removeChild(form);
51 form.parentNode.removeChild(form);
52 };
52 };
53
53
54 var createInlineForm = function(parent_tr, f_path, line) {
54 var createInlineForm = function(parent_tr, f_path, line) {
55 var tmpl = $('#comment-inline-form-template').html();
55 var tmpl = $('#comment-inline-form-template').html();
56 tmpl = tmpl.format(f_path, line);
56 tmpl = tmpl.format(f_path, line);
57 var form = tableTr('comment-form-inline', tmpl);
57 var form = tableTr('comment-form-inline', tmpl);
58 var form_hide_button = $(form).find('.hide-inline-form');
58 var form_hide_button = $(form).find('.hide-inline-form');
59
59
60 $(form_hide_button).click(function(e) {
60 $(form_hide_button).click(function(e) {
61 $('.inline-comments').removeClass('hide-comment-button');
61 $('.inline-comments').removeClass('hide-comment-button');
62 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
62 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
63 if ($(newtr.nextElementSibling).hasClass('inline-comments-button')) {
63 if ($(newtr.nextElementSibling).hasClass('inline-comments-button')) {
64 $(newtr.nextElementSibling).show();
64 $(newtr.nextElementSibling).show();
65 }
65 }
66 $(newtr).parents('.comment-form-inline').remove();
66 $(newtr).parents('.comment-form-inline').remove();
67 $(parent_tr).removeClass('form-open');
67 $(parent_tr).removeClass('form-open');
68 $(parent_tr).removeClass('hl-comment');
68 $(parent_tr).removeClass('hl-comment');
69 });
69 });
70
70
71 return form;
71 return form;
72 };
72 };
73
73
74 var getLineNo = function(tr) {
74 var getLineNo = function(tr) {
75 var line;
75 var line;
76 // Try to get the id and return "" (empty string) if it doesn't exist
76 // Try to get the id and return "" (empty string) if it doesn't exist
77 var o = ($(tr).find('.lineno.old').attr('id')||"").split('_');
77 var o = ($(tr).find('.lineno.old').attr('id')||"").split('_');
78 var n = ($(tr).find('.lineno.new').attr('id')||"").split('_');
78 var n = ($(tr).find('.lineno.new').attr('id')||"").split('_');
79 if (n.length >= 2) {
79 if (n.length >= 2) {
80 line = n[n.length-1];
80 line = n[n.length-1];
81 } else if (o.length >= 2) {
81 } else if (o.length >= 2) {
82 line = o[o.length-1];
82 line = o[o.length-1];
83 }
83 }
84 return line;
84 return line;
85 };
85 };
86
86
87 /**
87 /**
88 * make a single inline comment and place it inside
88 * make a single inline comment and place it inside
89 */
89 */
90 var renderInlineComment = function(json_data, show_add_button) {
90 var renderInlineComment = function(json_data, show_add_button) {
91 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
91 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
92 try {
92 try {
93 var html = json_data.rendered_text;
93 var html = json_data.rendered_text;
94 var lineno = json_data.line_no;
94 var lineno = json_data.line_no;
95 var target_id = json_data.target_id;
95 var target_id = json_data.target_id;
96 placeInline(target_id, lineno, html, show_add_button);
96 placeInline(target_id, lineno, html, show_add_button);
97 } catch (e) {
97 } catch (e) {
98 console.error(e);
98 console.error(e);
99 }
99 }
100 };
100 };
101
101
102 function bindDeleteCommentButtons() {
102 function bindDeleteCommentButtons() {
103 $('.delete-comment').one('click', function() {
103 $('.delete-comment').one('click', function() {
104 var comment_id = $(this).data("comment-id");
104 var comment_id = $(this).data("comment-id");
105
105
106 if (comment_id){
106 if (comment_id){
107 deleteComment(comment_id);
107 deleteComment(comment_id);
108 }
108 }
109 });
109 });
110 }
110 }
111
111
112 /**
112 /**
113 * Inject inline comment for on given TR this tr should be always an .line
113 * Inject inline comment for on given TR this tr should be always an .line
114 * tr containing the line. Code will detect comment, and always put the comment
114 * tr containing the line. Code will detect comment, and always put the comment
115 * block at the very bottom
115 * block at the very bottom
116 */
116 */
117 var injectInlineForm = function(tr){
117 var injectInlineForm = function(tr){
118 if (!$(tr).hasClass('line')) {
118 if (!$(tr).hasClass('line')) {
119 return;
119 return;
120 }
120 }
121
121
122 var _td = $(tr).find('.code').get(0);
122 var _td = $(tr).find('.code').get(0);
123 if ($(tr).hasClass('form-open') ||
123 if ($(tr).hasClass('form-open') ||
124 $(tr).hasClass('context') ||
124 $(tr).hasClass('context') ||
125 $(_td).hasClass('no-comment')) {
125 $(_td).hasClass('no-comment')) {
126 return;
126 return;
127 }
127 }
128 $(tr).addClass('form-open');
128 $(tr).addClass('form-open');
129 $(tr).addClass('hl-comment');
129 $(tr).addClass('hl-comment');
130 var node = $(tr.parentNode.parentNode.parentNode).find('.full_f_path').get(0);
130 var node = $(tr.parentNode.parentNode.parentNode).find('.full_f_path').get(0);
131 var f_path = $(node).attr('path');
131 var f_path = $(node).attr('path');
132 var lineno = getLineNo(tr);
132 var lineno = getLineNo(tr);
133 var form = createInlineForm(tr, f_path, lineno);
133 var form = createInlineForm(tr, f_path, lineno);
134
134
135 var parent = tr;
135 var parent = tr;
136 while (1) {
136 while (1) {
137 var n = parent.nextElementSibling;
137 var n = parent.nextElementSibling;
138 // next element are comments !
138 // next element are comments !
139 if ($(n).hasClass('inline-comments')) {
139 if ($(n).hasClass('inline-comments')) {
140 parent = n;
140 parent = n;
141 }
141 }
142 else {
142 else {
143 break;
143 break;
144 }
144 }
145 }
145 }
146 var _parent = $(parent).get(0);
146 var _parent = $(parent).get(0);
147 $(_parent).after(form);
147 $(_parent).after(form);
148 $('.comment-form-inline').prev('.inline-comments').addClass('hide-comment-button');
148 $('.comment-form-inline').prev('.inline-comments').addClass('hide-comment-button');
149 var f = $(form).get(0);
149 var f = $(form).get(0);
150
150
151 var _form = $(f).find('.inline-form').get(0);
151 var _form = $(f).find('.inline-form').get(0);
152
152
153 $('.switch-to-chat', _form).on('click', function(evt){
153 $('.switch-to-chat', _form).on('click', function(evt){
154 var fParent = $(_parent).closest('.injected_diff').parent().prev('*[fid]');
154 var fParent = $(_parent).closest('.injected_diff').parent().prev('*[fid]');
155 var fid = fParent.attr('fid');
155 var fid = fParent.attr('fid');
156
156
157 // activate chat and trigger subscription to channels
157 // activate chat and trigger subscription to channels
158 $.Topic('/chat_controller').publish({
158 $.Topic('/chat_controller').publish({
159 action:'subscribe_to_channels',
159 action:'subscribe_to_channels',
160 data: ['/chat${0}$/fid/{1}/{2}'.format(templateContext.repo_name, fid, lineno)]
160 data: ['/chat${0}$/fid/{1}/{2}'.format(templateContext.repo_name, fid, lineno)]
161 });
161 });
162 $(_form).closest('td').find('.comment-inline-form').addClass('hidden');
162 $(_form).closest('td').find('.comment-inline-form').addClass('hidden');
163 $(_form).closest('td').find('.chat-holder').removeClass('hidden');
163 $(_form).closest('td').find('.chat-holder').removeClass('hidden');
164 });
164 });
165
165
166 var pullRequestId = templateContext.pull_request_data.pull_request_id;
166 var pullRequestId = templateContext.pull_request_data.pull_request_id;
167 var commitId = templateContext.commit_data.commit_id;
167 var commitId = templateContext.commit_data.commit_id;
168
168
169 var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false);
169 var commentForm = new CommentForm(_form, commitId, pullRequestId, lineno, false);
170 var cm = commentForm.getCmInstance();
170 var cm = commentForm.getCmInstance();
171
171
172 // set a CUSTOM submit handler for inline comments.
172 // set a CUSTOM submit handler for inline comments.
173 commentForm.setHandleFormSubmit(function(o) {
173 commentForm.setHandleFormSubmit(function(o) {
174 var text = commentForm.cm.getValue();
174 var text = commentForm.cm.getValue();
175
175
176 if (text === "") {
176 if (text === "") {
177 return;
177 return;
178 }
178 }
179
179
180 if (lineno === undefined) {
180 if (lineno === undefined) {
181 alert('missing line !');
181 alert('missing line !');
182 return;
182 return;
183 }
183 }
184 if (f_path === undefined) {
184 if (f_path === undefined) {
185 alert('missing file path !');
185 alert('missing file path !');
186 return;
186 return;
187 }
187 }
188
188
189 var excludeCancelBtn = false;
189 var excludeCancelBtn = false;
190 var submitEvent = true;
190 var submitEvent = true;
191 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
191 commentForm.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
192 commentForm.cm.setOption("readOnly", true);
192 commentForm.cm.setOption("readOnly", true);
193 var postData = {
193 var postData = {
194 'text': text,
194 'text': text,
195 'f_path': f_path,
195 'f_path': f_path,
196 'line': lineno,
196 'line': lineno,
197 'csrf_token': CSRF_TOKEN
197 'csrf_token': CSRF_TOKEN
198 };
198 };
199 var submitSuccessCallback = function(o) {
199 var submitSuccessCallback = function(o) {
200 $(tr).removeClass('form-open');
200 $(tr).removeClass('form-open');
201 removeInlineForm(f);
201 removeInlineForm(f);
202 renderInlineComment(o);
202 renderInlineComment(o);
203 $('.inline-comments').removeClass('hide-comment-button');
203 $('.inline-comments').removeClass('hide-comment-button');
204
204
205 // re trigger the linkification of next/prev navigation
205 // re trigger the linkification of next/prev navigation
206 linkifyComments($('.inline-comment-injected'));
206 linkifyComments($('.inline-comment-injected'));
207 timeagoActivate();
207 timeagoActivate();
208 tooltip_activate();
208 tooltip_activate();
209 bindDeleteCommentButtons();
209 bindDeleteCommentButtons();
210 commentForm.setActionButtonsDisabled(false);
210 commentForm.setActionButtonsDisabled(false);
211
211
212 };
212 };
213 var submitFailCallback = function(){
213 var submitFailCallback = function(){
214 commentForm.resetCommentFormState(text)
214 commentForm.resetCommentFormState(text)
215 };
215 };
216 commentForm.submitAjaxPOST(
216 commentForm.submitAjaxPOST(
217 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
217 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
218 });
218 });
219
219
220 setTimeout(function() {
220 setTimeout(function() {
221 // callbacks
221 // callbacks
222 if (cm !== undefined) {
222 if (cm !== undefined) {
223 cm.focus();
223 cm.focus();
224 }
224 }
225 }, 10);
225 }, 10);
226
226
227 };
227 };
228
228
229 var deleteComment = function(comment_id) {
229 var deleteComment = function(comment_id) {
230 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
230 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
231 var postData = {
231 var postData = {
232 '_method': 'delete',
232 '_method': 'delete',
233 'csrf_token': CSRF_TOKEN
233 'csrf_token': CSRF_TOKEN
234 };
234 };
235
235
236 var success = function(o) {
236 var success = function(o) {
237 window.location.reload();
237 window.location.reload();
238 };
238 };
239 ajaxPOST(url, postData, success);
239 ajaxPOST(url, postData, success);
240 };
240 };
241
241
242 var createInlineAddButton = function(tr){
242 var createInlineAddButton = function(tr){
243 var label = _TM['Add another comment'];
243 var label = _gettext('Add another comment');
244 var html_el = document.createElement('div');
244 var html_el = document.createElement('div');
245 $(html_el).addClass('add-comment');
245 $(html_el).addClass('add-comment');
246 html_el.innerHTML = '<span class="btn btn-secondary">{0}</span>'.format(label);
246 html_el.innerHTML = '<span class="btn btn-secondary">{0}</span>'.format(label);
247 var add = new $(html_el);
247 var add = new $(html_el);
248 add.on('click', function(e) {
248 add.on('click', function(e) {
249 injectInlineForm(tr);
249 injectInlineForm(tr);
250 });
250 });
251 return add;
251 return add;
252 };
252 };
253
253
254 var placeAddButton = function(target_tr){
254 var placeAddButton = function(target_tr){
255 if(!target_tr){
255 if(!target_tr){
256 return;
256 return;
257 }
257 }
258 var last_node = target_tr;
258 var last_node = target_tr;
259 // scan
259 // scan
260 while (1){
260 while (1){
261 var n = last_node.nextElementSibling;
261 var n = last_node.nextElementSibling;
262 // next element are comments !
262 // next element are comments !
263 if($(n).hasClass('inline-comments')){
263 if($(n).hasClass('inline-comments')){
264 last_node = n;
264 last_node = n;
265 // also remove the comment button from previous
265 // also remove the comment button from previous
266 var comment_add_buttons = $(last_node).find('.add-comment');
266 var comment_add_buttons = $(last_node).find('.add-comment');
267 for(var i=0; i<comment_add_buttons.length; i++){
267 for(var i=0; i<comment_add_buttons.length; i++){
268 var b = comment_add_buttons[i];
268 var b = comment_add_buttons[i];
269 b.parentNode.removeChild(b);
269 b.parentNode.removeChild(b);
270 }
270 }
271 }
271 }
272 else{
272 else{
273 break;
273 break;
274 }
274 }
275 }
275 }
276 var add = createInlineAddButton(target_tr);
276 var add = createInlineAddButton(target_tr);
277 // get the comment div
277 // get the comment div
278 var comment_block = $(last_node).find('.comment')[0];
278 var comment_block = $(last_node).find('.comment')[0];
279 // attach add button
279 // attach add button
280 $(add).insertAfter(comment_block);
280 $(add).insertAfter(comment_block);
281 };
281 };
282
282
283 /**
283 /**
284 * Places the inline comment into the changeset block in proper line position
284 * Places the inline comment into the changeset block in proper line position
285 */
285 */
286 var placeInline = function(target_container, lineno, html, show_add_button) {
286 var placeInline = function(target_container, lineno, html, show_add_button) {
287 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
287 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
288
288
289 var lineid = "{0}_{1}".format(target_container, lineno);
289 var lineid = "{0}_{1}".format(target_container, lineno);
290 var target_line = $('#' + lineid).get(0);
290 var target_line = $('#' + lineid).get(0);
291 var comment = new $(tableTr('inline-comments', html));
291 var comment = new $(tableTr('inline-comments', html));
292 // check if there are comments already !
292 // check if there are comments already !
293 var parent_node = target_line.parentNode;
293 var parent_node = target_line.parentNode;
294 var root_parent = parent_node;
294 var root_parent = parent_node;
295 while (1) {
295 while (1) {
296 var n = parent_node.nextElementSibling;
296 var n = parent_node.nextElementSibling;
297 // next element are comments !
297 // next element are comments !
298 if ($(n).hasClass('inline-comments')) {
298 if ($(n).hasClass('inline-comments')) {
299 parent_node = n;
299 parent_node = n;
300 }
300 }
301 else {
301 else {
302 break;
302 break;
303 }
303 }
304 }
304 }
305 // put in the comment at the bottom
305 // put in the comment at the bottom
306 $(comment).insertAfter(parent_node);
306 $(comment).insertAfter(parent_node);
307 $(comment).find('.comment-inline').addClass('inline-comment-injected');
307 $(comment).find('.comment-inline').addClass('inline-comment-injected');
308 // scan nodes, and attach add button to last one
308 // scan nodes, and attach add button to last one
309 if (show_add_button) {
309 if (show_add_button) {
310 placeAddButton(root_parent);
310 placeAddButton(root_parent);
311 }
311 }
312
312
313 return target_line;
313 return target_line;
314 };
314 };
315
315
316 var linkifyComments = function(comments) {
316 var linkifyComments = function(comments) {
317
317
318 for (var i = 0; i < comments.length; i++) {
318 for (var i = 0; i < comments.length; i++) {
319 var comment_id = $(comments[i]).data('comment-id');
319 var comment_id = $(comments[i]).data('comment-id');
320 var prev_comment_id = $(comments[i - 1]).data('comment-id');
320 var prev_comment_id = $(comments[i - 1]).data('comment-id');
321 var next_comment_id = $(comments[i + 1]).data('comment-id');
321 var next_comment_id = $(comments[i + 1]).data('comment-id');
322
322
323 // place next/prev links
323 // place next/prev links
324 if (prev_comment_id) {
324 if (prev_comment_id) {
325 $('#prev_c_' + comment_id).show();
325 $('#prev_c_' + comment_id).show();
326 $('#prev_c_' + comment_id + " a.arrow_comment_link").attr(
326 $('#prev_c_' + comment_id + " a.arrow_comment_link").attr(
327 'href', '#comment-' + prev_comment_id).removeClass('disabled');
327 'href', '#comment-' + prev_comment_id).removeClass('disabled');
328 }
328 }
329 if (next_comment_id) {
329 if (next_comment_id) {
330 $('#next_c_' + comment_id).show();
330 $('#next_c_' + comment_id).show();
331 $('#next_c_' + comment_id + " a.arrow_comment_link").attr(
331 $('#next_c_' + comment_id + " a.arrow_comment_link").attr(
332 'href', '#comment-' + next_comment_id).removeClass('disabled');
332 'href', '#comment-' + next_comment_id).removeClass('disabled');
333 }
333 }
334 // place a first link to the total counter
334 // place a first link to the total counter
335 if (i === 0) {
335 if (i === 0) {
336 $('#inline-comments-counter').attr('href', '#comment-' + comment_id);
336 $('#inline-comments-counter').attr('href', '#comment-' + comment_id);
337 }
337 }
338 }
338 }
339
339
340 };
340 };
341
341
342 /**
342 /**
343 * Iterates over all the inlines, and places them inside proper blocks of data
343 * Iterates over all the inlines, and places them inside proper blocks of data
344 */
344 */
345 var renderInlineComments = function(file_comments, show_add_button) {
345 var renderInlineComments = function(file_comments, show_add_button) {
346 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
346 show_add_button = typeof show_add_button !== 'undefined' ? show_add_button : true;
347
347
348 for (var i = 0; i < file_comments.length; i++) {
348 for (var i = 0; i < file_comments.length; i++) {
349 var box = file_comments[i];
349 var box = file_comments[i];
350
350
351 var target_id = $(box).attr('target_id');
351 var target_id = $(box).attr('target_id');
352
352
353 // actually comments with line numbers
353 // actually comments with line numbers
354 var comments = box.children;
354 var comments = box.children;
355
355
356 for (var j = 0; j < comments.length; j++) {
356 for (var j = 0; j < comments.length; j++) {
357 var data = {
357 var data = {
358 'rendered_text': comments[j].outerHTML,
358 'rendered_text': comments[j].outerHTML,
359 'line_no': $(comments[j]).attr('line'),
359 'line_no': $(comments[j]).attr('line'),
360 'target_id': target_id
360 'target_id': target_id
361 };
361 };
362 renderInlineComment(data, show_add_button);
362 renderInlineComment(data, show_add_button);
363 }
363 }
364 }
364 }
365
365
366 // since order of injection is random, we're now re-iterating
366 // since order of injection is random, we're now re-iterating
367 // from correct order and filling in links
367 // from correct order and filling in links
368 linkifyComments($('.inline-comment-injected'));
368 linkifyComments($('.inline-comment-injected'));
369 bindDeleteCommentButtons();
369 bindDeleteCommentButtons();
370 firefoxAnchorFix();
370 firefoxAnchorFix();
371 };
371 };
372
372
373
373
374 /* Comment form for main and inline comments */
374 /* Comment form for main and inline comments */
375 var CommentForm = (function() {
375 var CommentForm = (function() {
376 "use strict";
376 "use strict";
377
377
378 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions) {
378 function CommentForm(formElement, commitId, pullRequestId, lineNo, initAutocompleteActions) {
379
379
380 this.withLineNo = function(selector) {
380 this.withLineNo = function(selector) {
381 var lineNo = this.lineNo;
381 var lineNo = this.lineNo;
382 if (lineNo === undefined) {
382 if (lineNo === undefined) {
383 return selector
383 return selector
384 } else {
384 } else {
385 return selector + '_' + lineNo;
385 return selector + '_' + lineNo;
386 }
386 }
387 };
387 };
388
388
389 this.commitId = commitId;
389 this.commitId = commitId;
390 this.pullRequestId = pullRequestId;
390 this.pullRequestId = pullRequestId;
391 this.lineNo = lineNo;
391 this.lineNo = lineNo;
392 this.initAutocompleteActions = initAutocompleteActions;
392 this.initAutocompleteActions = initAutocompleteActions;
393
393
394 this.previewButton = this.withLineNo('#preview-btn');
394 this.previewButton = this.withLineNo('#preview-btn');
395 this.previewContainer = this.withLineNo('#preview-container');
395 this.previewContainer = this.withLineNo('#preview-container');
396
396
397 this.previewBoxSelector = this.withLineNo('#preview-box');
397 this.previewBoxSelector = this.withLineNo('#preview-box');
398
398
399 this.editButton = this.withLineNo('#edit-btn');
399 this.editButton = this.withLineNo('#edit-btn');
400 this.editContainer = this.withLineNo('#edit-container');
400 this.editContainer = this.withLineNo('#edit-container');
401
401
402 this.cancelButton = this.withLineNo('#cancel-btn');
402 this.cancelButton = this.withLineNo('#cancel-btn');
403
403
404 this.statusChange = '#change_status';
404 this.statusChange = '#change_status';
405 this.cmBox = this.withLineNo('#text');
405 this.cmBox = this.withLineNo('#text');
406 this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions);
406 this.cm = initCommentBoxCodeMirror(this.cmBox, this.initAutocompleteActions);
407
407
408 this.submitForm = formElement;
408 this.submitForm = formElement;
409 this.submitButton = $(this.submitForm).find('input[type="submit"]');
409 this.submitButton = $(this.submitForm).find('input[type="submit"]');
410 this.submitButtonText = this.submitButton.val();
410 this.submitButtonText = this.submitButton.val();
411
411
412 this.previewUrl = pyroutes.url('changeset_comment_preview',
412 this.previewUrl = pyroutes.url('changeset_comment_preview',
413 {'repo_name': templateContext.repo_name});
413 {'repo_name': templateContext.repo_name});
414
414
415 // based on commitId, or pullReuqestId decide where do we submit
415 // based on commitId, or pullReuqestId decide where do we submit
416 // out data
416 // out data
417 if (this.commitId){
417 if (this.commitId){
418 this.submitUrl = pyroutes.url('changeset_comment',
418 this.submitUrl = pyroutes.url('changeset_comment',
419 {'repo_name': templateContext.repo_name,
419 {'repo_name': templateContext.repo_name,
420 'revision': this.commitId});
420 'revision': this.commitId});
421
421
422 } else if (this.pullRequestId) {
422 } else if (this.pullRequestId) {
423 this.submitUrl = pyroutes.url('pullrequest_comment',
423 this.submitUrl = pyroutes.url('pullrequest_comment',
424 {'repo_name': templateContext.repo_name,
424 {'repo_name': templateContext.repo_name,
425 'pull_request_id': this.pullRequestId});
425 'pull_request_id': this.pullRequestId});
426
426
427 } else {
427 } else {
428 throw new Error(
428 throw new Error(
429 'CommentForm requires pullRequestId, or commitId to be specified.')
429 'CommentForm requires pullRequestId, or commitId to be specified.')
430 }
430 }
431
431
432 this.getCmInstance = function(){
432 this.getCmInstance = function(){
433 return this.cm
433 return this.cm
434 };
434 };
435
435
436 var self = this;
436 var self = this;
437
437
438 this.getCommentStatus = function() {
438 this.getCommentStatus = function() {
439 return $(this.submitForm).find(this.statusChange).val();
439 return $(this.submitForm).find(this.statusChange).val();
440 };
440 };
441
441
442 this.isAllowedToSubmit = function() {
442 this.isAllowedToSubmit = function() {
443 return !$(this.submitButton).prop('disabled');
443 return !$(this.submitButton).prop('disabled');
444 };
444 };
445
445
446 this.initStatusChangeSelector = function(){
446 this.initStatusChangeSelector = function(){
447 var formatChangeStatus = function(state, escapeMarkup) {
447 var formatChangeStatus = function(state, escapeMarkup) {
448 var originalOption = state.element;
448 var originalOption = state.element;
449 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
449 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
450 '<span>' + escapeMarkup(state.text) + '</span>';
450 '<span>' + escapeMarkup(state.text) + '</span>';
451 };
451 };
452 var formatResult = function(result, container, query, escapeMarkup) {
452 var formatResult = function(result, container, query, escapeMarkup) {
453 return formatChangeStatus(result, escapeMarkup);
453 return formatChangeStatus(result, escapeMarkup);
454 };
454 };
455
455
456 var formatSelection = function(data, container, escapeMarkup) {
456 var formatSelection = function(data, container, escapeMarkup) {
457 return formatChangeStatus(data, escapeMarkup);
457 return formatChangeStatus(data, escapeMarkup);
458 };
458 };
459
459
460 $(this.submitForm).find(this.statusChange).select2({
460 $(this.submitForm).find(this.statusChange).select2({
461 placeholder: _TM['Status Review'],
461 placeholder: _gettext('Status Review'),
462 formatResult: formatResult,
462 formatResult: formatResult,
463 formatSelection: formatSelection,
463 formatSelection: formatSelection,
464 containerCssClass: "drop-menu status_box_menu",
464 containerCssClass: "drop-menu status_box_menu",
465 dropdownCssClass: "drop-menu-dropdown",
465 dropdownCssClass: "drop-menu-dropdown",
466 dropdownAutoWidth: true,
466 dropdownAutoWidth: true,
467 minimumResultsForSearch: -1
467 minimumResultsForSearch: -1
468 });
468 });
469 $(this.submitForm).find(this.statusChange).on('change', function() {
469 $(this.submitForm).find(this.statusChange).on('change', function() {
470 var status = self.getCommentStatus();
470 var status = self.getCommentStatus();
471 if (status && !self.lineNo) {
471 if (status && !self.lineNo) {
472 $(self.submitButton).prop('disabled', false);
472 $(self.submitButton).prop('disabled', false);
473 }
473 }
474 //todo, fix this name
474 //todo, fix this name
475 var placeholderText = _TM['Comment text will be set automatically based on currently selected status ({0}) ...'].format(status);
475 var placeholderText = _gettext('Comment text will be set automatically based on currently selected status ({0}) ...').format(status);
476 self.cm.setOption('placeholder', placeholderText);
476 self.cm.setOption('placeholder', placeholderText);
477 })
477 })
478 };
478 };
479
479
480 // reset the comment form into it's original state
480 // reset the comment form into it's original state
481 this.resetCommentFormState = function(content) {
481 this.resetCommentFormState = function(content) {
482 content = content || '';
482 content = content || '';
483
483
484 $(this.editContainer).show();
484 $(this.editContainer).show();
485 $(this.editButton).hide();
485 $(this.editButton).hide();
486
486
487 $(this.previewContainer).hide();
487 $(this.previewContainer).hide();
488 $(this.previewButton).show();
488 $(this.previewButton).show();
489
489
490 this.setActionButtonsDisabled(true);
490 this.setActionButtonsDisabled(true);
491 self.cm.setValue(content);
491 self.cm.setValue(content);
492 self.cm.setOption("readOnly", false);
492 self.cm.setOption("readOnly", false);
493 };
493 };
494
494
495 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
495 this.submitAjaxPOST = function(url, postData, successHandler, failHandler) {
496 failHandler = failHandler || function() {};
496 failHandler = failHandler || function() {};
497 var postData = toQueryString(postData);
497 var postData = toQueryString(postData);
498 var request = $.ajax({
498 var request = $.ajax({
499 url: url,
499 url: url,
500 type: 'POST',
500 type: 'POST',
501 data: postData,
501 data: postData,
502 headers: {'X-PARTIAL-XHR': true}
502 headers: {'X-PARTIAL-XHR': true}
503 })
503 })
504 .done(function(data) {
504 .done(function(data) {
505 successHandler(data);
505 successHandler(data);
506 })
506 })
507 .fail(function(data, textStatus, errorThrown){
507 .fail(function(data, textStatus, errorThrown){
508 alert(
508 alert(
509 "Error while submitting comment.\n" +
509 "Error while submitting comment.\n" +
510 "Error code {0} ({1}).".format(data.status, data.statusText));
510 "Error code {0} ({1}).".format(data.status, data.statusText));
511 failHandler()
511 failHandler()
512 });
512 });
513 return request;
513 return request;
514 };
514 };
515
515
516 // overwrite a submitHandler, we need to do it for inline comments
516 // overwrite a submitHandler, we need to do it for inline comments
517 this.setHandleFormSubmit = function(callback) {
517 this.setHandleFormSubmit = function(callback) {
518 this.handleFormSubmit = callback;
518 this.handleFormSubmit = callback;
519 };
519 };
520
520
521 // default handler for for submit for main comments
521 // default handler for for submit for main comments
522 this.handleFormSubmit = function() {
522 this.handleFormSubmit = function() {
523 var text = self.cm.getValue();
523 var text = self.cm.getValue();
524 var status = self.getCommentStatus();
524 var status = self.getCommentStatus();
525
525
526 if (text === "" && !status) {
526 if (text === "" && !status) {
527 return;
527 return;
528 }
528 }
529
529
530 var excludeCancelBtn = false;
530 var excludeCancelBtn = false;
531 var submitEvent = true;
531 var submitEvent = true;
532 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
532 self.setActionButtonsDisabled(true, excludeCancelBtn, submitEvent);
533 self.cm.setOption("readOnly", true);
533 self.cm.setOption("readOnly", true);
534 var postData = {
534 var postData = {
535 'text': text,
535 'text': text,
536 'changeset_status': status,
536 'changeset_status': status,
537 'csrf_token': CSRF_TOKEN
537 'csrf_token': CSRF_TOKEN
538 };
538 };
539
539
540 var submitSuccessCallback = function(o) {
540 var submitSuccessCallback = function(o) {
541 if (status) {
541 if (status) {
542 location.reload(true);
542 location.reload(true);
543 } else {
543 } else {
544 $('#injected_page_comments').append(o.rendered_text);
544 $('#injected_page_comments').append(o.rendered_text);
545 self.resetCommentFormState();
545 self.resetCommentFormState();
546 bindDeleteCommentButtons();
546 bindDeleteCommentButtons();
547 timeagoActivate();
547 timeagoActivate();
548 tooltip_activate();
548 tooltip_activate();
549 }
549 }
550 };
550 };
551 var submitFailCallback = function(){
551 var submitFailCallback = function(){
552 self.resetCommentFormState(text)
552 self.resetCommentFormState(text)
553 };
553 };
554 self.submitAjaxPOST(
554 self.submitAjaxPOST(
555 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
555 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
556 };
556 };
557
557
558 this.previewSuccessCallback = function(o) {
558 this.previewSuccessCallback = function(o) {
559 $(self.previewBoxSelector).html(o);
559 $(self.previewBoxSelector).html(o);
560 $(self.previewBoxSelector).removeClass('unloaded');
560 $(self.previewBoxSelector).removeClass('unloaded');
561
561
562 // swap buttons
562 // swap buttons
563 $(self.previewButton).hide();
563 $(self.previewButton).hide();
564 $(self.editButton).show();
564 $(self.editButton).show();
565
565
566 // unlock buttons
566 // unlock buttons
567 self.setActionButtonsDisabled(false);
567 self.setActionButtonsDisabled(false);
568 };
568 };
569
569
570 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
570 this.setActionButtonsDisabled = function(state, excludeCancelBtn, submitEvent) {
571 excludeCancelBtn = excludeCancelBtn || false;
571 excludeCancelBtn = excludeCancelBtn || false;
572 submitEvent = submitEvent || false;
572 submitEvent = submitEvent || false;
573
573
574 $(this.editButton).prop('disabled', state);
574 $(this.editButton).prop('disabled', state);
575 $(this.previewButton).prop('disabled', state);
575 $(this.previewButton).prop('disabled', state);
576
576
577 if (!excludeCancelBtn) {
577 if (!excludeCancelBtn) {
578 $(this.cancelButton).prop('disabled', state);
578 $(this.cancelButton).prop('disabled', state);
579 }
579 }
580
580
581 var submitState = state;
581 var submitState = state;
582 if (!submitEvent && this.getCommentStatus() && !this.lineNo) {
582 if (!submitEvent && this.getCommentStatus() && !this.lineNo) {
583 // if the value of commit review status is set, we allow
583 // if the value of commit review status is set, we allow
584 // submit button, but only on Main form, lineNo means inline
584 // submit button, but only on Main form, lineNo means inline
585 submitState = false
585 submitState = false
586 }
586 }
587 $(this.submitButton).prop('disabled', submitState);
587 $(this.submitButton).prop('disabled', submitState);
588 if (submitEvent) {
588 if (submitEvent) {
589 $(this.submitButton).val(_TM['Submitting...']);
589 $(this.submitButton).val(_gettext('Submitting...'));
590 } else {
590 } else {
591 $(this.submitButton).val(this.submitButtonText);
591 $(this.submitButton).val(this.submitButtonText);
592 }
592 }
593
593
594 };
594 };
595
595
596 // lock preview/edit/submit buttons on load, but exclude cancel button
596 // lock preview/edit/submit buttons on load, but exclude cancel button
597 var excludeCancelBtn = true;
597 var excludeCancelBtn = true;
598 this.setActionButtonsDisabled(true, excludeCancelBtn);
598 this.setActionButtonsDisabled(true, excludeCancelBtn);
599
599
600 // anonymous users don't have access to initialized CM instance
600 // anonymous users don't have access to initialized CM instance
601 if (this.cm !== undefined){
601 if (this.cm !== undefined){
602 this.cm.on('change', function(cMirror) {
602 this.cm.on('change', function(cMirror) {
603 if (cMirror.getValue() === "") {
603 if (cMirror.getValue() === "") {
604 self.setActionButtonsDisabled(true, excludeCancelBtn)
604 self.setActionButtonsDisabled(true, excludeCancelBtn)
605 } else {
605 } else {
606 self.setActionButtonsDisabled(false, excludeCancelBtn)
606 self.setActionButtonsDisabled(false, excludeCancelBtn)
607 }
607 }
608 });
608 });
609 }
609 }
610
610
611 $(this.editButton).on('click', function(e) {
611 $(this.editButton).on('click', function(e) {
612 e.preventDefault();
612 e.preventDefault();
613
613
614 $(self.previewButton).show();
614 $(self.previewButton).show();
615 $(self.previewContainer).hide();
615 $(self.previewContainer).hide();
616 $(self.editButton).hide();
616 $(self.editButton).hide();
617 $(self.editContainer).show();
617 $(self.editContainer).show();
618
618
619 });
619 });
620
620
621 $(this.previewButton).on('click', function(e) {
621 $(this.previewButton).on('click', function(e) {
622 e.preventDefault();
622 e.preventDefault();
623 var text = self.cm.getValue();
623 var text = self.cm.getValue();
624
624
625 if (text === "") {
625 if (text === "") {
626 return;
626 return;
627 }
627 }
628
628
629 var postData = {
629 var postData = {
630 'text': text,
630 'text': text,
631 'renderer': DEFAULT_RENDERER,
631 'renderer': DEFAULT_RENDERER,
632 'csrf_token': CSRF_TOKEN
632 'csrf_token': CSRF_TOKEN
633 };
633 };
634
634
635 // lock ALL buttons on preview
635 // lock ALL buttons on preview
636 self.setActionButtonsDisabled(true);
636 self.setActionButtonsDisabled(true);
637
637
638 $(self.previewBoxSelector).addClass('unloaded');
638 $(self.previewBoxSelector).addClass('unloaded');
639 $(self.previewBoxSelector).html(_TM['Loading ...']);
639 $(self.previewBoxSelector).html(_gettext('Loading ...'));
640 $(self.editContainer).hide();
640 $(self.editContainer).hide();
641 $(self.previewContainer).show();
641 $(self.previewContainer).show();
642
642
643 // by default we reset state of comment preserving the text
643 // by default we reset state of comment preserving the text
644 var previewFailCallback = function(){
644 var previewFailCallback = function(){
645 self.resetCommentFormState(text)
645 self.resetCommentFormState(text)
646 };
646 };
647 self.submitAjaxPOST(
647 self.submitAjaxPOST(
648 self.previewUrl, postData, self.previewSuccessCallback, previewFailCallback);
648 self.previewUrl, postData, self.previewSuccessCallback, previewFailCallback);
649
649
650 });
650 });
651
651
652 $(this.submitForm).submit(function(e) {
652 $(this.submitForm).submit(function(e) {
653 e.preventDefault();
653 e.preventDefault();
654 var allowedToSubmit = self.isAllowedToSubmit();
654 var allowedToSubmit = self.isAllowedToSubmit();
655 if (!allowedToSubmit){
655 if (!allowedToSubmit){
656 return false;
656 return false;
657 }
657 }
658 self.handleFormSubmit();
658 self.handleFormSubmit();
659 });
659 });
660
660
661 }
661 }
662
662
663 return CommentForm;
663 return CommentForm;
664 })();
664 })();
@@ -1,309 +1,309 b''
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 /**
19 /**
20 * Search file list
20 * Search file list
21 */
21 */
22 // global reference to file-node filter
22 // global reference to file-node filter
23 var _NODEFILTER = {};
23 var _NODEFILTER = {};
24
24
25 var fileBrowserListeners = function(node_list_url, url_base){
25 var fileBrowserListeners = function(node_list_url, url_base){
26 var n_filter = $('#node_filter').get(0);
26 var n_filter = $('#node_filter').get(0);
27
27
28 _NODEFILTER.filterTimeout = null;
28 _NODEFILTER.filterTimeout = null;
29 var nodes = null;
29 var nodes = null;
30
30
31 _NODEFILTER.fetchNodes = function(callback) {
31 _NODEFILTER.fetchNodes = function(callback) {
32 $.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': true}})
32 $.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': true}})
33 .done(function(data){
33 .done(function(data){
34 nodes = data.nodes;
34 nodes = data.nodes;
35 if (callback) {
35 if (callback) {
36 callback();
36 callback();
37 }
37 }
38 })
38 })
39 .fail(function(data){
39 .fail(function(data){
40 console.log('failed to load');
40 console.log('failed to load');
41 });
41 });
42 };
42 };
43
43
44 _NODEFILTER.fetchNodesCallback = function() {
44 _NODEFILTER.fetchNodesCallback = function() {
45 $('#node_filter_box_loading').hide();
45 $('#node_filter_box_loading').hide();
46 $('#node_filter_box').removeClass('hidden').show();
46 $('#node_filter_box').removeClass('hidden').show();
47 n_filter.focus();
47 n_filter.focus();
48 if ($('#node_filter').hasClass('init')){
48 if ($('#node_filter').hasClass('init')){
49 n_filter.value = '';
49 n_filter.value = '';
50 $('#node_filter').removeClass('init');
50 $('#node_filter').removeClass('init');
51 }
51 }
52 };
52 };
53
53
54 _NODEFILTER.initFilter = function(){
54 _NODEFILTER.initFilter = function(){
55 $('#node_filter_box_loading').removeClass('hidden').show();
55 $('#node_filter_box_loading').removeClass('hidden').show();
56 $('#search_activate_id').hide();
56 $('#search_activate_id').hide();
57 $('#search_deactivate_id').removeClass('hidden').show();
57 $('#search_deactivate_id').removeClass('hidden').show();
58 $('#add_node_id').hide();
58 $('#add_node_id').hide();
59 _NODEFILTER.fetchNodes(_NODEFILTER.fetchNodesCallback);
59 _NODEFILTER.fetchNodes(_NODEFILTER.fetchNodesCallback);
60 };
60 };
61
61
62 _NODEFILTER.resetFilter = function(){
62 _NODEFILTER.resetFilter = function(){
63 $('#node_filter_box_loading').hide();
63 $('#node_filter_box_loading').hide();
64 $('#node_filter_box').hide();
64 $('#node_filter_box').hide();
65 $('#search_activate_id').show();
65 $('#search_activate_id').show();
66 $('#search_deactivate_id').hide();
66 $('#search_deactivate_id').hide();
67 $('#add_node_id').show();
67 $('#add_node_id').show();
68 $('#tbody').show();
68 $('#tbody').show();
69 $('#tbody_filtered').hide();
69 $('#tbody_filtered').hide();
70 $('#node_filter').val('');
70 $('#node_filter').val('');
71 };
71 };
72
72
73 _NODEFILTER.fuzzy_match = function(filepath, query) {
73 _NODEFILTER.fuzzy_match = function(filepath, query) {
74 var highlight = [];
74 var highlight = [];
75 var order = 0;
75 var order = 0;
76 for (var i = 0; i < query.length; i++) {
76 for (var i = 0; i < query.length; i++) {
77 var match_position = filepath.indexOf(query[i]);
77 var match_position = filepath.indexOf(query[i]);
78 if (match_position !== -1) {
78 if (match_position !== -1) {
79 var prev_match_position = highlight[highlight.length-1];
79 var prev_match_position = highlight[highlight.length-1];
80 if (prev_match_position === undefined) {
80 if (prev_match_position === undefined) {
81 highlight.push(match_position);
81 highlight.push(match_position);
82 } else {
82 } else {
83 var current_match_position = prev_match_position + match_position + 1;
83 var current_match_position = prev_match_position + match_position + 1;
84 highlight.push(current_match_position);
84 highlight.push(current_match_position);
85 order = order + current_match_position - prev_match_position;
85 order = order + current_match_position - prev_match_position;
86 }
86 }
87 filepath = filepath.substring(match_position+1);
87 filepath = filepath.substring(match_position+1);
88 } else {
88 } else {
89 return false;
89 return false;
90 }
90 }
91 }
91 }
92 return {'order': order,
92 return {'order': order,
93 'highlight': highlight};
93 'highlight': highlight};
94 };
94 };
95
95
96 _NODEFILTER.sortPredicate = function(a, b) {
96 _NODEFILTER.sortPredicate = function(a, b) {
97 if (a.order < b.order) return -1;
97 if (a.order < b.order) return -1;
98 if (a.order > b.order) return 1;
98 if (a.order > b.order) return 1;
99 if (a.filepath < b.filepath) return -1;
99 if (a.filepath < b.filepath) return -1;
100 if (a.filepath > b.filepath) return 1;
100 if (a.filepath > b.filepath) return 1;
101 return 0;
101 return 0;
102 };
102 };
103
103
104 _NODEFILTER.updateFilter = function(elem, e) {
104 _NODEFILTER.updateFilter = function(elem, e) {
105 return function(){
105 return function(){
106 // Reset timeout
106 // Reset timeout
107 _NODEFILTER.filterTimeout = null;
107 _NODEFILTER.filterTimeout = null;
108 var query = elem.value.toLowerCase();
108 var query = elem.value.toLowerCase();
109 var match = [];
109 var match = [];
110 var matches_max = 20;
110 var matches_max = 20;
111 if (query !== ""){
111 if (query !== ""){
112 var results = [];
112 var results = [];
113 for(var k=0;k<nodes.length;k++){
113 for(var k=0;k<nodes.length;k++){
114 var result = _NODEFILTER.fuzzy_match(
114 var result = _NODEFILTER.fuzzy_match(
115 nodes[k].name.toLowerCase(), query);
115 nodes[k].name.toLowerCase(), query);
116 if (result) {
116 if (result) {
117 result.type = nodes[k].type;
117 result.type = nodes[k].type;
118 result.filepath = nodes[k].name;
118 result.filepath = nodes[k].name;
119 results.push(result);
119 results.push(result);
120 }
120 }
121 }
121 }
122 results = results.sort(_NODEFILTER.sortPredicate);
122 results = results.sort(_NODEFILTER.sortPredicate);
123 var limit = matches_max;
123 var limit = matches_max;
124 if (results.length < matches_max) {
124 if (results.length < matches_max) {
125 limit = results.length;
125 limit = results.length;
126 }
126 }
127 for (var i=0; i<limit; i++){
127 for (var i=0; i<limit; i++){
128 if(query && results.length > 0){
128 if(query && results.length > 0){
129 var n = results[i].filepath;
129 var n = results[i].filepath;
130 var t = results[i].type;
130 var t = results[i].type;
131 var n_hl = n.split("");
131 var n_hl = n.split("");
132 var pos = results[i].highlight;
132 var pos = results[i].highlight;
133 for (var j = 0; j < pos.length; j++) {
133 for (var j = 0; j < pos.length; j++) {
134 n_hl[pos[j]] = "<em>" + n_hl[pos[j]] + "</em>";
134 n_hl[pos[j]] = "<em>" + n_hl[pos[j]] + "</em>";
135 }
135 }
136 n_hl = n_hl.join("");
136 n_hl = n_hl.join("");
137 var new_url = url_base.replace('__FPATH__',n);
137 var new_url = url_base.replace('__FPATH__',n);
138
138
139 var typeObj = {
139 var typeObj = {
140 dir: 'icon-folder browser-dir',
140 dir: 'icon-folder browser-dir',
141 file: 'icon-file browser-file'
141 file: 'icon-file browser-file'
142 };
142 };
143 var typeIcon = '<i class="{0}"></i>'.format(typeObj[t]);
143 var typeIcon = '<i class="{0}"></i>'.format(typeObj[t]);
144 match.push('<tr class="browser-result"><td><a class="browser-{0} pjax-link" href="{1}">{2}{3}</a></td><td colspan="5"></td></tr>'.format(t,new_url,typeIcon, n_hl));
144 match.push('<tr class="browser-result"><td><a class="browser-{0} pjax-link" href="{1}">{2}{3}</a></td><td colspan="5"></td></tr>'.format(t,new_url,typeIcon, n_hl));
145 }
145 }
146 }
146 }
147 if(results.length > limit){
147 if(results.length > limit){
148 var truncated_count = results.length - matches_max;
148 var truncated_count = results.length - matches_max;
149 if (truncated_count === 1) {
149 if (truncated_count === 1) {
150 match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _TM['truncated result']));
150 match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated result')));
151 } else {
151 } else {
152 match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _TM['truncated results']));
152 match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated results')));
153 }
153 }
154 }
154 }
155 }
155 }
156 if (query !== ""){
156 if (query !== ""){
157 $('#tbody').hide();
157 $('#tbody').hide();
158 $('#tbody_filtered').show();
158 $('#tbody_filtered').show();
159
159
160 if (match.length === 0){
160 if (match.length === 0){
161 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
161 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_gettext('No matching files')));
162 }
162 }
163 $('#tbody_filtered').html(match.join(""));
163 $('#tbody_filtered').html(match.join(""));
164 }
164 }
165 else{
165 else{
166 $('#tbody').show();
166 $('#tbody').show();
167 $('#tbody_filtered').hide();
167 $('#tbody_filtered').hide();
168 }
168 }
169
169
170 };
170 };
171 };
171 };
172
172
173 var scrollDown = function(element){
173 var scrollDown = function(element){
174 var elementBottom = element.offset().top + $(element).outerHeight();
174 var elementBottom = element.offset().top + $(element).outerHeight();
175 var windowBottom = window.innerHeight + $(window).scrollTop();
175 var windowBottom = window.innerHeight + $(window).scrollTop();
176 if (elementBottom > windowBottom) {
176 if (elementBottom > windowBottom) {
177 var offset = elementBottom - window.innerHeight;
177 var offset = elementBottom - window.innerHeight;
178 $('html,body').scrollTop(offset);
178 $('html,body').scrollTop(offset);
179 return false;
179 return false;
180 }
180 }
181 return true;
181 return true;
182 };
182 };
183
183
184 var scrollUp = function(element){
184 var scrollUp = function(element){
185 if (element.offset().top < $(window).scrollTop()) {
185 if (element.offset().top < $(window).scrollTop()) {
186 $('html,body').scrollTop(element.offset().top);
186 $('html,body').scrollTop(element.offset().top);
187 return false;
187 return false;
188 }
188 }
189 return true;
189 return true;
190 };
190 };
191
191
192 $('#filter_activate').click(function() {
192 $('#filter_activate').click(function() {
193 _NODEFILTER.initFilter();
193 _NODEFILTER.initFilter();
194 });
194 });
195
195
196 $('#filter_deactivate').click(function() {
196 $('#filter_deactivate').click(function() {
197 _NODEFILTER.resetFilter();
197 _NODEFILTER.resetFilter();
198 });
198 });
199
199
200 $(n_filter).click(function() {
200 $(n_filter).click(function() {
201 if ($('#node_filter').hasClass('init')){
201 if ($('#node_filter').hasClass('init')){
202 n_filter.value = '';
202 n_filter.value = '';
203 $('#node_filter').removeClass('init');
203 $('#node_filter').removeClass('init');
204 }
204 }
205 });
205 });
206
206
207 $(n_filter).keydown(function(e) {
207 $(n_filter).keydown(function(e) {
208 if (e.keyCode === 40){ // Down
208 if (e.keyCode === 40){ // Down
209 if ($('.browser-highlight').length === 0){
209 if ($('.browser-highlight').length === 0){
210 $('.browser-result').first().addClass('browser-highlight');
210 $('.browser-result').first().addClass('browser-highlight');
211 } else {
211 } else {
212 var next = $('.browser-highlight').next();
212 var next = $('.browser-highlight').next();
213 if (next.length !== 0) {
213 if (next.length !== 0) {
214 $('.browser-highlight').removeClass('browser-highlight');
214 $('.browser-highlight').removeClass('browser-highlight');
215 next.addClass('browser-highlight');
215 next.addClass('browser-highlight');
216 }
216 }
217 }
217 }
218 scrollDown($('.browser-highlight'));
218 scrollDown($('.browser-highlight'));
219 }
219 }
220 if (e.keyCode === 38){ // Up
220 if (e.keyCode === 38){ // Up
221 e.preventDefault();
221 e.preventDefault();
222 if ($('.browser-highlight').length !== 0){
222 if ($('.browser-highlight').length !== 0){
223 var next = $('.browser-highlight').prev();
223 var next = $('.browser-highlight').prev();
224 if (next.length !== 0) {
224 if (next.length !== 0) {
225 $('.browser-highlight').removeClass('browser-highlight');
225 $('.browser-highlight').removeClass('browser-highlight');
226 next.addClass('browser-highlight');
226 next.addClass('browser-highlight');
227 }
227 }
228 }
228 }
229 scrollUp($('.browser-highlight'));
229 scrollUp($('.browser-highlight'));
230 }
230 }
231 if (e.keyCode === 13){ // Enter
231 if (e.keyCode === 13){ // Enter
232 if ($('.browser-highlight').length !== 0){
232 if ($('.browser-highlight').length !== 0){
233 var url = $('.browser-highlight').find('.pjax-link').attr('href');
233 var url = $('.browser-highlight').find('.pjax-link').attr('href');
234 $.pjax({url: url, container: '#pjax-container', timeout: pjaxTimeout});
234 $.pjax({url: url, container: '#pjax-container', timeout: pjaxTimeout});
235 }
235 }
236 }
236 }
237 if (e.keyCode === 27){ // Esc
237 if (e.keyCode === 27){ // Esc
238 _NODEFILTER.resetFilter();
238 _NODEFILTER.resetFilter();
239 $('html,body').scrollTop(0);
239 $('html,body').scrollTop(0);
240 }
240 }
241 });
241 });
242 var capture_keys = [40, 38, 39, 37, 13, 27];
242 var capture_keys = [40, 38, 39, 37, 13, 27];
243 $(n_filter).keyup(function(e) {
243 $(n_filter).keyup(function(e) {
244 if ($.inArray(e.keyCode, capture_keys) === -1){
244 if ($.inArray(e.keyCode, capture_keys) === -1){
245 clearTimeout(_NODEFILTER.filterTimeout);
245 clearTimeout(_NODEFILTER.filterTimeout);
246 _NODEFILTER.filterTimeout = setTimeout(_NODEFILTER.updateFilter(n_filter, e),200);
246 _NODEFILTER.filterTimeout = setTimeout(_NODEFILTER.updateFilter(n_filter, e),200);
247 }
247 }
248 });
248 });
249 };
249 };
250
250
251 var getIdentNode = function(n){
251 var getIdentNode = function(n){
252 // iterate through nodes until matched interesting node
252 // iterate through nodes until matched interesting node
253 if (typeof n === 'undefined'){
253 if (typeof n === 'undefined'){
254 return -1;
254 return -1;
255 }
255 }
256 if(typeof n.id !== "undefined" && n.id.match('L[0-9]+')){
256 if(typeof n.id !== "undefined" && n.id.match('L[0-9]+')){
257 return n;
257 return n;
258 }
258 }
259 else{
259 else{
260 return getIdentNode(n.parentNode);
260 return getIdentNode(n.parentNode);
261 }
261 }
262 };
262 };
263
263
264 var getSelectionLink = function(e) {
264 var getSelectionLink = function(e) {
265 // get selection from start/to nodes
265 // get selection from start/to nodes
266 if (typeof window.getSelection !== "undefined") {
266 if (typeof window.getSelection !== "undefined") {
267 s = window.getSelection();
267 s = window.getSelection();
268
268
269 from = getIdentNode(s.anchorNode);
269 from = getIdentNode(s.anchorNode);
270 till = getIdentNode(s.focusNode);
270 till = getIdentNode(s.focusNode);
271
271
272 f_int = parseInt(from.id.replace('L',''));
272 f_int = parseInt(from.id.replace('L',''));
273 t_int = parseInt(till.id.replace('L',''));
273 t_int = parseInt(till.id.replace('L',''));
274
274
275 if (f_int > t_int){
275 if (f_int > t_int){
276 // highlight from bottom
276 // highlight from bottom
277 offset = -35;
277 offset = -35;
278 ranges = [t_int,f_int];
278 ranges = [t_int,f_int];
279 }
279 }
280 else{
280 else{
281 // highligth from top
281 // highligth from top
282 offset = 35;
282 offset = 35;
283 ranges = [f_int,t_int];
283 ranges = [f_int,t_int];
284 }
284 }
285 // if we select more than 2 lines
285 // if we select more than 2 lines
286 if (ranges[0] !== ranges[1]){
286 if (ranges[0] !== ranges[1]){
287 if($('#linktt').length === 0){
287 if($('#linktt').length === 0){
288 hl_div = document.createElement('div');
288 hl_div = document.createElement('div');
289 hl_div.id = 'linktt';
289 hl_div.id = 'linktt';
290 }
290 }
291 hl_div.innerHTML = '';
291 hl_div.innerHTML = '';
292
292
293 anchor = '#L'+ranges[0]+'-'+ranges[1];
293 anchor = '#L'+ranges[0]+'-'+ranges[1];
294 var link = document.createElement('a');
294 var link = document.createElement('a');
295 link.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
295 link.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
296 link.innerHTML = _TM['Selection link'];
296 link.innerHTML = _gettext('Selection link');
297 hl_div.appendChild(link);
297 hl_div.appendChild(link);
298 $('#codeblock').append(hl_div);
298 $('#codeblock').append(hl_div);
299
299
300 var xy = $(till).offset();
300 var xy = $(till).offset();
301 $('#linktt').addClass('hl-tip-box tip-box');
301 $('#linktt').addClass('hl-tip-box tip-box');
302 $('#linktt').offset({top: xy.top + offset, left: xy.left});
302 $('#linktt').offset({top: xy.top + offset, left: xy.left});
303 $('#linktt').css('visibility','visible');
303 $('#linktt').css('visibility','visible');
304 }
304 }
305 else{
305 else{
306 $('#linktt').css('visibility','hidden');
306 $('#linktt').css('visibility','hidden');
307 }
307 }
308 }
308 }
309 };
309 };
@@ -1,74 +1,74 b''
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 var onSuccessFollow = function(target){
19 var onSuccessFollow = function(target){
20 var f = $(target);
20 var f = $(target);
21 var f_cnt = $('#current_followers_count');
21 var f_cnt = $('#current_followers_count');
22
22
23 if(f.hasClass('follow')){
23 if(f.hasClass('follow')){
24 f.removeClass('follow');
24 f.removeClass('follow');
25 f.addClass('following');
25 f.addClass('following');
26 f.attr('title', _TM['Stop following this repository']);
26 f.attr('title', _gettext('Stop following this repository'));
27 $(f).html(_TM['Unfollow']);
27 $(f).html(_gettext('Unfollow'));
28 if(f_cnt.length){
28 if(f_cnt.length){
29 var cnt = Number(f_cnt.html())+1;
29 var cnt = Number(f_cnt.html())+1;
30 f_cnt.html(cnt);
30 f_cnt.html(cnt);
31 }
31 }
32 }
32 }
33 else{
33 else{
34 f.removeClass('following');
34 f.removeClass('following');
35 f.addClass('follow');
35 f.addClass('follow');
36 f.attr('title', _TM['Start following this repository']);
36 f.attr('title', _gettext('Start following this repository'));
37 $(f).html(_TM['Follow']);
37 $(f).html(_gettext('Follow'));
38 if(f_cnt.length){
38 if(f_cnt.length){
39 var cnt = Number(f_cnt.html())-1;
39 var cnt = Number(f_cnt.html())-1;
40 f_cnt.html(cnt);
40 f_cnt.html(cnt);
41 }
41 }
42 }
42 }
43 };
43 };
44
44
45 // TODO:: check if the function is needed. 0 usage found
45 // TODO:: check if the function is needed. 0 usage found
46 var toggleFollowingUser = function(target,follows_user_id,token,user_id){
46 var toggleFollowingUser = function(target,follows_user_id,token,user_id){
47 var args = {
47 var args = {
48 'follows_user_id': follows_user_id,
48 'follows_user_id': follows_user_id,
49 'auth_token': token,
49 'auth_token': token,
50 'csrf_token': CSRF_TOKEN
50 'csrf_token': CSRF_TOKEN
51 };
51 };
52 if(user_id != undefined){
52 if(user_id != undefined){
53 args.user_id = user_id
53 args.user_id = user_id
54 }
54 }
55 ajaxPOST(pyroutes.url('toggle_following'), args, function(){
55 ajaxPOST(pyroutes.url('toggle_following'), args, function(){
56 onSuccessFollow(target);
56 onSuccessFollow(target);
57 });
57 });
58 return false;
58 return false;
59 };
59 };
60
60
61 var toggleFollowingRepo = function(target,follows_repo_id,token,user_id){
61 var toggleFollowingRepo = function(target,follows_repo_id,token,user_id){
62 var args = {
62 var args = {
63 'follows_repo_id': follows_repo_id,
63 'follows_repo_id': follows_repo_id,
64 'auth_token': token,
64 'auth_token': token,
65 'csrf_token': CSRF_TOKEN
65 'csrf_token': CSRF_TOKEN
66 };
66 };
67 if(user_id != undefined){
67 if(user_id != undefined){
68 args.user_id = user_id
68 args.user_id = user_id
69 }
69 }
70 ajaxPOST(pyroutes.url('toggle_following'), args, function(){
70 ajaxPOST(pyroutes.url('toggle_following'), args, function(){
71 onSuccessFollow(target);
71 onSuccessFollow(target);
72 });
72 });
73 return false;
73 return false;
74 };
74 };
@@ -1,126 +1,126 b''
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
1 // # Copyright (C) 2010-2016 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 /**
19 /**
20 * TOOLTIP IMPL.
20 * TOOLTIP IMPL.
21 */
21 */
22
22
23 var TTIP = {};
23 var TTIP = {};
24
24
25 TTIP.main = {
25 TTIP.main = {
26 offset: [15,15],
26 offset: [15,15],
27 maxWidth: 600,
27 maxWidth: 600,
28
28
29 set_listeners: function(tt){
29 set_listeners: function(tt){
30 $(tt).mouseover(tt, yt.show_tip);
30 $(tt).mouseover(tt, yt.show_tip);
31 $(tt).mousemove(tt, yt.move_tip);
31 $(tt).mousemove(tt, yt.move_tip);
32 $(tt).mouseout(tt, yt.close_tip);
32 $(tt).mouseout(tt, yt.close_tip);
33 },
33 },
34
34
35 init: function(){
35 init: function(){
36 $('#tip-box').remove();
36 $('#tip-box').remove();
37 yt.tipBox = document.createElement('div');
37 yt.tipBox = document.createElement('div');
38 document.body.appendChild(yt.tipBox);
38 document.body.appendChild(yt.tipBox);
39 yt.tipBox.id = 'tip-box';
39 yt.tipBox.id = 'tip-box';
40
40
41 $(yt.tipBox).hide();
41 $(yt.tipBox).hide();
42 $(yt.tipBox).css('position', 'absolute');
42 $(yt.tipBox).css('position', 'absolute');
43 if(yt.maxWidth !== null){
43 if(yt.maxWidth !== null){
44 $(yt.tipBox).css('max-width', yt.maxWidth+'px');
44 $(yt.tipBox).css('max-width', yt.maxWidth+'px');
45 }
45 }
46
46
47 var tooltips = $('.tooltip');
47 var tooltips = $('.tooltip');
48 var ttLen = tooltips.length;
48 var ttLen = tooltips.length;
49
49
50 for(i=0;i<ttLen;i++){
50 for(i=0;i<ttLen;i++){
51 yt.set_listeners(tooltips[i]);
51 yt.set_listeners(tooltips[i]);
52 }
52 }
53 },
53 },
54
54
55 show_tip: function(e, el){
55 show_tip: function(e, el){
56 e.stopPropagation();
56 e.stopPropagation();
57 e.preventDefault();
57 e.preventDefault();
58 var el = e.data || el;
58 var el = e.data || el;
59 if(el.tagName.toLowerCase() === 'img'){
59 if(el.tagName.toLowerCase() === 'img'){
60 yt.tipText = el.alt ? el.alt : '';
60 yt.tipText = el.alt ? el.alt : '';
61 } else {
61 } else {
62 yt.tipText = el.title ? el.title : '';
62 yt.tipText = el.title ? el.title : '';
63 }
63 }
64
64
65 if(yt.tipText !== ''){
65 if(yt.tipText !== ''){
66 // save org title
66 // save org title
67 $(el).attr('tt_title', yt.tipText);
67 $(el).attr('tt_title', yt.tipText);
68 // reset title to not show org tooltips
68 // reset title to not show org tooltips
69 $(el).attr('title', '');
69 $(el).attr('title', '');
70
70
71 yt.tipBox.innerHTML = yt.tipText;
71 yt.tipBox.innerHTML = yt.tipText;
72 $(yt.tipBox).show();
72 $(yt.tipBox).show();
73 }
73 }
74 },
74 },
75
75
76 move_tip: function(e, el){
76 move_tip: function(e, el){
77 e.stopPropagation();
77 e.stopPropagation();
78 e.preventDefault();
78 e.preventDefault();
79 var el = e.data || el;
79 var el = e.data || el;
80 var movePos = [e.pageX, e.pageY];
80 var movePos = [e.pageX, e.pageY];
81 $(yt.tipBox).css('top', (movePos[1] + yt.offset[1]) + 'px')
81 $(yt.tipBox).css('top', (movePos[1] + yt.offset[1]) + 'px')
82 $(yt.tipBox).css('left', (movePos[0] + yt.offset[0]) + 'px')
82 $(yt.tipBox).css('left', (movePos[0] + yt.offset[0]) + 'px')
83 },
83 },
84
84
85 close_tip: function(e, el){
85 close_tip: function(e, el){
86 e.stopPropagation();
86 e.stopPropagation();
87 e.preventDefault();
87 e.preventDefault();
88 var el = e.data || el;
88 var el = e.data || el;
89 $(yt.tipBox).hide();
89 $(yt.tipBox).hide();
90 $(el).attr('title', $(el).attr('tt_title'));
90 $(el).attr('title', $(el).attr('tt_title'));
91 $('#tip-box').hide();
91 $('#tip-box').hide();
92 }
92 }
93 };
93 };
94
94
95 /**
95 /**
96 * tooltip activate
96 * tooltip activate
97 */
97 */
98 var tooltip_activate = function(){
98 var tooltip_activate = function(){
99 yt = TTIP.main;
99 yt = TTIP.main;
100 $(document).ready(yt.init);
100 $(document).ready(yt.init);
101 };
101 };
102
102
103 /**
103 /**
104 * show changeset tooltip
104 * show changeset tooltip
105 */
105 */
106 var show_changeset_tooltip = function(){
106 var show_changeset_tooltip = function(){
107 $('.lazy-cs').mouseover(function(e) {
107 $('.lazy-cs').mouseover(function(e) {
108 var target = e.currentTarget;
108 var target = e.currentTarget;
109 var rid = $(target).attr('raw_id');
109 var rid = $(target).attr('raw_id');
110 var repo_name = $(target).attr('repo_name');
110 var repo_name = $(target).attr('repo_name');
111 var ttid = 'tt-'+rid;
111 var ttid = 'tt-'+rid;
112 var success = function(o){
112 var success = function(o){
113 $(target).addClass('tooltip')
113 $(target).addClass('tooltip')
114 $(target).attr('title', o['message']);
114 $(target).attr('title', o['message']);
115 TTIP.main.show_tip(e, target);
115 TTIP.main.show_tip(e, target);
116 }
116 }
117 if(rid && !$(target).hasClass('tooltip')){
117 if(rid && !$(target).hasClass('tooltip')){
118 $(target).attr('id', ttid);
118 $(target).attr('id', ttid);
119 $(target).attr('title', _TM['loading ...']);
119 $(target).attr('title', _gettext('loading ...'));
120 TTIP.main.set_listeners(target);
120 TTIP.main.set_listeners(target);
121 TTIP.main.show_tip(e, target);
121 TTIP.main.show_tip(e, target);
122 var url = pyroutes.url('changeset_info', {"repo_name":repo_name, "revision": rid});
122 var url = pyroutes.url('changeset_info', {"repo_name":repo_name, "revision": rid});
123 ajaxGET(url, success);
123 ajaxGET(url, success);
124 }
124 }
125 });
125 });
126 };
126 };
@@ -1,116 +1,116 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Authentication Settings')}
5 ${_('Authentication Settings')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}}
7 &middot; ${h.branding(c.rhodecode_name)}}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Authentication Plugins')}
14 ${_('Authentication Plugins')}
15 </%def>
15 </%def>
16
16
17 <%def name="menu_bar_nav()">
17 <%def name="menu_bar_nav()">
18 ${self.menu_items(active='admin')}
18 ${self.menu_items(active='admin')}
19 </%def>
19 </%def>
20
20
21 <%def name="main()">
21 <%def name="main()">
22
22
23 <div class="box">
23 <div class="box">
24 <div class="title">
24 <div class="title">
25 ${self.breadcrumbs()}
25 ${self.breadcrumbs()}
26 </div>
26 </div>
27
27
28 <div class='sidebar-col-wrapper'>
28 <div class='sidebar-col-wrapper'>
29
29
30 <div class="sidebar">
30 <div class="sidebar">
31 <ul class="nav nav-pills nav-stacked">
31 <ul class="nav nav-pills nav-stacked">
32 % for item in resource.get_root().get_nav_list():
32 % for item in resource.get_root().get_nav_list():
33 <li ${'class=active' if item == resource else ''}>
33 <li ${'class=active' if item == resource else ''}>
34 <a href="${request.resource_path(item, route_name='auth_home')}">${item.display_name}</a>
34 <a href="${request.resource_path(item, route_name='auth_home')}">${item.display_name}</a>
35 </li>
35 </li>
36 % endfor
36 % endfor
37 </ul>
37 </ul>
38 </div>
38 </div>
39
39
40 <div class="main-content-full-width">
40 <div class="main-content-full-width">
41 ${h.secure_form(request.resource_path(resource, route_name='auth_home'))}
41 ${h.secure_form(request.resource_path(resource, route_name='auth_home'))}
42 <div class="form">
42 <div class="form">
43
43
44 <div class="panel panel-default">
44 <div class="panel panel-default">
45
45
46 <div class="panel-heading">
46 <div class="panel-heading">
47 <h3 class="panel-title">${_("Enabled and Available Plugins")}</h3>
47 <h3 class="panel-title">${_("Enabled and Available Plugins")}</h3>
48 </div>
48 </div>
49
49
50 <div class="fields panel-body">
50 <div class="fields panel-body">
51
51
52 <div class="field">
52 <div class="field">
53 <div class="label">${_("Enabled Plugins")}</div>
53 <div class="label">${_("Enabled Plugins")}</div>
54 <div class="textarea text-area editor">
54 <div class="textarea text-area editor">
55 ${h.textarea('auth_plugins',cols=23,rows=5,class_="medium")}
55 ${h.textarea('auth_plugins',cols=23,rows=5,class_="medium")}
56 </div>
56 </div>
57 <p class="help-block">
57 <p class="help-block">
58 ${_('Add a list of plugins, separated by commas. '
58 ${_('Add a list of plugins, separated by commas. '
59 'The order of the plugins is also the order in which '
59 'The order of the plugins is also the order in which '
60 'RhodeCode Enterprise will try to authenticate a user.')}
60 'RhodeCode Enterprise will try to authenticate a user.')}
61 </p>
61 </p>
62 </div>
62 </div>
63
63
64 <div class="field">
64 <div class="field">
65 <div class="label">${_('Available Built-in Plugins')}</div>
65 <div class="label">${_('Available Built-in Plugins')}</div>
66 <ul class="auth_plugins">
66 <ul class="auth_plugins">
67 %for plugin in available_plugins:
67 %for plugin in available_plugins:
68 <li>
68 <li>
69 <div class="auth_buttons">
69 <div class="auth_buttons">
70 <span plugin_id="${plugin.get_id()}" class="toggle-plugin btn ${'btn-success' if plugin.get_id() in enabled_plugins else ''}">
70 <span plugin_id="${plugin.get_id()}" class="toggle-plugin btn ${'btn-success' if plugin.get_id() in enabled_plugins else ''}">
71 ${_('enabled') if plugin.get_id() in enabled_plugins else _('disabled')}
71 ${_('enabled') if plugin.get_id() in enabled_plugins else _('disabled')}
72 </span>
72 </span>
73 ${plugin.get_display_name()} (${plugin.get_id()})
73 ${plugin.get_display_name()} (${plugin.get_id()})
74 </div>
74 </div>
75 </li>
75 </li>
76 %endfor
76 %endfor
77 </ul>
77 </ul>
78 </div>
78 </div>
79
79
80 <div class="buttons">
80 <div class="buttons">
81 ${h.submit('save',_('Save'),class_="btn")}
81 ${h.submit('save',_('Save'),class_="btn")}
82 </div>
82 </div>
83 </div>
83 </div>
84 </div>
84 </div>
85 </div>
85 </div>
86 ${h.end_form()}
86 ${h.end_form()}
87 </div>
87 </div>
88 </div>
88 </div>
89 </div>
89 </div>
90
90
91 <script>
91 <script>
92 $('.toggle-plugin').click(function(e){
92 $('.toggle-plugin').click(function(e){
93 var auth_plugins_input = $('#auth_plugins');
93 var auth_plugins_input = $('#auth_plugins');
94 var notEmpty = function(element, index, array) {
94 var notEmpty = function(element, index, array) {
95 return (element != "");
95 return (element != "");
96 }
96 }
97 var elems = auth_plugins_input.val().split(',').filter(notEmpty);
97 var elems = auth_plugins_input.val().split(',').filter(notEmpty);
98 var cur_button = e.currentTarget;
98 var cur_button = e.currentTarget;
99 var plugin_id = $(cur_button).attr('plugin_id');
99 var plugin_id = $(cur_button).attr('plugin_id');
100 if($(cur_button).hasClass('btn-success')){
100 if($(cur_button).hasClass('btn-success')){
101 elems.splice(elems.indexOf(plugin_id), 1);
101 elems.splice(elems.indexOf(plugin_id), 1);
102 auth_plugins_input.val(elems.join(','));
102 auth_plugins_input.val(elems.join(','));
103 $(cur_button).removeClass('btn-success');
103 $(cur_button).removeClass('btn-success');
104 cur_button.innerHTML = _TM['disabled'];
104 cur_button.innerHTML = _gettext('disabled');
105 }
105 }
106 else{
106 else{
107 if(elems.indexOf(plugin_id) == -1){
107 if(elems.indexOf(plugin_id) == -1){
108 elems.push(plugin_id);
108 elems.push(plugin_id);
109 }
109 }
110 auth_plugins_input.val(elems.join(','));
110 auth_plugins_input.val(elems.join(','));
111 $(cur_button).addClass('btn-success');
111 $(cur_button).addClass('btn-success');
112 cur_button.innerHTML = _TM['enabled'];
112 cur_button.innerHTML = _gettext('enabled');
113 }
113 }
114 });
114 });
115 </script>
115 </script>
116 </%def>
116 </%def>
@@ -1,141 +1,141 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Users administration')}
5 ${_('Users administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_count">0</span>
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_count">0</span>
14 </%def>
14 </%def>
15
15
16 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
17 ${self.menu_items(active='admin')}
17 ${self.menu_items(active='admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box">
21 <div class="box">
22
22
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 <ul class="links">
25 <ul class="links">
26 <li>
26 <li>
27 <a href="${h.url('new_user')}" class="btn btn-small btn-success">${_(u'Add User')}</a>
27 <a href="${h.url('new_user')}" class="btn btn-small btn-success">${_(u'Add User')}</a>
28 </li>
28 </li>
29 </ul>
29 </ul>
30 </div>
30 </div>
31
31
32 <div id="repos_list_wrap">
32 <div id="repos_list_wrap">
33 <table id="user_list_table" class="display"></table>
33 <table id="user_list_table" class="display"></table>
34 </div>
34 </div>
35 </div>
35 </div>
36
36
37 <script>
37 <script>
38 $(document).ready(function() {
38 $(document).ready(function() {
39
39
40 var get_datatable_count = function(){
40 var get_datatable_count = function(){
41 var datatable = $('#user_list_table').dataTable();
41 var datatable = $('#user_list_table').dataTable();
42 var api = datatable.api();
42 var api = datatable.api();
43 var total = api.page.info().recordsDisplay;
43 var total = api.page.info().recordsDisplay;
44 var active = datatable.fnGetFilteredData();
44 var active = datatable.fnGetFilteredData();
45
45 var _text = _gettext("{0} active out of {1} users").format(active, total);
46 $('#user_count').text(_TM["{0} active out of {1} users"].format(active, total));
46 $('#user_count').text(_text);
47 };
47 };
48
48
49 // custom filter that filters by username OR email
49 // custom filter that filters by username OR email
50 $.fn.dataTable.ext.search.push(
50 $.fn.dataTable.ext.search.push(
51 function( settings, data, dataIndex ) {
51 function( settings, data, dataIndex ) {
52 var query = $('#q_filter').val();
52 var query = $('#q_filter').val();
53 var username = data[1];
53 var username = data[1];
54 var email = data[2];
54 var email = data[2];
55 var first_name = data[3];
55 var first_name = data[3];
56 var last_name = data[4];
56 var last_name = data[4];
57
57
58 var query_str = username + " " +
58 var query_str = username + " " +
59 email + " " +
59 email + " " +
60 first_name + " " +
60 first_name + " " +
61 last_name;
61 last_name;
62 if((query_str).indexOf(query) !== -1){
62 if((query_str).indexOf(query) !== -1){
63 return true;
63 return true;
64 }
64 }
65 return false;
65 return false;
66 }
66 }
67 );
67 );
68 // filtered data plugin
68 // filtered data plugin
69 $.fn.dataTableExt.oApi.fnGetFilteredData = function ( oSettings ) {
69 $.fn.dataTableExt.oApi.fnGetFilteredData = function ( oSettings ) {
70 var res = [];
70 var res = [];
71 for ( var i=0, iLen=oSettings.fnRecordsDisplay() ; i<iLen ; i++ ) {
71 for ( var i=0, iLen=oSettings.fnRecordsDisplay() ; i<iLen ; i++ ) {
72 var record = oSettings.aoData[i]._aData;
72 var record = oSettings.aoData[i]._aData;
73 if(record['active_raw']){
73 if(record['active_raw']){
74 res.push(record);
74 res.push(record);
75 }
75 }
76 }
76 }
77 return res.length;
77 return res.length;
78 };
78 };
79
79
80 // user list
80 // user list
81 $('#user_list_table').DataTable({
81 $('#user_list_table').DataTable({
82 data: ${c.data|n},
82 data: ${c.data|n},
83 dom: 'rtp',
83 dom: 'rtp',
84 pageLength: ${c.visual.admin_grid_items},
84 pageLength: ${c.visual.admin_grid_items},
85 order: [[ 1, "asc" ]],
85 order: [[ 1, "asc" ]],
86 columns: [
86 columns: [
87 { data: {"_": "gravatar"}, className: "td-gravatar" },
87 { data: {"_": "gravatar"}, className: "td-gravatar" },
88 { data: {"_": "username",
88 { data: {"_": "username",
89 "sort": "username_raw"}, title: "${_('Username')}", className: "td-user" },
89 "sort": "username_raw"}, title: "${_('Username')}", className: "td-user" },
90 { data: {"_": "email",
90 { data: {"_": "email",
91 "sort": "email"}, title: "${_('Email')}", className: "td-email" },
91 "sort": "email"}, title: "${_('Email')}", className: "td-email" },
92 { data: {"_": "first_name",
92 { data: {"_": "first_name",
93 "sort": "first_name"}, title: "${_('First Name')}", className: "td-user" },
93 "sort": "first_name"}, title: "${_('First Name')}", className: "td-user" },
94 { data: {"_": "last_name",
94 { data: {"_": "last_name",
95 "sort": "last_name"}, title: "${_('Last Name')}", className: "td-user" },
95 "sort": "last_name"}, title: "${_('Last Name')}", className: "td-user" },
96 { data: {"_": "last_login",
96 { data: {"_": "last_login",
97 "sort": "last_login_raw",
97 "sort": "last_login_raw",
98 "type": Number}, title: "${_('Last login')}", className: "td-time" },
98 "type": Number}, title: "${_('Last login')}", className: "td-time" },
99 { data: {"_": "active",
99 { data: {"_": "active",
100 "sort": "active_raw"}, title: "${_('Active')}", className: "td-active" },
100 "sort": "active_raw"}, title: "${_('Active')}", className: "td-active" },
101 { data: {"_": "admin",
101 { data: {"_": "admin",
102 "sort": "admin_raw"}, title: "${_('Admin')}", className: "td-admin" },
102 "sort": "admin_raw"}, title: "${_('Admin')}", className: "td-admin" },
103 { data: {"_": "extern_type",
103 { data: {"_": "extern_type",
104 "sort": "extern_type"}, title: "${_('Authentication type')}", className: "td-type" },
104 "sort": "extern_type"}, title: "${_('Authentication type')}", className: "td-type" },
105 { data: {"_": "action",
105 { data: {"_": "action",
106 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
106 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
107 ],
107 ],
108 language: {
108 language: {
109 paginate: DEFAULT_GRID_PAGINATION
109 paginate: DEFAULT_GRID_PAGINATION
110 },
110 },
111 "initComplete": function( settings, json ) {
111 "initComplete": function( settings, json ) {
112 get_datatable_count();
112 get_datatable_count();
113 tooltip_activate();
113 tooltip_activate();
114 },
114 },
115 "createdRow": function ( row, data, index ) {
115 "createdRow": function ( row, data, index ) {
116 if (!data['active_raw']){
116 if (!data['active_raw']){
117 $(row).addClass('closed')
117 $(row).addClass('closed')
118 }
118 }
119 }
119 }
120 });
120 });
121
121
122 // update the counter when doing search
122 // update the counter when doing search
123 $('#user_list_table').on( 'search.dt', function (e,settings) {
123 $('#user_list_table').on( 'search.dt', function (e,settings) {
124 get_datatable_count();
124 get_datatable_count();
125 });
125 });
126
126
127 // filter, filter both grids
127 // filter, filter both grids
128 $('#q_filter').on( 'keyup', function () {
128 $('#q_filter').on( 'keyup', function () {
129 var user_api = $('#user_list_table').dataTable().api();
129 var user_api = $('#user_list_table').dataTable().api();
130 user_api
130 user_api
131 .draw();
131 .draw();
132 });
132 });
133
133
134 // refilter table if page load via back button
134 // refilter table if page load via back button
135 $("#q_filter").trigger('keyup');
135 $("#q_filter").trigger('keyup');
136
136
137 });
137 });
138
138
139 </script>
139 </script>
140
140
141 </%def>
141 </%def>
@@ -1,415 +1,415 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <%inherit file="/base/base.html"/>
3 <%inherit file="/base/base.html"/>
4
4
5 <%def name="title()">
5 <%def name="title()">
6 ${_('%s Changelog') % c.repo_name}
6 ${_('%s Changelog') % c.repo_name}
7 %if c.changelog_for_path:
7 %if c.changelog_for_path:
8 /${c.changelog_for_path}
8 /${c.changelog_for_path}
9 %endif
9 %endif
10 %if c.rhodecode_name:
10 %if c.rhodecode_name:
11 &middot; ${h.branding(c.rhodecode_name)}
11 &middot; ${h.branding(c.rhodecode_name)}
12 %endif
12 %endif
13 </%def>
13 </%def>
14
14
15 <%def name="breadcrumbs_links()">
15 <%def name="breadcrumbs_links()">
16 %if c.changelog_for_path:
16 %if c.changelog_for_path:
17 /${c.changelog_for_path}
17 /${c.changelog_for_path}
18 %endif
18 %endif
19 ${ungettext('showing %d out of %d commit', 'showing %d out of %d commits', c.showing_commits) % (c.showing_commits, c.total_cs)}
19 ${ungettext('showing %d out of %d commit', 'showing %d out of %d commits', c.showing_commits) % (c.showing_commits, c.total_cs)}
20 </%def>
20 </%def>
21
21
22 <%def name="menu_bar_nav()">
22 <%def name="menu_bar_nav()">
23 ${self.menu_items(active='repositories')}
23 ${self.menu_items(active='repositories')}
24 </%def>
24 </%def>
25
25
26 <%def name="menu_bar_subnav()">
26 <%def name="menu_bar_subnav()">
27 ${self.repo_menu(active='changelog')}
27 ${self.repo_menu(active='changelog')}
28 </%def>
28 </%def>
29
29
30 <%def name="main()">
30 <%def name="main()">
31
31
32 <div class="box">
32 <div class="box">
33 <div class="title">
33 <div class="title">
34 ${self.repo_page_title(c.rhodecode_db_repo)}
34 ${self.repo_page_title(c.rhodecode_db_repo)}
35 <ul class="links">
35 <ul class="links">
36 <li>
36 <li>
37 <a href="#" class="btn btn-small" id="rev_range_container" style="display:none;"></a>
37 <a href="#" class="btn btn-small" id="rev_range_container" style="display:none;"></a>
38 %if c.rhodecode_db_repo.fork:
38 %if c.rhodecode_db_repo.fork:
39 <span>
39 <span>
40 <a id="compare_fork_button"
40 <a id="compare_fork_button"
41 title="${_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}"
41 title="${_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}"
42 class="btn btn-small"
42 class="btn btn-small"
43 href="${h.url('compare_url',
43 href="${h.url('compare_url',
44 repo_name=c.rhodecode_db_repo.fork.repo_name,
44 repo_name=c.rhodecode_db_repo.fork.repo_name,
45 source_ref_type=c.rhodecode_db_repo.landing_rev[0],
45 source_ref_type=c.rhodecode_db_repo.landing_rev[0],
46 source_ref=c.rhodecode_db_repo.landing_rev[1],
46 source_ref=c.rhodecode_db_repo.landing_rev[1],
47 target_repo=c.repo_name,
47 target_repo=c.repo_name,
48 target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],
48 target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],
49 target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1],
49 target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1],
50 merge=1)
50 merge=1)
51 }">
51 }">
52 <i class="icon-loop"></i>
52 <i class="icon-loop"></i>
53 ${_('Compare fork with Parent (%s)' % c.rhodecode_db_repo.fork.repo_name)}
53 ${_('Compare fork with Parent (%s)' % c.rhodecode_db_repo.fork.repo_name)}
54 </a>
54 </a>
55 </span>
55 </span>
56 %endif
56 %endif
57
57
58 ## pr open link
58 ## pr open link
59 %if h.is_hg(c.rhodecode_repo) or h.is_git(c.rhodecode_repo):
59 %if h.is_hg(c.rhodecode_repo) or h.is_git(c.rhodecode_repo):
60 <span>
60 <span>
61 <a id="open_new_pull_request" class="btn btn-small btn-success" href="${h.url('pullrequest_home',repo_name=c.repo_name)}">
61 <a id="open_new_pull_request" class="btn btn-small btn-success" href="${h.url('pullrequest_home',repo_name=c.repo_name)}">
62 ${_('Open new pull request')}
62 ${_('Open new pull request')}
63 </a>
63 </a>
64 </span>
64 </span>
65 %endif
65 %endif
66
66
67 ## clear selection
67 ## clear selection
68 <div title="${_('Clear selection')}" class="btn" id="rev_range_clear" style="display:none">
68 <div title="${_('Clear selection')}" class="btn" id="rev_range_clear" style="display:none">
69 ${_('Clear selection')}
69 ${_('Clear selection')}
70 </div>
70 </div>
71
71
72 </li>
72 </li>
73 </ul>
73 </ul>
74 </div>
74 </div>
75
75
76 % if c.pagination:
76 % if c.pagination:
77
77
78 <div class="graph-header">
78 <div class="graph-header">
79 <div id="filter_changelog">
79 <div id="filter_changelog">
80 ${h.hidden('branch_filter')}
80 ${h.hidden('branch_filter')}
81 %if c.selected_name:
81 %if c.selected_name:
82 <div class="btn btn-default" id="clear_filter" >
82 <div class="btn btn-default" id="clear_filter" >
83 ${_('Clear filter')}
83 ${_('Clear filter')}
84 </div>
84 </div>
85 %endif
85 %endif
86 </div>
86 </div>
87 ${self.breadcrumbs('breadcrumbs_light')}
87 ${self.breadcrumbs('breadcrumbs_light')}
88 </div>
88 </div>
89
89
90 <div id="graph">
90 <div id="graph">
91 <div class="graph-col-wrapper">
91 <div class="graph-col-wrapper">
92 <div id="graph_nodes">
92 <div id="graph_nodes">
93 <div id="graph_canvas" data-graph='${c.jsdata|n}'></div>
93 <div id="graph_canvas" data-graph='${c.jsdata|n}'></div>
94 </div>
94 </div>
95 <div id="graph_content" class="main-content graph_full_width">
95 <div id="graph_content" class="main-content graph_full_width">
96
96
97 <div class="table">
97 <div class="table">
98 <table id="changesets" class="rctable">
98 <table id="changesets" class="rctable">
99 <tr>
99 <tr>
100 <th></th>
100 <th></th>
101 <th></th>
101 <th></th>
102 <th>${_('Author')}</th>
102 <th>${_('Author')}</th>
103 <th>${_('Age')}</th>
103 <th>${_('Age')}</th>
104 <th></th>
104 <th></th>
105 <th>${_('Commit Message')}</th>
105 <th>${_('Commit Message')}</th>
106 <th>${_('Commit')}</th>
106 <th>${_('Commit')}</th>
107 <th></th>
107 <th></th>
108 <th>${_('Refs')}</th>
108 <th>${_('Refs')}</th>
109 </tr>
109 </tr>
110 <tbody>
110 <tbody>
111 %for cnt,commit in enumerate(c.pagination):
111 %for cnt,commit in enumerate(c.pagination):
112 <tr id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
112 <tr id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
113
113
114 <td class="td-checkbox">
114 <td class="td-checkbox">
115 ${h.checkbox(commit.raw_id,class_="commit-range")}
115 ${h.checkbox(commit.raw_id,class_="commit-range")}
116 </td>
116 </td>
117 <td class="td-status">
117 <td class="td-status">
118
118
119 %if c.statuses.get(commit.raw_id):
119 %if c.statuses.get(commit.raw_id):
120 <div class="changeset-status-ico">
120 <div class="changeset-status-ico">
121 %if c.statuses.get(commit.raw_id)[2]:
121 %if c.statuses.get(commit.raw_id)[2]:
122 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (h.commit_status_lbl(c.statuses.get(commit.raw_id)[0]), c.statuses.get(commit.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(commit.raw_id)[3],pull_request_id=c.statuses.get(commit.raw_id)[2])}">
122 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (h.commit_status_lbl(c.statuses.get(commit.raw_id)[0]), c.statuses.get(commit.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(commit.raw_id)[3],pull_request_id=c.statuses.get(commit.raw_id)[2])}">
123 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
123 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
124 </a>
124 </a>
125 %else:
125 %else:
126 <a class="tooltip" title="${_('Commit status: %s') % h.commit_status_lbl(c.statuses.get(commit.raw_id)[0])}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
126 <a class="tooltip" title="${_('Commit status: %s') % h.commit_status_lbl(c.statuses.get(commit.raw_id)[0])}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
127 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
127 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
128 </a>
128 </a>
129 %endif
129 %endif
130 </div>
130 </div>
131 %endif
131 %endif
132 </td>
132 </td>
133 <td class="td-user">
133 <td class="td-user">
134 ${self.gravatar(h.email_or_none(commit.author))}
134 ${self.gravatar(h.email_or_none(commit.author))}
135 <span title="${commit.author}" class="user">${h.link_to_user(commit.author, length=22)}</span>
135 <span title="${commit.author}" class="user">${h.link_to_user(commit.author, length=22)}</span>
136 </td>
136 </td>
137 <td class="td-time">
137 <td class="td-time">
138 ${h.age_component(commit.date)}
138 ${h.age_component(commit.date)}
139 </td>
139 </td>
140
140
141 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_('Expand commit message')}">
141 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_('Expand commit message')}">
142 <div class="show_more_col">
142 <div class="show_more_col">
143 <i class="show_more"></i>&nbsp;
143 <i class="show_more"></i>&nbsp;
144 </div>
144 </div>
145 </td>
145 </td>
146 <td class="mid td-description">
146 <td class="mid td-description">
147 <div class="log-container truncate-wrap">
147 <div class="log-container truncate-wrap">
148 <div class="message truncate" id="c-${commit.raw_id}">${h.urlify_commit_message(commit.message, c.repo_name)}</div>
148 <div class="message truncate" id="c-${commit.raw_id}">${h.urlify_commit_message(commit.message, c.repo_name)}</div>
149 </div>
149 </div>
150 </td>
150 </td>
151
151
152 <td class="td-hash">
152 <td class="td-hash">
153 <code>
153 <code>
154 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id)}">
154 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id)}">
155 <span class="commit_hash">${h.show_id(commit)}</span>
155 <span class="commit_hash">${h.show_id(commit)}</span>
156 </a>
156 </a>
157 </code>
157 </code>
158 </td>
158 </td>
159
159
160 <td class="td-comments comments-col">
160 <td class="td-comments comments-col">
161 %if c.comments.get(commit.raw_id):
161 %if c.comments.get(commit.raw_id):
162 <a title="${_('Commit has comments')}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
162 <a title="${_('Commit has comments')}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
163 ${len(c.comments[commit.raw_id])} <i class="icon-comment icon-comment-colored"></i>
163 ${len(c.comments[commit.raw_id])} <i class="icon-comment icon-comment-colored"></i>
164 </a>
164 </a>
165 %endif
165 %endif
166 </td>
166 </td>
167
167
168 <td class="td-tags tags-col truncate-wrap">
168 <td class="td-tags tags-col truncate-wrap">
169 <div class="truncate tags-truncate" id="t-${commit.raw_id}">
169 <div class="truncate tags-truncate" id="t-${commit.raw_id}">
170 ## branch
170 ## branch
171 %if commit.branch:
171 %if commit.branch:
172 <span class="branchtag tag" title="${_('Branch %s') % commit.branch}">
172 <span class="branchtag tag" title="${_('Branch %s') % commit.branch}">
173 <a href="${h.url('changelog_home',repo_name=c.repo_name,branch=commit.branch)}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a>
173 <a href="${h.url('changelog_home',repo_name=c.repo_name,branch=commit.branch)}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a>
174 </span>
174 </span>
175 %endif
175 %endif
176
176
177 ## bookmarks
177 ## bookmarks
178 %if h.is_hg(c.rhodecode_repo):
178 %if h.is_hg(c.rhodecode_repo):
179 %for book in commit.bookmarks:
179 %for book in commit.bookmarks:
180 <span class="tag booktag" title="${_('Bookmark %s') % book}">
180 <span class="tag booktag" title="${_('Bookmark %s') % book}">
181 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
181 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
182 </span>
182 </span>
183 %endfor
183 %endfor
184 %endif
184 %endif
185
185
186 ## tags
186 ## tags
187 %for tag in commit.tags:
187 %for tag in commit.tags:
188 <span class="tagtag tag" title="${_('Tag %s') % tag}">
188 <span class="tagtag tag" title="${_('Tag %s') % tag}">
189 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
189 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
190 </span>
190 </span>
191 %endfor
191 %endfor
192
192
193 </div>
193 </div>
194 </td>
194 </td>
195 </tr>
195 </tr>
196 %endfor
196 %endfor
197 </tbody>
197 </tbody>
198 </table>
198 </table>
199 </div>
199 </div>
200 </div>
200 </div>
201 </div>
201 </div>
202 <div class="pagination-wh pagination-left">
202 <div class="pagination-wh pagination-left">
203 ${c.pagination.pager('$link_previous ~2~ $link_next')}
203 ${c.pagination.pager('$link_previous ~2~ $link_next')}
204 </div>
204 </div>
205
205
206 <script type="text/javascript" src="${h.url('/js/jquery.commits-graph.js')}"></script>
206 <script type="text/javascript" src="${h.url('/js/jquery.commits-graph.js')}"></script>
207 <script type="text/javascript">
207 <script type="text/javascript">
208 var cache = {};
208 var cache = {};
209 $(function(){
209 $(function(){
210
210
211 // Create links to commit ranges when range checkboxes are selected
211 // Create links to commit ranges when range checkboxes are selected
212 var $commitCheckboxes = $('.commit-range');
212 var $commitCheckboxes = $('.commit-range');
213 // cache elements
213 // cache elements
214 var $commitRangeContainer = $('#rev_range_container');
214 var $commitRangeContainer = $('#rev_range_container');
215 var $commitRangeClear = $('#rev_range_clear');
215 var $commitRangeClear = $('#rev_range_clear');
216
216
217 var checkboxRangeSelector = function(e){
217 var checkboxRangeSelector = function(e){
218 var selectedCheckboxes = [];
218 var selectedCheckboxes = [];
219 for (pos in $commitCheckboxes){
219 for (pos in $commitCheckboxes){
220 if($commitCheckboxes[pos].checked){
220 if($commitCheckboxes[pos].checked){
221 selectedCheckboxes.push($commitCheckboxes[pos]);
221 selectedCheckboxes.push($commitCheckboxes[pos]);
222 }
222 }
223 }
223 }
224 var open_new_pull_request = $('#open_new_pull_request');
224 var open_new_pull_request = $('#open_new_pull_request');
225 if(open_new_pull_request){
225 if(open_new_pull_request){
226 var selected_changes = selectedCheckboxes.length;
226 var selected_changes = selectedCheckboxes.length;
227 if (selected_changes > 1 || selected_changes == 1 && templateContext.repo_type != 'hg') {
227 if (selected_changes > 1 || selected_changes == 1 && templateContext.repo_type != 'hg') {
228 open_new_pull_request.hide();
228 open_new_pull_request.hide();
229 } else {
229 } else {
230 if (selected_changes == 1) {
230 if (selected_changes == 1) {
231 open_new_pull_request.html(_TM['Open new pull request for selected commit']);
231 open_new_pull_request.html(_gettext('Open new pull request for selected commit'));
232 } else if (selected_changes == 0) {
232 } else if (selected_changes == 0) {
233 open_new_pull_request.html(_TM['Open new pull request']);
233 open_new_pull_request.html(_gettext('Open new pull request'));
234 }
234 }
235 open_new_pull_request.show();
235 open_new_pull_request.show();
236 }
236 }
237 }
237 }
238
238
239 if (selectedCheckboxes.length>0){
239 if (selectedCheckboxes.length>0){
240 var revEnd = selectedCheckboxes[0].name;
240 var revEnd = selectedCheckboxes[0].name;
241 var revStart = selectedCheckboxes[selectedCheckboxes.length-1].name;
241 var revStart = selectedCheckboxes[selectedCheckboxes.length-1].name;
242 var url = pyroutes.url('changeset_home',
242 var url = pyroutes.url('changeset_home',
243 {'repo_name': '${c.repo_name}',
243 {'repo_name': '${c.repo_name}',
244 'revision': revStart+'...'+revEnd});
244 'revision': revStart+'...'+revEnd});
245
245
246 var link = (revStart == revEnd)
246 var link = (revStart == revEnd)
247 ? _TM['Show selected commit __S']
247 ? _gettext('Show selected commit __S')
248 : _TM['Show selected commits __S ... __E'];
248 : _gettext('Show selected commits __S ... __E');
249
249
250 link = link.replace('__S', revStart.substr(0,6));
250 link = link.replace('__S', revStart.substr(0,6));
251 link = link.replace('__E', revEnd.substr(0,6));
251 link = link.replace('__E', revEnd.substr(0,6));
252
252
253 $commitRangeContainer
253 $commitRangeContainer
254 .attr('href',url)
254 .attr('href',url)
255 .html(link)
255 .html(link)
256 .show();
256 .show();
257
257
258 $commitRangeClear.show();
258 $commitRangeClear.show();
259 var _url = pyroutes.url('pullrequest_home',
259 var _url = pyroutes.url('pullrequest_home',
260 {'repo_name': '${c.repo_name}',
260 {'repo_name': '${c.repo_name}',
261 'commit': revEnd});
261 'commit': revEnd});
262 open_new_pull_request.attr('href', _url);
262 open_new_pull_request.attr('href', _url);
263 $('#compare_fork_button').hide();
263 $('#compare_fork_button').hide();
264 } else {
264 } else {
265 $commitRangeContainer.hide();
265 $commitRangeContainer.hide();
266 $commitRangeClear.hide();
266 $commitRangeClear.hide();
267
267
268 %if c.branch_name:
268 %if c.branch_name:
269 var _url = pyroutes.url('pullrequest_home',
269 var _url = pyroutes.url('pullrequest_home',
270 {'repo_name': '${c.repo_name}',
270 {'repo_name': '${c.repo_name}',
271 'branch':'${c.branch_name}'});
271 'branch':'${c.branch_name}'});
272 open_new_pull_request.attr('href', _url);
272 open_new_pull_request.attr('href', _url);
273 %else:
273 %else:
274 var _url = pyroutes.url('pullrequest_home',
274 var _url = pyroutes.url('pullrequest_home',
275 {'repo_name': '${c.repo_name}'});
275 {'repo_name': '${c.repo_name}'});
276 open_new_pull_request.attr('href', _url);
276 open_new_pull_request.attr('href', _url);
277 %endif
277 %endif
278 $('#compare_fork_button').show();
278 $('#compare_fork_button').show();
279 }
279 }
280 };
280 };
281
281
282 $commitCheckboxes.on('click', checkboxRangeSelector);
282 $commitCheckboxes.on('click', checkboxRangeSelector);
283
283
284 $commitRangeClear.on('click',function(e) {
284 $commitRangeClear.on('click',function(e) {
285 $commitCheckboxes.attr('checked', false)
285 $commitCheckboxes.attr('checked', false)
286 checkboxRangeSelector();
286 checkboxRangeSelector();
287 e.preventDefault();
287 e.preventDefault();
288 });
288 });
289
289
290 // make sure the buttons are consistent when navigate back and forth
290 // make sure the buttons are consistent when navigate back and forth
291 checkboxRangeSelector();
291 checkboxRangeSelector();
292
292
293
293
294 var msgs = $('.message');
294 var msgs = $('.message');
295 // get first element height
295 // get first element height
296 var el = $('#graph_content .container')[0];
296 var el = $('#graph_content .container')[0];
297 var row_h = el.clientHeight;
297 var row_h = el.clientHeight;
298 for (var i=0; i < msgs.length; i++) {
298 for (var i=0; i < msgs.length; i++) {
299 var m = msgs[i];
299 var m = msgs[i];
300
300
301 var h = m.clientHeight;
301 var h = m.clientHeight;
302 var pad = $(m).css('padding');
302 var pad = $(m).css('padding');
303 if (h > row_h) {
303 if (h > row_h) {
304 var offset = row_h - (h+12);
304 var offset = row_h - (h+12);
305 $(m.nextElementSibling).css('display','block');
305 $(m.nextElementSibling).css('display','block');
306 $(m.nextElementSibling).css('margin-top',offset+'px');
306 $(m.nextElementSibling).css('margin-top',offset+'px');
307 }
307 }
308 }
308 }
309
309
310 $('.expand_commit').on('click',function(e){
310 $('.expand_commit').on('click',function(e){
311 var target_expand = $(this);
311 var target_expand = $(this);
312 var cid = target_expand.data('commitId');
312 var cid = target_expand.data('commitId');
313
313
314 if (target_expand.hasClass('open')){
314 if (target_expand.hasClass('open')){
315 $('#c-'+cid).css({'height': '1.5em', 'white-space': 'nowrap', 'text-overflow': 'ellipsis', 'overflow':'hidden'});
315 $('#c-'+cid).css({'height': '1.5em', 'white-space': 'nowrap', 'text-overflow': 'ellipsis', 'overflow':'hidden'});
316 $('#t-'+cid).css({'height': 'auto', 'line-height': '.9em', 'text-overflow': 'ellipsis', 'overflow':'hidden', 'white-space':'nowrap'});
316 $('#t-'+cid).css({'height': 'auto', 'line-height': '.9em', 'text-overflow': 'ellipsis', 'overflow':'hidden', 'white-space':'nowrap'});
317 target_expand.removeClass('open');
317 target_expand.removeClass('open');
318 }
318 }
319 else {
319 else {
320 $('#c-'+cid).css({'height': 'auto', 'white-space': 'pre-line', 'text-overflow': 'initial', 'overflow':'visible'});
320 $('#c-'+cid).css({'height': 'auto', 'white-space': 'pre-line', 'text-overflow': 'initial', 'overflow':'visible'});
321 $('#t-'+cid).css({'height': 'auto', 'max-height': 'none', 'text-overflow': 'initial', 'overflow':'visible', 'white-space':'normal'});
321 $('#t-'+cid).css({'height': 'auto', 'max-height': 'none', 'text-overflow': 'initial', 'overflow':'visible', 'white-space':'normal'});
322 target_expand.addClass('open');
322 target_expand.addClass('open');
323 }
323 }
324 // redraw the graph
324 // redraw the graph
325 graph_options.height = $("#changesets").height();
325 graph_options.height = $("#changesets").height();
326 $("canvas").remove();
326 $("canvas").remove();
327 $("[data-graph]").commits(graph_options);
327 $("[data-graph]").commits(graph_options);
328 });
328 });
329
329
330 $("#clear_filter").on("click", function() {
330 $("#clear_filter").on("click", function() {
331 var filter = {'repo_name': '${c.repo_name}'};
331 var filter = {'repo_name': '${c.repo_name}'};
332 window.location = pyroutes.url('changelog_home', filter);
332 window.location = pyroutes.url('changelog_home', filter);
333 });
333 });
334
334
335 $("#branch_filter").select2({
335 $("#branch_filter").select2({
336 'dropdownAutoWidth': true,
336 'dropdownAutoWidth': true,
337 'width': 'resolve',
337 'width': 'resolve',
338 'placeholder': "${c.selected_name or _('Filter changelog')}",
338 'placeholder': "${c.selected_name or _('Filter changelog')}",
339 containerCssClass: "drop-menu",
339 containerCssClass: "drop-menu",
340 dropdownCssClass: "drop-menu-dropdown",
340 dropdownCssClass: "drop-menu-dropdown",
341 query: function(query){
341 query: function(query){
342 var key = 'cache';
342 var key = 'cache';
343 var cached = cache[key] ;
343 var cached = cache[key] ;
344 if(cached) {
344 if(cached) {
345 var data = {results: []};
345 var data = {results: []};
346 //filter results
346 //filter results
347 $.each(cached.results, function(){
347 $.each(cached.results, function(){
348 var section = this.text;
348 var section = this.text;
349 var children = [];
349 var children = [];
350 $.each(this.children, function(){
350 $.each(this.children, function(){
351 if(query.term.length == 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ){
351 if(query.term.length == 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ){
352 children.push({'id': this.id, 'text': this.text, 'type': this.type})
352 children.push({'id': this.id, 'text': this.text, 'type': this.type})
353 }
353 }
354 });
354 });
355 data.results.push({'text': section, 'children': children});
355 data.results.push({'text': section, 'children': children});
356 query.callback({results: data.results});
356 query.callback({results: data.results});
357 });
357 });
358 }else{
358 }else{
359 $.ajax({
359 $.ajax({
360 url: pyroutes.url('repo_refs_changelog_data', {'repo_name': '${c.repo_name}'}),
360 url: pyroutes.url('repo_refs_changelog_data', {'repo_name': '${c.repo_name}'}),
361 data: {},
361 data: {},
362 dataType: 'json',
362 dataType: 'json',
363 type: 'GET',
363 type: 'GET',
364 success: function(data) {
364 success: function(data) {
365 cache[key] = data;
365 cache[key] = data;
366 query.callback({results: data.results});
366 query.callback({results: data.results});
367 }
367 }
368 })
368 })
369 }
369 }
370 }
370 }
371 });
371 });
372
372
373 $('#branch_filter').on('change', function(e){
373 $('#branch_filter').on('change', function(e){
374 var data = $('#branch_filter').select2('data');
374 var data = $('#branch_filter').select2('data');
375 var selected = data.text;
375 var selected = data.text;
376 var filter = {'repo_name': '${c.repo_name}'};
376 var filter = {'repo_name': '${c.repo_name}'};
377 if(data.type == 'branch' || data.type == 'branch_closed'){
377 if(data.type == 'branch' || data.type == 'branch_closed'){
378 filter["branch"] = selected;
378 filter["branch"] = selected;
379 }
379 }
380 else if (data.type == 'book'){
380 else if (data.type == 'book'){
381 filter["bookmark"] = selected;
381 filter["bookmark"] = selected;
382 }
382 }
383 window.location = pyroutes.url('changelog_home', filter);
383 window.location = pyroutes.url('changelog_home', filter);
384 });
384 });
385
385
386 // Determine max number of edges per row in graph
386 // Determine max number of edges per row in graph
387 var jsdata = $.parseJSON($("[data-graph]").attr('data-graph'));
387 var jsdata = $.parseJSON($("[data-graph]").attr('data-graph'));
388 var edgeCount = 1;
388 var edgeCount = 1;
389 $.each(jsdata, function(i, item){
389 $.each(jsdata, function(i, item){
390 $.each(item[2], function(key, value) {
390 $.each(item[2], function(key, value) {
391 if (value[1] > edgeCount){
391 if (value[1] > edgeCount){
392 edgeCount = value[1];
392 edgeCount = value[1];
393 }
393 }
394 });
394 });
395 });
395 });
396 var x_step = Math.min(18, Math.floor(86 / edgeCount));
396 var x_step = Math.min(18, Math.floor(86 / edgeCount));
397 var graph_options = {
397 var graph_options = {
398 width: 100,
398 width: 100,
399 height: $("#changesets").height(),
399 height: $("#changesets").height(),
400 x_step: x_step,
400 x_step: x_step,
401 y_step: 42,
401 y_step: 42,
402 dotRadius: 3.5,
402 dotRadius: 3.5,
403 lineWidth: 2.5
403 lineWidth: 2.5
404 };
404 };
405 $("[data-graph]").commits(graph_options);
405 $("[data-graph]").commits(graph_options);
406
406
407 });
407 });
408
408
409 </script>
409 </script>
410 %else:
410 %else:
411 ${_('There are no changes yet')}
411 ${_('There are no changes yet')}
412 %endif
412 %endif
413 </div>
413 </div>
414 </div>
414 </div>
415 </%def>
415 </%def>
@@ -1,322 +1,322 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 %if c.compare_home:
5 %if c.compare_home:
6 ${_('%s Compare') % c.repo_name}
6 ${_('%s Compare') % c.repo_name}
7 %else:
7 %else:
8 ${_('%s Compare') % c.repo_name} - ${'%s@%s' % (c.source_repo.repo_name, c.source_ref)} &gt; ${'%s@%s' % (c.target_repo.repo_name, c.target_ref)}
8 ${_('%s Compare') % c.repo_name} - ${'%s@%s' % (c.source_repo.repo_name, c.source_ref)} &gt; ${'%s@%s' % (c.target_repo.repo_name, c.target_ref)}
9 %endif
9 %endif
10 %if c.rhodecode_name:
10 %if c.rhodecode_name:
11 &middot; ${h.branding(c.rhodecode_name)}
11 &middot; ${h.branding(c.rhodecode_name)}
12 %endif
12 %endif
13 </%def>
13 </%def>
14
14
15 <%def name="breadcrumbs_links()">
15 <%def name="breadcrumbs_links()">
16 ${ungettext('%s commit','%s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
16 ${ungettext('%s commit','%s commits', len(c.commit_ranges)) % len(c.commit_ranges)}
17 </%def>
17 </%def>
18
18
19 <%def name="menu_bar_nav()">
19 <%def name="menu_bar_nav()">
20 ${self.menu_items(active='repositories')}
20 ${self.menu_items(active='repositories')}
21 </%def>
21 </%def>
22
22
23 <%def name="menu_bar_subnav()">
23 <%def name="menu_bar_subnav()">
24 ${self.repo_menu(active='compare')}
24 ${self.repo_menu(active='compare')}
25 </%def>
25 </%def>
26
26
27 <%def name="main()">
27 <%def name="main()">
28 <script type="text/javascript">
28 <script type="text/javascript">
29 // set fake commitId on this commit-range page
29 // set fake commitId on this commit-range page
30 templateContext.commit_data.commit_id = "${h.EmptyCommit().raw_id}";
30 templateContext.commit_data.commit_id = "${h.EmptyCommit().raw_id}";
31 </script>
31 </script>
32
32
33 <div class="box">
33 <div class="box">
34 <div class="title">
34 <div class="title">
35 ${self.repo_page_title(c.rhodecode_db_repo)}
35 ${self.repo_page_title(c.rhodecode_db_repo)}
36 <div class="breadcrumbs">
36 <div class="breadcrumbs">
37 ${_('Compare Commits')}
37 ${_('Compare Commits')}
38 </div>
38 </div>
39 </div>
39 </div>
40
40
41 <div class="table">
41 <div class="table">
42 <div id="codeblock" class="diffblock">
42 <div id="codeblock" class="diffblock">
43 <div class="code-header" >
43 <div class="code-header" >
44 <div class="compare_header">
44 <div class="compare_header">
45 ## The hidden elements are replaced with a select2 widget
45 ## The hidden elements are replaced with a select2 widget
46 <div class="compare-label">${_('Target')}</div>${h.hidden('compare_source')}
46 <div class="compare-label">${_('Target')}</div>${h.hidden('compare_source')}
47 <div class="compare-label">${_('Source')}</div>${h.hidden('compare_target')}
47 <div class="compare-label">${_('Source')}</div>${h.hidden('compare_target')}
48
48
49 %if not c.preview_mode:
49 %if not c.preview_mode:
50 <div class="compare-label"></div>
50 <div class="compare-label"></div>
51 <div class="compare-buttons">
51 <div class="compare-buttons">
52 %if not c.compare_home:
52 %if not c.compare_home:
53 <a id="btn-swap" class="btn btn-primary" href="${c.swap_url}"><i class="icon-refresh"></i> ${_('Swap')}</a>
53 <a id="btn-swap" class="btn btn-primary" href="${c.swap_url}"><i class="icon-refresh"></i> ${_('Swap')}</a>
54 %endif
54 %endif
55 <div id="compare_revs" class="btn btn-primary"><i class ="icon-loop"></i> ${_('Compare Commits')}</div>
55 <div id="compare_revs" class="btn btn-primary"><i class ="icon-loop"></i> ${_('Compare Commits')}</div>
56 %if c.files:
56 %if c.files:
57 <div id="compare_changeset_status_toggle" class="btn btn-primary">${_('Comment')}</div>
57 <div id="compare_changeset_status_toggle" class="btn btn-primary">${_('Comment')}</div>
58 %endif
58 %endif
59 </div>
59 </div>
60 %endif
60 %endif
61 </div>
61 </div>
62 </div>
62 </div>
63 </div>
63 </div>
64 ## use JS script to load it quickly before potentially large diffs render long time
64 ## use JS script to load it quickly before potentially large diffs render long time
65 ## this prevents from situation when large diffs block rendering of select2 fields
65 ## this prevents from situation when large diffs block rendering of select2 fields
66 <script type="text/javascript">
66 <script type="text/javascript">
67
67
68 var cache = {};
68 var cache = {};
69
69
70 var formatSelection = function(repoName){
70 var formatSelection = function(repoName){
71 return function(data, container, escapeMarkup) {
71 return function(data, container, escapeMarkup) {
72 var selection = data ? this.text(data) : "";
72 var selection = data ? this.text(data) : "";
73 return escapeMarkup('{0}@{1}'.format(repoName, selection));
73 return escapeMarkup('{0}@{1}'.format(repoName, selection));
74 }
74 }
75 };
75 };
76
76
77 var feedCompareData = function(query, cachedValue){
77 var feedCompareData = function(query, cachedValue){
78 var data = {results: []};
78 var data = {results: []};
79 //filter results
79 //filter results
80 $.each(cachedValue.results, function() {
80 $.each(cachedValue.results, function() {
81 var section = this.text;
81 var section = this.text;
82 var children = [];
82 var children = [];
83 $.each(this.children, function() {
83 $.each(this.children, function() {
84 if (query.term.length === 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
84 if (query.term.length === 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
85 children.push({
85 children.push({
86 'id': this.id,
86 'id': this.id,
87 'text': this.text,
87 'text': this.text,
88 'type': this.type
88 'type': this.type
89 })
89 })
90 }
90 }
91 });
91 });
92 data.results.push({
92 data.results.push({
93 'text': section,
93 'text': section,
94 'children': children
94 'children': children
95 })
95 })
96 });
96 });
97 //push the typed in changeset
97 //push the typed in changeset
98 data.results.push({
98 data.results.push({
99 'text': _TM['specify commit'],
99 'text': _gettext('specify commit'),
100 'children': [{
100 'children': [{
101 'id': query.term,
101 'id': query.term,
102 'text': query.term,
102 'text': query.term,
103 'type': 'rev'
103 'type': 'rev'
104 }]
104 }]
105 });
105 });
106 query.callback(data);
106 query.callback(data);
107 };
107 };
108
108
109 var loadCompareData = function(repoName, query, cache){
109 var loadCompareData = function(repoName, query, cache){
110 $.ajax({
110 $.ajax({
111 url: pyroutes.url('repo_refs_data', {'repo_name': repoName}),
111 url: pyroutes.url('repo_refs_data', {'repo_name': repoName}),
112 data: {},
112 data: {},
113 dataType: 'json',
113 dataType: 'json',
114 type: 'GET',
114 type: 'GET',
115 success: function(data) {
115 success: function(data) {
116 cache[repoName] = data;
116 cache[repoName] = data;
117 query.callback({results: data.results});
117 query.callback({results: data.results});
118 }
118 }
119 })
119 })
120 };
120 };
121
121
122 var enable_fields = ${"false" if c.preview_mode else "true"};
122 var enable_fields = ${"false" if c.preview_mode else "true"};
123 $("#compare_source").select2({
123 $("#compare_source").select2({
124 placeholder: "${'%s@%s' % (c.source_repo.repo_name, c.source_ref)}",
124 placeholder: "${'%s@%s' % (c.source_repo.repo_name, c.source_ref)}",
125 containerCssClass: "drop-menu",
125 containerCssClass: "drop-menu",
126 dropdownCssClass: "drop-menu-dropdown",
126 dropdownCssClass: "drop-menu-dropdown",
127 formatSelection: formatSelection("${c.source_repo.repo_name}"),
127 formatSelection: formatSelection("${c.source_repo.repo_name}"),
128 dropdownAutoWidth: true,
128 dropdownAutoWidth: true,
129 query: function(query) {
129 query: function(query) {
130 var repoName = '${c.source_repo.repo_name}';
130 var repoName = '${c.source_repo.repo_name}';
131 var cachedValue = cache[repoName];
131 var cachedValue = cache[repoName];
132
132
133 if (cachedValue){
133 if (cachedValue){
134 feedCompareData(query, cachedValue);
134 feedCompareData(query, cachedValue);
135 }
135 }
136 else {
136 else {
137 loadCompareData(repoName, query, cache);
137 loadCompareData(repoName, query, cache);
138 }
138 }
139 }
139 }
140 }).select2("enable", enable_fields);
140 }).select2("enable", enable_fields);
141
141
142 $("#compare_target").select2({
142 $("#compare_target").select2({
143 placeholder: "${'%s@%s' % (c.target_repo.repo_name, c.target_ref)}",
143 placeholder: "${'%s@%s' % (c.target_repo.repo_name, c.target_ref)}",
144 dropdownAutoWidth: true,
144 dropdownAutoWidth: true,
145 containerCssClass: "drop-menu",
145 containerCssClass: "drop-menu",
146 dropdownCssClass: "drop-menu-dropdown",
146 dropdownCssClass: "drop-menu-dropdown",
147 formatSelection: formatSelection("${c.target_repo.repo_name}"),
147 formatSelection: formatSelection("${c.target_repo.repo_name}"),
148 query: function(query) {
148 query: function(query) {
149 var repoName = '${c.target_repo.repo_name}';
149 var repoName = '${c.target_repo.repo_name}';
150 var cachedValue = cache[repoName];
150 var cachedValue = cache[repoName];
151
151
152 if (cachedValue){
152 if (cachedValue){
153 feedCompareData(query, cachedValue);
153 feedCompareData(query, cachedValue);
154 }
154 }
155 else {
155 else {
156 loadCompareData(repoName, query, cache);
156 loadCompareData(repoName, query, cache);
157 }
157 }
158 }
158 }
159 }).select2("enable", enable_fields);
159 }).select2("enable", enable_fields);
160 var initial_compare_source = {id: "${c.source_ref}", type:"${c.source_ref_type}"};
160 var initial_compare_source = {id: "${c.source_ref}", type:"${c.source_ref_type}"};
161 var initial_compare_target = {id: "${c.target_ref}", type:"${c.target_ref_type}"};
161 var initial_compare_target = {id: "${c.target_ref}", type:"${c.target_ref_type}"};
162
162
163 $('#compare_revs').on('click', function(e) {
163 $('#compare_revs').on('click', function(e) {
164 var source = $('#compare_source').select2('data') || initial_compare_source;
164 var source = $('#compare_source').select2('data') || initial_compare_source;
165 var target = $('#compare_target').select2('data') || initial_compare_target;
165 var target = $('#compare_target').select2('data') || initial_compare_target;
166 if (source && target) {
166 if (source && target) {
167 var url_data = {
167 var url_data = {
168 repo_name: "${c.repo_name}",
168 repo_name: "${c.repo_name}",
169 source_ref: source.id,
169 source_ref: source.id,
170 source_ref_type: source.type,
170 source_ref_type: source.type,
171 target_ref: target.id,
171 target_ref: target.id,
172 target_ref_type: target.type
172 target_ref_type: target.type
173 };
173 };
174 window.location = pyroutes.url('compare_url', url_data);
174 window.location = pyroutes.url('compare_url', url_data);
175 }
175 }
176 });
176 });
177 $('#compare_changeset_status_toggle').on('click', function(e) {
177 $('#compare_changeset_status_toggle').on('click', function(e) {
178 $('#compare_changeset_status').toggle();
178 $('#compare_changeset_status').toggle();
179 });
179 });
180
180
181 </script>
181 </script>
182
182
183 ## changeset status form
183 ## changeset status form
184 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
184 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
185 ## main comment form and it status
185 ## main comment form and it status
186 <%
186 <%
187 def revs(_revs):
187 def revs(_revs):
188 form_inputs = []
188 form_inputs = []
189 for cs in _revs:
189 for cs in _revs:
190 tmpl = '<input type="hidden" data-commit-id="%(cid)s" name="commit_ids" value="%(cid)s">' % {'cid': cs.raw_id}
190 tmpl = '<input type="hidden" data-commit-id="%(cid)s" name="commit_ids" value="%(cid)s">' % {'cid': cs.raw_id}
191 form_inputs.append(tmpl)
191 form_inputs.append(tmpl)
192 return form_inputs
192 return form_inputs
193 %>
193 %>
194 <div id="compare_changeset_status" style="display: none;">
194 <div id="compare_changeset_status" style="display: none;">
195 ${comment.comments(h.url('changeset_comment', repo_name=c.repo_name, revision='0'*16), None, is_compare=True, form_extras=revs(c.commit_ranges))}
195 ${comment.comments(h.url('changeset_comment', repo_name=c.repo_name, revision='0'*16), None, is_compare=True, form_extras=revs(c.commit_ranges))}
196 <script type="text/javascript">
196 <script type="text/javascript">
197
197
198 mainCommentForm.setHandleFormSubmit(function(o) {
198 mainCommentForm.setHandleFormSubmit(function(o) {
199 var text = mainCommentForm.cm.getValue();
199 var text = mainCommentForm.cm.getValue();
200 var status = mainCommentForm.getCommentStatus();
200 var status = mainCommentForm.getCommentStatus();
201
201
202 if (text === "" && !status) {
202 if (text === "" && !status) {
203 return;
203 return;
204 }
204 }
205
205
206 // we can pick which commits we want to make the comment by
206 // we can pick which commits we want to make the comment by
207 // selecting them via click on preview pane, this will alter the hidden inputs
207 // selecting them via click on preview pane, this will alter the hidden inputs
208 var cherryPicked = $('#changeset_compare_view_content .compare_select.hl').length > 0;
208 var cherryPicked = $('#changeset_compare_view_content .compare_select.hl').length > 0;
209
209
210 var commitIds = [];
210 var commitIds = [];
211 $('#changeset_compare_view_content .compare_select').each(function(el) {
211 $('#changeset_compare_view_content .compare_select').each(function(el) {
212 var commitId = this.id.replace('row-', '');
212 var commitId = this.id.replace('row-', '');
213 if ($(this).hasClass('hl') || !cherryPicked) {
213 if ($(this).hasClass('hl') || !cherryPicked) {
214 $("input[data-commit-id='{0}']".format(commitId)).val(commitId)
214 $("input[data-commit-id='{0}']".format(commitId)).val(commitId)
215 commitIds.push(commitId);
215 commitIds.push(commitId);
216 } else {
216 } else {
217 $("input[data-commit-id='{0}']".format(commitId)).val('')
217 $("input[data-commit-id='{0}']".format(commitId)).val('')
218 }
218 }
219 });
219 });
220
220
221 mainCommentForm.setActionButtonsDisabled(true);
221 mainCommentForm.setActionButtonsDisabled(true);
222 mainCommentForm.cm.setOption("readOnly", true);
222 mainCommentForm.cm.setOption("readOnly", true);
223 var postData = {
223 var postData = {
224 'text': text,
224 'text': text,
225 'changeset_status': status,
225 'changeset_status': status,
226 'commit_ids': commitIds,
226 'commit_ids': commitIds,
227 'csrf_token': CSRF_TOKEN
227 'csrf_token': CSRF_TOKEN
228 };
228 };
229
229
230 var submitSuccessCallback = function(o) {
230 var submitSuccessCallback = function(o) {
231 location.reload(true);
231 location.reload(true);
232 };
232 };
233 var submitFailCallback = function(){
233 var submitFailCallback = function(){
234 mainCommentForm.resetCommentFormState(text)
234 mainCommentForm.resetCommentFormState(text)
235 };
235 };
236 mainCommentForm.submitAjaxPOST(
236 mainCommentForm.submitAjaxPOST(
237 mainCommentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
237 mainCommentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
238 });
238 });
239 </script>
239 </script>
240
240
241 </div>
241 </div>
242
242
243 %if c.compare_home:
243 %if c.compare_home:
244 <div id="changeset_compare_view_content">
244 <div id="changeset_compare_view_content">
245 <div class="help-block">${_('Compare commits, branches, bookmarks or tags.')}</div>
245 <div class="help-block">${_('Compare commits, branches, bookmarks or tags.')}</div>
246 </div>
246 </div>
247 %else:
247 %else:
248 <div id="changeset_compare_view_content">
248 <div id="changeset_compare_view_content">
249 ##CS
249 ##CS
250 <%include file="compare_commits.html"/>
250 <%include file="compare_commits.html"/>
251
251
252 ## FILES
252 ## FILES
253 <div class="cs_files_title">
253 <div class="cs_files_title">
254 <span class="cs_files_expand">
254 <span class="cs_files_expand">
255 <span id="expand_all_files">${_('Expand All')}</span> | <span id="collapse_all_files">${_('Collapse All')}</span>
255 <span id="expand_all_files">${_('Expand All')}</span> | <span id="collapse_all_files">${_('Collapse All')}</span>
256 </span>
256 </span>
257 <h2>
257 <h2>
258 ${diff_block.diff_summary_text(len(c.files), c.lines_added, c.lines_deleted, c.limited_diff)}
258 ${diff_block.diff_summary_text(len(c.files), c.lines_added, c.lines_deleted, c.limited_diff)}
259 </h2>
259 </h2>
260 </div>
260 </div>
261 <div class="cs_files">
261 <div class="cs_files">
262 %if not c.files:
262 %if not c.files:
263 <p class="empty_data">${_('No files')}</p>
263 <p class="empty_data">${_('No files')}</p>
264 %endif
264 %endif
265 <table class="compare_view_files">
265 <table class="compare_view_files">
266 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
266 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
267 %for FID, change, path, stats, file in c.files:
267 %for FID, change, path, stats, file in c.files:
268 <tr class="cs_${change} collapse_file" fid="${FID}">
268 <tr class="cs_${change} collapse_file" fid="${FID}">
269 <td class="cs_icon_td">
269 <td class="cs_icon_td">
270 <span class="collapse_file_icon" fid="${FID}"></span>
270 <span class="collapse_file_icon" fid="${FID}"></span>
271 </td>
271 </td>
272 <td class="cs_icon_td">
272 <td class="cs_icon_td">
273 <div class="flag_status not_reviewed hidden"></div>
273 <div class="flag_status not_reviewed hidden"></div>
274 </td>
274 </td>
275 <td class="cs_${change}" id="a_${FID}">
275 <td class="cs_${change}" id="a_${FID}">
276 <div class="node">
276 <div class="node">
277 <a href="#a_${FID}">
277 <a href="#a_${FID}">
278 <i class="icon-file-${change.lower()}"></i>
278 <i class="icon-file-${change.lower()}"></i>
279 ${h.safe_unicode(path)}
279 ${h.safe_unicode(path)}
280 </a>
280 </a>
281 </div>
281 </div>
282 </td>
282 </td>
283 <td>
283 <td>
284 <div class="changes pull-right">${h.fancy_file_stats(stats)}</div>
284 <div class="changes pull-right">${h.fancy_file_stats(stats)}</div>
285 <div class="comment-bubble pull-right" data-path="${path}">
285 <div class="comment-bubble pull-right" data-path="${path}">
286 <i class="icon-comment"></i>
286 <i class="icon-comment"></i>
287 </div>
287 </div>
288 </td>
288 </td>
289 </tr>
289 </tr>
290 <tr fid="${FID}" id="diff_${FID}" class="diff_links">
290 <tr fid="${FID}" id="diff_${FID}" class="diff_links">
291 <td></td>
291 <td></td>
292 <td></td>
292 <td></td>
293 <td class="cs_${change}">
293 <td class="cs_${change}">
294 %if c.target_repo.repo_name == c.repo_name:
294 %if c.target_repo.repo_name == c.repo_name:
295 ${diff_block.diff_menu(c.repo_name, h.safe_unicode(path), c.source_ref, c.target_ref, change, file)}
295 ${diff_block.diff_menu(c.repo_name, h.safe_unicode(path), c.source_ref, c.target_ref, change, file)}
296 %else:
296 %else:
297 ## this is slightly different case later, since the target repo can have this
297 ## this is slightly different case later, since the target repo can have this
298 ## file in target state than the source repo
298 ## file in target state than the source repo
299 ${diff_block.diff_menu(c.target_repo.repo_name, h.safe_unicode(path), c.source_ref, c.target_ref, change, file)}
299 ${diff_block.diff_menu(c.target_repo.repo_name, h.safe_unicode(path), c.source_ref, c.target_ref, change, file)}
300 %endif
300 %endif
301 </td>
301 </td>
302 <td class="td-actions rc-form">
302 <td class="td-actions rc-form">
303 </td>
303 </td>
304 </tr>
304 </tr>
305 <tr id="tr_${FID}">
305 <tr id="tr_${FID}">
306 <td></td>
306 <td></td>
307 <td></td>
307 <td></td>
308 <td class="injected_diff" colspan="2">
308 <td class="injected_diff" colspan="2">
309 ${diff_block.diff_block_simple([c.changes[FID]])}
309 ${diff_block.diff_block_simple([c.changes[FID]])}
310 </td>
310 </td>
311 </tr>
311 </tr>
312 %endfor
312 %endfor
313 </table>
313 </table>
314 % if c.limited_diff:
314 % if c.limited_diff:
315 ${diff_block.changeset_message()}
315 ${diff_block.changeset_message()}
316 % endif
316 % endif
317 </div>
317 </div>
318 %endif
318 %endif
319 </div>
319 </div>
320 </div>
320 </div>
321 </div>
321 </div>
322 </%def>
322 </%def>
@@ -1,967 +1,967 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/debug_style/index.html"/>
2 <%inherit file="/debug_style/index.html"/>
3
3
4 <%def name="breadcrumbs_links()">
4 <%def name="breadcrumbs_links()">
5 ${h.link_to(_('Style'), h.url('debug_style_home'))}
5 ${h.link_to(_('Style'), h.url('debug_style_home'))}
6 &raquo;
6 &raquo;
7 ${c.active}
7 ${c.active}
8 </%def>
8 </%def>
9
9
10
10
11 <%def name="real_main()">
11 <%def name="real_main()">
12 <div class="box">
12 <div class="box">
13 <div class="title">
13 <div class="title">
14 ${self.breadcrumbs()}
14 ${self.breadcrumbs()}
15 </div>
15 </div>
16
16
17 <div class='sidebar-col-wrapper'>
17 <div class='sidebar-col-wrapper'>
18 ${self.sidebar()}
18 ${self.sidebar()}
19
19
20 <div class="main-content">
20 <div class="main-content">
21
21
22 <h2>Collapsable Content</h2>
22 <h2>Collapsable Content</h2>
23 <p>Where a section may have a very long list of information, it can be desirable to use collapsable content. There is a premade function for showing/hiding elements, though its use may or may not be practical, depending on the situation. Use it, or don't, on a case-by-case basis.</p>
23 <p>Where a section may have a very long list of information, it can be desirable to use collapsable content. There is a premade function for showing/hiding elements, though its use may or may not be practical, depending on the situation. Use it, or don't, on a case-by-case basis.</p>
24
24
25 <p><strong>To use the collapsable-content function:</strong> Create a toggle button using <code>&lt;div class="btn-collapse"&gt;Show More&lt;/div&gt;</code> and a data attribute using <code>data-toggle</code>. Clicking this button will toggle any sibling element(s) containing the class <code>collapsable-content</code> and an identical <code>data-toggle</code> attribute. It will also change the button to read "Show Less"; another click toggles it back to the previous state. Ideally, use pre-existing elements and add the class and attribute; creating a new div around the existing content may lead to unexpected results, as the toggle function will use <code>display:block</code> if no previous display specification was found.
25 <p><strong>To use the collapsable-content function:</strong> Create a toggle button using <code>&lt;div class="btn-collapse"&gt;Show More&lt;/div&gt;</code> and a data attribute using <code>data-toggle</code>. Clicking this button will toggle any sibling element(s) containing the class <code>collapsable-content</code> and an identical <code>data-toggle</code> attribute. It will also change the button to read "Show Less"; another click toggles it back to the previous state. Ideally, use pre-existing elements and add the class and attribute; creating a new div around the existing content may lead to unexpected results, as the toggle function will use <code>display:block</code> if no previous display specification was found.
26 </p>
26 </p>
27 <p>Notes:</p>
27 <p>Notes:</p>
28 <ul>
28 <ul>
29 <li>Changes made to the text of the button will require adjustment to the function, but for the sake of consistency and user experience, this is best avoided. </li>
29 <li>Changes made to the text of the button will require adjustment to the function, but for the sake of consistency and user experience, this is best avoided. </li>
30 <li>Collapsable content inside of a pjax loaded container will require <code>collapsableContent();</code> to be called from within the container. No variables are necessary.</li>
30 <li>Collapsable content inside of a pjax loaded container will require <code>collapsableContent();</code> to be called from within the container. No variables are necessary.</li>
31 </ul>
31 </ul>
32
32
33 </div> <!-- .main-content -->
33 </div> <!-- .main-content -->
34 </div> <!-- .sidebar-col-wrapper -->
34 </div> <!-- .sidebar-col-wrapper -->
35 </div> <!-- .box -->
35 </div> <!-- .box -->
36
36
37 <!-- CONTENT -->
37 <!-- CONTENT -->
38 <div id="content" class="wrapper">
38 <div id="content" class="wrapper">
39
39
40 <div class="main">
40 <div class="main">
41
41
42 <div class="box">
42 <div class="box">
43 <div class="title">
43 <div class="title">
44 <h1>
44 <h1>
45 Diff: enable filename with spaces on diffs
45 Diff: enable filename with spaces on diffs
46 </h1>
46 </h1>
47 <h1>
47 <h1>
48 <i class="icon-hg" ></i>
48 <i class="icon-hg" ></i>
49
49
50 <i class="icon-lock"></i>
50 <i class="icon-lock"></i>
51 <span><a href="/rhodecode-momentum">rhodecode-momentum</a></span>
51 <span><a href="/rhodecode-momentum">rhodecode-momentum</a></span>
52
52
53 </h1>
53 </h1>
54 </div>
54 </div>
55
55
56 <div class="box pr-summary">
56 <div class="box pr-summary">
57 <div class="summary-details block-left">
57 <div class="summary-details block-left">
58
58
59 <div class="pr-details-title">
59 <div class="pr-details-title">
60
60
61 Pull request #720 From Tue, 17 Feb 2015 16:21:38
61 Pull request #720 From Tue, 17 Feb 2015 16:21:38
62 <div class="btn-collapse" data-toggle="description">Show More</div>
62 <div class="btn-collapse" data-toggle="description">Show More</div>
63 </div>
63 </div>
64 <div id="summary" class="fields pr-details-content">
64 <div id="summary" class="fields pr-details-content">
65 <div class="field">
65 <div class="field">
66 <div class="label-summary">
66 <div class="label-summary">
67 <label>Origin:</label>
67 <label>Origin:</label>
68 </div>
68 </div>
69 <div class="input">
69 <div class="input">
70 <div>
70 <div>
71 <span class="tag">
71 <span class="tag">
72 <a href="/andersonsantos/rhodecode-momentum-fork#fix_574">book: fix_574</a>
72 <a href="/andersonsantos/rhodecode-momentum-fork#fix_574">book: fix_574</a>
73 </span>
73 </span>
74 <span class="clone-url">
74 <span class="clone-url">
75 <a href="/andersonsantos/rhodecode-momentum-fork">https://code.rhodecode.com/andersonsantos/rhodecode-momentum-fork</a>
75 <a href="/andersonsantos/rhodecode-momentum-fork">https://code.rhodecode.com/andersonsantos/rhodecode-momentum-fork</a>
76 </span>
76 </span>
77 </div>
77 </div>
78 <div>
78 <div>
79 <br>
79 <br>
80 <input type="text" value="hg pull -r 46b3d50315f0 https://code.rhodecode.com/andersonsantos/rhodecode-momentum-fork" readonly="readonly">
80 <input type="text" value="hg pull -r 46b3d50315f0 https://code.rhodecode.com/andersonsantos/rhodecode-momentum-fork" readonly="readonly">
81 </div>
81 </div>
82 </div>
82 </div>
83 </div>
83 </div>
84 <div class="field">
84 <div class="field">
85 <div class="label-summary">
85 <div class="label-summary">
86 <label>Review:</label>
86 <label>Review:</label>
87 </div>
87 </div>
88 <div class="input">
88 <div class="input">
89 <div class="flag_status under_review tooltip pull-left" title="Pull request status calculated from votes"></div>
89 <div class="flag_status under_review tooltip pull-left" title="Pull request status calculated from votes"></div>
90 <span class="changeset-status-lbl tooltip" title="Pull request status calculated from votes">
90 <span class="changeset-status-lbl tooltip" title="Pull request status calculated from votes">
91 Under Review
91 Under Review
92 </span>
92 </span>
93
93
94 </div>
94 </div>
95 </div>
95 </div>
96 <div class="field collapsable-content" data-toggle="description">
96 <div class="field collapsable-content" data-toggle="description">
97 <div class="label-summary">
97 <div class="label-summary">
98 <label>Description:</label>
98 <label>Description:</label>
99 </div>
99 </div>
100 <div class="input">
100 <div class="input">
101 <div class="pr-description">Fixing issue <a class="issue- tracker-link" href="http://bugs.rhodecode.com/issues/574"># 574</a>, changing regex for capturing filenames</div>
101 <div class="pr-description">Fixing issue <a class="issue- tracker-link" href="http://bugs.rhodecode.com/issues/574"># 574</a>, changing regex for capturing filenames</div>
102 </div>
102 </div>
103 </div>
103 </div>
104 <div class="field collapsable-content" data-toggle="description">
104 <div class="field collapsable-content" data-toggle="description">
105 <div class="label-summary">
105 <div class="label-summary">
106 <label>Comments:</label>
106 <label>Comments:</label>
107 </div>
107 </div>
108 <div class="input">
108 <div class="input">
109 <div>
109 <div>
110 <div class="comments-number">
110 <div class="comments-number">
111 <a href="#inline-comments-container">0 Pull request comments</a>,
111 <a href="#inline-comments-container">0 Pull request comments</a>,
112 0 Inline Comments
112 0 Inline Comments
113 </div>
113 </div>
114 </div>
114 </div>
115 </div>
115 </div>
116 </div>
116 </div>
117 </div>
117 </div>
118 </div>
118 </div>
119 <div>
119 <div>
120 <div class="reviewers-title block-right">
120 <div class="reviewers-title block-right">
121 <div class="pr-details-title">
121 <div class="pr-details-title">
122 Author
122 Author
123 </div>
123 </div>
124 </div>
124 </div>
125 <div class="block-right pr-details-content reviewers">
125 <div class="block-right pr-details-content reviewers">
126 <ul class="group_members">
126 <ul class="group_members">
127 <li>
127 <li>
128 <img class="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=32" height="16" width="16">
128 <img class="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=32" height="16" width="16">
129 <span class="user"> <a href="/_profiles/lolek">lolek (Lolek Santos)</a></span>
129 <span class="user"> <a href="/_profiles/lolek">lolek (Lolek Santos)</a></span>
130 </li>
130 </li>
131 </ul>
131 </ul>
132 </div>
132 </div>
133 <div class="reviewers-title block-right">
133 <div class="reviewers-title block-right">
134 <div class="pr-details-title">
134 <div class="pr-details-title">
135 Pull request reviewers
135 Pull request reviewers
136 <span class="btn-collapse" data-toggle="reviewers">Show More</span>
136 <span class="btn-collapse" data-toggle="reviewers">Show More</span>
137 </div>
137 </div>
138
138
139 </div>
139 </div>
140 <div id="reviewers" class="block-right pr-details-content reviewers">
140 <div id="reviewers" class="block-right pr-details-content reviewers">
141
141
142 <ul id="review_members" class="group_members">
142 <ul id="review_members" class="group_members">
143 <li id="reviewer_70">
143 <li id="reviewer_70">
144 <div class="reviewers_member">
144 <div class="reviewers_member">
145 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
145 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
146 <div class="flag_status rejected pull-left reviewer_member_status"></div>
146 <div class="flag_status rejected pull-left reviewer_member_status"></div>
147 </div>
147 </div>
148 <img class="gravatar" src="https://secure.gravatar.com/avatar/153a0fab13160b3e64a2cbc7c0373506?d=identicon&amp;s=32" height="16" width="16">
148 <img class="gravatar" src="https://secure.gravatar.com/avatar/153a0fab13160b3e64a2cbc7c0373506?d=identicon&amp;s=32" height="16" width="16">
149 <span class="user"> <a href="/_profiles/jenkins-tests">jenkins-tests</a> (reviewer)</span>
149 <span class="user"> <a href="/_profiles/jenkins-tests">jenkins-tests</a> (reviewer)</span>
150 </div>
150 </div>
151 <input id="reviewer_70_input" type="hidden" value="70" name="review_members">
151 <input id="reviewer_70_input" type="hidden" value="70" name="review_members">
152 <div class="reviewer_member_remove action_button" onclick="removeReviewMember(70, true)" style="visibility: hidden;">
152 <div class="reviewer_member_remove action_button" onclick="removeReviewMember(70, true)" style="visibility: hidden;">
153 <i class="icon-remove-sign"></i>
153 <i class="icon-remove-sign"></i>
154 </div>
154 </div>
155 </li>
155 </li>
156 <li id="reviewer_33" class="collapsable-content" data-toggle="reviewers">
156 <li id="reviewer_33" class="collapsable-content" data-toggle="reviewers">
157 <div class="reviewers_member">
157 <div class="reviewers_member">
158 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
158 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
159 <div class="flag_status approved pull-left reviewer_member_status"></div>
159 <div class="flag_status approved pull-left reviewer_member_status"></div>
160 </div>
160 </div>
161 <img class="gravatar" src="https://secure.gravatar.com/avatar/ffd6a317ec2b66be880143cd8459d0d9?d=identicon&amp;s=32" height="16" width="16">
161 <img class="gravatar" src="https://secure.gravatar.com/avatar/ffd6a317ec2b66be880143cd8459d0d9?d=identicon&amp;s=32" height="16" width="16">
162 <span class="user"> <a href="/_profiles/jenkins-tests">garbas (Rok Garbas)</a> (reviewer)</span>
162 <span class="user"> <a href="/_profiles/jenkins-tests">garbas (Rok Garbas)</a> (reviewer)</span>
163 </div>
163 </div>
164 </li>
164 </li>
165 <li id="reviewer_2" class="collapsable-content" data-toggle="reviewers">
165 <li id="reviewer_2" class="collapsable-content" data-toggle="reviewers">
166 <div class="reviewers_member">
166 <div class="reviewers_member">
167 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
167 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
168 <div class="flag_status not_reviewed pull-left reviewer_member_status"></div>
168 <div class="flag_status not_reviewed pull-left reviewer_member_status"></div>
169 </div>
169 </div>
170 <img class="gravatar" src="https://secure.gravatar.com/avatar/aad9d40cac1259ea39b5578554ad9d64?d=identicon&amp;s=32" height="16" width="16">
170 <img class="gravatar" src="https://secure.gravatar.com/avatar/aad9d40cac1259ea39b5578554ad9d64?d=identicon&amp;s=32" height="16" width="16">
171 <span class="user"> <a href="/_profiles/jenkins-tests">marcink (Marcin Kuzminski)</a> (reviewer)</span>
171 <span class="user"> <a href="/_profiles/jenkins-tests">marcink (Marcin Kuzminski)</a> (reviewer)</span>
172 </div>
172 </div>
173 </li>
173 </li>
174 <li id="reviewer_36" class="collapsable-content" data-toggle="reviewers">
174 <li id="reviewer_36" class="collapsable-content" data-toggle="reviewers">
175 <div class="reviewers_member">
175 <div class="reviewers_member">
176 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
176 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
177 <div class="flag_status approved pull-left reviewer_member_status"></div>
177 <div class="flag_status approved pull-left reviewer_member_status"></div>
178 </div>
178 </div>
179 <img class="gravatar" src="https://secure.gravatar.com/avatar/7a4da001a0af0016ed056ab523255db9?d=identicon&amp;s=32" height="16" width="16">
179 <img class="gravatar" src="https://secure.gravatar.com/avatar/7a4da001a0af0016ed056ab523255db9?d=identicon&amp;s=32" height="16" width="16">
180 <span class="user"> <a href="/_profiles/jenkins-tests">johbo (Johannes Bornhold)</a> (reviewer)</span>
180 <span class="user"> <a href="/_profiles/jenkins-tests">johbo (Johannes Bornhold)</a> (reviewer)</span>
181 </div>
181 </div>
182 </li>
182 </li>
183 <li id="reviewer_47" class="collapsable-content" data-toggle="reviewers">
183 <li id="reviewer_47" class="collapsable-content" data-toggle="reviewers">
184 <div class="reviewers_member">
184 <div class="reviewers_member">
185 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
185 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
186 <div class="flag_status under_review pull-left reviewer_member_status"></div>
186 <div class="flag_status under_review pull-left reviewer_member_status"></div>
187 </div>
187 </div>
188 <img class="gravatar" src="https://secure.gravatar.com/avatar/8f6dc00dce79d6bd7d415be5cea6a008?d=identicon&amp;s=32" height="16" width="16">
188 <img class="gravatar" src="https://secure.gravatar.com/avatar/8f6dc00dce79d6bd7d415be5cea6a008?d=identicon&amp;s=32" height="16" width="16">
189 <span class="user"> <a href="/_profiles/jenkins-tests">lisaq (Lisa Quatmann)</a> (reviewer)</span>
189 <span class="user"> <a href="/_profiles/jenkins-tests">lisaq (Lisa Quatmann)</a> (reviewer)</span>
190 </div>
190 </div>
191 </li>
191 </li>
192 <li id="reviewer_49" class="collapsable-content" data-toggle="reviewers">
192 <li id="reviewer_49" class="collapsable-content" data-toggle="reviewers">
193 <div class="reviewers_member">
193 <div class="reviewers_member">
194 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
194 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
195 <div class="flag_status approved pull-left reviewer_member_status"></div>
195 <div class="flag_status approved pull-left reviewer_member_status"></div>
196 </div>
196 </div>
197 <img class="gravatar" src="https://secure.gravatar.com/avatar/89f722927932a8f737a0feafb03a606e?d=identicon&amp;s=32" height="16" width="16">
197 <img class="gravatar" src="https://secure.gravatar.com/avatar/89f722927932a8f737a0feafb03a606e?d=identicon&amp;s=32" height="16" width="16">
198 <span class="user"> <a href="/_profiles/jenkins-tests">paris (Paris Kolios)</a> (reviewer)</span>
198 <span class="user"> <a href="/_profiles/jenkins-tests">paris (Paris Kolios)</a> (reviewer)</span>
199 </div>
199 </div>
200 </li>
200 </li>
201 <li id="reviewer_50" class="collapsable-content" data-toggle="reviewers">
201 <li id="reviewer_50" class="collapsable-content" data-toggle="reviewers">
202 <div class="reviewers_member">
202 <div class="reviewers_member">
203 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
203 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
204 <div class="flag_status approved pull-left reviewer_member_status"></div>
204 <div class="flag_status approved pull-left reviewer_member_status"></div>
205 </div>
205 </div>
206 <img class="gravatar" src="https://secure.gravatar.com/avatar/081322c975e8545ec269372405fbd016?d=identicon&amp;s=32" height="16" width="16">
206 <img class="gravatar" src="https://secure.gravatar.com/avatar/081322c975e8545ec269372405fbd016?d=identicon&amp;s=32" height="16" width="16">
207 <span class="user"> <a href="/_profiles/jenkins-tests">ergo (Marcin Lulek)</a> (reviewer)</span>
207 <span class="user"> <a href="/_profiles/jenkins-tests">ergo (Marcin Lulek)</a> (reviewer)</span>
208 </div>
208 </div>
209 </li>
209 </li>
210 <li id="reviewer_54" class="collapsable-content" data-toggle="reviewers">
210 <li id="reviewer_54" class="collapsable-content" data-toggle="reviewers">
211 <div class="reviewers_member">
211 <div class="reviewers_member">
212 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
212 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
213 <div class="flag_status under_review pull-left reviewer_member_status"></div>
213 <div class="flag_status under_review pull-left reviewer_member_status"></div>
214 </div>
214 </div>
215 <img class="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=32" height="16" width="16">
215 <img class="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=32" height="16" width="16">
216 <span class="user"> <a href="/_profiles/jenkins-tests">anderson (Anderson Santos)</a> (reviewer)</span>
216 <span class="user"> <a href="/_profiles/jenkins-tests">anderson (Anderson Santos)</a> (reviewer)</span>
217 </div>
217 </div>
218 </li>
218 </li>
219 <li id="reviewer_57" class="collapsable-content" data-toggle="reviewers">
219 <li id="reviewer_57" class="collapsable-content" data-toggle="reviewers">
220 <div class="reviewers_member">
220 <div class="reviewers_member">
221 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
221 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
222 <div class="flag_status approved pull-left reviewer_member_status"></div>
222 <div class="flag_status approved pull-left reviewer_member_status"></div>
223 </div>
223 </div>
224 <img class="gravatar" src="https://secure.gravatar.com/avatar/23e2ee8f5fd462cba8129a40cc1e896c?d=identicon&amp;s=32" height="16" width="16">
224 <img class="gravatar" src="https://secure.gravatar.com/avatar/23e2ee8f5fd462cba8129a40cc1e896c?d=identicon&amp;s=32" height="16" width="16">
225 <span class="user"> <a href="/_profiles/jenkins-tests">gmgauthier (Greg Gauthier)</a> (reviewer)</span>
225 <span class="user"> <a href="/_profiles/jenkins-tests">gmgauthier (Greg Gauthier)</a> (reviewer)</span>
226 </div>
226 </div>
227 </li>
227 </li>
228 <li id="reviewer_31" class="collapsable-content" data-toggle="reviewers">
228 <li id="reviewer_31" class="collapsable-content" data-toggle="reviewers">
229 <div class="reviewers_member">
229 <div class="reviewers_member">
230 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
230 <div class="reviewer_status tooltip pull-left" title="Not Reviewed">
231 <div class="flag_status under_review pull-left reviewer_member_status"></div>
231 <div class="flag_status under_review pull-left reviewer_member_status"></div>
232 </div>
232 </div>
233 <img class="gravatar" src="https://secure.gravatar.com/avatar/0c9a7e6674b6f0b35d98dbe073e3f0ab?d=identicon&amp;s=32" height="16" width="16">
233 <img class="gravatar" src="https://secure.gravatar.com/avatar/0c9a7e6674b6f0b35d98dbe073e3f0ab?d=identicon&amp;s=32" height="16" width="16">
234 <span class="user"> <a href="/_profiles/jenkins-tests">ostrobel (Oliver Strobel)</a> (reviewer)</span>
234 <span class="user"> <a href="/_profiles/jenkins-tests">ostrobel (Oliver Strobel)</a> (reviewer)</span>
235 </div>
235 </div>
236 </li>
236 </li>
237 </ul>
237 </ul>
238 <div id="add_reviewer_input" class="ac" style="display: none;">
238 <div id="add_reviewer_input" class="ac" style="display: none;">
239 </div>
239 </div>
240 </div>
240 </div>
241 </div>
241 </div>
242 </div>
242 </div>
243 </div>
243 </div>
244 <div class="box">
244 <div class="box">
245 <div class="table" >
245 <div class="table" >
246 <div id="changeset_compare_view_content">
246 <div id="changeset_compare_view_content">
247 <div class="compare_view_commits_title">
247 <div class="compare_view_commits_title">
248 <h2>Compare View: 6 commits<span class="btn-collapse" data-toggle="commits">Show More</span></h2>
248 <h2>Compare View: 6 commits<span class="btn-collapse" data-toggle="commits">Show More</span></h2>
249
249
250 </div>
250 </div>
251 <div class="container">
251 <div class="container">
252
252
253
253
254 <table class="rctable compare_view_commits">
254 <table class="rctable compare_view_commits">
255 <tr>
255 <tr>
256 <th>Time</th>
256 <th>Time</th>
257 <th>Author</th>
257 <th>Author</th>
258 <th>Commit</th>
258 <th>Commit</th>
259 <th></th>
259 <th></th>
260 <th>Title</th>
260 <th>Title</th>
261 </tr>
261 </tr>
262 <tr id="row-7e83e5cd7812dd9e055ce30e77c65cdc08154b43" commit_id="7e83e5cd7812dd9e055ce30e77c65cdc08154b43" class="compare_select">
262 <tr id="row-7e83e5cd7812dd9e055ce30e77c65cdc08154b43" commit_id="7e83e5cd7812dd9e055ce30e77c65cdc08154b43" class="compare_select">
263 <td class="td-time">
263 <td class="td-time">
264 <span class="tooltip" title="3 hours and 23 minutes ago" tt_title="3 hours and 23 minutes ago">2015-02-18 10:13:34</span>
264 <span class="tooltip" title="3 hours and 23 minutes ago" tt_title="3 hours and 23 minutes ago">2015-02-18 10:13:34</span>
265 </td>
265 </td>
266 <td class="td-user">
266 <td class="td-user">
267 <div class="gravatar_with_user">
267 <div class="gravatar_with_user">
268 <img class="gravatar" alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=16">
268 <img class="gravatar" alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=16">
269 <span title="Lolek Santos <lolek@rhodecode.com>" class="user">brian (Brian Butler)</span>
269 <span title="Lolek Santos <lolek@rhodecode.com>" class="user">brian (Brian Butler)</span>
270 </div>
270 </div>
271 </td>
271 </td>
272 <td class="td-hash">
272 <td class="td-hash">
273 <code>
273 <code>
274 <a href="/brian/documentation-rep/changeset/7e83e5cd7812dd9e055ce30e77c65cdc08154b43">r395:7e83e5cd7812</a>
274 <a href="/brian/documentation-rep/changeset/7e83e5cd7812dd9e055ce30e77c65cdc08154b43">r395:7e83e5cd7812</a>
275 </code>
275 </code>
276 </td>
276 </td>
277 <td class="expand_commit" data-commit-id="7e83e5cd7812dd9e055ce30e77c65cdc08154b43" title="Expand commit message">
277 <td class="expand_commit" data-commit-id="7e83e5cd7812dd9e055ce30e77c65cdc08154b43" title="Expand commit message">
278 <div class="show_more_col">
278 <div class="show_more_col">
279 <i class="show_more"></i>
279 <i class="show_more"></i>
280 </div>
280 </div>
281 </td>
281 </td>
282 <td class="mid td-description">
282 <td class="mid td-description">
283 <div class="log-container truncate-wrap">
283 <div class="log-container truncate-wrap">
284 <div id="c-7e83e5cd7812dd9e055ce30e77c65cdc08154b43" class="message truncate">rep: added how we doc to guide</div>
284 <div id="c-7e83e5cd7812dd9e055ce30e77c65cdc08154b43" class="message truncate">rep: added how we doc to guide</div>
285 </div>
285 </div>
286 </td>
286 </td>
287 </tr>
287 </tr>
288 <tr id="row-48ce1581bdb3aa7679c246cbdd3fb030623f5c87" commit_id="48ce1581bdb3aa7679c246cbdd3fb030623f5c87" class="compare_select">
288 <tr id="row-48ce1581bdb3aa7679c246cbdd3fb030623f5c87" commit_id="48ce1581bdb3aa7679c246cbdd3fb030623f5c87" class="compare_select">
289 <td class="td-time">
289 <td class="td-time">
290 <span class="tooltip" title="4 hours and 18 minutes ago">2015-02-18 09:18:31</span>
290 <span class="tooltip" title="4 hours and 18 minutes ago">2015-02-18 09:18:31</span>
291 </td>
291 </td>
292 <td class="td-user">
292 <td class="td-user">
293 <div class="gravatar_with_user">
293 <div class="gravatar_with_user">
294 <img class="gravatar" alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=16">
294 <img class="gravatar" alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=16">
295 <span title="Lolek Santos <lolek@rhodecode.com>" class="user">brian (Brian Butler)</span>
295 <span title="Lolek Santos <lolek@rhodecode.com>" class="user">brian (Brian Butler)</span>
296 </div>
296 </div>
297 </td>
297 </td>
298 <td class="td-hash">
298 <td class="td-hash">
299 <code>
299 <code>
300 <a href="/brian/documentation-rep/changeset/48ce1581bdb3aa7679c246cbdd3fb030623f5c87">r394:48ce1581bdb3</a>
300 <a href="/brian/documentation-rep/changeset/48ce1581bdb3aa7679c246cbdd3fb030623f5c87">r394:48ce1581bdb3</a>
301 </code>
301 </code>
302 </td>
302 </td>
303 <td class="expand_commit" data-commit-id="48ce1581bdb3aa7679c246cbdd3fb030623f5c87" title="Expand commit message">
303 <td class="expand_commit" data-commit-id="48ce1581bdb3aa7679c246cbdd3fb030623f5c87" title="Expand commit message">
304 <div class="show_more_col">
304 <div class="show_more_col">
305 <i class="show_more"></i>
305 <i class="show_more"></i>
306 </div>
306 </div>
307 </td>
307 </td>
308 <td class="mid td-description">
308 <td class="mid td-description">
309 <div class="log-container truncate-wrap">
309 <div class="log-container truncate-wrap">
310 <div id="c-48ce1581bdb3aa7679c246cbdd3fb030623f5c87" class="message truncate">repo 0004 - typo</div>
310 <div id="c-48ce1581bdb3aa7679c246cbdd3fb030623f5c87" class="message truncate">repo 0004 - typo</div>
311 </div>
311 </div>
312 </td>
312 </td>
313 </tr>
313 </tr>
314 <tr id="row-982d857aafb4c71e7686e419c32b71c9a837257d" commit_id="982d857aafb4c71e7686e419c32b71c9a837257d" class="compare_select collapsable-content" data-toggle="commits">
314 <tr id="row-982d857aafb4c71e7686e419c32b71c9a837257d" commit_id="982d857aafb4c71e7686e419c32b71c9a837257d" class="compare_select collapsable-content" data-toggle="commits">
315 <td class="td-time">
315 <td class="td-time">
316 <span class="tooltip" title="4 hours and 22 minutes ago">2015-02-18 09:14:45</span>
316 <span class="tooltip" title="4 hours and 22 minutes ago">2015-02-18 09:14:45</span>
317 </td>
317 </td>
318 <td class="td-user">
318 <td class="td-user">
319 <span class="gravatar" commit_id="982d857aafb4c71e7686e419c32b71c9a837257d">
319 <span class="gravatar" commit_id="982d857aafb4c71e7686e419c32b71c9a837257d">
320 <img alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=28" height="14" width="14">
320 <img alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=28" height="14" width="14">
321 </span>
321 </span>
322 <span class="author">brian (Brian Butler)</span>
322 <span class="author">brian (Brian Butler)</span>
323 </td>
323 </td>
324 <td class="td-hash">
324 <td class="td-hash">
325 <code>
325 <code>
326 <a href="/brian/documentation-rep/changeset/982d857aafb4c71e7686e419c32b71c9a837257d">r393:982d857aafb4</a>
326 <a href="/brian/documentation-rep/changeset/982d857aafb4c71e7686e419c32b71c9a837257d">r393:982d857aafb4</a>
327 </code>
327 </code>
328 </td>
328 </td>
329 <td class="expand_commit" data-commit-id="982d857aafb4c71e7686e419c32b71c9a837257d" title="Expand commit message">
329 <td class="expand_commit" data-commit-id="982d857aafb4c71e7686e419c32b71c9a837257d" title="Expand commit message">
330 <div class="show_more_col">
330 <div class="show_more_col">
331 <i class="show_more"></i>
331 <i class="show_more"></i>
332 </div>
332 </div>
333 </td>
333 </td>
334 <td class="mid td-description">
334 <td class="mid td-description">
335 <div class="log-container truncate-wrap">
335 <div class="log-container truncate-wrap">
336 <div id="c-982d857aafb4c71e7686e419c32b71c9a837257d" class="message truncate">internals: how to doc section added</div>
336 <div id="c-982d857aafb4c71e7686e419c32b71c9a837257d" class="message truncate">internals: how to doc section added</div>
337 </div>
337 </div>
338 </td>
338 </td>
339 </tr>
339 </tr>
340 <tr id="row-4c7258ad1af6dae91bbaf87a933e3597e676fab8" commit_id="4c7258ad1af6dae91bbaf87a933e3597e676fab8" class="compare_select collapsable-content" data-toggle="commits">
340 <tr id="row-4c7258ad1af6dae91bbaf87a933e3597e676fab8" commit_id="4c7258ad1af6dae91bbaf87a933e3597e676fab8" class="compare_select collapsable-content" data-toggle="commits">
341 <td class="td-time">
341 <td class="td-time">
342 <span class="tooltip" title="20 hours and 16 minutes ago">2015-02-17 17:20:44</span>
342 <span class="tooltip" title="20 hours and 16 minutes ago">2015-02-17 17:20:44</span>
343 </td>
343 </td>
344 <td class="td-user">
344 <td class="td-user">
345 <span class="gravatar" commit_id="4c7258ad1af6dae91bbaf87a933e3597e676fab8">
345 <span class="gravatar" commit_id="4c7258ad1af6dae91bbaf87a933e3597e676fab8">
346 <img alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=28" height="14" width="14">
346 <img alt="gravatar" src="https://secure.gravatar.com/avatar/02cc31cea73b88b7209ba302c5967a9d?d=identicon&amp;s=28" height="14" width="14">
347 </span>
347 </span>
348 <span class="author">brian (Brian Butler)</span>
348 <span class="author">brian (Brian Butler)</span>
349 </td>
349 </td>
350 <td class="td-hash">
350 <td class="td-hash">
351 <code>
351 <code>
352 <a href="/brian/documentation-rep/changeset/4c7258ad1af6dae91bbaf87a933e3597e676fab8">r392:4c7258ad1af6</a>
352 <a href="/brian/documentation-rep/changeset/4c7258ad1af6dae91bbaf87a933e3597e676fab8">r392:4c7258ad1af6</a>
353 </code>
353 </code>
354 </td>
354 </td>
355 <td class="expand_commit" data-commit-id="4c7258ad1af6dae91bbaf87a933e3597e676fab8" title="Expand commit message">
355 <td class="expand_commit" data-commit-id="4c7258ad1af6dae91bbaf87a933e3597e676fab8" title="Expand commit message">
356 <div class="show_more_col">
356 <div class="show_more_col">
357 <i class="show_more"></i>
357 <i class="show_more"></i>
358 </div>
358 </div>
359 </td>
359 </td>
360 <td class="mid td-description">
360 <td class="mid td-description">
361 <div class="log-container truncate-wrap">
361 <div class="log-container truncate-wrap">
362 <div id="c-4c7258ad1af6dae91bbaf87a933e3597e676fab8" class="message truncate">REP: 0004 Documentation standards</div>
362 <div id="c-4c7258ad1af6dae91bbaf87a933e3597e676fab8" class="message truncate">REP: 0004 Documentation standards</div>
363 </div>
363 </div>
364 </td>
364 </td>
365 </tr>
365 </tr>
366 <tr id="row-46b3d50315f0f2b1f64485ac95af4f384948f9cb" commit_id="46b3d50315f0f2b1f64485ac95af4f384948f9cb" class="compare_select collapsable-content" data-toggle="commits">
366 <tr id="row-46b3d50315f0f2b1f64485ac95af4f384948f9cb" commit_id="46b3d50315f0f2b1f64485ac95af4f384948f9cb" class="compare_select collapsable-content" data-toggle="commits">
367 <td class="td-time">
367 <td class="td-time">
368 <span class="tooltip" title="18 hours and 19 minutes ago">2015-02-17 16:18:49</span>
368 <span class="tooltip" title="18 hours and 19 minutes ago">2015-02-17 16:18:49</span>
369 </td>
369 </td>
370 <td class="td-user">
370 <td class="td-user">
371 <span class="gravatar" commit_id="46b3d50315f0f2b1f64485ac95af4f384948f9cb">
371 <span class="gravatar" commit_id="46b3d50315f0f2b1f64485ac95af4f384948f9cb">
372 <img alt="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=28" height="14" width="14">
372 <img alt="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=28" height="14" width="14">
373 </span>
373 </span>
374 <span class="author">anderson (Anderson Santos)</span>
374 <span class="author">anderson (Anderson Santos)</span>
375 </td>
375 </td>
376 <td class="td-hash">
376 <td class="td-hash">
377 <code>
377 <code>
378 <a href="/andersonsantos/rhodecode-momentum-fork/changeset/46b3d50315f0f2b1f64485ac95af4f384948f9cb">r8743:46b3d50315f0</a>
378 <a href="/andersonsantos/rhodecode-momentum-fork/changeset/46b3d50315f0f2b1f64485ac95af4f384948f9cb">r8743:46b3d50315f0</a>
379 </code>
379 </code>
380 </td>
380 </td>
381 <td class="expand_commit" data-commit-id="46b3d50315f0f2b1f64485ac95af4f384948f9cb" title="Expand commit message">
381 <td class="expand_commit" data-commit-id="46b3d50315f0f2b1f64485ac95af4f384948f9cb" title="Expand commit message">
382 <div class="show_more_col">
382 <div class="show_more_col">
383 <i class="show_more" ></i>
383 <i class="show_more" ></i>
384 </div>
384 </div>
385 </td>
385 </td>
386 <td class="mid td-description">
386 <td class="mid td-description">
387 <div class="log-container truncate-wrap">
387 <div class="log-container truncate-wrap">
388 <div id="c-46b3d50315f0f2b1f64485ac95af4f384948f9cb" class="message truncate">Diff: created tests for the diff with filenames with spaces</div>
388 <div id="c-46b3d50315f0f2b1f64485ac95af4f384948f9cb" class="message truncate">Diff: created tests for the diff with filenames with spaces</div>
389
389
390 </div>
390 </div>
391 </td>
391 </td>
392 </tr>
392 </tr>
393 <tr id="row-1e57d2549bd6c34798075bf05ac39f708bb33b90" commit_id="1e57d2549bd6c34798075bf05ac39f708bb33b90" class="compare_select collapsable-content" data-toggle="commits">
393 <tr id="row-1e57d2549bd6c34798075bf05ac39f708bb33b90" commit_id="1e57d2549bd6c34798075bf05ac39f708bb33b90" class="compare_select collapsable-content" data-toggle="commits">
394 <td class="td-time">
394 <td class="td-time">
395 <span class="tooltip" title="2 days ago">2015-02-16 10:06:08</span>
395 <span class="tooltip" title="2 days ago">2015-02-16 10:06:08</span>
396 </td>
396 </td>
397 <td class="td-user">
397 <td class="td-user">
398 <span class="gravatar" commit_id="1e57d2549bd6c34798075bf05ac39f708bb33b90">
398 <span class="gravatar" commit_id="1e57d2549bd6c34798075bf05ac39f708bb33b90">
399 <img alt="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=28" height="14" width="14">
399 <img alt="gravatar" src="https://secure.gravatar.com/avatar/72706ebd30734451af9ff3fb59f05ff1?d=identicon&amp;s=28" height="14" width="14">
400 </span>
400 </span>
401 <span class="author">anderson (Anderson Santos)</span>
401 <span class="author">anderson (Anderson Santos)</span>
402 </td>
402 </td>
403 <td class="td-hash">
403 <td class="td-hash">
404 <code>
404 <code>
405 <a href="/andersonsantos/rhodecode-momentum-fork/changeset/1e57d2549bd6c34798075bf05ac39f708bb33b90">r8742:1e57d2549bd6</a>
405 <a href="/andersonsantos/rhodecode-momentum-fork/changeset/1e57d2549bd6c34798075bf05ac39f708bb33b90">r8742:1e57d2549bd6</a>
406 </code>
406 </code>
407 </td>
407 </td>
408 <td class="expand_commit" data-commit-id="1e57d2549bd6c34798075bf05ac39f708bb33b90" title="Expand commit message">
408 <td class="expand_commit" data-commit-id="1e57d2549bd6c34798075bf05ac39f708bb33b90" title="Expand commit message">
409 <div class="show_more_col">
409 <div class="show_more_col">
410 <i class="show_more" ></i>
410 <i class="show_more" ></i>
411 </div>
411 </div>
412 </td>
412 </td>
413 <td class="mid td-description">
413 <td class="mid td-description">
414 <div class="log-container truncate-wrap">
414 <div class="log-container truncate-wrap">
415 <div id="c-1e57d2549bd6c34798075bf05ac39f708bb33b90" class="message truncate">Diff: fix renaming files with spaces <a class="issue-tracker-link" href="http://bugs.rhodecode.com/issues/574">#574</a></div>
415 <div id="c-1e57d2549bd6c34798075bf05ac39f708bb33b90" class="message truncate">Diff: fix renaming files with spaces <a class="issue-tracker-link" href="http://bugs.rhodecode.com/issues/574">#574</a></div>
416
416
417 </div>
417 </div>
418 </td>
418 </td>
419 </tr>
419 </tr>
420 </table>
420 </table>
421 </div>
421 </div>
422
422
423 <script>
423 <script>
424 $('.expand_commit').on('click',function(e){
424 $('.expand_commit').on('click',function(e){
425 $(this).children('i').hide();
425 $(this).children('i').hide();
426 var cid = $(this).data('commitId');
426 var cid = $(this).data('commitId');
427 $('#c-'+cid).css({'height': 'auto', 'margin': '.65em 1em .65em 0','white-space': 'pre-line', 'text-overflow': 'initial', 'overflow':'visible'})
427 $('#c-'+cid).css({'height': 'auto', 'margin': '.65em 1em .65em 0','white-space': 'pre-line', 'text-overflow': 'initial', 'overflow':'visible'})
428 $('#t-'+cid).css({'height': 'auto', 'text-overflow': 'initial', 'overflow':'visible', 'white-space':'normal'})
428 $('#t-'+cid).css({'height': 'auto', 'text-overflow': 'initial', 'overflow':'visible', 'white-space':'normal'})
429 });
429 });
430 $('.compare_select').on('click',function(e){
430 $('.compare_select').on('click',function(e){
431 var cid = $(this).attr('commit_id');
431 var cid = $(this).attr('commit_id');
432 $('#row-'+cid).toggleClass('hl', !$('#row-'+cid).hasClass('hl'));
432 $('#row-'+cid).toggleClass('hl', !$('#row-'+cid).hasClass('hl'));
433 });
433 });
434 </script>
434 </script>
435 <div class="cs_files_title">
435 <div class="cs_files_title">
436 <span class="cs_files_expand">
436 <span class="cs_files_expand">
437 <span id="expand_all_files">Expand All</span> | <span id="collapse_all_files">Collapse All</span>
437 <span id="expand_all_files">Expand All</span> | <span id="collapse_all_files">Collapse All</span>
438 </span>
438 </span>
439 <h2>
439 <h2>
440 7 files changed: 55 inserted, 9 deleted
440 7 files changed: 55 inserted, 9 deleted
441 </h2>
441 </h2>
442 </div>
442 </div>
443 <div class="cs_files">
443 <div class="cs_files">
444 <table class="compare_view_files">
444 <table class="compare_view_files">
445
445
446 <tr class="cs_A expand_file" fid="c--efbe5b7a3f13">
446 <tr class="cs_A expand_file" fid="c--efbe5b7a3f13">
447 <td class="cs_icon_td">
447 <td class="cs_icon_td">
448 <span class="expand_file_icon" fid="c--efbe5b7a3f13"></span>
448 <span class="expand_file_icon" fid="c--efbe5b7a3f13"></span>
449 </td>
449 </td>
450 <td class="cs_icon_td">
450 <td class="cs_icon_td">
451 <div class="flag_status not_reviewed hidden"></div>
451 <div class="flag_status not_reviewed hidden"></div>
452 </td>
452 </td>
453 <td id="a_c--efbe5b7a3f13">
453 <td id="a_c--efbe5b7a3f13">
454 <a class="compare_view_filepath" href="#a_c--efbe5b7a3f13">
454 <a class="compare_view_filepath" href="#a_c--efbe5b7a3f13">
455 rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff
455 rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff
456 </a>
456 </a>
457 <span id="diff_c--efbe5b7a3f13" class="diff_links" style="display: none;">
457 <span id="diff_c--efbe5b7a3f13" class="diff_links" style="display: none;">
458 <a href="/andersonsantos/rhodecode-momentum-fork/diff/rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
458 <a href="/andersonsantos/rhodecode-momentum-fork/diff/rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
459 Unified Diff
459 Unified Diff
460 </a>
460 </a>
461 |
461 |
462 <a href="/andersonsantos/rhodecode-momentum-fork/diff-2way/rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
462 <a href="/andersonsantos/rhodecode-momentum-fork/diff-2way/rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
463 Side-by-side Diff
463 Side-by-side Diff
464 </a>
464 </a>
465 </span>
465 </span>
466 </td>
466 </td>
467 <td>
467 <td>
468 <div class="changes pull-right"><div style="width:100px"><div class="added top-right-rounded-corner-mid bottom-right-rounded-corner-mid top-left-rounded-corner-mid bottom-left-rounded-corner-mid" style="width:100.0%">4</div><div class="deleted top-right-rounded-corner-mid bottom-right-rounded-corner-mid" style="width:0%"></div></div></div>
468 <div class="changes pull-right"><div style="width:100px"><div class="added top-right-rounded-corner-mid bottom-right-rounded-corner-mid top-left-rounded-corner-mid bottom-left-rounded-corner-mid" style="width:100.0%">4</div><div class="deleted top-right-rounded-corner-mid bottom-right-rounded-corner-mid" style="width:0%"></div></div></div>
469 <div class="comment-bubble pull-right" data-path="rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff">
469 <div class="comment-bubble pull-right" data-path="rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff">
470 <i class="icon-comment"></i>
470 <i class="icon-comment"></i>
471 </div>
471 </div>
472 </td>
472 </td>
473 </tr>
473 </tr>
474 <tr id="tr_c--efbe5b7a3f13">
474 <tr id="tr_c--efbe5b7a3f13">
475 <td></td>
475 <td></td>
476 <td></td>
476 <td></td>
477 <td class="injected_diff" colspan="2">
477 <td class="injected_diff" colspan="2">
478
478
479 <div class="diff-container" id="diff-container-140716195039928">
479 <div class="diff-container" id="diff-container-140716195039928">
480 <div id="c--efbe5b7a3f13_target" ></div>
480 <div id="c--efbe5b7a3f13_target" ></div>
481 <div id="c--efbe5b7a3f13" class="diffblock margined comm" >
481 <div id="c--efbe5b7a3f13" class="diffblock margined comm" >
482 <div class="code-body">
482 <div class="code-body">
483 <div class="full_f_path" path="rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff" style="display: none;"></div>
483 <div class="full_f_path" path="rhodecode/tests/fixtures/git_diff_rename_file_with_spaces.diff" style="display: none;"></div>
484 <table class="code-difftable">
484 <table class="code-difftable">
485 <tr class="line context">
485 <tr class="line context">
486 <td class="add-comment-line"><span class="add-comment-content"></span></td>
486 <td class="add-comment-line"><span class="add-comment-content"></span></td>
487 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
487 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
488 <td class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n"></a></td>
488 <td class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n"></a></td>
489 <td class="code no-comment">
489 <td class="code no-comment">
490 <pre>new file 100644</pre>
490 <pre>new file 100644</pre>
491 </td>
491 </td>
492 </tr>
492 </tr>
493 <tr class="line add">
493 <tr class="line add">
494 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
494 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
495 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
495 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
496 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n1" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n1">1</a></td>
496 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n1" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n1">1</a></td>
497 <td class="code">
497 <td class="code">
498 <pre>diff --git a/file_with_ spaces.txt b/file_with_ two spaces.txt
498 <pre>diff --git a/file_with_ spaces.txt b/file_with_ two spaces.txt
499 </pre>
499 </pre>
500 </td>
500 </td>
501 </tr>
501 </tr>
502 <tr class="line add">
502 <tr class="line add">
503 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
503 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
504 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
504 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
505 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n2" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n2">2</a></td>
505 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n2" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n2">2</a></td>
506 <td class="code">
506 <td class="code">
507 <pre>similarity index 100%
507 <pre>similarity index 100%
508 </pre>
508 </pre>
509 </td>
509 </td>
510 </tr>
510 </tr>
511 <tr class="line add">
511 <tr class="line add">
512 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
512 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
513 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
513 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
514 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n3" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n3">3</a></td>
514 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n3" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n3">3</a></td>
515 <td class="code">
515 <td class="code">
516 <pre>rename from file_with_ spaces.txt
516 <pre>rename from file_with_ spaces.txt
517 </pre>
517 </pre>
518 </td>
518 </td>
519 </tr>
519 </tr>
520 <tr class="line add">
520 <tr class="line add">
521 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
521 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
522 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
522 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o"></a></td>
523 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n4" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n4">4</a></td>
523 <td id="rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n4" class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n4">4</a></td>
524 <td class="code">
524 <td class="code">
525 <pre>rename to file_with_ two spaces.txt
525 <pre>rename to file_with_ two spaces.txt
526 </pre>
526 </pre>
527 </td>
527 </td>
528 </tr>
528 </tr>
529 <tr class="line context">
529 <tr class="line context">
530 <td class="add-comment-line"><span class="add-comment-content"></span></td>
530 <td class="add-comment-line"><span class="add-comment-content"></span></td>
531 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o...">...</a></td>
531 <td class="lineno old"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_o...">...</a></td>
532 <td class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n...">...</a></td>
532 <td class="lineno new"><a href="#rhodecodetestsfixturesgit_diff_rename_file_with_spacesdiff_n...">...</a></td>
533 <td class="code no-comment">
533 <td class="code no-comment">
534 <pre> No newline at end of file</pre>
534 <pre> No newline at end of file</pre>
535 </td>
535 </td>
536 </tr>
536 </tr>
537 </table>
537 </table>
538 </div>
538 </div>
539 </div>
539 </div>
540 </div>
540 </div>
541
541
542 </td>
542 </td>
543 </tr>
543 </tr>
544 <tr class="cs_A expand_file" fid="c--c21377f778f9">
544 <tr class="cs_A expand_file" fid="c--c21377f778f9">
545 <td class="cs_icon_td">
545 <td class="cs_icon_td">
546 <span class="expand_file_icon" fid="c--c21377f778f9"></span>
546 <span class="expand_file_icon" fid="c--c21377f778f9"></span>
547 </td>
547 </td>
548 <td class="cs_icon_td">
548 <td class="cs_icon_td">
549 <div class="flag_status not_reviewed hidden"></div>
549 <div class="flag_status not_reviewed hidden"></div>
550 </td>
550 </td>
551 <td id="a_c--c21377f778f9">
551 <td id="a_c--c21377f778f9">
552 <a class="compare_view_filepath" href="#a_c--c21377f778f9">
552 <a class="compare_view_filepath" href="#a_c--c21377f778f9">
553 rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff
553 rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff
554 </a>
554 </a>
555 <span id="diff_c--c21377f778f9" class="diff_links" style="display: none;">
555 <span id="diff_c--c21377f778f9" class="diff_links" style="display: none;">
556 <a href="/andersonsantos/rhodecode-momentum-fork/diff/rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
556 <a href="/andersonsantos/rhodecode-momentum-fork/diff/rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
557 Unified Diff
557 Unified Diff
558 </a>
558 </a>
559 |
559 |
560 <a href="/andersonsantos/rhodecode-momentum-fork/diff-2way/rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
560 <a href="/andersonsantos/rhodecode-momentum-fork/diff-2way/rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
561 Side-by-side Diff
561 Side-by-side Diff
562 </a>
562 </a>
563 </span>
563 </span>
564 </td>
564 </td>
565 <td>
565 <td>
566 <div class="changes pull-right"><div style="width:100px"><div class="added top-right-rounded-corner-mid bottom-right-rounded-corner-mid top-left-rounded-corner-mid bottom-left-rounded-corner-mid" style="width:100.0%">3</div><div class="deleted top-right-rounded-corner-mid bottom-right-rounded-corner-mid" style="width:0%"></div></div></div>
566 <div class="changes pull-right"><div style="width:100px"><div class="added top-right-rounded-corner-mid bottom-right-rounded-corner-mid top-left-rounded-corner-mid bottom-left-rounded-corner-mid" style="width:100.0%">3</div><div class="deleted top-right-rounded-corner-mid bottom-right-rounded-corner-mid" style="width:0%"></div></div></div>
567 <div class="comment-bubble pull-right" data-path="rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff">
567 <div class="comment-bubble pull-right" data-path="rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff">
568 <i class="icon-comment"></i>
568 <i class="icon-comment"></i>
569 </div>
569 </div>
570 </td>
570 </td>
571 </tr>
571 </tr>
572 <tr id="tr_c--c21377f778f9">
572 <tr id="tr_c--c21377f778f9">
573 <td></td>
573 <td></td>
574 <td></td>
574 <td></td>
575 <td class="injected_diff" colspan="2">
575 <td class="injected_diff" colspan="2">
576
576
577 <div class="diff-container" id="diff-container-140716195038344">
577 <div class="diff-container" id="diff-container-140716195038344">
578 <div id="c--c21377f778f9_target" ></div>
578 <div id="c--c21377f778f9_target" ></div>
579 <div id="c--c21377f778f9" class="diffblock margined comm" >
579 <div id="c--c21377f778f9" class="diffblock margined comm" >
580 <div class="code-body">
580 <div class="code-body">
581 <div class="full_f_path" path="rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff" style="display: none;"></div>
581 <div class="full_f_path" path="rhodecode/tests/fixtures/hg_diff_copy_file_with_spaces.diff" style="display: none;"></div>
582 <table class="code-difftable">
582 <table class="code-difftable">
583 <tr class="line context">
583 <tr class="line context">
584 <td class="add-comment-line"><span class="add-comment-content"></span></td>
584 <td class="add-comment-line"><span class="add-comment-content"></span></td>
585 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
585 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
586 <td class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n"></a></td>
586 <td class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n"></a></td>
587 <td class="code no-comment">
587 <td class="code no-comment">
588 <pre>new file 100644</pre>
588 <pre>new file 100644</pre>
589 </td>
589 </td>
590 </tr>
590 </tr>
591 <tr class="line add">
591 <tr class="line add">
592 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
592 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
593 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
593 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
594 <td id="rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n1" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n1">1</a></td>
594 <td id="rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n1" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n1">1</a></td>
595 <td class="code">
595 <td class="code">
596 <pre>diff --git a/file_changed_without_spaces.txt b/file_copied_ with spaces.txt
596 <pre>diff --git a/file_changed_without_spaces.txt b/file_copied_ with spaces.txt
597 </pre>
597 </pre>
598 </td>
598 </td>
599 </tr>
599 </tr>
600 <tr class="line add">
600 <tr class="line add">
601 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
601 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
602 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
602 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
603 <td id="rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n2" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n2">2</a></td>
603 <td id="rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n2" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n2">2</a></td>
604 <td class="code">
604 <td class="code">
605 <pre>copy from file_changed_without_spaces.txt
605 <pre>copy from file_changed_without_spaces.txt
606 </pre>
606 </pre>
607 </td>
607 </td>
608 </tr>
608 </tr>
609 <tr class="line add">
609 <tr class="line add">
610 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
610 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
611 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
611 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o"></a></td>
612 <td id="rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n3" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n3">3</a></td>
612 <td id="rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n3" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n3">3</a></td>
613 <td class="code">
613 <td class="code">
614 <pre>copy to file_copied_ with spaces.txt
614 <pre>copy to file_copied_ with spaces.txt
615 </pre>
615 </pre>
616 </td>
616 </td>
617 </tr>
617 </tr>
618 <tr class="line context">
618 <tr class="line context">
619 <td class="add-comment-line"><span class="add-comment-content"></span></td>
619 <td class="add-comment-line"><span class="add-comment-content"></span></td>
620 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o...">...</a></td>
620 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_o...">...</a></td>
621 <td class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n...">...</a></td>
621 <td class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_copy_file_with_spacesdiff_n...">...</a></td>
622 <td class="code no-comment">
622 <td class="code no-comment">
623 <pre> No newline at end of file</pre>
623 <pre> No newline at end of file</pre>
624 </td>
624 </td>
625 </tr>
625 </tr>
626 </table>
626 </table>
627 </div>
627 </div>
628 </div>
628 </div>
629 </div>
629 </div>
630
630
631 </td>
631 </td>
632 </tr>
632 </tr>
633 <tr class="cs_A expand_file" fid="c--ee62085ad7a8">
633 <tr class="cs_A expand_file" fid="c--ee62085ad7a8">
634 <td class="cs_icon_td">
634 <td class="cs_icon_td">
635 <span class="expand_file_icon" fid="c--ee62085ad7a8"></span>
635 <span class="expand_file_icon" fid="c--ee62085ad7a8"></span>
636 </td>
636 </td>
637 <td class="cs_icon_td">
637 <td class="cs_icon_td">
638 <div class="flag_status not_reviewed hidden"></div>
638 <div class="flag_status not_reviewed hidden"></div>
639 </td>
639 </td>
640 <td id="a_c--ee62085ad7a8">
640 <td id="a_c--ee62085ad7a8">
641 <a class="compare_view_filepath" href="#a_c--ee62085ad7a8">
641 <a class="compare_view_filepath" href="#a_c--ee62085ad7a8">
642 rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff
642 rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff
643 </a>
643 </a>
644 <span id="diff_c--ee62085ad7a8" class="diff_links" style="display: none;">
644 <span id="diff_c--ee62085ad7a8" class="diff_links" style="display: none;">
645 <a href="/andersonsantos/rhodecode-momentum-fork/diff/rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
645 <a href="/andersonsantos/rhodecode-momentum-fork/diff/rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
646 Unified Diff
646 Unified Diff
647 </a>
647 </a>
648 |
648 |
649 <a href="/andersonsantos/rhodecode-momentum-fork/diff-2way/rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
649 <a href="/andersonsantos/rhodecode-momentum-fork/diff-2way/rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff?diff2=46b3d50315f0f2b1f64485ac95af4f384948f9cb&amp;diff1=b78e2376b986b2cf656a2b4390b09f303291c886&amp;fulldiff=1&amp;diff=diff">
650 Side-by-side Diff
650 Side-by-side Diff
651 </a>
651 </a>
652 </span>
652 </span>
653 </td>
653 </td>
654 <td>
654 <td>
655 <div class="changes pull-right"><div style="width:100px"><div class="added top-right-rounded-corner-mid bottom-right-rounded-corner-mid top-left-rounded-corner-mid bottom-left-rounded-corner-mid" style="width:100.0%">3</div><div class="deleted top-right-rounded-corner-mid bottom-right-rounded-corner-mid" style="width:0%"></div></div></div>
655 <div class="changes pull-right"><div style="width:100px"><div class="added top-right-rounded-corner-mid bottom-right-rounded-corner-mid top-left-rounded-corner-mid bottom-left-rounded-corner-mid" style="width:100.0%">3</div><div class="deleted top-right-rounded-corner-mid bottom-right-rounded-corner-mid" style="width:0%"></div></div></div>
656 <div class="comment-bubble pull-right" data-path="rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff">
656 <div class="comment-bubble pull-right" data-path="rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff">
657 <i class="icon-comment"></i>
657 <i class="icon-comment"></i>
658 </div>
658 </div>
659 </td>
659 </td>
660 </tr>
660 </tr>
661 <tr id="tr_c--ee62085ad7a8">
661 <tr id="tr_c--ee62085ad7a8">
662 <td></td>
662 <td></td>
663 <td></td>
663 <td></td>
664 <td class="injected_diff" colspan="2">
664 <td class="injected_diff" colspan="2">
665
665
666 <div class="diff-container" id="diff-container-140716195039496">
666 <div class="diff-container" id="diff-container-140716195039496">
667 <div id="c--ee62085ad7a8_target" ></div>
667 <div id="c--ee62085ad7a8_target" ></div>
668 <div id="c--ee62085ad7a8" class="diffblock margined comm" >
668 <div id="c--ee62085ad7a8" class="diffblock margined comm" >
669 <div class="code-body">
669 <div class="code-body">
670 <div class="full_f_path" path="rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff" style="display: none;"></div>
670 <div class="full_f_path" path="rhodecode/tests/fixtures/hg_diff_rename_file_with_spaces.diff" style="display: none;"></div>
671 <table class="code-difftable">
671 <table class="code-difftable">
672 <tr class="line context">
672 <tr class="line context">
673 <td class="add-comment-line"><span class="add-comment-content"></span></td>
673 <td class="add-comment-line"><span class="add-comment-content"></span></td>
674 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
674 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
675 <td class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n"></a></td>
675 <td class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n"></a></td>
676 <td class="code no-comment">
676 <td class="code no-comment">
677 <pre>new file 100644</pre>
677 <pre>new file 100644</pre>
678 </td>
678 </td>
679 </tr>
679 </tr>
680 <tr class="line add">
680 <tr class="line add">
681 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
681 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
682 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
682 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
683 <td id="rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n1" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n1">1</a></td>
683 <td id="rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n1" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n1">1</a></td>
684 <td class="code">
684 <td class="code">
685 <pre>diff --git a/file_ with update.txt b/file_changed _.txt
685 <pre>diff --git a/file_ with update.txt b/file_changed _.txt
686 </pre>
686 </pre>
687 </td>
687 </td>
688 </tr>
688 </tr>
689 <tr class="line add">
689 <tr class="line add">
690 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
690 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
691 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
691 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
692 <td id="rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n2" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n2">2</a></td>
692 <td id="rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n2" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n2">2</a></td>
693 <td class="code">
693 <td class="code">
694 <pre>rename from file_ with update.txt
694 <pre>rename from file_ with update.txt
695 </pre>
695 </pre>
696 </td>
696 </td>
697 </tr>
697 </tr>
698 <tr class="line add">
698 <tr class="line add">
699 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
699 <td class="add-comment-line"><span class="add-comment-content"><a href="#"><span class="icon-comment-add"></span></a></span></td>
700 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
700 <td class="lineno old"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_o"></a></td>
701 <td id="rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n3" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n3">3</a></td>
701 <td id="rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n3" class="lineno new"><a href="#rhodecodetestsfixtureshg_diff_rename_file_with_spacesdiff_n3">3</a></td>
702 <td class="code">
702 <td class="code">
703 <pre>rename to file_changed _.txt</pre>
703 <pre>rename to file_changed _.txt</pre>
704 </td>
704 </td>
705 </tr>
705 </tr>
706 </table>
706 </table>
707 </div>
707 </div>
708 </div>
708 </div>
709 </div>
709 </div>
710
710
711 </td>
711 </td>
712 </tr>
712 </tr>
713
713
714 </table>
714 </table>
715 </div>
715 </div>
716 </div>
716 </div>
717 </div>
717 </div>
718
718
719 </td>
719 </td>
720 </tr>
720 </tr>
721 </table>
721 </table>
722 </div>
722 </div>
723 </div>
723 </div>
724 </div>
724 </div>
725
725
726
726
727
727
728
728
729 <div id="comment-inline-form-template" style="display: none;">
729 <div id="comment-inline-form-template" style="display: none;">
730 <div class="comment-inline-form ac">
730 <div class="comment-inline-form ac">
731 <div class="overlay"><div class="overlay-text">Submitting...</div></div>
731 <div class="overlay"><div class="overlay-text">Submitting...</div></div>
732 <form action="#" class="inline-form" method="get">
732 <form action="#" class="inline-form" method="get">
733 <div id="edit-container_{1}" class="clearfix">
733 <div id="edit-container_{1}" class="clearfix">
734 <div class="comment-title pull-left">
734 <div class="comment-title pull-left">
735 Commenting on line {1}.
735 Commenting on line {1}.
736 </div>
736 </div>
737 <div class="comment-help pull-right">
737 <div class="comment-help pull-right">
738 Comments parsed using <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">RST</a> syntax with <span class="tooltip" title="Use @username inside this text to send notification to this RhodeCode user">@mention</span> support.
738 Comments parsed using <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">RST</a> syntax with <span class="tooltip" title="Use @username inside this text to send notification to this RhodeCode user">@mention</span> support.
739 </div>
739 </div>
740 <div style="clear: both"></div>
740 <div style="clear: both"></div>
741 <textarea id="text_{1}" name="text" class="comment-block-ta ac-input"></textarea>
741 <textarea id="text_{1}" name="text" class="comment-block-ta ac-input"></textarea>
742 </div>
742 </div>
743 <div id="preview-container_{1}" class="clearfix" style="display: none;">
743 <div id="preview-container_{1}" class="clearfix" style="display: none;">
744 <div class="comment-help">
744 <div class="comment-help">
745 Comment preview
745 Comment preview
746 </div>
746 </div>
747 <div id="preview-box_{1}" class="preview-box"></div>
747 <div id="preview-box_{1}" class="preview-box"></div>
748 </div>
748 </div>
749 <div class="comment-button pull-right">
749 <div class="comment-button pull-right">
750 <input type="hidden" name="f_path" value="{0}">
750 <input type="hidden" name="f_path" value="{0}">
751 <input type="hidden" name="line" value="{1}">
751 <input type="hidden" name="line" value="{1}">
752 <div id="preview-btn_{1}" class="btn btn-default">Preview</div>
752 <div id="preview-btn_{1}" class="btn btn-default">Preview</div>
753 <div id="edit-btn_{1}" class="btn" style="display: none;">Edit</div>
753 <div id="edit-btn_{1}" class="btn" style="display: none;">Edit</div>
754 <input class="btn btn-success save-inline-form" id="save" name="save" type="submit" value="Comment" />
754 <input class="btn btn-success save-inline-form" id="save" name="save" type="submit" value="Comment" />
755 </div>
755 </div>
756 <div class="comment-button hide-inline-form-button">
756 <div class="comment-button hide-inline-form-button">
757 <input class="btn hide-inline-form" id="hide-inline-form" name="hide-inline-form" type="reset" value="Cancel" />
757 <input class="btn hide-inline-form" id="hide-inline-form" name="hide-inline-form" type="reset" value="Cancel" />
758 </div>
758 </div>
759 </form>
759 </form>
760 </div>
760 </div>
761 </div>
761 </div>
762
762
763
763
764
764
765 <div class="comments">
765 <div class="comments">
766 <div id="inline-comments-container">
766 <div id="inline-comments-container">
767
767
768 <h2>0 Pull Request Comments</h2>
768 <h2>0 Pull Request Comments</h2>
769
769
770
770
771 </div>
771 </div>
772
772
773 </div>
773 </div>
774
774
775
775
776
776
777
777
778 <div class="pull-request-merge">
778 <div class="pull-request-merge">
779 </div>
779 </div>
780 <div class="comments">
780 <div class="comments">
781 <div class="comment-form ac">
781 <div class="comment-form ac">
782 <form action="/rhodecode-momentum/pull-request-comment/720" id="comments_form" method="POST">
782 <form action="/rhodecode-momentum/pull-request-comment/720" id="comments_form" method="POST">
783 <div style="display: none;"><input id="csrf_token" name="csrf_token" type="hidden" value="6dbc0b19ac65237df65d57202a3e1f2df4153e38" /></div>
783 <div style="display: none;"><input id="csrf_token" name="csrf_token" type="hidden" value="6dbc0b19ac65237df65d57202a3e1f2df4153e38" /></div>
784 <div id="edit-container" class="clearfix">
784 <div id="edit-container" class="clearfix">
785 <div class="comment-title pull-left">
785 <div class="comment-title pull-left">
786 Create a comment on this Pull Request.
786 Create a comment on this Pull Request.
787 </div>
787 </div>
788 <div class="comment-help pull-right">
788 <div class="comment-help pull-right">
789 Comments parsed using <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">RST</a> syntax with <span class="tooltip" title="Use @username inside this text to send notification to this RhodeCode user">@mention</span> support.
789 Comments parsed using <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">RST</a> syntax with <span class="tooltip" title="Use @username inside this text to send notification to this RhodeCode user">@mention</span> support.
790 </div>
790 </div>
791 <div style="clear: both"></div>
791 <div style="clear: both"></div>
792 <textarea class="comment-block-ta" id="text" name="text"></textarea>
792 <textarea class="comment-block-ta" id="text" name="text"></textarea>
793 </div>
793 </div>
794
794
795 <div id="preview-container" class="clearfix" style="display: none;">
795 <div id="preview-container" class="clearfix" style="display: none;">
796 <div class="comment-title">
796 <div class="comment-title">
797 Comment preview
797 Comment preview
798 </div>
798 </div>
799 <div id="preview-box" class="preview-box"></div>
799 <div id="preview-box" class="preview-box"></div>
800 </div>
800 </div>
801
801
802 <div id="comment_form_extras">
802 <div id="comment_form_extras">
803 </div>
803 </div>
804 <div class="action-button pull-right">
804 <div class="action-button pull-right">
805 <div id="preview-btn" class="btn">
805 <div id="preview-btn" class="btn">
806 Preview
806 Preview
807 </div>
807 </div>
808 <div id="edit-btn" class="btn" style="display: none;">
808 <div id="edit-btn" class="btn" style="display: none;">
809 Edit
809 Edit
810 </div>
810 </div>
811 <div class="comment-button">
811 <div class="comment-button">
812 <input class="btn btn-small btn-success comment-button-input" id="save" name="save" type="submit" value="Comment" />
812 <input class="btn btn-small btn-success comment-button-input" id="save" name="save" type="submit" value="Comment" />
813 </div>
813 </div>
814 </div>
814 </div>
815 </form>
815 </form>
816 </div>
816 </div>
817 </div>
817 </div>
818 <script>
818 <script>
819
819
820 $(document).ready(function() {
820 $(document).ready(function() {
821
821
822 var cm = initCommentBoxCodeMirror('#text');
822 var cm = initCommentBoxCodeMirror('#text');
823
823
824 // main form preview
824 // main form preview
825 $('#preview-btn').on('click', function(e) {
825 $('#preview-btn').on('click', function(e) {
826 $('#preview-btn').hide();
826 $('#preview-btn').hide();
827 $('#edit-btn').show();
827 $('#edit-btn').show();
828 var _text = cm.getValue();
828 var _text = cm.getValue();
829 if (!_text) {
829 if (!_text) {
830 return;
830 return;
831 }
831 }
832 var post_data = {
832 var post_data = {
833 'text': _text,
833 'text': _text,
834 'renderer': DEFAULT_RENDERER,
834 'renderer': DEFAULT_RENDERER,
835 'csrf_token': CSRF_TOKEN
835 'csrf_token': CSRF_TOKEN
836 };
836 };
837 var previewbox = $('#preview-box');
837 var previewbox = $('#preview-box');
838 previewbox.addClass('unloaded');
838 previewbox.addClass('unloaded');
839 previewbox.html(_TM['Loading ...']);
839 previewbox.html(_gettext('Loading ...'));
840 $('#edit-container').hide();
840 $('#edit-container').hide();
841 $('#preview-container').show();
841 $('#preview-container').show();
842
842
843 var url = pyroutes.url('changeset_comment_preview', {'repo_name': 'rhodecode-momentum'});
843 var url = pyroutes.url('changeset_comment_preview', {'repo_name': 'rhodecode-momentum'});
844
844
845 ajaxPOST(url, post_data, function(o) {
845 ajaxPOST(url, post_data, function(o) {
846 previewbox.html(o);
846 previewbox.html(o);
847 previewbox.removeClass('unloaded');
847 previewbox.removeClass('unloaded');
848 });
848 });
849 });
849 });
850 $('#edit-btn').on('click', function(e) {
850 $('#edit-btn').on('click', function(e) {
851 $('#preview-btn').show();
851 $('#preview-btn').show();
852 $('#edit-btn').hide();
852 $('#edit-btn').hide();
853 $('#edit-container').show();
853 $('#edit-container').show();
854 $('#preview-container').hide();
854 $('#preview-container').hide();
855 });
855 });
856
856
857 var formatChangeStatus = function(state, escapeMarkup) {
857 var formatChangeStatus = function(state, escapeMarkup) {
858 var originalOption = state.element;
858 var originalOption = state.element;
859 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
859 return '<div class="flag_status ' + $(originalOption).data('status') + ' pull-left"></div>' +
860 '<span>' + escapeMarkup(state.text) + '</span>';
860 '<span>' + escapeMarkup(state.text) + '</span>';
861 };
861 };
862
862
863 var formatResult = function(result, container, query, escapeMarkup) {
863 var formatResult = function(result, container, query, escapeMarkup) {
864 return formatChangeStatus(result, escapeMarkup);
864 return formatChangeStatus(result, escapeMarkup);
865 };
865 };
866
866
867 var formatSelection = function(data, container, escapeMarkup) {
867 var formatSelection = function(data, container, escapeMarkup) {
868 return formatChangeStatus(data, escapeMarkup);
868 return formatChangeStatus(data, escapeMarkup);
869 };
869 };
870
870
871 $('#change_status').select2({
871 $('#change_status').select2({
872 placeholder: "Status Review",
872 placeholder: "Status Review",
873 formatResult: formatResult,
873 formatResult: formatResult,
874 formatSelection: formatSelection,
874 formatSelection: formatSelection,
875 containerCssClass: "drop-menu status_box_menu",
875 containerCssClass: "drop-menu status_box_menu",
876 dropdownCssClass: "drop-menu-dropdown",
876 dropdownCssClass: "drop-menu-dropdown",
877 dropdownAutoWidth: true,
877 dropdownAutoWidth: true,
878 minimumResultsForSearch: -1
878 minimumResultsForSearch: -1
879 });
879 });
880 });
880 });
881 </script>
881 </script>
882
882
883
883
884 <script type="text/javascript">
884 <script type="text/javascript">
885 // TODO: switch this to pyroutes
885 // TODO: switch this to pyroutes
886 AJAX_COMMENT_DELETE_URL = "/rhodecode-momentum/pull-request-comment/__COMMENT_ID__/delete";
886 AJAX_COMMENT_DELETE_URL = "/rhodecode-momentum/pull-request-comment/__COMMENT_ID__/delete";
887
887
888 $(function(){
888 $(function(){
889 ReviewerAutoComplete('user');
889 ReviewerAutoComplete('user');
890
890
891 $('#open_edit_reviewers').on('click', function(e){
891 $('#open_edit_reviewers').on('click', function(e){
892 $('#open_edit_reviewers').hide();
892 $('#open_edit_reviewers').hide();
893 $('#close_edit_reviewers').show();
893 $('#close_edit_reviewers').show();
894 $('#add_reviewer_input').show();
894 $('#add_reviewer_input').show();
895 $('.reviewer_member_remove').css('visibility', 'visible');
895 $('.reviewer_member_remove').css('visibility', 'visible');
896 });
896 });
897
897
898 $('#close_edit_reviewers').on('click', function(e){
898 $('#close_edit_reviewers').on('click', function(e){
899 $('#open_edit_reviewers').show();
899 $('#open_edit_reviewers').show();
900 $('#close_edit_reviewers').hide();
900 $('#close_edit_reviewers').hide();
901 $('#add_reviewer_input').hide();
901 $('#add_reviewer_input').hide();
902 $('.reviewer_member_remove').css('visibility', 'hidden');
902 $('.reviewer_member_remove').css('visibility', 'hidden');
903 });
903 });
904
904
905 $('.show-inline-comments').on('change', function(e){
905 $('.show-inline-comments').on('change', function(e){
906 var show = 'none';
906 var show = 'none';
907 var target = e.currentTarget;
907 var target = e.currentTarget;
908 if(target.checked){
908 if(target.checked){
909 show = ''
909 show = ''
910 }
910 }
911 var boxid = $(target).attr('id_for');
911 var boxid = $(target).attr('id_for');
912 var comments = $('#{0} .inline-comments'.format(boxid));
912 var comments = $('#{0} .inline-comments'.format(boxid));
913 var fn_display = function(idx){
913 var fn_display = function(idx){
914 $(this).css('display', show);
914 $(this).css('display', show);
915 };
915 };
916 $(comments).each(fn_display);
916 $(comments).each(fn_display);
917 var btns = $('#{0} .inline-comments-button'.format(boxid));
917 var btns = $('#{0} .inline-comments-button'.format(boxid));
918 $(btns).each(fn_display);
918 $(btns).each(fn_display);
919 });
919 });
920
920
921 // inject comments into they proper positions
921 // inject comments into they proper positions
922 var file_comments = $('.inline-comment-placeholder');
922 var file_comments = $('.inline-comment-placeholder');
923 renderInlineComments(file_comments);
923 renderInlineComments(file_comments);
924 var commentTotals = {};
924 var commentTotals = {};
925 $.each(file_comments, function(i, comment) {
925 $.each(file_comments, function(i, comment) {
926 var path = $(comment).attr('path');
926 var path = $(comment).attr('path');
927 var comms = $(comment).children().length;
927 var comms = $(comment).children().length;
928 if (path in commentTotals) {
928 if (path in commentTotals) {
929 commentTotals[path] += comms;
929 commentTotals[path] += comms;
930 } else {
930 } else {
931 commentTotals[path] = comms;
931 commentTotals[path] = comms;
932 }
932 }
933 });
933 });
934 $.each(commentTotals, function(path, total) {
934 $.each(commentTotals, function(path, total) {
935 var elem = $('.comment-bubble[data-path="'+ path +'"]')
935 var elem = $('.comment-bubble[data-path="'+ path +'"]')
936 elem.css('visibility', 'visible');
936 elem.css('visibility', 'visible');
937 elem.html(elem.html() + ' ' + total );
937 elem.html(elem.html() + ' ' + total );
938 });
938 });
939
939
940 $('#merge_pull_request_form').submit(function() {
940 $('#merge_pull_request_form').submit(function() {
941 if (!$('#merge_pull_request').attr('disabled')) {
941 if (!$('#merge_pull_request').attr('disabled')) {
942 $('#merge_pull_request').attr('disabled', 'disabled');
942 $('#merge_pull_request').attr('disabled', 'disabled');
943 }
943 }
944 return true;
944 return true;
945 });
945 });
946
946
947 $('#update_pull_request').on('click', function(e){
947 $('#update_pull_request').on('click', function(e){
948 updateReviewers(undefined, "rhodecode-momentum", "720");
948 updateReviewers(undefined, "rhodecode-momentum", "720");
949 });
949 });
950
950
951 $('#update_commits').on('click', function(e){
951 $('#update_commits').on('click', function(e){
952 updateCommits("rhodecode-momentum", "720");
952 updateCommits("rhodecode-momentum", "720");
953 });
953 });
954
954
955 $('#close_pull_request').on('click', function(e){
955 $('#close_pull_request').on('click', function(e){
956 closePullRequest("rhodecode-momentum", "720");
956 closePullRequest("rhodecode-momentum", "720");
957 });
957 });
958 })
958 })
959 </script>
959 </script>
960
960
961 </div>
961 </div>
962 </div></div>
962 </div></div>
963
963
964 </div>
964 </div>
965
965
966
966
967 </%def>
967 </%def>
@@ -1,336 +1,336 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title(*args)">
3 <%def name="title(*args)">
4 ${_('%s Files') % c.repo_name}
4 ${_('%s Files') % c.repo_name}
5 %if hasattr(c,'file'):
5 %if hasattr(c,'file'):
6 &middot; ${h.safe_unicode(c.file.path) or '\\'}
6 &middot; ${h.safe_unicode(c.file.path) or '\\'}
7 %endif
7 %endif
8
8
9 %if c.rhodecode_name:
9 %if c.rhodecode_name:
10 &middot; ${h.branding(c.rhodecode_name)}
10 &middot; ${h.branding(c.rhodecode_name)}
11 %endif
11 %endif
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15 ${_('Files')}
15 ${_('Files')}
16 %if c.file:
16 %if c.file:
17 @ ${h.show_id(c.commit)}
17 @ ${h.show_id(c.commit)}
18 %endif
18 %endif
19 </%def>
19 </%def>
20
20
21 <%def name="menu_bar_nav()">
21 <%def name="menu_bar_nav()">
22 ${self.menu_items(active='repositories')}
22 ${self.menu_items(active='repositories')}
23 </%def>
23 </%def>
24
24
25 <%def name="menu_bar_subnav()">
25 <%def name="menu_bar_subnav()">
26 ${self.repo_menu(active='files')}
26 ${self.repo_menu(active='files')}
27 </%def>
27 </%def>
28
28
29 <%def name="main()">
29 <%def name="main()">
30 <div class="title">
30 <div class="title">
31 ${self.repo_page_title(c.rhodecode_db_repo)}
31 ${self.repo_page_title(c.rhodecode_db_repo)}
32 </div>
32 </div>
33
33
34 <div id="pjax-container" class="summary">
34 <div id="pjax-container" class="summary">
35 <div id="files_data">
35 <div id="files_data">
36 <%include file='files_pjax.html'/>
36 <%include file='files_pjax.html'/>
37 </div>
37 </div>
38 </div>
38 </div>
39 <script>
39 <script>
40 var curState = {
40 var curState = {
41 commit_id: "${c.commit.raw_id}"
41 commit_id: "${c.commit.raw_id}"
42 };
42 };
43
43
44 var getState = function(context) {
44 var getState = function(context) {
45 var url = $(location).attr('href');
45 var url = $(location).attr('href');
46 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
46 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
47 var _annotate_url = '${h.url("files_annotate_home",repo_name=c.repo_name,revision='',f_path='')}';
47 var _annotate_url = '${h.url("files_annotate_home",repo_name=c.repo_name,revision='',f_path='')}';
48 _base_url = _base_url.replace('//', '/');
48 _base_url = _base_url.replace('//', '/');
49 _annotate_url = _annotate_url.replace('//', '/');
49 _annotate_url = _annotate_url.replace('//', '/');
50
50
51 //extract f_path from url.
51 //extract f_path from url.
52 var parts = url.split(_base_url);
52 var parts = url.split(_base_url);
53 if (parts.length != 2) {
53 if (parts.length != 2) {
54 parts = url.split(_annotate_url);
54 parts = url.split(_annotate_url);
55 if (parts.length != 2) {
55 if (parts.length != 2) {
56 var rev = "tip";
56 var rev = "tip";
57 var f_path = "";
57 var f_path = "";
58 } else {
58 } else {
59 var parts2 = parts[1].split('/');
59 var parts2 = parts[1].split('/');
60 var rev = parts2.shift(); // pop the first element which is the revision
60 var rev = parts2.shift(); // pop the first element which is the revision
61 var f_path = parts2.join('/');
61 var f_path = parts2.join('/');
62 }
62 }
63
63
64 } else {
64 } else {
65 var parts2 = parts[1].split('/');
65 var parts2 = parts[1].split('/');
66 var rev = parts2.shift(); // pop the first element which is the revision
66 var rev = parts2.shift(); // pop the first element which is the revision
67 var f_path = parts2.join('/');
67 var f_path = parts2.join('/');
68 }
68 }
69
69
70 var _node_list_url = pyroutes.url('files_nodelist_home',
70 var _node_list_url = pyroutes.url('files_nodelist_home',
71 {repo_name: templateContext.repo_name,
71 {repo_name: templateContext.repo_name,
72 revision: rev, f_path: f_path});
72 revision: rev, f_path: f_path});
73 var _url_base = pyroutes.url('files_home',
73 var _url_base = pyroutes.url('files_home',
74 {repo_name: templateContext.repo_name,
74 {repo_name: templateContext.repo_name,
75 revision: rev, f_path:'__FPATH__'});
75 revision: rev, f_path:'__FPATH__'});
76 return {
76 return {
77 url: url,
77 url: url,
78 f_path: f_path,
78 f_path: f_path,
79 rev: rev,
79 rev: rev,
80 commit_id: curState.commit_id,
80 commit_id: curState.commit_id,
81 node_list_url: _node_list_url,
81 node_list_url: _node_list_url,
82 url_base: _url_base
82 url_base: _url_base
83 };
83 };
84 };
84 };
85
85
86 var metadataRequest = null;
86 var metadataRequest = null;
87 var getFilesMetadata = function() {
87 var getFilesMetadata = function() {
88 if (metadataRequest && metadataRequest.readyState != 4) {
88 if (metadataRequest && metadataRequest.readyState != 4) {
89 metadataRequest.abort();
89 metadataRequest.abort();
90 }
90 }
91 if (source_page) {
91 if (source_page) {
92 return false;
92 return false;
93 }
93 }
94 var state = getState('metadata');
94 var state = getState('metadata');
95 var url_data = {
95 var url_data = {
96 'repo_name': templateContext.repo_name,
96 'repo_name': templateContext.repo_name,
97 'revision': state.commit_id,
97 'revision': state.commit_id,
98 'f_path': state.f_path
98 'f_path': state.f_path
99 };
99 };
100
100
101 var url = pyroutes.url('files_metadata_list_home', url_data);
101 var url = pyroutes.url('files_metadata_list_home', url_data);
102
102
103 metadataRequest = $.ajax({url: url});
103 metadataRequest = $.ajax({url: url});
104
104
105 metadataRequest.done(function(data) {
105 metadataRequest.done(function(data) {
106 var data = data.metadata;
106 var data = data.metadata;
107 var dataLength = data.length;
107 var dataLength = data.length;
108 for (var i = 0; i < dataLength; i++) {
108 for (var i = 0; i < dataLength; i++) {
109 var rowData = data[i];
109 var rowData = data[i];
110 var name = rowData.name.replace('\\', '\\\\');
110 var name = rowData.name.replace('\\', '\\\\');
111
111
112 $('td[title="size-' + name + '"]').html(rowData.size);
112 $('td[title="size-' + name + '"]').html(rowData.size);
113 var timeComponent = AgeModule.createTimeComponent(
113 var timeComponent = AgeModule.createTimeComponent(
114 rowData.modified_ts, rowData.modified_at);
114 rowData.modified_ts, rowData.modified_at);
115 $('td[title="modified_at-' + name + '"]').html(timeComponent);
115 $('td[title="modified_at-' + name + '"]').html(timeComponent);
116
116
117 $('td[title="revision-' + name + '"]').html(
117 $('td[title="revision-' + name + '"]').html(
118 '<div class="tooltip" title="{0}"><pre>r{1}:{2}</pre></div>'.format(
118 '<div class="tooltip" title="{0}"><pre>r{1}:{2}</pre></div>'.format(
119 data[i].message, data[i].revision, data[i].short_id));
119 data[i].message, data[i].revision, data[i].short_id));
120 $('td[title="author-' + name + '"]').html(
120 $('td[title="author-' + name + '"]').html(
121 '<span title="{0}">{1}</span>'.format(
121 '<span title="{0}">{1}</span>'.format(
122 data[i].author, data[i].user_profile));
122 data[i].author, data[i].user_profile));
123 }
123 }
124 tooltip_activate();
124 tooltip_activate();
125 timeagoActivate();
125 timeagoActivate();
126 });
126 });
127 metadataRequest.fail(function (data, textStatus, errorThrown) {
127 metadataRequest.fail(function (data, textStatus, errorThrown) {
128 console.log(data);
128 console.log(data);
129 if (data.status != 0) {
129 if (data.status != 0) {
130 alert("Error while fetching metadata.\nError code {0} ({1}).Please consider reloading the page".format(data.status,data.statusText));
130 alert("Error while fetching metadata.\nError code {0} ({1}).Please consider reloading the page".format(data.status,data.statusText));
131 }
131 }
132 });
132 });
133 };
133 };
134
134
135 var callbacks = function() {
135 var callbacks = function() {
136 var state = getState('callbacks');
136 var state = getState('callbacks');
137 tooltip_activate();
137 tooltip_activate();
138 timeagoActivate();
138 timeagoActivate();
139
139
140 // used for history, and switch to
140 // used for history, and switch to
141 var initialCommitData = {
141 var initialCommitData = {
142 id: null,
142 id: null,
143 text: "${_("Switch To Commit")}",
143 text: "${_("Switch To Commit")}",
144 type: 'sha',
144 type: 'sha',
145 raw_id: null,
145 raw_id: null,
146 files_url: null
146 files_url: null
147 };
147 };
148
148
149 if ($('#trimmed_message_box').height() < 50) {
149 if ($('#trimmed_message_box').height() < 50) {
150 $('#message_expand').hide();
150 $('#message_expand').hide();
151 }
151 }
152
152
153 $('#message_expand').on('click', function(e) {
153 $('#message_expand').on('click', function(e) {
154 $('#trimmed_message_box').css('max-height', 'none');
154 $('#trimmed_message_box').css('max-height', 'none');
155 $(this).hide();
155 $(this).hide();
156 });
156 });
157
157
158
158
159 if (source_page) {
159 if (source_page) {
160 // variants for with source code, not tree view
160 // variants for with source code, not tree view
161
161
162 if (location.href.indexOf('#') != -1) {
162 if (location.href.indexOf('#') != -1) {
163 page_highlights = location.href.substring(location.href.indexOf('#') + 1).split('L');
163 page_highlights = location.href.substring(location.href.indexOf('#') + 1).split('L');
164 if (page_highlights.length == 2) {
164 if (page_highlights.length == 2) {
165 highlight_ranges = page_highlights[1].split(",");
165 highlight_ranges = page_highlights[1].split(",");
166
166
167 var h_lines = [];
167 var h_lines = [];
168 for (pos in highlight_ranges) {
168 for (pos in highlight_ranges) {
169 var _range = highlight_ranges[pos].split('-');
169 var _range = highlight_ranges[pos].split('-');
170 if (_range.length == 2) {
170 if (_range.length == 2) {
171 var start = parseInt(_range[0]);
171 var start = parseInt(_range[0]);
172 var end = parseInt(_range[1]);
172 var end = parseInt(_range[1]);
173 if (start < end) {
173 if (start < end) {
174 for (var i = start; i <= end; i++) {
174 for (var i = start; i <= end; i++) {
175 h_lines.push(i);
175 h_lines.push(i);
176 }
176 }
177 }
177 }
178 }
178 }
179 else {
179 else {
180 h_lines.push(parseInt(highlight_ranges[pos]));
180 h_lines.push(parseInt(highlight_ranges[pos]));
181 }
181 }
182 }
182 }
183
183
184 for (pos in h_lines) {
184 for (pos in h_lines) {
185 // @comment-highlight-color
185 // @comment-highlight-color
186 $('#L' + h_lines[pos]).css('background-color', '#ffd887');
186 $('#L' + h_lines[pos]).css('background-color', '#ffd887');
187 }
187 }
188
188
189 var _first_line = $('#L' + h_lines[0]).get(0);
189 var _first_line = $('#L' + h_lines[0]).get(0);
190 if (_first_line) {
190 if (_first_line) {
191 var line = $('#L' + h_lines[0]);
191 var line = $('#L' + h_lines[0]);
192 offsetScroll(line, 70);
192 offsetScroll(line, 70);
193 }
193 }
194 }
194 }
195 }
195 }
196
196
197 // select code link event
197 // select code link event
198 $("#hlcode").mouseup(getSelectionLink);
198 $("#hlcode").mouseup(getSelectionLink);
199
199
200 // file history select2
200 // file history select2
201 select2FileHistorySwitcher('#diff1', initialCommitData, state);
201 select2FileHistorySwitcher('#diff1', initialCommitData, state);
202 $('#diff1').on('change', function(e) {
202 $('#diff1').on('change', function(e) {
203 $('#diff').removeClass('disabled').removeAttr("disabled");
203 $('#diff').removeClass('disabled').removeAttr("disabled");
204 $('#show_rev').removeClass('disabled').removeAttr("disabled");
204 $('#show_rev').removeClass('disabled').removeAttr("disabled");
205 });
205 });
206
206
207 // show more authors
207 // show more authors
208 $('#show_authors').on('click', function(e) {
208 $('#show_authors').on('click', function(e) {
209 e.preventDefault();
209 e.preventDefault();
210 var url = pyroutes.url('files_authors_home',
210 var url = pyroutes.url('files_authors_home',
211 {'repo_name': templateContext.repo_name,
211 {'repo_name': templateContext.repo_name,
212 'revision': state.rev, 'f_path': state.f_path});
212 'revision': state.rev, 'f_path': state.f_path});
213
213
214 $.pjax({
214 $.pjax({
215 url: url,
215 url: url,
216 data: 'annotate=${"1" if c.annotate else "0"}',
216 data: 'annotate=${"1" if c.annotate else "0"}',
217 container: '#file_authors',
217 container: '#file_authors',
218 push: false,
218 push: false,
219 timeout: pjaxTimeout
219 timeout: pjaxTimeout
220 }).complete(function(){
220 }).complete(function(){
221 $('#show_authors').hide();
221 $('#show_authors').hide();
222 })
222 })
223 });
223 });
224
224
225 // load file short history
225 // load file short history
226 $('#file_history_overview').on('click', function(e) {
226 $('#file_history_overview').on('click', function(e) {
227 e.preventDefault();
227 e.preventDefault();
228 path = state.f_path;
228 path = state.f_path;
229 if (path.indexOf("#") >= 0) {
229 if (path.indexOf("#") >= 0) {
230 path = path.slice(0, path.indexOf("#"));
230 path = path.slice(0, path.indexOf("#"));
231 }
231 }
232 var url = pyroutes.url('changelog_file_home',
232 var url = pyroutes.url('changelog_file_home',
233 {'repo_name': templateContext.repo_name,
233 {'repo_name': templateContext.repo_name,
234 'revision': state.rev, 'f_path': path, 'limit': 6});
234 'revision': state.rev, 'f_path': path, 'limit': 6});
235 $('#file_history_container').show();
235 $('#file_history_container').show();
236 $('#file_history_container').html('<div class="file-history-inner">{0}</div>'.format(_TM['Loading ...']));
236 $('#file_history_container').html('<div class="file-history-inner">{0}</div>'.format(_gettext('Loading ...')));
237
237
238 $.pjax({
238 $.pjax({
239 url: url,
239 url: url,
240 container: '#file_history_container',
240 container: '#file_history_container',
241 push: false,
241 push: false,
242 timeout: pjaxTimeout
242 timeout: pjaxTimeout
243 })
243 })
244 });
244 });
245
245
246 }
246 }
247 else {
247 else {
248 getFilesMetadata();
248 getFilesMetadata();
249
249
250 // fuzzy file filter
250 // fuzzy file filter
251 fileBrowserListeners(state.node_list_url, state.url_base);
251 fileBrowserListeners(state.node_list_url, state.url_base);
252
252
253 // switch to widget
253 // switch to widget
254 select2RefSwitcher('#refs_filter', initialCommitData);
254 select2RefSwitcher('#refs_filter', initialCommitData);
255 $('#refs_filter').on('change', function(e) {
255 $('#refs_filter').on('change', function(e) {
256 var data = $('#refs_filter').select2('data');
256 var data = $('#refs_filter').select2('data');
257 curState.commit_id = data.raw_id;
257 curState.commit_id = data.raw_id;
258 $.pjax({url: data.files_url, container: '#pjax-container', timeout: pjaxTimeout});
258 $.pjax({url: data.files_url, container: '#pjax-container', timeout: pjaxTimeout});
259 });
259 });
260
260
261 $("#prev_commit_link").on('click', function(e) {
261 $("#prev_commit_link").on('click', function(e) {
262 var data = $(this).data();
262 var data = $(this).data();
263 curState.commit_id = data.commitId;
263 curState.commit_id = data.commitId;
264 });
264 });
265
265
266 $("#next_commit_link").on('click', function(e) {
266 $("#next_commit_link").on('click', function(e) {
267 var data = $(this).data();
267 var data = $(this).data();
268 curState.commit_id = data.commitId;
268 curState.commit_id = data.commitId;
269 });
269 });
270
270
271 $('#at_rev').on("keypress", function(e) {
271 $('#at_rev').on("keypress", function(e) {
272 /* ENTER PRESSED */
272 /* ENTER PRESSED */
273 if (e.keyCode === 13) {
273 if (e.keyCode === 13) {
274 var rev = $('#at_rev').val();
274 var rev = $('#at_rev').val();
275 // explicit reload page here. with pjax entering bad input
275 // explicit reload page here. with pjax entering bad input
276 // produces not so nice results
276 // produces not so nice results
277 window.location = pyroutes.url('files_home',
277 window.location = pyroutes.url('files_home',
278 {'repo_name': templateContext.repo_name,
278 {'repo_name': templateContext.repo_name,
279 'revision': rev, 'f_path': state.f_path});
279 'revision': rev, 'f_path': state.f_path});
280 }
280 }
281 });
281 });
282 }
282 }
283 };
283 };
284
284
285 var pjaxTimeout = 5000;
285 var pjaxTimeout = 5000;
286
286
287 $(document).pjax(".pjax-link", "#pjax-container", {
287 $(document).pjax(".pjax-link", "#pjax-container", {
288 "fragment": "#pjax-content",
288 "fragment": "#pjax-content",
289 "maxCacheLength": 1000,
289 "maxCacheLength": 1000,
290 "timeout": pjaxTimeout
290 "timeout": pjaxTimeout
291 });
291 });
292
292
293 // define global back/forward states
293 // define global back/forward states
294 var isPjaxPopState = false;
294 var isPjaxPopState = false;
295 $(document).on('pjax:popstate', function() {
295 $(document).on('pjax:popstate', function() {
296 isPjaxPopState = true;
296 isPjaxPopState = true;
297 });
297 });
298
298
299 $(document).on('pjax:end', function(xhr, options) {
299 $(document).on('pjax:end', function(xhr, options) {
300 if (isPjaxPopState) {
300 if (isPjaxPopState) {
301 isPjaxPopState = false;
301 isPjaxPopState = false;
302 callbacks();
302 callbacks();
303 _NODEFILTER.resetFilter();
303 _NODEFILTER.resetFilter();
304 }
304 }
305
305
306 // run callback for tracking if defined for google analytics etc.
306 // run callback for tracking if defined for google analytics etc.
307 // this is used to trigger tracking on pjax
307 // this is used to trigger tracking on pjax
308 if (typeof window.rhodecode_statechange_callback !== 'undefined') {
308 if (typeof window.rhodecode_statechange_callback !== 'undefined') {
309 var state = getState('statechange');
309 var state = getState('statechange');
310 rhodecode_statechange_callback(state.url, null)
310 rhodecode_statechange_callback(state.url, null)
311 }
311 }
312 });
312 });
313
313
314 $(document).on('pjax:success', function(event, xhr, options) {
314 $(document).on('pjax:success', function(event, xhr, options) {
315 if (event.target.id == "file_history_container") {
315 if (event.target.id == "file_history_container") {
316 $('#file_history_overview').hide();
316 $('#file_history_overview').hide();
317 $('#file_history_overview_full').show();
317 $('#file_history_overview_full').show();
318 timeagoActivate();
318 timeagoActivate();
319 tooltip_activate();
319 tooltip_activate();
320 } else {
320 } else {
321 callbacks();
321 callbacks();
322 }
322 }
323 });
323 });
324
324
325 $(document).ready(function() {
325 $(document).ready(function() {
326 callbacks();
326 callbacks();
327 var search_GET = "${request.GET.get('search','')}";
327 var search_GET = "${request.GET.get('search','')}";
328 if (search_GET == "1") {
328 if (search_GET == "1") {
329 _NODEFILTER.initFilter();
329 _NODEFILTER.initFilter();
330 }
330 }
331 });
331 });
332
332
333 </script>
333 </script>
334
334
335
335
336 </%def>
336 </%def>
@@ -1,232 +1,232 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Files Add') % c.repo_name}
4 ${_('%s Files Add') % c.repo_name}
5 %if c.rhodecode_name:
5 %if c.rhodecode_name:
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
8 </%def>
8 </%def>
9
9
10 <%def name="menu_bar_nav()">
10 <%def name="menu_bar_nav()">
11 ${self.menu_items(active='repositories')}
11 ${self.menu_items(active='repositories')}
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15 ${_('Add new file')} @ ${h.show_id(c.commit)}
15 ${_('Add new file')} @ ${h.show_id(c.commit)}
16 </%def>
16 </%def>
17
17
18 <%def name="menu_bar_subnav()">
18 <%def name="menu_bar_subnav()">
19 ${self.repo_menu(active='files')}
19 ${self.repo_menu(active='files')}
20 </%def>
20 </%def>
21
21
22 <%def name="main()">
22 <%def name="main()">
23 <div class="box">
23 <div class="box">
24 <div class="title">
24 <div class="title">
25 ${self.repo_page_title(c.rhodecode_db_repo)}
25 ${self.repo_page_title(c.rhodecode_db_repo)}
26 </div>
26 </div>
27 <div class="edit-file-title">
27 <div class="edit-file-title">
28 ${self.breadcrumbs()}
28 ${self.breadcrumbs()}
29 </div>
29 </div>
30 ${h.secure_form(h.url.current(),method='post',id='eform',enctype="multipart/form-data", class_="form-horizontal")}
30 ${h.secure_form(h.url.current(),method='post',id='eform',enctype="multipart/form-data", class_="form-horizontal")}
31 <div class="edit-file-fieldset">
31 <div class="edit-file-fieldset">
32 <div class="fieldset">
32 <div class="fieldset">
33 <div id="destination-label" class="left-label">
33 <div id="destination-label" class="left-label">
34 ${_('Path')}:
34 ${_('Path')}:
35 </div>
35 </div>
36 <div class="right-content">
36 <div class="right-content">
37 <div id="specify-custom-path-container">
37 <div id="specify-custom-path-container">
38 <span id="path-breadcrumbs">${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path)}</span>
38 <span id="path-breadcrumbs">${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path)}</span>
39 <a class="custom-path-link" id="specify-custom-path" href="#">${_('Specify Custom Path')}</a>
39 <a class="custom-path-link" id="specify-custom-path" href="#">${_('Specify Custom Path')}</a>
40 </div>
40 </div>
41 <div id="remove-custom-path-container" style="display: none;">
41 <div id="remove-custom-path-container" style="display: none;">
42 ${c.repo_name}/
42 ${c.repo_name}/
43 <input type="input-small" value="${c.f_path}" size="46" name="location" id="location">
43 <input type="input-small" value="${c.f_path}" size="46" name="location" id="location">
44 <a class="custom-path-link" id="remove-custom-path" href="#">${_('Remove Custom Path')}</a>
44 <a class="custom-path-link" id="remove-custom-path" href="#">${_('Remove Custom Path')}</a>
45 </div>
45 </div>
46 </div>
46 </div>
47 </div>
47 </div>
48 <div id="filename_container" class="fieldset">
48 <div id="filename_container" class="fieldset">
49 <div class="filename-label left-label">
49 <div class="filename-label left-label">
50 ${_('Filename')}:
50 ${_('Filename')}:
51 </div>
51 </div>
52 <div class="right-content">
52 <div class="right-content">
53 <input class="input-small" type="text" value="" size="46" name="filename" id="filename">
53 <input class="input-small" type="text" value="" size="46" name="filename" id="filename">
54 <p>${_('or')} <a id="upload_file_enable" href="#">${_('Upload File')}</a></p>
54 <p>${_('or')} <a id="upload_file_enable" href="#">${_('Upload File')}</a></p>
55 </div>
55 </div>
56 </div>
56 </div>
57 <div id="upload_file_container" class="fieldset" style="display: none;">
57 <div id="upload_file_container" class="fieldset" style="display: none;">
58 <div class="filename-label left-label">
58 <div class="filename-label left-label">
59 ${_('Upload file')}:
59 ${_('Upload file')}:
60 </div>
60 </div>
61 <div class="right-content file-upload-input">
61 <div class="right-content file-upload-input">
62 <label for="upload_file" class="btn btn-default">Browse</label>
62 <label for="upload_file" class="btn btn-default">Browse</label>
63 <span id="selected-file">${_('No file selected')}</span>
63 <span id="selected-file">${_('No file selected')}</span>
64 <input type="file" name="upload_file" id="upload_file">
64 <input type="file" name="upload_file" id="upload_file">
65 <p>${_('or')} <a id="file_enable" href="#">${_('Create New File')}</a></p>
65 <p>${_('or')} <a id="file_enable" href="#">${_('Create New File')}</a></p>
66 </div>
66 </div>
67 </div>
67 </div>
68 </div>
68 </div>
69 <div class="table">
69 <div class="table">
70 <div id="files_data">
70 <div id="files_data">
71 <div id="codeblock" class="codeblock">
71 <div id="codeblock" class="codeblock">
72 <div class="code-header form" id="set_mode_header">
72 <div class="code-header form" id="set_mode_header">
73 <div class="fields">
73 <div class="fields">
74 ${h.dropdownmenu('set_mode','plain',[('plain',_('plain'))],enable_filter=True)}
74 ${h.dropdownmenu('set_mode','plain',[('plain',_('plain'))],enable_filter=True)}
75 <label for="line_wrap">${_('line wraps')}</label>
75 <label for="line_wrap">${_('line wraps')}</label>
76 ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])}
76 ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])}
77
77
78 <div id="render_preview" class="btn btn-small preview hidden" >${_('Preview')}</div>
78 <div id="render_preview" class="btn btn-small preview hidden" >${_('Preview')}</div>
79 </div>
79 </div>
80 </div>
80 </div>
81 <div id="editor_container">
81 <div id="editor_container">
82 <pre id="editor_pre"></pre>
82 <pre id="editor_pre"></pre>
83 <textarea id="editor" name="content" ></textarea>
83 <textarea id="editor" name="content" ></textarea>
84 <div id="editor_preview"></div>
84 <div id="editor_preview"></div>
85 </div>
85 </div>
86 </div>
86 </div>
87 </div>
87 </div>
88 </div>
88 </div>
89
89
90 <div class="edit-file-fieldset">
90 <div class="edit-file-fieldset">
91 <div class="fieldset">
91 <div class="fieldset">
92 <div id="commit-message-label" class="commit-message-label left-label">
92 <div id="commit-message-label" class="commit-message-label left-label">
93 ${_('Commit Message')}:
93 ${_('Commit Message')}:
94 </div>
94 </div>
95 <div class="right-content">
95 <div class="right-content">
96 <div class="message">
96 <div class="message">
97 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
97 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
98 </div>
98 </div>
99 </div>
99 </div>
100 </div>
100 </div>
101 <div class="pull-right">
101 <div class="pull-right">
102 ${h.reset('reset',_('Cancel'),class_="btn btn-small")}
102 ${h.reset('reset',_('Cancel'),class_="btn btn-small")}
103 ${h.submit('commit_btn',_('Commit changes'),class_="btn btn-small btn-success")}
103 ${h.submit('commit_btn',_('Commit changes'),class_="btn btn-small btn-success")}
104 </div>
104 </div>
105 </div>
105 </div>
106 ${h.end_form()}
106 ${h.end_form()}
107 </div>
107 </div>
108 <script type="text/javascript">
108 <script type="text/javascript">
109
109
110 $('#commit_btn').on('click', function() {
110 $('#commit_btn').on('click', function() {
111 var button = $(this);
111 var button = $(this);
112 if (button.hasClass('clicked')) {
112 if (button.hasClass('clicked')) {
113 button.attr('disabled', true);
113 button.attr('disabled', true);
114 } else {
114 } else {
115 button.addClass('clicked');
115 button.addClass('clicked');
116 }
116 }
117 });
117 });
118
118
119 $('#specify-custom-path').on('click', function(e){
119 $('#specify-custom-path').on('click', function(e){
120 e.preventDefault();
120 e.preventDefault();
121 $('#specify-custom-path-container').hide();
121 $('#specify-custom-path-container').hide();
122 $('#remove-custom-path-container').show();
122 $('#remove-custom-path-container').show();
123 $('#destination-label').css('margin-top', '13px');
123 $('#destination-label').css('margin-top', '13px');
124 });
124 });
125
125
126 $('#remove-custom-path').on('click', function(e){
126 $('#remove-custom-path').on('click', function(e){
127 e.preventDefault();
127 e.preventDefault();
128 $('#specify-custom-path-container').show();
128 $('#specify-custom-path-container').show();
129 $('#remove-custom-path-container').hide();
129 $('#remove-custom-path-container').hide();
130 $('#location').val('${c.f_path}');
130 $('#location').val('${c.f_path}');
131 $('#destination-label').css('margin-top', '0');
131 $('#destination-label').css('margin-top', '0');
132 });
132 });
133
133
134 var hide_upload = function(){
134 var hide_upload = function(){
135 $('#files_data').show();
135 $('#files_data').show();
136 $('#upload_file_container').hide();
136 $('#upload_file_container').hide();
137 $('#filename_container').show();
137 $('#filename_container').show();
138 };
138 };
139
139
140 $('#file_enable').on('click', function(e){
140 $('#file_enable').on('click', function(e){
141 e.preventDefault();
141 e.preventDefault();
142 hide_upload();
142 hide_upload();
143 });
143 });
144
144
145 $('#upload_file_enable').on('click', function(e){
145 $('#upload_file_enable').on('click', function(e){
146 e.preventDefault();
146 e.preventDefault();
147 $('#files_data').hide();
147 $('#files_data').hide();
148 $('#upload_file_container').show();
148 $('#upload_file_container').show();
149 $('#filename_container').hide();
149 $('#filename_container').hide();
150 if (detectIE() && detectIE() <= 9) {
150 if (detectIE() && detectIE() <= 9) {
151 $('#upload_file_container .file-upload-input label').hide();
151 $('#upload_file_container .file-upload-input label').hide();
152 $('#upload_file_container .file-upload-input span').hide();
152 $('#upload_file_container .file-upload-input span').hide();
153 $('#upload_file_container .file-upload-input input').show();
153 $('#upload_file_container .file-upload-input input').show();
154 }
154 }
155 });
155 });
156
156
157 $('#upload_file').on('change', function() {
157 $('#upload_file').on('change', function() {
158 if (detectIE() && detectIE() <= 9) {
158 if (detectIE() && detectIE() <= 9) {
159 if (this.files && this.files[0]) {
159 if (this.files && this.files[0]) {
160 $('#selected-file').html(this.files[0].name);
160 $('#selected-file').html(this.files[0].name);
161 }
161 }
162 }
162 }
163 });
163 });
164
164
165 hide_upload();
165 hide_upload();
166
166
167 var renderer = "";
167 var renderer = "";
168 var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}";
168 var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}";
169 var myCodeMirror = initCodeMirror('editor', reset_url, false);
169 var myCodeMirror = initCodeMirror('editor', reset_url, false);
170
170
171 var modes_select = $('#set_mode');
171 var modes_select = $('#set_mode');
172 fillCodeMirrorOptions(modes_select);
172 fillCodeMirrorOptions(modes_select);
173
173
174 var filename_selector = '#filename';
174 var filename_selector = '#filename';
175 var callback = function(filename, mimetype, mode){
175 var callback = function(filename, mimetype, mode){
176 CodeMirrorPreviewEnable(mode);
176 CodeMirrorPreviewEnable(mode);
177 };
177 };
178 // on change of select field set mode
178 // on change of select field set mode
179 setCodeMirrorModeFromSelect(
179 setCodeMirrorModeFromSelect(
180 modes_select, filename_selector, myCodeMirror, callback);
180 modes_select, filename_selector, myCodeMirror, callback);
181
181
182 // on entering the new filename set mode, from given extension
182 // on entering the new filename set mode, from given extension
183 setCodeMirrorModeFromInput(
183 setCodeMirrorModeFromInput(
184 modes_select, filename_selector, myCodeMirror, callback);
184 modes_select, filename_selector, myCodeMirror, callback);
185
185
186 // if the file is renderable set line wraps automatically
186 // if the file is renderable set line wraps automatically
187 if (renderer !== ""){
187 if (renderer !== ""){
188 var line_wrap = 'on';
188 var line_wrap = 'on';
189 $($('#line_wrap option[value="'+line_wrap+'"]')[0]).attr("selected", "selected");
189 $($('#line_wrap option[value="'+line_wrap+'"]')[0]).attr("selected", "selected");
190 setCodeMirrorLineWrap(myCodeMirror, true);
190 setCodeMirrorLineWrap(myCodeMirror, true);
191 }
191 }
192
192
193 // on select line wraps change the editor
193 // on select line wraps change the editor
194 $('#line_wrap').on('change', function(e){
194 $('#line_wrap').on('change', function(e){
195 var selected = e.currentTarget;
195 var selected = e.currentTarget;
196 var line_wraps = {'on': true, 'off': false}[selected.value];
196 var line_wraps = {'on': true, 'off': false}[selected.value];
197 setCodeMirrorLineWrap(myCodeMirror, line_wraps)
197 setCodeMirrorLineWrap(myCodeMirror, line_wraps)
198 });
198 });
199
199
200 // render preview/edit button
200 // render preview/edit button
201 $('#render_preview').on('click', function(e){
201 $('#render_preview').on('click', function(e){
202 if($(this).hasClass('preview')){
202 if($(this).hasClass('preview')){
203 $(this).removeClass('preview');
203 $(this).removeClass('preview');
204 $(this).html("${_('Edit')}");
204 $(this).html("${_('Edit')}");
205 $('#editor_preview').show();
205 $('#editor_preview').show();
206 $(myCodeMirror.getWrapperElement()).hide();
206 $(myCodeMirror.getWrapperElement()).hide();
207
207
208 var possible_renderer = {
208 var possible_renderer = {
209 'rst':'rst',
209 'rst':'rst',
210 'markdown':'markdown',
210 'markdown':'markdown',
211 'gfm': 'markdown'}[myCodeMirror.getMode().name];
211 'gfm': 'markdown'}[myCodeMirror.getMode().name];
212 var _text = myCodeMirror.getValue();
212 var _text = myCodeMirror.getValue();
213 var _renderer = possible_renderer || DEFAULT_RENDERER;
213 var _renderer = possible_renderer || DEFAULT_RENDERER;
214 var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
214 var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
215 $('#editor_preview').html(_TM['Loading ...']);
215 $('#editor_preview').html(_gettext('Loading ...'));
216 var url = pyroutes.url('changeset_comment_preview', {'repo_name': '${c.repo_name}'});
216 var url = pyroutes.url('changeset_comment_preview', {'repo_name': '${c.repo_name}'});
217
217
218 ajaxPOST(url, post_data, function(o){
218 ajaxPOST(url, post_data, function(o){
219 $('#editor_preview').html(o);
219 $('#editor_preview').html(o);
220 })
220 })
221 }
221 }
222 else{
222 else{
223 $(this).addClass('preview');
223 $(this).addClass('preview');
224 $(this).html("${_('Preview')}");
224 $(this).html("${_('Preview')}");
225 $('#editor_preview').hide();
225 $('#editor_preview').hide();
226 $(myCodeMirror.getWrapperElement()).show();
226 $(myCodeMirror.getWrapperElement()).show();
227 }
227 }
228 });
228 });
229 $('#filename').focus();
229 $('#filename').focus();
230
230
231 </script>
231 </script>
232 </%def>
232 </%def>
@@ -1,193 +1,193 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s File Edit') % c.repo_name}
4 ${_('%s File Edit') % c.repo_name}
5 %if c.rhodecode_name:
5 %if c.rhodecode_name:
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
8 </%def>
8 </%def>
9
9
10 <%def name="menu_bar_nav()">
10 <%def name="menu_bar_nav()">
11 ${self.menu_items(active='repositories')}
11 ${self.menu_items(active='repositories')}
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15 ${_('Edit file')} @ ${h.show_id(c.commit)}
15 ${_('Edit file')} @ ${h.show_id(c.commit)}
16 </%def>
16 </%def>
17
17
18 <%def name="menu_bar_subnav()">
18 <%def name="menu_bar_subnav()">
19 ${self.repo_menu(active='files')}
19 ${self.repo_menu(active='files')}
20 </%def>
20 </%def>
21
21
22 <%def name="main()">
22 <%def name="main()">
23 <% renderer = h.renderer_from_filename(c.f_path)%>
23 <% renderer = h.renderer_from_filename(c.f_path)%>
24 <div class="box">
24 <div class="box">
25 <div class="title">
25 <div class="title">
26 ${self.repo_page_title(c.rhodecode_db_repo)}
26 ${self.repo_page_title(c.rhodecode_db_repo)}
27 </div>
27 </div>
28 <div class="edit-file-title">
28 <div class="edit-file-title">
29 ${self.breadcrumbs()}
29 ${self.breadcrumbs()}
30 </div>
30 </div>
31 <div class="edit-file-fieldset">
31 <div class="edit-file-fieldset">
32 <div class="fieldset">
32 <div class="fieldset">
33 <div id="destination-label" class="left-label">
33 <div id="destination-label" class="left-label">
34 ${_('Path')}:
34 ${_('Path')}:
35 </div>
35 </div>
36 <div class="right-content">
36 <div class="right-content">
37 <div id="specify-custom-path-container">
37 <div id="specify-custom-path-container">
38 <span id="path-breadcrumbs">${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path)}</span>
38 <span id="path-breadcrumbs">${h.files_breadcrumbs(c.repo_name,c.commit.raw_id,c.f_path)}</span>
39 </div>
39 </div>
40 </div>
40 </div>
41 </div>
41 </div>
42 </div>
42 </div>
43
43
44 <div class="table">
44 <div class="table">
45 ${h.secure_form(h.url.current(),method='post',id='eform')}
45 ${h.secure_form(h.url.current(),method='post',id='eform')}
46 <div id="codeblock" class="codeblock" >
46 <div id="codeblock" class="codeblock" >
47 <div class="code-header">
47 <div class="code-header">
48 <div class="stats">
48 <div class="stats">
49 <i class="icon-file"></i>
49 <i class="icon-file"></i>
50 <span class="item">${h.link_to("r%s:%s" % (c.file.commit.idx,h.short_id(c.file.commit.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.commit.raw_id))}</span>
50 <span class="item">${h.link_to("r%s:%s" % (c.file.commit.idx,h.short_id(c.file.commit.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.commit.raw_id))}</span>
51 <span class="item">${h.format_byte_size_binary(c.file.size)}</span>
51 <span class="item">${h.format_byte_size_binary(c.file.size)}</span>
52 <span class="item last">${c.file.mimetype}</span>
52 <span class="item last">${c.file.mimetype}</span>
53 <div class="buttons">
53 <div class="buttons">
54 <a class="btn btn-mini" href="${h.url('changelog_file_home',repo_name=c.repo_name, revision=c.commit.raw_id, f_path=c.f_path)}">
54 <a class="btn btn-mini" href="${h.url('changelog_file_home',repo_name=c.repo_name, revision=c.commit.raw_id, f_path=c.f_path)}">
55 <i class="icon-time"></i> ${_('history')}
55 <i class="icon-time"></i> ${_('history')}
56 </a>
56 </a>
57
57
58 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
58 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
59 % if not c.file.is_binary:
59 % if not c.file.is_binary:
60 %if True:
60 %if True:
61 ${h.link_to(_('source'), h.url('files_home', repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path),class_="btn btn-mini")}
61 ${h.link_to(_('source'), h.url('files_home', repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path),class_="btn btn-mini")}
62 %else:
62 %else:
63 ${h.link_to(_('annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path),class_="btn btn-mini")}
63 ${h.link_to(_('annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path),class_="btn btn-mini")}
64 %endif
64 %endif
65 ${h.link_to(_('raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path),class_="btn btn-mini")}
65 ${h.link_to(_('raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path),class_="btn btn-mini")}
66 <a class="btn btn-mini" href="${h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}">
66 <a class="btn btn-mini" href="${h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.f_path)}">
67 <i class="icon-archive"></i> ${_('download')}
67 <i class="icon-archive"></i> ${_('download')}
68 </a>
68 </a>
69 % endif
69 % endif
70 % endif
70 % endif
71 </div>
71 </div>
72 </div>
72 </div>
73 <div class="form">
73 <div class="form">
74 <label for="set_mode">${_('Editing file')}:</label>
74 <label for="set_mode">${_('Editing file')}:</label>
75 ${'%s /' % c.file.dir_path if c.file.dir_path else c.file.dir_path}
75 ${'%s /' % c.file.dir_path if c.file.dir_path else c.file.dir_path}
76 <input id="filename" type="text" name="filename" value="${c.file.name}">
76 <input id="filename" type="text" name="filename" value="${c.file.name}">
77
77
78 ${h.dropdownmenu('set_mode','plain',[('plain',_('plain'))],enable_filter=True)}
78 ${h.dropdownmenu('set_mode','plain',[('plain',_('plain'))],enable_filter=True)}
79 <label for="line_wrap">${_('line wraps')}</label>
79 <label for="line_wrap">${_('line wraps')}</label>
80 ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])}
80 ${h.dropdownmenu('line_wrap', 'off', [('on', _('on')), ('off', _('off')),])}
81
81
82 <div id="render_preview" class="btn btn-small preview hidden">${_('Preview')}</div>
82 <div id="render_preview" class="btn btn-small preview hidden">${_('Preview')}</div>
83 </div>
83 </div>
84 </div>
84 </div>
85 <div id="editor_container">
85 <div id="editor_container">
86 <pre id="editor_pre"></pre>
86 <pre id="editor_pre"></pre>
87 <textarea id="editor" name="content" >${h.escape(c.file.content)|n}</textarea>
87 <textarea id="editor" name="content" >${h.escape(c.file.content)|n}</textarea>
88 <div id="editor_preview" ></div>
88 <div id="editor_preview" ></div>
89 </div>
89 </div>
90 </div>
90 </div>
91 </div>
91 </div>
92
92
93 <div class="edit-file-fieldset">
93 <div class="edit-file-fieldset">
94 <div class="fieldset">
94 <div class="fieldset">
95 <div id="commit-message-label" class="commit-message-label left-label">
95 <div id="commit-message-label" class="commit-message-label left-label">
96 ${_('Commit Message')}:
96 ${_('Commit Message')}:
97 </div>
97 </div>
98 <div class="right-content">
98 <div class="right-content">
99 <div class="message">
99 <div class="message">
100 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
100 <textarea id="commit" name="message" placeholder="${c.default_message}"></textarea>
101 </div>
101 </div>
102 </div>
102 </div>
103 </div>
103 </div>
104 <div class="pull-right">
104 <div class="pull-right">
105 ${h.reset('reset',_('Cancel'),class_="btn btn-small")}
105 ${h.reset('reset',_('Cancel'),class_="btn btn-small")}
106 ${h.submit('commit',_('Commit changes'),class_="btn btn-small btn-success")}
106 ${h.submit('commit',_('Commit changes'),class_="btn btn-small btn-success")}
107 </div>
107 </div>
108 </div>
108 </div>
109 ${h.end_form()}
109 ${h.end_form()}
110 </div>
110 </div>
111
111
112 <script type="text/javascript">
112 <script type="text/javascript">
113 $(document).ready(function(){
113 $(document).ready(function(){
114 var renderer = "${renderer}";
114 var renderer = "${renderer}";
115 var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.file.path)}";
115 var reset_url = "${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id,f_path=c.file.path)}";
116 var myCodeMirror = initCodeMirror('editor', reset_url);
116 var myCodeMirror = initCodeMirror('editor', reset_url);
117
117
118 var modes_select = $('#set_mode');
118 var modes_select = $('#set_mode');
119 fillCodeMirrorOptions(modes_select);
119 fillCodeMirrorOptions(modes_select);
120
120
121 // try to detect the mode based on the file we edit
121 // try to detect the mode based on the file we edit
122 var mimetype = "${c.file.mimetype}";
122 var mimetype = "${c.file.mimetype}";
123 var detected_mode = detectCodeMirrorMode(
123 var detected_mode = detectCodeMirrorMode(
124 "${c.file.name}", mimetype);
124 "${c.file.name}", mimetype);
125
125
126 if(detected_mode){
126 if(detected_mode){
127 setCodeMirrorMode(myCodeMirror, detected_mode);
127 setCodeMirrorMode(myCodeMirror, detected_mode);
128 $(modes_select).select2("val", mimetype);
128 $(modes_select).select2("val", mimetype);
129 $(modes_select).change();
129 $(modes_select).change();
130 setCodeMirrorMode(myCodeMirror, detected_mode);
130 setCodeMirrorMode(myCodeMirror, detected_mode);
131 }
131 }
132
132
133 var filename_selector = '#filename';
133 var filename_selector = '#filename';
134 var callback = function(filename, mimetype, mode){
134 var callback = function(filename, mimetype, mode){
135 CodeMirrorPreviewEnable(mode);
135 CodeMirrorPreviewEnable(mode);
136 };
136 };
137 // on change of select field set mode
137 // on change of select field set mode
138 setCodeMirrorModeFromSelect(
138 setCodeMirrorModeFromSelect(
139 modes_select, filename_selector, myCodeMirror, callback);
139 modes_select, filename_selector, myCodeMirror, callback);
140
140
141 // on entering the new filename set mode, from given extension
141 // on entering the new filename set mode, from given extension
142 setCodeMirrorModeFromInput(
142 setCodeMirrorModeFromInput(
143 modes_select, filename_selector, myCodeMirror, callback);
143 modes_select, filename_selector, myCodeMirror, callback);
144
144
145 // if the file is renderable set line wraps automatically
145 // if the file is renderable set line wraps automatically
146 if (renderer !== ""){
146 if (renderer !== ""){
147 var line_wrap = 'on';
147 var line_wrap = 'on';
148 $($('#line_wrap option[value="'+line_wrap+'"]')[0]).attr("selected", "selected");
148 $($('#line_wrap option[value="'+line_wrap+'"]')[0]).attr("selected", "selected");
149 setCodeMirrorLineWrap(myCodeMirror, true);
149 setCodeMirrorLineWrap(myCodeMirror, true);
150 }
150 }
151 // on select line wraps change the editor
151 // on select line wraps change the editor
152 $('#line_wrap').on('change', function(e){
152 $('#line_wrap').on('change', function(e){
153 var selected = e.currentTarget;
153 var selected = e.currentTarget;
154 var line_wraps = {'on': true, 'off': false}[selected.value];
154 var line_wraps = {'on': true, 'off': false}[selected.value];
155 setCodeMirrorLineWrap(myCodeMirror, line_wraps)
155 setCodeMirrorLineWrap(myCodeMirror, line_wraps)
156 });
156 });
157
157
158 // render preview/edit button
158 // render preview/edit button
159 if (mimetype === 'text/x-rst' || mimetype === 'text/plain') {
159 if (mimetype === 'text/x-rst' || mimetype === 'text/plain') {
160 $('#render_preview').removeClass('hidden');
160 $('#render_preview').removeClass('hidden');
161 }
161 }
162 $('#render_preview').on('click', function(e){
162 $('#render_preview').on('click', function(e){
163 if($(this).hasClass('preview')){
163 if($(this).hasClass('preview')){
164 $(this).removeClass('preview');
164 $(this).removeClass('preview');
165 $(this).html("${_('Edit')}");
165 $(this).html("${_('Edit')}");
166 $('#editor_preview').show();
166 $('#editor_preview').show();
167 $(myCodeMirror.getWrapperElement()).hide();
167 $(myCodeMirror.getWrapperElement()).hide();
168
168
169 var possible_renderer = {
169 var possible_renderer = {
170 'rst':'rst',
170 'rst':'rst',
171 'markdown':'markdown',
171 'markdown':'markdown',
172 'gfm': 'markdown'}[myCodeMirror.getMode().name];
172 'gfm': 'markdown'}[myCodeMirror.getMode().name];
173 var _text = myCodeMirror.getValue();
173 var _text = myCodeMirror.getValue();
174 var _renderer = possible_renderer || DEFAULT_RENDERER;
174 var _renderer = possible_renderer || DEFAULT_RENDERER;
175 var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
175 var post_data = {'text': _text, 'renderer': _renderer, 'csrf_token': CSRF_TOKEN};
176 $('#editor_preview').html(_TM['Loading ...']);
176 $('#editor_preview').html(_gettext('Loading ...'));
177 var url = pyroutes.url('changeset_comment_preview', {'repo_name': '${c.repo_name}'});
177 var url = pyroutes.url('changeset_comment_preview', {'repo_name': '${c.repo_name}'});
178
178
179 ajaxPOST(url, post_data, function(o){
179 ajaxPOST(url, post_data, function(o){
180 $('#editor_preview').html(o);
180 $('#editor_preview').html(o);
181 })
181 })
182 }
182 }
183 else{
183 else{
184 $(this).addClass('preview');
184 $(this).addClass('preview');
185 $(this).html("${_('Preview')}");
185 $(this).html("${_('Preview')}");
186 $('#editor_preview').hide();
186 $('#editor_preview').hide();
187 $(myCodeMirror.getWrapperElement()).show();
187 $(myCodeMirror.getWrapperElement()).show();
188 }
188 }
189 });
189 });
190
190
191 })
191 })
192 </script>
192 </script>
193 </%def>
193 </%def>
@@ -1,568 +1,568 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Pull Request #%s') % (c.repo_name, c.pull_request.pull_request_id)}
4 ${_('%s Pull Request #%s') % (c.repo_name, c.pull_request.pull_request_id)}
5 %if c.rhodecode_name:
5 %if c.rhodecode_name:
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
8 </%def>
8 </%def>
9
9
10 <%def name="breadcrumbs_links()">
10 <%def name="breadcrumbs_links()">
11 <span id="pr-title">
11 <span id="pr-title">
12 ${c.pull_request.title}
12 ${c.pull_request.title}
13 %if c.pull_request.is_closed():
13 %if c.pull_request.is_closed():
14 (${_('Closed')})
14 (${_('Closed')})
15 %endif
15 %endif
16 </span>
16 </span>
17 <div id="pr-title-edit" class="input" style="display: none;">
17 <div id="pr-title-edit" class="input" style="display: none;">
18 ${h.text('pullrequest_title', id_="pr-title-input", class_="large", value=c.pull_request.title)}
18 ${h.text('pullrequest_title', id_="pr-title-input", class_="large", value=c.pull_request.title)}
19 </div>
19 </div>
20 </%def>
20 </%def>
21
21
22 <%def name="menu_bar_nav()">
22 <%def name="menu_bar_nav()">
23 ${self.menu_items(active='repositories')}
23 ${self.menu_items(active='repositories')}
24 </%def>
24 </%def>
25
25
26 <%def name="menu_bar_subnav()">
26 <%def name="menu_bar_subnav()">
27 ${self.repo_menu(active='showpullrequest')}
27 ${self.repo_menu(active='showpullrequest')}
28 </%def>
28 </%def>
29
29
30 <%def name="main()">
30 <%def name="main()">
31 <script type="text/javascript">
31 <script type="text/javascript">
32 // TODO: marcink switch this to pyroutes
32 // TODO: marcink switch this to pyroutes
33 AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
33 AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
34 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
34 templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
35 </script>
35 </script>
36 <div class="box">
36 <div class="box">
37 <div class="title">
37 <div class="title">
38 ${self.repo_page_title(c.rhodecode_db_repo)}
38 ${self.repo_page_title(c.rhodecode_db_repo)}
39 </div>
39 </div>
40
40
41 ${self.breadcrumbs()}
41 ${self.breadcrumbs()}
42
42
43
43
44 <div class="box pr-summary">
44 <div class="box pr-summary">
45 <div class="summary-details block-left">
45 <div class="summary-details block-left">
46 <%summary = lambda n:{False:'summary-short'}.get(n)%>
46 <%summary = lambda n:{False:'summary-short'}.get(n)%>
47 <div class="pr-details-title">
47 <div class="pr-details-title">
48 ${_('Pull request #%s') % c.pull_request.pull_request_id} ${_('From')} ${h.format_date(c.pull_request.created_on)}
48 ${_('Pull request #%s') % c.pull_request.pull_request_id} ${_('From')} ${h.format_date(c.pull_request.created_on)}
49 %if c.allowed_to_update:
49 %if c.allowed_to_update:
50 <span id="open_edit_pullrequest" class="block-right action_button">${_('Edit')}</span>
50 <span id="open_edit_pullrequest" class="block-right action_button">${_('Edit')}</span>
51 <span id="close_edit_pullrequest" class="block-right action_button" style="display: none;">${_('Close')}</span>
51 <span id="close_edit_pullrequest" class="block-right action_button" style="display: none;">${_('Close')}</span>
52 %endif
52 %endif
53 </div>
53 </div>
54
54
55 <div id="summary" class="fields pr-details-content">
55 <div id="summary" class="fields pr-details-content">
56 <div class="field">
56 <div class="field">
57 <div class="label-summary">
57 <div class="label-summary">
58 <label>${_('Origin')}:</label>
58 <label>${_('Origin')}:</label>
59 </div>
59 </div>
60 <div class="input">
60 <div class="input">
61 <div class="pr-origininfo">
61 <div class="pr-origininfo">
62 ## branch link is only valid if it is a branch
62 ## branch link is only valid if it is a branch
63 <span class="tag">
63 <span class="tag">
64 %if c.pull_request.source_ref_parts.type == 'branch':
64 %if c.pull_request.source_ref_parts.type == 'branch':
65 <a href="${h.url('changelog_home', repo_name=c.pull_request.source_repo.repo_name, branch=c.pull_request.source_ref_parts.name)}">${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}</a>
65 <a href="${h.url('changelog_home', repo_name=c.pull_request.source_repo.repo_name, branch=c.pull_request.source_ref_parts.name)}">${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}</a>
66 %else:
66 %else:
67 ${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}
67 ${c.pull_request.source_ref_parts.type}: ${c.pull_request.source_ref_parts.name}
68 %endif
68 %endif
69 </span>
69 </span>
70 <span class="clone-url">
70 <span class="clone-url">
71 <a href="${h.url('summary_home', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.clone_url()}</a>
71 <a href="${h.url('summary_home', repo_name=c.pull_request.source_repo.repo_name)}">${c.pull_request.source_repo.clone_url()}</a>
72 </span>
72 </span>
73 </div>
73 </div>
74 <div class="pr-pullinfo">
74 <div class="pr-pullinfo">
75 %if h.is_hg(c.pull_request.source_repo):
75 %if h.is_hg(c.pull_request.source_repo):
76 <input type="text" value="hg pull -r ${h.short_id(c.source_ref)} ${c.pull_request.source_repo.clone_url()}" readonly="readonly">
76 <input type="text" value="hg pull -r ${h.short_id(c.source_ref)} ${c.pull_request.source_repo.clone_url()}" readonly="readonly">
77 %elif h.is_git(c.pull_request.source_repo):
77 %elif h.is_git(c.pull_request.source_repo):
78 <input type="text" value="git pull ${c.pull_request.source_repo.clone_url()} ${c.pull_request.source_ref_parts.name}" readonly="readonly">
78 <input type="text" value="git pull ${c.pull_request.source_repo.clone_url()} ${c.pull_request.source_ref_parts.name}" readonly="readonly">
79 %endif
79 %endif
80 </div>
80 </div>
81 </div>
81 </div>
82 </div>
82 </div>
83 <div class="field">
83 <div class="field">
84 <div class="label-summary">
84 <div class="label-summary">
85 <label>${_('Target')}:</label>
85 <label>${_('Target')}:</label>
86 </div>
86 </div>
87 <div class="input">
87 <div class="input">
88 <div class="pr-targetinfo">
88 <div class="pr-targetinfo">
89 ## branch link is only valid if it is a branch
89 ## branch link is only valid if it is a branch
90 <span class="tag">
90 <span class="tag">
91 %if c.pull_request.target_ref_parts.type == 'branch':
91 %if c.pull_request.target_ref_parts.type == 'branch':
92 <a href="${h.url('changelog_home', repo_name=c.pull_request.target_repo.repo_name, branch=c.pull_request.target_ref_parts.name)}">${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}</a>
92 <a href="${h.url('changelog_home', repo_name=c.pull_request.target_repo.repo_name, branch=c.pull_request.target_ref_parts.name)}">${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}</a>
93 %else:
93 %else:
94 ${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}
94 ${c.pull_request.target_ref_parts.type}: ${c.pull_request.target_ref_parts.name}
95 %endif
95 %endif
96 </span>
96 </span>
97 <span class="clone-url">
97 <span class="clone-url">
98 <a href="${h.url('summary_home', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.clone_url()}</a>
98 <a href="${h.url('summary_home', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.clone_url()}</a>
99 </span>
99 </span>
100 </div>
100 </div>
101 </div>
101 </div>
102 </div>
102 </div>
103 <div class="field">
103 <div class="field">
104 <div class="label-summary">
104 <div class="label-summary">
105 <label>${_('Review')}:</label>
105 <label>${_('Review')}:</label>
106 </div>
106 </div>
107 <div class="input">
107 <div class="input">
108 %if c.pull_request_review_status:
108 %if c.pull_request_review_status:
109 <div class="${'flag_status %s' % c.pull_request_review_status} tooltip pull-left"></div>
109 <div class="${'flag_status %s' % c.pull_request_review_status} tooltip pull-left"></div>
110 <span class="changeset-status-lbl tooltip">
110 <span class="changeset-status-lbl tooltip">
111 %if c.pull_request.is_closed():
111 %if c.pull_request.is_closed():
112 ${_('Closed')},
112 ${_('Closed')},
113 %endif
113 %endif
114 ${h.commit_status_lbl(c.pull_request_review_status)}
114 ${h.commit_status_lbl(c.pull_request_review_status)}
115 </span>
115 </span>
116 - ${ungettext('calculated based on %s reviewer vote', 'calculated based on %s reviewers votes', len(c.pull_request_reviewers)) % len(c.pull_request_reviewers)}
116 - ${ungettext('calculated based on %s reviewer vote', 'calculated based on %s reviewers votes', len(c.pull_request_reviewers)) % len(c.pull_request_reviewers)}
117 %endif
117 %endif
118 </div>
118 </div>
119 </div>
119 </div>
120 <div class="field">
120 <div class="field">
121 <div class="pr-description-label label-summary">
121 <div class="pr-description-label label-summary">
122 <label>${_('Description')}:</label>
122 <label>${_('Description')}:</label>
123 </div>
123 </div>
124 <div id="pr-desc" class="input">
124 <div id="pr-desc" class="input">
125 <div class="pr-description">${h.urlify_commit_message(c.pull_request.description, c.repo_name)}</div>
125 <div class="pr-description">${h.urlify_commit_message(c.pull_request.description, c.repo_name)}</div>
126 </div>
126 </div>
127 <div id="pr-desc-edit" class="input textarea editor" style="display: none;">
127 <div id="pr-desc-edit" class="input textarea editor" style="display: none;">
128 <textarea id="pr-description-input" size="30">${c.pull_request.description}</textarea>
128 <textarea id="pr-description-input" size="30">${c.pull_request.description}</textarea>
129 </div>
129 </div>
130 </div>
130 </div>
131 <div class="field">
131 <div class="field">
132 <div class="label-summary">
132 <div class="label-summary">
133 <label>${_('Comments')}:</label>
133 <label>${_('Comments')}:</label>
134 </div>
134 </div>
135 <div class="input">
135 <div class="input">
136 <div>
136 <div>
137 <div class="comments-number">
137 <div class="comments-number">
138 %if c.comments:
138 %if c.comments:
139 <a href="#comments">${ungettext("%d Pull request comment", "%d Pull request comments", len(c.comments)) % len(c.comments)}</a>,
139 <a href="#comments">${ungettext("%d Pull request comment", "%d Pull request comments", len(c.comments)) % len(c.comments)}</a>,
140 %else:
140 %else:
141 ${ungettext("%d Pull request comment", "%d Pull request comments", len(c.comments)) % len(c.comments)}
141 ${ungettext("%d Pull request comment", "%d Pull request comments", len(c.comments)) % len(c.comments)}
142 %endif
142 %endif
143 %if c.inline_cnt:
143 %if c.inline_cnt:
144 ## this is replaced with a proper link to first comment via JS linkifyComments() func
144 ## this is replaced with a proper link to first comment via JS linkifyComments() func
145 <a href="#inline-comments" id="inline-comments-counter">${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}</a>
145 <a href="#inline-comments" id="inline-comments-counter">${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}</a>
146 %else:
146 %else:
147 ${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}
147 ${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}
148 %endif
148 %endif
149
149
150 % if c.outdated_cnt:
150 % if c.outdated_cnt:
151 ,${ungettext("%d Outdated Comment", "%d Outdated Comments", c.outdated_cnt) % c.outdated_cnt} <span id="show-outdated-comments" class="btn btn-link">${_('(Show)')}</span>
151 ,${ungettext("%d Outdated Comment", "%d Outdated Comments", c.outdated_cnt) % c.outdated_cnt} <span id="show-outdated-comments" class="btn btn-link">${_('(Show)')}</span>
152 % endif
152 % endif
153 </div>
153 </div>
154 </div>
154 </div>
155 </div>
155 </div>
156 </div>
156 </div>
157 <div id="pr-save" class="field" style="display: none;">
157 <div id="pr-save" class="field" style="display: none;">
158 <div class="label-summary"></div>
158 <div class="label-summary"></div>
159 <div class="input">
159 <div class="input">
160 <span id="edit_pull_request" class="btn btn-small">${_('Save Changes')}</span>
160 <span id="edit_pull_request" class="btn btn-small">${_('Save Changes')}</span>
161 </div>
161 </div>
162 </div>
162 </div>
163 </div>
163 </div>
164 </div>
164 </div>
165 <div>
165 <div>
166 ## AUTHOR
166 ## AUTHOR
167 <div class="reviewers-title block-right">
167 <div class="reviewers-title block-right">
168 <div class="pr-details-title">
168 <div class="pr-details-title">
169 ${_('Author')}
169 ${_('Author')}
170 </div>
170 </div>
171 </div>
171 </div>
172 <div class="block-right pr-details-content reviewers">
172 <div class="block-right pr-details-content reviewers">
173 <ul class="group_members">
173 <ul class="group_members">
174 <li>
174 <li>
175 ${self.gravatar_with_user(c.pull_request.author.email, 16)}
175 ${self.gravatar_with_user(c.pull_request.author.email, 16)}
176 </li>
176 </li>
177 </ul>
177 </ul>
178 </div>
178 </div>
179 ## REVIEWERS
179 ## REVIEWERS
180 <div class="reviewers-title block-right">
180 <div class="reviewers-title block-right">
181 <div class="pr-details-title">
181 <div class="pr-details-title">
182 ${_('Pull request reviewers')}
182 ${_('Pull request reviewers')}
183 %if c.allowed_to_update:
183 %if c.allowed_to_update:
184 <span id="open_edit_reviewers" class="block-right action_button">${_('Edit')}</span>
184 <span id="open_edit_reviewers" class="block-right action_button">${_('Edit')}</span>
185 <span id="close_edit_reviewers" class="block-right action_button" style="display: none;">${_('Close')}</span>
185 <span id="close_edit_reviewers" class="block-right action_button" style="display: none;">${_('Close')}</span>
186 %endif
186 %endif
187 </div>
187 </div>
188 </div>
188 </div>
189 <div id="reviewers" class="block-right pr-details-content reviewers">
189 <div id="reviewers" class="block-right pr-details-content reviewers">
190 ## members goes here !
190 ## members goes here !
191 <ul id="review_members" class="group_members">
191 <ul id="review_members" class="group_members">
192 %for member,status in c.pull_request_reviewers:
192 %for member,status in c.pull_request_reviewers:
193 <li id="reviewer_${member.user_id}">
193 <li id="reviewer_${member.user_id}">
194 <div class="reviewers_member">
194 <div class="reviewers_member">
195 <div class="reviewer_status tooltip" title="${h.tooltip(h.commit_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
195 <div class="reviewer_status tooltip" title="${h.tooltip(h.commit_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
196 <div class="${'flag_status %s' % (status[0][1].status if status else 'not_reviewed')} pull-left reviewer_member_status"></div>
196 <div class="${'flag_status %s' % (status[0][1].status if status else 'not_reviewed')} pull-left reviewer_member_status"></div>
197 </div>
197 </div>
198 <div id="reviewer_${member.user_id}_name" class="reviewer_name">
198 <div id="reviewer_${member.user_id}_name" class="reviewer_name">
199 ${self.gravatar_with_user(member.email, 16)} <div class="reviewer">(${_('owner') if c.pull_request.user_id == member.user_id else _('reviewer')})</div>
199 ${self.gravatar_with_user(member.email, 16)} <div class="reviewer">(${_('owner') if c.pull_request.user_id == member.user_id else _('reviewer')})</div>
200 </div>
200 </div>
201 <input id="reviewer_${member.user_id}_input" type="hidden" value="${member.user_id}" name="review_members" />
201 <input id="reviewer_${member.user_id}_input" type="hidden" value="${member.user_id}" name="review_members" />
202 %if c.allowed_to_update:
202 %if c.allowed_to_update:
203 <div class="reviewer_member_remove action_button" onclick="removeReviewMember(${member.user_id}, true)" style="visibility: hidden;">
203 <div class="reviewer_member_remove action_button" onclick="removeReviewMember(${member.user_id}, true)" style="visibility: hidden;">
204 <i class="icon-remove-sign" ></i>
204 <i class="icon-remove-sign" ></i>
205 </div>
205 </div>
206 %endif
206 %endif
207 </div>
207 </div>
208 </li>
208 </li>
209 %endfor
209 %endfor
210 </ul>
210 </ul>
211 %if not c.pull_request.is_closed():
211 %if not c.pull_request.is_closed():
212 <div id="add_reviewer_input" class='ac' style="display: none;">
212 <div id="add_reviewer_input" class='ac' style="display: none;">
213 %if c.allowed_to_update:
213 %if c.allowed_to_update:
214 <div class="reviewer_ac">
214 <div class="reviewer_ac">
215 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer'))}
215 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer'))}
216 <div id="reviewers_container"></div>
216 <div id="reviewers_container"></div>
217 </div>
217 </div>
218 <div>
218 <div>
219 <span id="update_pull_request" class="btn btn-small">${_('Save Changes')}</span>
219 <span id="update_pull_request" class="btn btn-small">${_('Save Changes')}</span>
220 </div>
220 </div>
221 %endif
221 %endif
222 </div>
222 </div>
223 %endif
223 %endif
224 </div>
224 </div>
225 </div>
225 </div>
226 </div>
226 </div>
227 <div class="box">
227 <div class="box">
228 ##DIFF
228 ##DIFF
229 <div class="table" >
229 <div class="table" >
230 <div id="changeset_compare_view_content">
230 <div id="changeset_compare_view_content">
231 ##CS
231 ##CS
232 % if c.missing_requirements:
232 % if c.missing_requirements:
233 <div class="box">
233 <div class="box">
234 <div class="alert alert-warning">
234 <div class="alert alert-warning">
235 <div>
235 <div>
236 <strong>${_('Missing requirements:')}</strong>
236 <strong>${_('Missing requirements:')}</strong>
237 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
237 ${_('These commits cannot be displayed, because this repository uses the Mercurial largefiles extension, which was not enabled.')}
238 </div>
238 </div>
239 </div>
239 </div>
240 </div>
240 </div>
241 % elif c.missing_commits:
241 % elif c.missing_commits:
242 <div class="box">
242 <div class="box">
243 <div class="alert alert-warning">
243 <div class="alert alert-warning">
244 <div>
244 <div>
245 <strong>${_('Missing commits')}:</strong>
245 <strong>${_('Missing commits')}:</strong>
246 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}
246 ${_('This pull request cannot be displayed, because one or more commits no longer exist in the source repository.')}
247 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}
247 ${_('Please update this pull request, push the commits back into the source repository, or consider closing this pull request.')}
248 </div>
248 </div>
249 </div>
249 </div>
250 </div>
250 </div>
251 % endif
251 % endif
252 <div class="compare_view_commits_title">
252 <div class="compare_view_commits_title">
253 % if c.allowed_to_update and not c.pull_request.is_closed():
253 % if c.allowed_to_update and not c.pull_request.is_closed():
254 <button id="update_commits" class="btn btn-small">${_('Update commits')}</button>
254 <button id="update_commits" class="btn btn-small">${_('Update commits')}</button>
255 % endif
255 % endif
256 % if len(c.commit_ranges):
256 % if len(c.commit_ranges):
257 <h2>${ungettext('Compare View: %s commit','Compare View: %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}</h2>
257 <h2>${ungettext('Compare View: %s commit','Compare View: %s commits', len(c.commit_ranges)) % len(c.commit_ranges)}</h2>
258 % endif
258 % endif
259 </div>
259 </div>
260 % if not c.missing_commits:
260 % if not c.missing_commits:
261 <%include file="/compare/compare_commits.html" />
261 <%include file="/compare/compare_commits.html" />
262 ## FILES
262 ## FILES
263 <div class="cs_files_title">
263 <div class="cs_files_title">
264 <span class="cs_files_expand">
264 <span class="cs_files_expand">
265 <span id="expand_all_files">${_('Expand All')}</span> | <span id="collapse_all_files">${_('Collapse All')}</span>
265 <span id="expand_all_files">${_('Expand All')}</span> | <span id="collapse_all_files">${_('Collapse All')}</span>
266 </span>
266 </span>
267 <h2>
267 <h2>
268 ${diff_block.diff_summary_text(len(c.files), c.lines_added, c.lines_deleted, c.limited_diff)}
268 ${diff_block.diff_summary_text(len(c.files), c.lines_added, c.lines_deleted, c.limited_diff)}
269 </h2>
269 </h2>
270 </div>
270 </div>
271 % endif
271 % endif
272 <div class="cs_files">
272 <div class="cs_files">
273 %if not c.files and not c.missing_commits:
273 %if not c.files and not c.missing_commits:
274 <span class="empty_data">${_('No files')}</span>
274 <span class="empty_data">${_('No files')}</span>
275 %endif
275 %endif
276 <table class="compare_view_files">
276 <table class="compare_view_files">
277 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
277 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
278 %for FID, change, path, stats in c.files:
278 %for FID, change, path, stats in c.files:
279 <tr class="cs_${change} collapse_file" fid="${FID}">
279 <tr class="cs_${change} collapse_file" fid="${FID}">
280 <td class="cs_icon_td">
280 <td class="cs_icon_td">
281 <span class="collapse_file_icon" fid="${FID}"></span>
281 <span class="collapse_file_icon" fid="${FID}"></span>
282 </td>
282 </td>
283 <td class="cs_icon_td">
283 <td class="cs_icon_td">
284 <div class="flag_status not_reviewed hidden"></div>
284 <div class="flag_status not_reviewed hidden"></div>
285 </td>
285 </td>
286 <td class="cs_${change}" id="a_${FID}">
286 <td class="cs_${change}" id="a_${FID}">
287 <div class="node">
287 <div class="node">
288 <a href="#a_${FID}">
288 <a href="#a_${FID}">
289 <i class="icon-file-${change.lower()}"></i>
289 <i class="icon-file-${change.lower()}"></i>
290 ${h.safe_unicode(path)}
290 ${h.safe_unicode(path)}
291 </a>
291 </a>
292 </div>
292 </div>
293 </td>
293 </td>
294 <td>
294 <td>
295 <div class="changes pull-right">${h.fancy_file_stats(stats)}</div>
295 <div class="changes pull-right">${h.fancy_file_stats(stats)}</div>
296 <div class="comment-bubble pull-right" data-path="${path}">
296 <div class="comment-bubble pull-right" data-path="${path}">
297 <i class="icon-comment"></i>
297 <i class="icon-comment"></i>
298 </div>
298 </div>
299 </td>
299 </td>
300 </tr>
300 </tr>
301 <tr fid="${FID}" id="diff_${FID}" class="diff_links">
301 <tr fid="${FID}" id="diff_${FID}" class="diff_links">
302 <td></td>
302 <td></td>
303 <td></td>
303 <td></td>
304 <td class="cs_${change}">
304 <td class="cs_${change}">
305 %if c.target_repo.repo_name == c.repo_name:
305 %if c.target_repo.repo_name == c.repo_name:
306 ${diff_block.diff_menu(c.repo_name, h.safe_unicode(path), c.target_ref, c.source_ref, change)}
306 ${diff_block.diff_menu(c.repo_name, h.safe_unicode(path), c.target_ref, c.source_ref, change)}
307 %else:
307 %else:
308 ## this is slightly different case later, since the other repo can have this
308 ## this is slightly different case later, since the other repo can have this
309 ## file in other state than the origin repo
309 ## file in other state than the origin repo
310 ${diff_block.diff_menu(c.target_repo.repo_name, h.safe_unicode(path), c.target_ref, c.source_ref, change)}
310 ${diff_block.diff_menu(c.target_repo.repo_name, h.safe_unicode(path), c.target_ref, c.source_ref, change)}
311 %endif
311 %endif
312 </td>
312 </td>
313 <td class="td-actions rc-form">
313 <td class="td-actions rc-form">
314 </td>
314 </td>
315 </tr>
315 </tr>
316 <tr id="tr_${FID}">
316 <tr id="tr_${FID}">
317 <td></td>
317 <td></td>
318 <td></td>
318 <td></td>
319 <td class="injected_diff" colspan="2">
319 <td class="injected_diff" colspan="2">
320 ${diff_block.diff_block_simple([c.changes[FID]])}
320 ${diff_block.diff_block_simple([c.changes[FID]])}
321 </td>
321 </td>
322 </tr>
322 </tr>
323
323
324 ## Loop through inline comments
324 ## Loop through inline comments
325 % if c.outdated_comments.get(path,False):
325 % if c.outdated_comments.get(path,False):
326 <tr class="outdated">
326 <tr class="outdated">
327 <td></td>
327 <td></td>
328 <td></td>
328 <td></td>
329 <td colspan="2">
329 <td colspan="2">
330 <p>${_('Outdated Inline Comments')}:</p>
330 <p>${_('Outdated Inline Comments')}:</p>
331 </td>
331 </td>
332 </tr>
332 </tr>
333 <tr class="outdated">
333 <tr class="outdated">
334 <td></td>
334 <td></td>
335 <td></td>
335 <td></td>
336 <td colspan="2" class="outdated_comment_block">
336 <td colspan="2" class="outdated_comment_block">
337 % for line, comments in c.outdated_comments[path].iteritems():
337 % for line, comments in c.outdated_comments[path].iteritems():
338 <div class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
338 <div class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
339 % for co in comments:
339 % for co in comments:
340 ${comment.comment_block_outdated(co)}
340 ${comment.comment_block_outdated(co)}
341 % endfor
341 % endfor
342 </div>
342 </div>
343 % endfor
343 % endfor
344 </td>
344 </td>
345 </tr>
345 </tr>
346 % endif
346 % endif
347 %endfor
347 %endfor
348 ## Loop through inline comments for deleted files
348 ## Loop through inline comments for deleted files
349 %for path in c.deleted_files:
349 %for path in c.deleted_files:
350 <tr class="outdated deleted">
350 <tr class="outdated deleted">
351 <td></td>
351 <td></td>
352 <td></td>
352 <td></td>
353 <td>${path}</td>
353 <td>${path}</td>
354 </tr>
354 </tr>
355 <tr class="outdated deleted">
355 <tr class="outdated deleted">
356 <td></td>
356 <td></td>
357 <td></td>
357 <td></td>
358 <td>(${_('Removed')})</td>
358 <td>(${_('Removed')})</td>
359 </tr>
359 </tr>
360 % if path in c.outdated_comments:
360 % if path in c.outdated_comments:
361 <tr class="outdated deleted">
361 <tr class="outdated deleted">
362 <td></td>
362 <td></td>
363 <td></td>
363 <td></td>
364 <td colspan="2">
364 <td colspan="2">
365 <p>${_('Outdated Inline Comments')}:</p>
365 <p>${_('Outdated Inline Comments')}:</p>
366 </td>
366 </td>
367 </tr>
367 </tr>
368 <tr class="outdated">
368 <tr class="outdated">
369 <td></td>
369 <td></td>
370 <td></td>
370 <td></td>
371 <td colspan="2" class="outdated_comment_block">
371 <td colspan="2" class="outdated_comment_block">
372 % for line, comments in c.outdated_comments[path].iteritems():
372 % for line, comments in c.outdated_comments[path].iteritems():
373 <div class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
373 <div class="inline-comment-placeholder" path="${path}" target_id="${h.safeid(h.safe_unicode(path))}">
374 % for co in comments:
374 % for co in comments:
375 ${comment.comment_block_outdated(co)}
375 ${comment.comment_block_outdated(co)}
376 % endfor
376 % endfor
377 </div>
377 </div>
378 % endfor
378 % endfor
379 </td>
379 </td>
380 </tr>
380 </tr>
381 % endif
381 % endif
382 %endfor
382 %endfor
383 </table>
383 </table>
384 </div>
384 </div>
385 % if c.limited_diff:
385 % if c.limited_diff:
386 <h5>${_('Commit was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a huge diff might take some time and resources")}')">${_('Show full diff')}</a></h5>
386 <h5>${_('Commit was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a huge diff might take some time and resources")}')">${_('Show full diff')}</a></h5>
387 % endif
387 % endif
388 </div>
388 </div>
389 </div>
389 </div>
390
390
391 % if c.limited_diff:
391 % if c.limited_diff:
392 <p>${_('Commit was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a huge diff might take some time and resources")}')">${_('Show full diff')}</a></p>
392 <p>${_('Commit was too big and was cut off...')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a huge diff might take some time and resources")}')">${_('Show full diff')}</a></p>
393 % endif
393 % endif
394
394
395 ## template for inline comment form
395 ## template for inline comment form
396 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
396 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
397 ${comment.comment_inline_form()}
397 ${comment.comment_inline_form()}
398
398
399 ## render comments and inlines
399 ## render comments and inlines
400 ${comment.generate_comments(include_pull_request=True, is_pull_request=True)}
400 ${comment.generate_comments(include_pull_request=True, is_pull_request=True)}
401
401
402 % if not c.pull_request.is_closed():
402 % if not c.pull_request.is_closed():
403 ## main comment form and it status
403 ## main comment form and it status
404 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
404 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
405 pull_request_id=c.pull_request.pull_request_id),
405 pull_request_id=c.pull_request.pull_request_id),
406 c.pull_request_review_status,
406 c.pull_request_review_status,
407 is_pull_request=True, change_status=c.allowed_to_change_status)}
407 is_pull_request=True, change_status=c.allowed_to_change_status)}
408 %endif
408 %endif
409
409
410 <script type="text/javascript">
410 <script type="text/javascript">
411 if (location.href.indexOf('#') != -1) {
411 if (location.href.indexOf('#') != -1) {
412 var id = '#'+location.href.substring(location.href.indexOf('#') + 1).split('#');
412 var id = '#'+location.href.substring(location.href.indexOf('#') + 1).split('#');
413 var line = $('html').find(id);
413 var line = $('html').find(id);
414 offsetScroll(line, 70);
414 offsetScroll(line, 70);
415 }
415 }
416 $(function(){
416 $(function(){
417 ReviewerAutoComplete('user');
417 ReviewerAutoComplete('user');
418 // custom code mirror
418 // custom code mirror
419 var codeMirrorInstance = initPullRequestsCodeMirror('#pr-description-input');
419 var codeMirrorInstance = initPullRequestsCodeMirror('#pr-description-input');
420
420
421 var PRDetails = {
421 var PRDetails = {
422 editButton: $('#open_edit_pullrequest'),
422 editButton: $('#open_edit_pullrequest'),
423 closeButton: $('#close_edit_pullrequest'),
423 closeButton: $('#close_edit_pullrequest'),
424 viewFields: $('#pr-desc, #pr-title'),
424 viewFields: $('#pr-desc, #pr-title'),
425 editFields: $('#pr-desc-edit, #pr-title-edit, #pr-save'),
425 editFields: $('#pr-desc-edit, #pr-title-edit, #pr-save'),
426
426
427 init: function() {
427 init: function() {
428 var that = this;
428 var that = this;
429 this.editButton.on('click', function(e) { that.edit(); });
429 this.editButton.on('click', function(e) { that.edit(); });
430 this.closeButton.on('click', function(e) { that.view(); });
430 this.closeButton.on('click', function(e) { that.view(); });
431 },
431 },
432
432
433 edit: function(event) {
433 edit: function(event) {
434 this.viewFields.hide();
434 this.viewFields.hide();
435 this.editButton.hide();
435 this.editButton.hide();
436 this.editFields.show();
436 this.editFields.show();
437 codeMirrorInstance.refresh();
437 codeMirrorInstance.refresh();
438 },
438 },
439
439
440 view: function(event) {
440 view: function(event) {
441 this.editFields.hide();
441 this.editFields.hide();
442 this.closeButton.hide();
442 this.closeButton.hide();
443 this.viewFields.show();
443 this.viewFields.show();
444 }
444 }
445 };
445 };
446
446
447 var ReviewersPanel = {
447 var ReviewersPanel = {
448 editButton: $('#open_edit_reviewers'),
448 editButton: $('#open_edit_reviewers'),
449 closeButton: $('#close_edit_reviewers'),
449 closeButton: $('#close_edit_reviewers'),
450 addButton: $('#add_reviewer_input'),
450 addButton: $('#add_reviewer_input'),
451 removeButtons: $('.reviewer_member_remove'),
451 removeButtons: $('.reviewer_member_remove'),
452
452
453 init: function() {
453 init: function() {
454 var that = this;
454 var that = this;
455 this.editButton.on('click', function(e) { that.edit(); });
455 this.editButton.on('click', function(e) { that.edit(); });
456 this.closeButton.on('click', function(e) { that.close(); });
456 this.closeButton.on('click', function(e) { that.close(); });
457 },
457 },
458
458
459 edit: function(event) {
459 edit: function(event) {
460 this.editButton.hide();
460 this.editButton.hide();
461 this.closeButton.show();
461 this.closeButton.show();
462 this.addButton.show();
462 this.addButton.show();
463 this.removeButtons.css('visibility', 'visible');
463 this.removeButtons.css('visibility', 'visible');
464 },
464 },
465
465
466 close: function(event) {
466 close: function(event) {
467 this.editButton.show();
467 this.editButton.show();
468 this.closeButton.hide();
468 this.closeButton.hide();
469 this.addButton.hide();
469 this.addButton.hide();
470 this.removeButtons.css('visibility', 'hidden');
470 this.removeButtons.css('visibility', 'hidden');
471 }
471 }
472 };
472 };
473
473
474 PRDetails.init();
474 PRDetails.init();
475 ReviewersPanel.init();
475 ReviewersPanel.init();
476
476
477 $('#show-outdated-comments').on('click', function(e){
477 $('#show-outdated-comments').on('click', function(e){
478 var button = $(this);
478 var button = $(this);
479 var outdated = $('.outdated');
479 var outdated = $('.outdated');
480 if (button.html() === "(Show)") {
480 if (button.html() === "(Show)") {
481 button.html("(Hide)");
481 button.html("(Hide)");
482 outdated.show();
482 outdated.show();
483 } else {
483 } else {
484 button.html("(Show)");
484 button.html("(Show)");
485 outdated.hide();
485 outdated.hide();
486 }
486 }
487 });
487 });
488
488
489 $('.show-inline-comments').on('change', function(e){
489 $('.show-inline-comments').on('change', function(e){
490 var show = 'none';
490 var show = 'none';
491 var target = e.currentTarget;
491 var target = e.currentTarget;
492 if(target.checked){
492 if(target.checked){
493 show = ''
493 show = ''
494 }
494 }
495 var boxid = $(target).attr('id_for');
495 var boxid = $(target).attr('id_for');
496 var comments = $('#{0} .inline-comments'.format(boxid));
496 var comments = $('#{0} .inline-comments'.format(boxid));
497 var fn_display = function(idx){
497 var fn_display = function(idx){
498 $(this).css('display', show);
498 $(this).css('display', show);
499 };
499 };
500 $(comments).each(fn_display);
500 $(comments).each(fn_display);
501 var btns = $('#{0} .inline-comments-button'.format(boxid));
501 var btns = $('#{0} .inline-comments-button'.format(boxid));
502 $(btns).each(fn_display);
502 $(btns).each(fn_display);
503 });
503 });
504
504
505 // inject comments into their proper positions
505 // inject comments into their proper positions
506 var file_comments = $('.inline-comment-placeholder');
506 var file_comments = $('.inline-comment-placeholder');
507 %if c.pull_request.is_closed():
507 %if c.pull_request.is_closed():
508 renderInlineComments(file_comments, false);
508 renderInlineComments(file_comments, false);
509 %else:
509 %else:
510 renderInlineComments(file_comments, true);
510 renderInlineComments(file_comments, true);
511 %endif
511 %endif
512 var commentTotals = {};
512 var commentTotals = {};
513 $.each(file_comments, function(i, comment) {
513 $.each(file_comments, function(i, comment) {
514 var path = $(comment).attr('path');
514 var path = $(comment).attr('path');
515 var comms = $(comment).children().length;
515 var comms = $(comment).children().length;
516 if (path in commentTotals) {
516 if (path in commentTotals) {
517 commentTotals[path] += comms;
517 commentTotals[path] += comms;
518 } else {
518 } else {
519 commentTotals[path] = comms;
519 commentTotals[path] = comms;
520 }
520 }
521 });
521 });
522 $.each(commentTotals, function(path, total) {
522 $.each(commentTotals, function(path, total) {
523 var elem = $('.comment-bubble[data-path="'+ path +'"]');
523 var elem = $('.comment-bubble[data-path="'+ path +'"]');
524 elem.css('visibility', 'visible');
524 elem.css('visibility', 'visible');
525 elem.html(elem.html() + ' ' + total );
525 elem.html(elem.html() + ' ' + total );
526 });
526 });
527
527
528 $('#merge_pull_request_form').submit(function() {
528 $('#merge_pull_request_form').submit(function() {
529 if (!$('#merge_pull_request').attr('disabled')) {
529 if (!$('#merge_pull_request').attr('disabled')) {
530 $('#merge_pull_request').attr('disabled', 'disabled');
530 $('#merge_pull_request').attr('disabled', 'disabled');
531 }
531 }
532 return true;
532 return true;
533 });
533 });
534
534
535 $('#edit_pull_request').on('click', function(e){
535 $('#edit_pull_request').on('click', function(e){
536 var title = $('#pr-title-input').val();
536 var title = $('#pr-title-input').val();
537 var description = codeMirrorInstance.getValue();
537 var description = codeMirrorInstance.getValue();
538 editPullRequest(
538 editPullRequest(
539 "${c.repo_name}", "${c.pull_request.pull_request_id}",
539 "${c.repo_name}", "${c.pull_request.pull_request_id}",
540 title, description);
540 title, description);
541 });
541 });
542
542
543 $('#update_pull_request').on('click', function(e){
543 $('#update_pull_request').on('click', function(e){
544 updateReviewers(undefined, "${c.repo_name}", "${c.pull_request.pull_request_id}");
544 updateReviewers(undefined, "${c.repo_name}", "${c.pull_request.pull_request_id}");
545 });
545 });
546
546
547 $('#update_commits').on('click', function(e){
547 $('#update_commits').on('click', function(e){
548 var isDisabled = !$(e.currentTarget).attr('disabled');
548 var isDisabled = !$(e.currentTarget).attr('disabled');
549 $(e.currentTarget).text(_TM['Updating...']);
549 $(e.currentTarget).text(_gettext('Updating...'));
550 $(e.currentTarget).attr('disabled', 'disabled');
550 $(e.currentTarget).attr('disabled', 'disabled');
551 if(isDisabled){
551 if(isDisabled){
552 updateCommits("${c.repo_name}", "${c.pull_request.pull_request_id}");
552 updateCommits("${c.repo_name}", "${c.pull_request.pull_request_id}");
553 }
553 }
554
554
555 });
555 });
556 // fixing issue with caches on firefox
556 // fixing issue with caches on firefox
557 $('#update_commits').removeAttr("disabled");
557 $('#update_commits').removeAttr("disabled");
558
558
559 $('#close_pull_request').on('click', function(e){
559 $('#close_pull_request').on('click', function(e){
560 closePullRequest("${c.repo_name}", "${c.pull_request.pull_request_id}");
560 closePullRequest("${c.repo_name}", "${c.pull_request.pull_request_id}");
561 });
561 });
562 })
562 })
563 </script>
563 </script>
564
564
565 </div>
565 </div>
566 </div>
566 </div>
567
567
568 </%def>
568 </%def>
General Comments 0
You need to be logged in to leave comments. Login now