##// END OF EJS Templates
html: clarify type of button elements - make it clear if we really want "submit"...
Mads Kiilerich -
r8713:5572eb8d stable
parent child Browse files
Show More
@@ -1,1381 +1,1383 b''
1 /**
1 /**
2 Kallithea JS Files
2 Kallithea JS Files
3 **/
3 **/
4 'use strict';
4 'use strict';
5
5
6 if (typeof console == "undefined" || typeof console.log == "undefined"){
6 if (typeof console == "undefined" || typeof console.log == "undefined"){
7 console = { log: function() {} }
7 console = { log: function() {} }
8 }
8 }
9
9
10 /**
10 /**
11 * INJECT .html_escape function into String
11 * INJECT .html_escape function into String
12 * Usage: "unsafe string".html_escape()
12 * Usage: "unsafe string".html_escape()
13 *
13 *
14 * This is the Javascript equivalent of kallithea.lib.helpers.html_escape(). It
14 * This is the Javascript equivalent of kallithea.lib.helpers.html_escape(). It
15 * will escape HTML characters to prevent XSS or other issues. It should be
15 * will escape HTML characters to prevent XSS or other issues. It should be
16 * used in all cases where Javascript code is inserting potentially unsafe data
16 * used in all cases where Javascript code is inserting potentially unsafe data
17 * into the document.
17 * into the document.
18 *
18 *
19 * For example:
19 * For example:
20 * <script>confirm("boo")</script>
20 * <script>confirm("boo")</script>
21 * is changed into:
21 * is changed into:
22 * &lt;script&gt;confirm(&quot;boo&quot;)&lt;/script&gt;
22 * &lt;script&gt;confirm(&quot;boo&quot;)&lt;/script&gt;
23 *
23 *
24 */
24 */
25 String.prototype.html_escape = function() {
25 String.prototype.html_escape = function() {
26 return this
26 return this
27 .replace(/&/g,'&amp;')
27 .replace(/&/g,'&amp;')
28 .replace(/</g,'&lt;')
28 .replace(/</g,'&lt;')
29 .replace(/>/g,'&gt;')
29 .replace(/>/g,'&gt;')
30 .replace(/"/g, '&quot;')
30 .replace(/"/g, '&quot;')
31 .replace(/'/g, '&#039;');
31 .replace(/'/g, '&#039;');
32 }
32 }
33
33
34 /**
34 /**
35 * INJECT .format function into String
35 * INJECT .format function into String
36 * Usage: "My name is {0} {1}".format("Johny","Bravo")
36 * Usage: "My name is {0} {1}".format("Johny","Bravo")
37 * Return "My name is Johny Bravo"
37 * Return "My name is Johny Bravo"
38 * Inspired by https://gist.github.com/1049426
38 * Inspired by https://gist.github.com/1049426
39 */
39 */
40 String.prototype.format = function() {
40 String.prototype.format = function() {
41 function format() {
41 function format() {
42 var str = this;
42 var str = this;
43 var len = arguments.length+1;
43 var len = arguments.length+1;
44 var safe = undefined;
44 var safe = undefined;
45 var arg = undefined;
45 var arg = undefined;
46
46
47 // For each {0} {1} {n...} replace with the argument in that position. If
47 // For each {0} {1} {n...} replace with the argument in that position. If
48 // the argument is an object or an array it will be stringified to JSON.
48 // the argument is an object or an array it will be stringified to JSON.
49 for (var i=0; i < len; arg = arguments[i++]) {
49 for (var i=0; i < len; arg = arguments[i++]) {
50 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
50 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
51 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
51 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
52 }
52 }
53 return str;
53 return str;
54 }
54 }
55
55
56 // Save a reference of what may already exist under the property native.
56 // Save a reference of what may already exist under the property native.
57 // Allows for doing something like: if("".format.native) { /* use native */ }
57 // Allows for doing something like: if("".format.native) { /* use native */ }
58 format.native = String.prototype.format;
58 format.native = String.prototype.format;
59
59
60 // Replace the prototype property
60 // Replace the prototype property
61 return format;
61 return format;
62
62
63 }();
63 }();
64
64
65 String.prototype.strip = function(char) {
65 String.prototype.strip = function(char) {
66 if(char === undefined){
66 if(char === undefined){
67 char = '\\s';
67 char = '\\s';
68 }
68 }
69 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
69 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
70 }
70 }
71
71
72 String.prototype.lstrip = function(char) {
72 String.prototype.lstrip = function(char) {
73 if(char === undefined){
73 if(char === undefined){
74 char = '\\s';
74 char = '\\s';
75 }
75 }
76 return this.replace(new RegExp('^'+char+'+'),'');
76 return this.replace(new RegExp('^'+char+'+'),'');
77 }
77 }
78
78
79 String.prototype.rstrip = function(char) {
79 String.prototype.rstrip = function(char) {
80 if(char === undefined){
80 if(char === undefined){
81 char = '\\s';
81 char = '\\s';
82 }
82 }
83 return this.replace(new RegExp(''+char+'+$'),'');
83 return this.replace(new RegExp(''+char+'+$'),'');
84 }
84 }
85
85
86 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
86 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
87 under MIT license / public domain, see
87 under MIT license / public domain, see
88 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
88 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
89 if(!Array.prototype.indexOf) {
89 if(!Array.prototype.indexOf) {
90 Array.prototype.indexOf = function (searchElement, fromIndex) {
90 Array.prototype.indexOf = function (searchElement, fromIndex) {
91 if ( this === undefined || this === null ) {
91 if ( this === undefined || this === null ) {
92 throw new TypeError( '"this" is null or not defined' );
92 throw new TypeError( '"this" is null or not defined' );
93 }
93 }
94
94
95 var length = this.length >>> 0; // Hack to convert object.length to a UInt32
95 var length = this.length >>> 0; // Hack to convert object.length to a UInt32
96
96
97 fromIndex = +fromIndex || 0;
97 fromIndex = +fromIndex || 0;
98
98
99 if (Math.abs(fromIndex) === Infinity) {
99 if (Math.abs(fromIndex) === Infinity) {
100 fromIndex = 0;
100 fromIndex = 0;
101 }
101 }
102
102
103 if (fromIndex < 0) {
103 if (fromIndex < 0) {
104 fromIndex += length;
104 fromIndex += length;
105 if (fromIndex < 0) {
105 if (fromIndex < 0) {
106 fromIndex = 0;
106 fromIndex = 0;
107 }
107 }
108 }
108 }
109
109
110 for (;fromIndex < length; fromIndex++) {
110 for (;fromIndex < length; fromIndex++) {
111 if (this[fromIndex] === searchElement) {
111 if (this[fromIndex] === searchElement) {
112 return fromIndex;
112 return fromIndex;
113 }
113 }
114 }
114 }
115
115
116 return -1;
116 return -1;
117 };
117 };
118 }
118 }
119
119
120 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Compatibility
120 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Compatibility
121 under MIT license / public domain, see
121 under MIT license / public domain, see
122 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
122 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
123 if (!Array.prototype.filter)
123 if (!Array.prototype.filter)
124 {
124 {
125 Array.prototype.filter = function(fun /*, thisArg */)
125 Array.prototype.filter = function(fun /*, thisArg */)
126 {
126 {
127 if (this === void 0 || this === null)
127 if (this === void 0 || this === null)
128 throw new TypeError();
128 throw new TypeError();
129
129
130 var t = Object(this);
130 var t = Object(this);
131 var len = t.length >>> 0;
131 var len = t.length >>> 0;
132 if (typeof fun !== "function")
132 if (typeof fun !== "function")
133 throw new TypeError();
133 throw new TypeError();
134
134
135 var res = [];
135 var res = [];
136 var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
136 var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
137 for (var i = 0; i < len; i++)
137 for (var i = 0; i < len; i++)
138 {
138 {
139 if (i in t)
139 if (i in t)
140 {
140 {
141 var val = t[i];
141 var val = t[i];
142
142
143 // NOTE: Technically this should Object.defineProperty at
143 // NOTE: Technically this should Object.defineProperty at
144 // the next index, as push can be affected by
144 // the next index, as push can be affected by
145 // properties on Object.prototype and Array.prototype.
145 // properties on Object.prototype and Array.prototype.
146 // But that method's new, and collisions should be
146 // But that method's new, and collisions should be
147 // rare, so use the more-compatible alternative.
147 // rare, so use the more-compatible alternative.
148 if (fun.call(thisArg, val, i, t))
148 if (fun.call(thisArg, val, i, t))
149 res.push(val);
149 res.push(val);
150 }
150 }
151 }
151 }
152
152
153 return res;
153 return res;
154 };
154 };
155 }
155 }
156
156
157 /**
157 /**
158 * A customized version of PyRoutes.JS from https://pypi.python.org/pypi/pyroutes.js/
158 * A customized version of PyRoutes.JS from https://pypi.python.org/pypi/pyroutes.js/
159 * which is copyright Stephane Klein and was made available under the BSD License.
159 * which is copyright Stephane Klein and was made available under the BSD License.
160 *
160 *
161 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
161 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
162 */
162 */
163 var pyroutes = (function() {
163 var pyroutes = (function() {
164 var matchlist = {};
164 var matchlist = {};
165 var sprintf = (function() {
165 var sprintf = (function() {
166 function get_type(variable) {
166 function get_type(variable) {
167 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
167 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
168 }
168 }
169 function str_repeat(input, multiplier) {
169 function str_repeat(input, multiplier) {
170 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
170 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
171 return output.join('');
171 return output.join('');
172 }
172 }
173
173
174 function str_format() {
174 function str_format() {
175 if (!str_format.cache.hasOwnProperty(arguments[0])) {
175 if (!str_format.cache.hasOwnProperty(arguments[0])) {
176 str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
176 str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
177 }
177 }
178 return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
178 return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
179 }
179 }
180
180
181 str_format.format = function(parse_tree, argv) {
181 str_format.format = function(parse_tree, argv) {
182 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
182 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
183 for (i = 0; i < tree_length; i++) {
183 for (i = 0; i < tree_length; i++) {
184 node_type = get_type(parse_tree[i]);
184 node_type = get_type(parse_tree[i]);
185 if (node_type === 'string') {
185 if (node_type === 'string') {
186 output.push(parse_tree[i]);
186 output.push(parse_tree[i]);
187 }
187 }
188 else if (node_type === 'array') {
188 else if (node_type === 'array') {
189 match = parse_tree[i]; // convenience purposes only
189 match = parse_tree[i]; // convenience purposes only
190 if (match[2]) { // keyword argument
190 if (match[2]) { // keyword argument
191 arg = argv[cursor];
191 arg = argv[cursor];
192 for (k = 0; k < match[2].length; k++) {
192 for (k = 0; k < match[2].length; k++) {
193 if (!arg.hasOwnProperty(match[2][k])) {
193 if (!arg.hasOwnProperty(match[2][k])) {
194 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
194 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
195 }
195 }
196 arg = arg[match[2][k]];
196 arg = arg[match[2][k]];
197 }
197 }
198 }
198 }
199 else if (match[1]) { // positional argument (explicit)
199 else if (match[1]) { // positional argument (explicit)
200 arg = argv[match[1]];
200 arg = argv[match[1]];
201 }
201 }
202 else { // positional argument (implicit)
202 else { // positional argument (implicit)
203 arg = argv[cursor++];
203 arg = argv[cursor++];
204 }
204 }
205
205
206 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
206 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
207 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
207 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
208 }
208 }
209 switch (match[8]) {
209 switch (match[8]) {
210 case 'b': arg = arg.toString(2); break;
210 case 'b': arg = arg.toString(2); break;
211 case 'c': arg = String.fromCharCode(arg); break;
211 case 'c': arg = String.fromCharCode(arg); break;
212 case 'd': arg = parseInt(arg, 10); break;
212 case 'd': arg = parseInt(arg, 10); break;
213 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
213 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
214 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
214 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
215 case 'o': arg = arg.toString(8); break;
215 case 'o': arg = arg.toString(8); break;
216 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
216 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
217 case 'u': arg = Math.abs(arg); break;
217 case 'u': arg = Math.abs(arg); break;
218 case 'x': arg = arg.toString(16); break;
218 case 'x': arg = arg.toString(16); break;
219 case 'X': arg = arg.toString(16).toUpperCase(); break;
219 case 'X': arg = arg.toString(16).toUpperCase(); break;
220 }
220 }
221 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
221 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
222 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
222 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
223 pad_length = match[6] - String(arg).length;
223 pad_length = match[6] - String(arg).length;
224 pad = match[6] ? str_repeat(pad_character, pad_length) : '';
224 pad = match[6] ? str_repeat(pad_character, pad_length) : '';
225 output.push(match[5] ? arg + pad : pad + arg);
225 output.push(match[5] ? arg + pad : pad + arg);
226 }
226 }
227 }
227 }
228 return output.join('');
228 return output.join('');
229 };
229 };
230
230
231 str_format.cache = {};
231 str_format.cache = {};
232
232
233 str_format.parse = function(fmt) {
233 str_format.parse = function(fmt) {
234 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
234 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
235 while (_fmt) {
235 while (_fmt) {
236 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
236 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
237 parse_tree.push(match[0]);
237 parse_tree.push(match[0]);
238 }
238 }
239 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
239 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
240 parse_tree.push('%');
240 parse_tree.push('%');
241 }
241 }
242 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
242 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
243 if (match[2]) {
243 if (match[2]) {
244 arg_names |= 1;
244 arg_names |= 1;
245 var field_list = [], replacement_field = match[2], field_match = [];
245 var field_list = [], replacement_field = match[2], field_match = [];
246 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
246 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
247 field_list.push(field_match[1]);
247 field_list.push(field_match[1]);
248 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
248 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
249 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
249 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
250 field_list.push(field_match[1]);
250 field_list.push(field_match[1]);
251 }
251 }
252 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
252 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
253 field_list.push(field_match[1]);
253 field_list.push(field_match[1]);
254 }
254 }
255 else {
255 else {
256 throw('[sprintf] huh?');
256 throw('[sprintf] huh?');
257 }
257 }
258 }
258 }
259 }
259 }
260 else {
260 else {
261 throw('[sprintf] huh?');
261 throw('[sprintf] huh?');
262 }
262 }
263 match[2] = field_list;
263 match[2] = field_list;
264 }
264 }
265 else {
265 else {
266 arg_names |= 2;
266 arg_names |= 2;
267 }
267 }
268 if (arg_names === 3) {
268 if (arg_names === 3) {
269 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
269 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
270 }
270 }
271 parse_tree.push(match);
271 parse_tree.push(match);
272 }
272 }
273 else {
273 else {
274 throw('[sprintf] huh?');
274 throw('[sprintf] huh?');
275 }
275 }
276 _fmt = _fmt.substring(match[0].length);
276 _fmt = _fmt.substring(match[0].length);
277 }
277 }
278 return parse_tree;
278 return parse_tree;
279 };
279 };
280
280
281 return str_format;
281 return str_format;
282 })();
282 })();
283
283
284 return {
284 return {
285 'url': function(route_name, params) {
285 'url': function(route_name, params) {
286 var result = route_name;
286 var result = route_name;
287 if (typeof(params) != 'object'){
287 if (typeof(params) != 'object'){
288 params = {};
288 params = {};
289 }
289 }
290 if (matchlist.hasOwnProperty(route_name)) {
290 if (matchlist.hasOwnProperty(route_name)) {
291 var route = matchlist[route_name];
291 var route = matchlist[route_name];
292 // param substitution
292 // param substitution
293 for(var i=0; i < route[1].length; i++) {
293 for(var i=0; i < route[1].length; i++) {
294 if (!params.hasOwnProperty(route[1][i]))
294 if (!params.hasOwnProperty(route[1][i]))
295 throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
295 throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
296 }
296 }
297 result = sprintf(route[0], params);
297 result = sprintf(route[0], params);
298
298
299 var ret = [];
299 var ret = [];
300 //extra params => GET
300 //extra params => GET
301 for(var param in params){
301 for(var param in params){
302 if (route[1].indexOf(param) == -1){
302 if (route[1].indexOf(param) == -1){
303 ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
303 ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
304 }
304 }
305 }
305 }
306 var _parts = ret.join("&");
306 var _parts = ret.join("&");
307 if(_parts){
307 if(_parts){
308 result = result +'?'+ _parts
308 result = result +'?'+ _parts
309 }
309 }
310 }
310 }
311
311
312 return result;
312 return result;
313 },
313 },
314 'register': function(route_name, route_tmpl, req_params) {
314 'register': function(route_name, route_tmpl, req_params) {
315 if (typeof(req_params) != 'object') {
315 if (typeof(req_params) != 'object') {
316 req_params = [];
316 req_params = [];
317 }
317 }
318 var keys = [];
318 var keys = [];
319 for (var i=0; i < req_params.length; i++) {
319 for (var i=0; i < req_params.length; i++) {
320 keys.push(req_params[i]);
320 keys.push(req_params[i]);
321 }
321 }
322 matchlist[route_name] = [
322 matchlist[route_name] = [
323 unescape(route_tmpl),
323 unescape(route_tmpl),
324 keys
324 keys
325 ]
325 ]
326 },
326 },
327 '_routes': function(){
327 '_routes': function(){
328 return matchlist;
328 return matchlist;
329 }
329 }
330 }
330 }
331 })();
331 })();
332
332
333
333
334 /**
334 /**
335 * turns objects into GET query string
335 * turns objects into GET query string
336 */
336 */
337 function _toQueryString(o) {
337 function _toQueryString(o) {
338 if(typeof o !== 'object') {
338 if(typeof o !== 'object') {
339 return false;
339 return false;
340 }
340 }
341 var _p, _qs = [];
341 var _p, _qs = [];
342 for(_p in o) {
342 for(_p in o) {
343 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
343 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
344 }
344 }
345 return _qs.join('&');
345 return _qs.join('&');
346 }
346 }
347
347
348 /**
348 /**
349 * Load HTML into DOM using Ajax
349 * Load HTML into DOM using Ajax
350 *
350 *
351 * @param $target: load html async and place it (or an error message) here
351 * @param $target: load html async and place it (or an error message) here
352 * @param success: success callback function
352 * @param success: success callback function
353 * @param args: query parameters to pass to url
353 * @param args: query parameters to pass to url
354 */
354 */
355 function asynchtml(url, $target, success, args){
355 function asynchtml(url, $target, success, args){
356 if(args===undefined){
356 if(args===undefined){
357 args=null;
357 args=null;
358 }
358 }
359 $target.html(_TM['Loading ...']).css('opacity','0.3');
359 $target.html(_TM['Loading ...']).css('opacity','0.3');
360
360
361 return $.ajax({url: url, data: args, headers: {'X-PARTIAL-XHR': '1'}, cache: false, dataType: 'html'})
361 return $.ajax({url: url, data: args, headers: {'X-PARTIAL-XHR': '1'}, cache: false, dataType: 'html'})
362 .done(function(html) {
362 .done(function(html) {
363 $target.html(html);
363 $target.html(html);
364 $target.css('opacity','1.0');
364 $target.css('opacity','1.0');
365 //execute the given original callback
365 //execute the given original callback
366 if (success !== undefined && success) {
366 if (success !== undefined && success) {
367 success();
367 success();
368 }
368 }
369 })
369 })
370 .fail(function(jqXHR, textStatus) {
370 .fail(function(jqXHR, textStatus) {
371 if (textStatus == "abort")
371 if (textStatus == "abort")
372 return;
372 return;
373 $target.html('<span class="bg-danger">ERROR: {0}</span>'.format(textStatus));
373 $target.html('<span class="bg-danger">ERROR: {0}</span>'.format(textStatus));
374 $target.css('opacity','1.0');
374 $target.css('opacity','1.0');
375 })
375 })
376 ;
376 ;
377 }
377 }
378
378
379 function ajaxGET(url, success, failure) {
379 function ajaxGET(url, success, failure) {
380 if(failure === undefined) {
380 if(failure === undefined) {
381 failure = function(jqXHR, textStatus) {
381 failure = function(jqXHR, textStatus) {
382 if (textStatus != "abort")
382 if (textStatus != "abort")
383 alert("Ajax GET error: " + textStatus);
383 alert("Ajax GET error: " + textStatus);
384 };
384 };
385 }
385 }
386 return $.ajax({url: url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
386 return $.ajax({url: url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
387 .done(success)
387 .done(success)
388 .fail(failure);
388 .fail(failure);
389 }
389 }
390
390
391 function ajaxPOST(url, postData, success, failure) {
391 function ajaxPOST(url, postData, success, failure) {
392 postData['_session_csrf_secret_token'] = _session_csrf_secret_token;
392 postData['_session_csrf_secret_token'] = _session_csrf_secret_token;
393 if(failure === undefined) {
393 if(failure === undefined) {
394 failure = function(jqXHR, textStatus) {
394 failure = function(jqXHR, textStatus) {
395 if (textStatus != "abort")
395 if (textStatus != "abort")
396 alert("Error posting to server: " + textStatus);
396 alert("Error posting to server: " + textStatus);
397 };
397 };
398 }
398 }
399 return $.ajax({url: url, data: _toQueryString(postData), type: 'POST', headers: {'X-PARTIAL-XHR': '1'}, cache: false})
399 return $.ajax({url: url, data: _toQueryString(postData), type: 'POST', headers: {'X-PARTIAL-XHR': '1'}, cache: false})
400 .done(success)
400 .done(success)
401 .fail(failure);
401 .fail(failure);
402 }
402 }
403
403
404
404
405 /**
405 /**
406 * activate .show_more links
406 * activate .show_more links
407 * the .show_more must have an id that is the the id of an element to hide prefixed with _
407 * the .show_more must have an id that is the the id of an element to hide prefixed with _
408 * the parentnode will be displayed
408 * the parentnode will be displayed
409 */
409 */
410 function show_more_event(){
410 function show_more_event(){
411 $('.show_more').click(function(e){
411 $('.show_more').click(function(e){
412 var el = e.currentTarget;
412 var el = e.currentTarget;
413 $('#' + el.id.substring(1)).hide();
413 $('#' + el.id.substring(1)).hide();
414 $(el.parentNode).show();
414 $(el.parentNode).show();
415 });
415 });
416 }
416 }
417
417
418
418
419 function _onSuccessFollow(target){
419 function _onSuccessFollow(target){
420 var $target = $(target);
420 var $target = $(target);
421 var $f_cnt = $('#current_followers_count');
421 var $f_cnt = $('#current_followers_count');
422 if ($target.hasClass('follow')) {
422 if ($target.hasClass('follow')) {
423 $target.removeClass('follow').addClass('following');
423 $target.removeClass('follow').addClass('following');
424 $target.prop('title', _TM['Stop following this repository']);
424 $target.prop('title', _TM['Stop following this repository']);
425 if ($f_cnt.html()) {
425 if ($f_cnt.html()) {
426 const cnt = Number($f_cnt.html())+1;
426 const cnt = Number($f_cnt.html())+1;
427 $f_cnt.html(cnt);
427 $f_cnt.html(cnt);
428 }
428 }
429 } else {
429 } else {
430 $target.removeClass('following').addClass('follow');
430 $target.removeClass('following').addClass('follow');
431 $target.prop('title', _TM['Start following this repository']);
431 $target.prop('title', _TM['Start following this repository']);
432 if ($f_cnt.html()) {
432 if ($f_cnt.html()) {
433 const cnt = Number($f_cnt.html())-1;
433 const cnt = Number($f_cnt.html())-1;
434 $f_cnt.html(cnt);
434 $f_cnt.html(cnt);
435 }
435 }
436 }
436 }
437 }
437 }
438
438
439 function toggleFollowingRepo(target, follows_repository_id){
439 function toggleFollowingRepo(target, follows_repository_id){
440 var args = {
440 var args = {
441 'follows_repository_id': follows_repository_id,
441 'follows_repository_id': follows_repository_id,
442 '_session_csrf_secret_token': _session_csrf_secret_token
442 '_session_csrf_secret_token': _session_csrf_secret_token
443 }
443 }
444 $.post(TOGGLE_FOLLOW_URL, args, function(){
444 $.post(TOGGLE_FOLLOW_URL, args, function(){
445 _onSuccessFollow(target);
445 _onSuccessFollow(target);
446 });
446 });
447 return false;
447 return false;
448 }
448 }
449
449
450 function showRepoSize(target, repo_name){
450 function showRepoSize(target, repo_name){
451 var args = '_session_csrf_secret_token=' + _session_csrf_secret_token;
451 var args = '_session_csrf_secret_token=' + _session_csrf_secret_token;
452
452
453 if(!$("#" + target).hasClass('loaded')){
453 if(!$("#" + target).hasClass('loaded')){
454 $("#" + target).html(_TM['Loading ...']);
454 $("#" + target).html(_TM['Loading ...']);
455 var url = pyroutes.url('repo_size', {"repo_name":repo_name});
455 var url = pyroutes.url('repo_size', {"repo_name":repo_name});
456 $.post(url, args, function(data) {
456 $.post(url, args, function(data) {
457 $("#" + target).html(data);
457 $("#" + target).html(data);
458 $("#" + target).addClass('loaded');
458 $("#" + target).addClass('loaded');
459 });
459 });
460 }
460 }
461 return false;
461 return false;
462 }
462 }
463
463
464 /**
464 /**
465 * load tooltips dynamically based on data attributes, used for .lazy-cs changeset links
465 * load tooltips dynamically based on data attributes, used for .lazy-cs changeset links
466 */
466 */
467 function get_changeset_tooltip() {
467 function get_changeset_tooltip() {
468 var $target = $(this);
468 var $target = $(this);
469 var tooltip = $target.data('tooltip');
469 var tooltip = $target.data('tooltip');
470 if (!tooltip) {
470 if (!tooltip) {
471 var raw_id = $target.data('raw_id');
471 var raw_id = $target.data('raw_id');
472 var repo_name = $target.data('repo_name');
472 var repo_name = $target.data('repo_name');
473 var url = pyroutes.url('changeset_info', {"repo_name": repo_name, "revision": raw_id});
473 var url = pyroutes.url('changeset_info', {"repo_name": repo_name, "revision": raw_id});
474
474
475 $.ajax(url, {
475 $.ajax(url, {
476 async: false,
476 async: false,
477 success: function(data) {
477 success: function(data) {
478 tooltip = data["message"];
478 tooltip = data["message"];
479 }
479 }
480 });
480 });
481 $target.data('tooltip', tooltip);
481 $target.data('tooltip', tooltip);
482 }
482 }
483 return tooltip;
483 return tooltip;
484 }
484 }
485
485
486 /**
486 /**
487 * activate tooltips and popups
487 * activate tooltips and popups
488 */
488 */
489 function tooltip_activate(){
489 function tooltip_activate(){
490 function placement(p, e){
490 function placement(p, e){
491 if(e.getBoundingClientRect().top > 2*$(window).height()/3){
491 if(e.getBoundingClientRect().top > 2*$(window).height()/3){
492 return 'top';
492 return 'top';
493 }else{
493 }else{
494 return 'bottom';
494 return 'bottom';
495 }
495 }
496 }
496 }
497 $(document).ready(function(){
497 $(document).ready(function(){
498 $('[data-toggle="tooltip"]').tooltip({
498 $('[data-toggle="tooltip"]').tooltip({
499 container: 'body',
499 container: 'body',
500 placement: placement
500 placement: placement
501 });
501 });
502 $('[data-toggle="popover"]').popover({
502 $('[data-toggle="popover"]').popover({
503 html: true,
503 html: true,
504 container: 'body',
504 container: 'body',
505 placement: placement,
505 placement: placement,
506 trigger: 'hover',
506 trigger: 'hover',
507 template: '<div class="popover cs-popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
507 template: '<div class="popover cs-popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
508 });
508 });
509 $('.lazy-cs').tooltip({
509 $('.lazy-cs').tooltip({
510 title: get_changeset_tooltip,
510 title: get_changeset_tooltip,
511 placement: placement
511 placement: placement
512 });
512 });
513 });
513 });
514 }
514 }
515
515
516
516
517 /**
517 /**
518 * Comment handling
518 * Comment handling
519 */
519 */
520
520
521 // move comments to their right location, inside new trs
521 // move comments to their right location, inside new trs
522 function move_comments($anchorcomments) {
522 function move_comments($anchorcomments) {
523 $anchorcomments.each(function(i, anchorcomment) {
523 $anchorcomments.each(function(i, anchorcomment) {
524 var $anchorcomment = $(anchorcomment);
524 var $anchorcomment = $(anchorcomment);
525 var target_id = $anchorcomment.data('target-id');
525 var target_id = $anchorcomment.data('target-id');
526 var $comment_div = _get_add_comment_div(target_id);
526 var $comment_div = _get_add_comment_div(target_id);
527 var f_path = $anchorcomment.data('f_path');
527 var f_path = $anchorcomment.data('f_path');
528 var line_no = $anchorcomment.data('line_no');
528 var line_no = $anchorcomment.data('line_no');
529 if ($comment_div[0]) {
529 if ($comment_div[0]) {
530 $comment_div.append($anchorcomment.children());
530 $comment_div.append($anchorcomment.children());
531 if (f_path && line_no !== '') {
531 if (f_path && line_no !== '') {
532 _comment_div_append_add($comment_div, f_path, line_no);
532 _comment_div_append_add($comment_div, f_path, line_no);
533 } else {
533 } else {
534 _comment_div_append_form($comment_div, f_path, line_no);
534 _comment_div_append_form($comment_div, f_path, line_no);
535 }
535 }
536 } else {
536 } else {
537 $anchorcomment.before("<span class='bg-warning'>Comment to {0} line {1} which is outside the diff context:</span>".format(f_path || '?', line_no || '?'));
537 $anchorcomment.before("<span class='bg-warning'>Comment to {0} line {1} which is outside the diff context:</span>".format(f_path || '?', line_no || '?'));
538 }
538 }
539 });
539 });
540 linkInlineComments($('.firstlink'), $('.comment:first-child'));
540 linkInlineComments($('.firstlink'), $('.comment:first-child'));
541 }
541 }
542
542
543 // comment bubble was clicked - insert new tr and show form
543 // comment bubble was clicked - insert new tr and show form
544 function show_comment_form($bubble) {
544 function show_comment_form($bubble) {
545 var children = $bubble.closest('tr.line').children('[id]');
545 var children = $bubble.closest('tr.line').children('[id]');
546 var line_td_id = children[children.length - 1].id;
546 var line_td_id = children[children.length - 1].id;
547 var $comment_div = _get_add_comment_div(line_td_id);
547 var $comment_div = _get_add_comment_div(line_td_id);
548 var f_path = $bubble.closest('[data-f_path]').data('f_path');
548 var f_path = $bubble.closest('[data-f_path]').data('f_path');
549 var parts = line_td_id.split('_');
549 var parts = line_td_id.split('_');
550 var line_no = parts[parts.length-1];
550 var line_no = parts[parts.length-1];
551 comment_div_state($comment_div, f_path, line_no, true);
551 comment_div_state($comment_div, f_path, line_no, true);
552 }
552 }
553
553
554 // return comment div for target_id - add it if it doesn't exist yet
554 // return comment div for target_id - add it if it doesn't exist yet
555 function _get_add_comment_div(target_id) {
555 function _get_add_comment_div(target_id) {
556 var comments_box_id = 'comments-' + target_id;
556 var comments_box_id = 'comments-' + target_id;
557 var $comments_box = $('#' + comments_box_id);
557 var $comments_box = $('#' + comments_box_id);
558 if (!$comments_box.length) {
558 if (!$comments_box.length) {
559 var html = '<tr><td id="{0}" colspan="3" class="inline-comments"></td></tr>'.format(comments_box_id);
559 var html = '<tr><td id="{0}" colspan="3" class="inline-comments"></td></tr>'.format(comments_box_id);
560 $('#' + target_id).closest('tr').after(html);
560 $('#' + target_id).closest('tr').after(html);
561 $comments_box = $('#' + comments_box_id);
561 $comments_box = $('#' + comments_box_id);
562 }
562 }
563 return $comments_box;
563 return $comments_box;
564 }
564 }
565
565
566 // Set $comment_div state - showing or not showing form and Add button.
566 // Set $comment_div state - showing or not showing form and Add button.
567 // An Add button is shown on non-empty forms when no form is shown.
567 // An Add button is shown on non-empty forms when no form is shown.
568 // The form is controlled by show_form_opt - if undefined, form is only shown for general comments.
568 // The form is controlled by show_form_opt - if undefined, form is only shown for general comments.
569 function comment_div_state($comment_div, f_path, line_no, show_form_opt) {
569 function comment_div_state($comment_div, f_path, line_no, show_form_opt) {
570 var show_form = show_form_opt !== undefined ? show_form_opt : !f_path && !line_no;
570 var show_form = show_form_opt !== undefined ? show_form_opt : !f_path && !line_no;
571 var $forms = $comment_div.children('.comment-inline-form');
571 var $forms = $comment_div.children('.comment-inline-form');
572 var $buttonrow = $comment_div.children('.add-button-row');
572 var $buttonrow = $comment_div.children('.add-button-row');
573 var $comments = $comment_div.children('.comment:not(.submitting)');
573 var $comments = $comment_div.children('.comment:not(.submitting)');
574 $forms.remove();
574 $forms.remove();
575 $buttonrow.remove();
575 $buttonrow.remove();
576 if (show_form) {
576 if (show_form) {
577 _comment_div_append_form($comment_div, f_path, line_no);
577 _comment_div_append_form($comment_div, f_path, line_no);
578 } else if ($comments.length) {
578 } else if ($comments.length) {
579 _comment_div_append_add($comment_div, f_path, line_no);
579 _comment_div_append_add($comment_div, f_path, line_no);
580 } else {
580 } else {
581 $comment_div.parent('tr').remove();
581 $comment_div.parent('tr').remove();
582 }
582 }
583 }
583 }
584
584
585 // append an Add button to $comment_div and hook it up to show form
585 // append an Add button to $comment_div and hook it up to show form
586 function _comment_div_append_add($comment_div, f_path, line_no) {
586 function _comment_div_append_add($comment_div, f_path, line_no) {
587 var addlabel = TRANSLATION_MAP['Add Another Comment'];
587 var addlabel = TRANSLATION_MAP['Add Another Comment'];
588 var $add = $('<div class="add-button-row"><span class="btn btn-default btn-xs add-button">{0}</span></div>'.format(addlabel));
588 var $add = $('<div class="add-button-row"><span class="btn btn-default btn-xs add-button">{0}</span></div>'.format(addlabel));
589 $comment_div.append($add);
589 $comment_div.append($add);
590 $add.children('.add-button').click(function() {
590 $add.children('.add-button').click(function() {
591 comment_div_state($comment_div, f_path, line_no, true);
591 comment_div_state($comment_div, f_path, line_no, true);
592 });
592 });
593 }
593 }
594
594
595 // append a comment form to $comment_div
595 // append a comment form to $comment_div
596 // Note: var AJAX_COMMENT_URL must have been defined before invoking this function
596 // Note: var AJAX_COMMENT_URL must have been defined before invoking this function
597 function _comment_div_append_form($comment_div, f_path, line_no) {
597 function _comment_div_append_form($comment_div, f_path, line_no) {
598 var $form_div = $('#comment-inline-form-template').children()
598 var $form_div = $('#comment-inline-form-template').children()
599 .clone()
599 .clone()
600 .addClass('comment-inline-form');
600 .addClass('comment-inline-form');
601 $comment_div.append($form_div);
601 $comment_div.append($form_div);
602 var $preview = $comment_div.find("div.comment-preview");
602 var $preview = $comment_div.find("div.comment-preview");
603 var $form = $comment_div.find("form");
603 var $form = $comment_div.find("form");
604 var $textarea = $form.find('textarea');
604 var $textarea = $form.find('textarea');
605
605
606 $form.submit(function(e) {
606 $form.submit(function(e) {
607 e.preventDefault();
607 e.preventDefault();
608
608
609 var text = $textarea.val();
609 var text = $textarea.val();
610 var review_status = $form.find('input:radio[name=changeset_status]:checked').val();
610 var review_status = $form.find('input:radio[name=changeset_status]:checked').val();
611 var pr_close = $form.find('input:checkbox[name=save_close]:checked').length ? 'on' : '';
611 var pr_close = $form.find('input:checkbox[name=save_close]:checked').length ? 'on' : '';
612 var pr_delete = $form.find('input:checkbox[name=save_delete]:checked').length ? 'delete' : '';
612 var pr_delete = $form.find('input:checkbox[name=save_delete]:checked').length ? 'delete' : '';
613
613
614 if (!text && !review_status && !pr_close && !pr_delete) {
614 if (!text && !review_status && !pr_close && !pr_delete) {
615 alert("Please provide a comment");
615 alert("Please provide a comment");
616 return false;
616 return false;
617 }
617 }
618
618
619 if (pr_delete) {
619 if (pr_delete) {
620 if (text || review_status || pr_close) {
620 if (text || review_status || pr_close) {
621 alert('Cannot delete pull request while making other changes');
621 alert('Cannot delete pull request while making other changes');
622 return false;
622 return false;
623 }
623 }
624 if (!confirm('Confirm to delete this pull request')) {
624 if (!confirm('Confirm to delete this pull request')) {
625 return false;
625 return false;
626 }
626 }
627 var comments = $('.comment').length;
627 var comments = $('.comment').length;
628 if (comments > 0 &&
628 if (comments > 0 &&
629 !confirm('Confirm again to delete this pull request with {0} comments'.format(comments))) {
629 !confirm('Confirm again to delete this pull request with {0} comments'.format(comments))) {
630 return false;
630 return false;
631 }
631 }
632 }
632 }
633
633
634 if (review_status) {
634 if (review_status) {
635 var $review_status = $preview.find('.automatic-comment');
635 var $review_status = $preview.find('.automatic-comment');
636 var review_status_lbl = $("#comment-inline-form-template input.status_change_radio[value='" + review_status + "']").parent().text().strip();
636 var review_status_lbl = $("#comment-inline-form-template input.status_change_radio[value='" + review_status + "']").parent().text().strip();
637 $review_status.find('.comment-status-label').text(review_status_lbl);
637 $review_status.find('.comment-status-label').text(review_status_lbl);
638 $review_status.show();
638 $review_status.show();
639 }
639 }
640 $preview.find('.comment-text div').text(text);
640 $preview.find('.comment-text div').text(text);
641 $preview.show();
641 $preview.show();
642 $textarea.val('');
642 $textarea.val('');
643 if (f_path && line_no) {
643 if (f_path && line_no) {
644 $form.hide();
644 $form.hide();
645 }
645 }
646
646
647 var postData = {
647 var postData = {
648 'text': text,
648 'text': text,
649 'f_path': f_path,
649 'f_path': f_path,
650 'line': line_no,
650 'line': line_no,
651 'changeset_status': review_status,
651 'changeset_status': review_status,
652 'save_close': pr_close,
652 'save_close': pr_close,
653 'save_delete': pr_delete
653 'save_delete': pr_delete
654 };
654 };
655 function success(json_data) {
655 function success(json_data) {
656 if (pr_delete) {
656 if (pr_delete) {
657 location = json_data['location'];
657 location = json_data['location'];
658 } else {
658 } else {
659 $comment_div.append(json_data['rendered_text']);
659 $comment_div.append(json_data['rendered_text']);
660 comment_div_state($comment_div, f_path, line_no);
660 comment_div_state($comment_div, f_path, line_no);
661 linkInlineComments($('.firstlink'), $('.comment:first-child'));
661 linkInlineComments($('.firstlink'), $('.comment:first-child'));
662 if ((review_status || pr_close) && !f_path && !line_no) {
662 if ((review_status || pr_close) && !f_path && !line_no) {
663 // Page changed a lot - reload it after closing the submitted form
663 // Page changed a lot - reload it after closing the submitted form
664 comment_div_state($comment_div, f_path, line_no, false);
664 comment_div_state($comment_div, f_path, line_no, false);
665 location.reload(true);
665 location.reload(true);
666 }
666 }
667 }
667 }
668 }
668 }
669 function failure(x, s, e) {
669 function failure(x, s, e) {
670 $preview.removeClass('submitting').children('.panel').addClass('panel-danger');
670 $preview.removeClass('submitting').children('.panel').addClass('panel-danger');
671 var $status = $preview.find('.comment-submission-status');
671 var $status = $preview.find('.comment-submission-status');
672 $('<span>', {
672 $('<span>', {
673 'title': e,
673 'title': e,
674 text: _TM['Unable to post']
674 text: _TM['Unable to post']
675 }).replaceAll($status.contents());
675 }).replaceAll($status.contents());
676 $('<div>', {
676 $('<div>', {
677 'class': 'btn-group'
677 'class': 'btn-group'
678 }).append(
678 }).append(
679 $('<button>', {
679 $('<button>', {
680 'type': 'button',
680 'class': 'btn btn-default btn-xs',
681 'class': 'btn btn-default btn-xs',
681 text: _TM['Retry']
682 text: _TM['Retry']
682 }).click(function() {
683 }).click(function() {
683 $status.text(_TM['Submitting ...']);
684 $status.text(_TM['Submitting ...']);
684 $preview.addClass('submitting').children('.panel').removeClass('panel-danger');
685 $preview.addClass('submitting').children('.panel').removeClass('panel-danger');
685 ajaxPOST(AJAX_COMMENT_URL, postData, success, failure);
686 ajaxPOST(AJAX_COMMENT_URL, postData, success, failure);
686 }),
687 }),
687 $('<button>', {
688 $('<button>', {
689 'type': 'button',
688 'class': 'btn btn-default btn-xs',
690 'class': 'btn btn-default btn-xs',
689 text: _TM['Cancel']
691 text: _TM['Cancel']
690 }).click(function() {
692 }).click(function() {
691 comment_div_state($comment_div, f_path, line_no);
693 comment_div_state($comment_div, f_path, line_no);
692 })
694 })
693 ).appendTo($status);
695 ).appendTo($status);
694 }
696 }
695 ajaxPOST(AJAX_COMMENT_URL, postData, success, failure);
697 ajaxPOST(AJAX_COMMENT_URL, postData, success, failure);
696 });
698 });
697
699
698 // add event handler for hide/cancel buttons
700 // add event handler for hide/cancel buttons
699 $form.find('.hide-inline-form').click(function() {
701 $form.find('.hide-inline-form').click(function() {
700 comment_div_state($comment_div, f_path, line_no);
702 comment_div_state($comment_div, f_path, line_no);
701 });
703 });
702
704
703 tooltip_activate();
705 tooltip_activate();
704 if ($textarea.length > 0) {
706 if ($textarea.length > 0) {
705 MentionsAutoComplete($textarea);
707 MentionsAutoComplete($textarea);
706 }
708 }
707 if (f_path) {
709 if (f_path) {
708 $textarea.focus();
710 $textarea.focus();
709 }
711 }
710 }
712 }
711
713
712
714
713 // Note: var AJAX_COMMENT_URL must have been defined before invoking this function
715 // Note: var AJAX_COMMENT_URL must have been defined before invoking this function
714 function deleteComment(comment_id) {
716 function deleteComment(comment_id) {
715 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
717 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
716 var postData = {};
718 var postData = {};
717 function success() {
719 function success() {
718 $('#comment-'+comment_id).remove();
720 $('#comment-'+comment_id).remove();
719 // Ignore that this might leave a stray Add button (or have a pending form with another comment) ...
721 // Ignore that this might leave a stray Add button (or have a pending form with another comment) ...
720 }
722 }
721 ajaxPOST(url, postData, success);
723 ajaxPOST(url, postData, success);
722 }
724 }
723
725
724
726
725 /**
727 /**
726 * Double link comments
728 * Double link comments
727 */
729 */
728 function linkInlineComments($firstlinks, $comments){
730 function linkInlineComments($firstlinks, $comments){
729 if ($comments.length > 0) {
731 if ($comments.length > 0) {
730 $firstlinks.html('<a href="#{0}">First comment</a>'.format($comments.prop('id')));
732 $firstlinks.html('<a href="#{0}">First comment</a>'.format($comments.prop('id')));
731 }
733 }
732 if ($comments.length <= 1) {
734 if ($comments.length <= 1) {
733 return;
735 return;
734 }
736 }
735
737
736 $comments.each(function(i){
738 $comments.each(function(i){
737 var prev = '';
739 var prev = '';
738 if (i > 0){
740 if (i > 0){
739 var prev_anchor = $($comments.get(i-1)).prop('id');
741 var prev_anchor = $($comments.get(i-1)).prop('id');
740 prev = '<a href="#{0}">Previous comment</a>'.format(prev_anchor);
742 prev = '<a href="#{0}">Previous comment</a>'.format(prev_anchor);
741 }
743 }
742 var next = '';
744 var next = '';
743 if (i+1 < $comments.length){
745 if (i+1 < $comments.length){
744 var next_anchor = $($comments.get(i+1)).prop('id');
746 var next_anchor = $($comments.get(i+1)).prop('id');
745 next = '<a href="#{0}">Next comment</a>'.format(next_anchor);
747 next = '<a href="#{0}">Next comment</a>'.format(next_anchor);
746 }
748 }
747 $(this).find('.comment-prev-next-links').html(
749 $(this).find('.comment-prev-next-links').html(
748 '<div class="prev-comment">{0}</div>'.format(prev) +
750 '<div class="prev-comment">{0}</div>'.format(prev) +
749 '<div class="next-comment">{0}</div>'.format(next));
751 '<div class="next-comment">{0}</div>'.format(next));
750 });
752 });
751 }
753 }
752
754
753 /* activate files.html stuff */
755 /* activate files.html stuff */
754 function fileBrowserListeners(node_list_url, url_base){
756 function fileBrowserListeners(node_list_url, url_base){
755 var $node_filter = $('#node_filter');
757 var $node_filter = $('#node_filter');
756
758
757 var filterTimeout = null;
759 var filterTimeout = null;
758 var nodes = null;
760 var nodes = null;
759
761
760 function initFilter(){
762 function initFilter(){
761 $('#node_filter_box_loading').show();
763 $('#node_filter_box_loading').show();
762 $('#search_activate_id').hide();
764 $('#search_activate_id').hide();
763 $('#add_node_id').hide();
765 $('#add_node_id').hide();
764 $.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
766 $.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
765 .done(function(json) {
767 .done(function(json) {
766 nodes = json.nodes;
768 nodes = json.nodes;
767 $('#node_filter_box_loading').hide();
769 $('#node_filter_box_loading').hide();
768 $('#node_filter_box').show();
770 $('#node_filter_box').show();
769 $node_filter.focus();
771 $node_filter.focus();
770 if($node_filter.hasClass('init')){
772 if($node_filter.hasClass('init')){
771 $node_filter.val('');
773 $node_filter.val('');
772 $node_filter.removeClass('init');
774 $node_filter.removeClass('init');
773 }
775 }
774 })
776 })
775 .fail(function() {
777 .fail(function() {
776 console.log('fileBrowserListeners initFilter failed to load');
778 console.log('fileBrowserListeners initFilter failed to load');
777 })
779 })
778 ;
780 ;
779 }
781 }
780
782
781 function updateFilter(e) {
783 function updateFilter(e) {
782 return function(){
784 return function(){
783 // Reset timeout
785 // Reset timeout
784 filterTimeout = null;
786 filterTimeout = null;
785 var query = e.currentTarget.value.toLowerCase();
787 var query = e.currentTarget.value.toLowerCase();
786 var match = [];
788 var match = [];
787 var matches = 0;
789 var matches = 0;
788 var matches_max = 20;
790 var matches_max = 20;
789 if (query != ""){
791 if (query != ""){
790 for(var i=0;i<nodes.length;i++){
792 for(var i=0;i<nodes.length;i++){
791 var pos = nodes[i].name.toLowerCase().indexOf(query);
793 var pos = nodes[i].name.toLowerCase().indexOf(query);
792 if(query && pos != -1){
794 if(query && pos != -1){
793 matches++
795 matches++
794 //show only certain amount to not kill browser
796 //show only certain amount to not kill browser
795 if (matches > matches_max){
797 if (matches > matches_max){
796 break;
798 break;
797 }
799 }
798
800
799 var n = nodes[i].name;
801 var n = nodes[i].name;
800 var t = nodes[i].type;
802 var t = nodes[i].type;
801 var n_hl = n.substring(0,pos)
803 var n_hl = n.substring(0,pos)
802 + "<b>{0}</b>".format(n.substring(pos,pos+query.length))
804 + "<b>{0}</b>".format(n.substring(pos,pos+query.length))
803 + n.substring(pos+query.length);
805 + n.substring(pos+query.length);
804 var new_url = url_base.replace('__FPATH__',n);
806 var new_url = url_base.replace('__FPATH__',n);
805 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
807 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
806 }
808 }
807 if(match.length >= matches_max){
809 if(match.length >= matches_max){
808 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
810 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
809 break;
811 break;
810 }
812 }
811 }
813 }
812 }
814 }
813 if(query != ""){
815 if(query != ""){
814 $('#tbody').hide();
816 $('#tbody').hide();
815 $('#tbody_filtered').show();
817 $('#tbody_filtered').show();
816
818
817 if (match.length==0){
819 if (match.length==0){
818 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
820 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
819 }
821 }
820
822
821 $('#tbody_filtered').html(match.join(""));
823 $('#tbody_filtered').html(match.join(""));
822 }
824 }
823 else{
825 else{
824 $('#tbody').show();
826 $('#tbody').show();
825 $('#tbody_filtered').hide();
827 $('#tbody_filtered').hide();
826 }
828 }
827 }
829 }
828 }
830 }
829
831
830 $('#filter_activate').click(function(){
832 $('#filter_activate').click(function(){
831 initFilter();
833 initFilter();
832 });
834 });
833 $node_filter.click(function(){
835 $node_filter.click(function(){
834 if($node_filter.hasClass('init')){
836 if($node_filter.hasClass('init')){
835 $node_filter.val('');
837 $node_filter.val('');
836 $node_filter.removeClass('init');
838 $node_filter.removeClass('init');
837 }
839 }
838 });
840 });
839 $node_filter.keyup(function(e){
841 $node_filter.keyup(function(e){
840 clearTimeout(filterTimeout);
842 clearTimeout(filterTimeout);
841 filterTimeout = setTimeout(updateFilter(e),600);
843 filterTimeout = setTimeout(updateFilter(e),600);
842 });
844 });
843 }
845 }
844
846
845
847
846 function initCodeMirror(textarea_id, baseUrl, resetUrl){
848 function initCodeMirror(textarea_id, baseUrl, resetUrl){
847 var myCodeMirror = CodeMirror.fromTextArea($('#' + textarea_id)[0], {
849 var myCodeMirror = CodeMirror.fromTextArea($('#' + textarea_id)[0], {
848 mode: "null",
850 mode: "null",
849 lineNumbers: true,
851 lineNumbers: true,
850 indentUnit: 4,
852 indentUnit: 4,
851 autofocus: true
853 autofocus: true
852 });
854 });
853 CodeMirror.modeURL = baseUrl + "/codemirror/mode/%N/%N.js";
855 CodeMirror.modeURL = baseUrl + "/codemirror/mode/%N/%N.js";
854
856
855 $('#reset').click(function(){
857 $('#reset').click(function(){
856 window.location=resetUrl;
858 window.location=resetUrl;
857 });
859 });
858
860
859 $('#file_enable').click(function(){
861 $('#file_enable').click(function(){
860 $('#upload_file_container').hide();
862 $('#upload_file_container').hide();
861 $('#filename_container').show();
863 $('#filename_container').show();
862 $('#body').show();
864 $('#body').show();
863 });
865 });
864
866
865 $('#upload_file_enable').click(function(){
867 $('#upload_file_enable').click(function(){
866 $('#upload_file_container').show();
868 $('#upload_file_container').show();
867 $('#filename_container').hide();
869 $('#filename_container').hide();
868 $('#body').hide();
870 $('#body').hide();
869 });
871 });
870
872
871 return myCodeMirror
873 return myCodeMirror
872 }
874 }
873
875
874 function setCodeMirrorMode(codeMirrorInstance, mode) {
876 function setCodeMirrorMode(codeMirrorInstance, mode) {
875 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
877 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
876 }
878 }
877
879
878
880
879 function _getIdentNode(n){
881 function _getIdentNode(n){
880 //iterate thrugh nodes until matching interesting node
882 //iterate thrugh nodes until matching interesting node
881
883
882 if (typeof n == 'undefined'){
884 if (typeof n == 'undefined'){
883 return -1
885 return -1
884 }
886 }
885
887
886 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
888 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
887 return n
889 return n
888 }
890 }
889 else{
891 else{
890 return _getIdentNode(n.parentNode);
892 return _getIdentNode(n.parentNode);
891 }
893 }
892 }
894 }
893
895
894 /* generate links for multi line selects that can be shown by files.html page_highlights.
896 /* generate links for multi line selects that can be shown by files.html page_highlights.
895 * This is a mouseup handler for hlcode from CodeHtmlFormatter and pygmentize */
897 * This is a mouseup handler for hlcode from CodeHtmlFormatter and pygmentize */
896 function getSelectionLink() {
898 function getSelectionLink() {
897 //get selection from start/to nodes
899 //get selection from start/to nodes
898 if (typeof window.getSelection != "undefined") {
900 if (typeof window.getSelection != "undefined") {
899 var s = window.getSelection();
901 var s = window.getSelection();
900
902
901 var from = _getIdentNode(s.anchorNode);
903 var from = _getIdentNode(s.anchorNode);
902 var till = _getIdentNode(s.focusNode);
904 var till = _getIdentNode(s.focusNode);
903
905
904 //var f_int = parseInt(from.id.replace('L',''));
906 //var f_int = parseInt(from.id.replace('L',''));
905 //var t_int = parseInt(till.id.replace('L',''));
907 //var t_int = parseInt(till.id.replace('L',''));
906
908
907 var yoffset = 35;
909 var yoffset = 35;
908 var ranges = [parseInt(from.id.replace('L','')), parseInt(till.id.replace('L',''))];
910 var ranges = [parseInt(from.id.replace('L','')), parseInt(till.id.replace('L',''))];
909 if (ranges[0] > ranges[1]){
911 if (ranges[0] > ranges[1]){
910 //highlight from bottom
912 //highlight from bottom
911 yoffset = -yoffset;
913 yoffset = -yoffset;
912 ranges = [ranges[1], ranges[0]];
914 ranges = [ranges[1], ranges[0]];
913 }
915 }
914 var $hl_div = $('div#linktt');
916 var $hl_div = $('div#linktt');
915 // if we select more than 2 lines
917 // if we select more than 2 lines
916 if (ranges[0] != ranges[1]){
918 if (ranges[0] != ranges[1]){
917 if ($hl_div.length) {
919 if ($hl_div.length) {
918 $hl_div.html('');
920 $hl_div.html('');
919 } else {
921 } else {
920 $hl_div = $('<div id="linktt" class="hl-tip-box">');
922 $hl_div = $('<div id="linktt" class="hl-tip-box">');
921 $('body').prepend($hl_div);
923 $('body').prepend($hl_div);
922 }
924 }
923
925
924 $hl_div.append($('<a>').html(_TM['Selection Link']).prop('href', location.href.substring(0, location.href.indexOf('#')) + '#L' + ranges[0] + '-'+ranges[1]));
926 $hl_div.append($('<a>').html(_TM['Selection Link']).prop('href', location.href.substring(0, location.href.indexOf('#')) + '#L' + ranges[0] + '-'+ranges[1]));
925 var xy = $(till).offset();
927 var xy = $(till).offset();
926 $hl_div.css('top', (xy.top + yoffset) + 'px').css('left', xy.left + 'px');
928 $hl_div.css('top', (xy.top + yoffset) + 'px').css('left', xy.left + 'px');
927 $hl_div.show();
929 $hl_div.show();
928 }
930 }
929 else{
931 else{
930 $hl_div.hide();
932 $hl_div.hide();
931 }
933 }
932 }
934 }
933 }
935 }
934
936
935 /**
937 /**
936 * Autocomplete functionality
938 * Autocomplete functionality
937 */
939 */
938
940
939 // Highlight the snippet if it is found in the full text, while escaping any existing markup.
941 // Highlight the snippet if it is found in the full text, while escaping any existing markup.
940 // Snippet must be lowercased already.
942 // Snippet must be lowercased already.
941 function autocompleteHighlightMatch(full, snippet) {
943 function autocompleteHighlightMatch(full, snippet) {
942 var matchindex = full.toLowerCase().indexOf(snippet);
944 var matchindex = full.toLowerCase().indexOf(snippet);
943 if (matchindex <0)
945 if (matchindex <0)
944 return full.html_escape();
946 return full.html_escape();
945 return full.substring(0, matchindex).html_escape()
947 return full.substring(0, matchindex).html_escape()
946 + '<span class="select2-match">'
948 + '<span class="select2-match">'
947 + full.substr(matchindex, snippet.length).html_escape()
949 + full.substr(matchindex, snippet.length).html_escape()
948 + '</span>'
950 + '</span>'
949 + full.substring(matchindex + snippet.length).html_escape();
951 + full.substring(matchindex + snippet.length).html_escape();
950 }
952 }
951
953
952 // Return html snippet for showing the provided gravatar url
954 // Return html snippet for showing the provided gravatar url
953 function gravatar(gravatar_lnk, size, cssclass) {
955 function gravatar(gravatar_lnk, size, cssclass) {
954 if (!gravatar_lnk) {
956 if (!gravatar_lnk) {
955 return '';
957 return '';
956 }
958 }
957 if (gravatar_lnk == 'default') {
959 if (gravatar_lnk == 'default') {
958 return '<i class="icon-user {1}" style="font-size: {0}px;"></i>'.format(size, cssclass);
960 return '<i class="icon-user {1}" style="font-size: {0}px;"></i>'.format(size, cssclass);
959 }
961 }
960 return ('<i class="icon-gravatar {2}"' +
962 return ('<i class="icon-gravatar {2}"' +
961 ' style="font-size: {0}px;background-image: url(\'{1}\'); background-size: {0}px"' +
963 ' style="font-size: {0}px;background-image: url(\'{1}\'); background-size: {0}px"' +
962 '></i>').format(size, gravatar_lnk, cssclass);
964 '></i>').format(size, gravatar_lnk, cssclass);
963 }
965 }
964
966
965 function autocompleteGravatar(res, gravatar_lnk, size, group) {
967 function autocompleteGravatar(res, gravatar_lnk, size, group) {
966 var elem;
968 var elem;
967 if (group !== undefined) {
969 if (group !== undefined) {
968 elem = '<i class="perm-gravatar-ac icon-users"></i>';
970 elem = '<i class="perm-gravatar-ac icon-users"></i>';
969 } else {
971 } else {
970 elem = gravatar(gravatar_lnk, size, "perm-gravatar-ac");
972 elem = gravatar(gravatar_lnk, size, "perm-gravatar-ac");
971 }
973 }
972 return '<div class="ac-container-wrap">{0}{1}</div>'.format(elem, res);
974 return '<div class="ac-container-wrap">{0}{1}</div>'.format(elem, res);
973 }
975 }
974
976
975 // Custom formatter to highlight the matching letters and do HTML escaping
977 // Custom formatter to highlight the matching letters and do HTML escaping
976 function autocompleteFormatter(oResultData, sQuery, sResultMatch) {
978 function autocompleteFormatter(oResultData, sQuery, sResultMatch) {
977 var query;
979 var query;
978 if (sQuery && sQuery.toLowerCase) // YAHOO AutoComplete
980 if (sQuery && sQuery.toLowerCase) // YAHOO AutoComplete
979 query = sQuery.toLowerCase();
981 query = sQuery.toLowerCase();
980 else if (sResultMatch && sResultMatch.term) // select2 - parameter names doesn't match
982 else if (sResultMatch && sResultMatch.term) // select2 - parameter names doesn't match
981 query = sResultMatch.term.toLowerCase();
983 query = sResultMatch.term.toLowerCase();
982
984
983 // group
985 // group
984 if (oResultData.type == "group") {
986 if (oResultData.type == "group") {
985 return autocompleteGravatar(
987 return autocompleteGravatar(
986 "{0}: {1}".format(
988 "{0}: {1}".format(
987 _TM['Group'],
989 _TM['Group'],
988 autocompleteHighlightMatch(oResultData.grname, query)),
990 autocompleteHighlightMatch(oResultData.grname, query)),
989 null, null, true);
991 null, null, true);
990 }
992 }
991
993
992 // users
994 // users
993 if (oResultData.nname) {
995 if (oResultData.nname) {
994 var displayname = autocompleteHighlightMatch(oResultData.nname, query);
996 var displayname = autocompleteHighlightMatch(oResultData.nname, query);
995 if (oResultData.fname && oResultData.lname) {
997 if (oResultData.fname && oResultData.lname) {
996 displayname = "{0} {1} ({2})".format(
998 displayname = "{0} {1} ({2})".format(
997 autocompleteHighlightMatch(oResultData.fname, query),
999 autocompleteHighlightMatch(oResultData.fname, query),
998 autocompleteHighlightMatch(oResultData.lname, query),
1000 autocompleteHighlightMatch(oResultData.lname, query),
999 displayname);
1001 displayname);
1000 }
1002 }
1001
1003
1002 return autocompleteGravatar(displayname, oResultData.gravatar_lnk, oResultData.gravatar_size);
1004 return autocompleteGravatar(displayname, oResultData.gravatar_lnk, oResultData.gravatar_size);
1003 }
1005 }
1004
1006
1005 return '';
1007 return '';
1006 }
1008 }
1007
1009
1008 function SimpleUserAutoComplete($inputElement) {
1010 function SimpleUserAutoComplete($inputElement) {
1009 $inputElement.select2({
1011 $inputElement.select2({
1010 formatInputTooShort: $inputElement.attr('placeholder'),
1012 formatInputTooShort: $inputElement.attr('placeholder'),
1011 initSelection : function (element, callback) {
1013 initSelection : function (element, callback) {
1012 $.ajax({
1014 $.ajax({
1013 url: pyroutes.url('users_and_groups_data'),
1015 url: pyroutes.url('users_and_groups_data'),
1014 dataType: 'json',
1016 dataType: 'json',
1015 data: {
1017 data: {
1016 key: element.val()
1018 key: element.val()
1017 },
1019 },
1018 success: function(data){
1020 success: function(data){
1019 callback(data.results[0]);
1021 callback(data.results[0]);
1020 }
1022 }
1021 });
1023 });
1022 },
1024 },
1023 minimumInputLength: 1,
1025 minimumInputLength: 1,
1024 ajax: {
1026 ajax: {
1025 url: pyroutes.url('users_and_groups_data'),
1027 url: pyroutes.url('users_and_groups_data'),
1026 dataType: 'json',
1028 dataType: 'json',
1027 data: function(term){
1029 data: function(term){
1028 return {
1030 return {
1029 query: term
1031 query: term
1030 };
1032 };
1031 },
1033 },
1032 results: function (data){
1034 results: function (data){
1033 return data;
1035 return data;
1034 },
1036 },
1035 cache: true
1037 cache: true
1036 },
1038 },
1037 formatSelection: autocompleteFormatter,
1039 formatSelection: autocompleteFormatter,
1038 formatResult: autocompleteFormatter,
1040 formatResult: autocompleteFormatter,
1039 id: function(item) { return item.nname; },
1041 id: function(item) { return item.nname; },
1040 });
1042 });
1041 }
1043 }
1042
1044
1043 function MembersAutoComplete($inputElement, $typeElement) {
1045 function MembersAutoComplete($inputElement, $typeElement) {
1044
1046
1045 $inputElement.select2({
1047 $inputElement.select2({
1046 placeholder: $inputElement.attr('placeholder'),
1048 placeholder: $inputElement.attr('placeholder'),
1047 minimumInputLength: 1,
1049 minimumInputLength: 1,
1048 ajax: {
1050 ajax: {
1049 url: pyroutes.url('users_and_groups_data'),
1051 url: pyroutes.url('users_and_groups_data'),
1050 dataType: 'json',
1052 dataType: 'json',
1051 data: function(term){
1053 data: function(term){
1052 return {
1054 return {
1053 query: term,
1055 query: term,
1054 types: 'users,groups'
1056 types: 'users,groups'
1055 };
1057 };
1056 },
1058 },
1057 results: function (data){
1059 results: function (data){
1058 return data;
1060 return data;
1059 },
1061 },
1060 cache: true
1062 cache: true
1061 },
1063 },
1062 formatSelection: autocompleteFormatter,
1064 formatSelection: autocompleteFormatter,
1063 formatResult: autocompleteFormatter,
1065 formatResult: autocompleteFormatter,
1064 id: function(item) { return item.type == 'user' ? item.nname : item.grname },
1066 id: function(item) { return item.type == 'user' ? item.nname : item.grname },
1065 }).on("select2-selecting", function(e) {
1067 }).on("select2-selecting", function(e) {
1066 // e.choice.id is automatically used as selection value - just set the type of the selection
1068 // e.choice.id is automatically used as selection value - just set the type of the selection
1067 $typeElement.val(e.choice.type);
1069 $typeElement.val(e.choice.type);
1068 });
1070 });
1069 }
1071 }
1070
1072
1071 function MentionsAutoComplete($inputElement) {
1073 function MentionsAutoComplete($inputElement) {
1072 $inputElement.atwho({
1074 $inputElement.atwho({
1073 at: "@",
1075 at: "@",
1074 callbacks: {
1076 callbacks: {
1075 remoteFilter: function(query, callback) {
1077 remoteFilter: function(query, callback) {
1076 $.getJSON(
1078 $.getJSON(
1077 pyroutes.url('users_and_groups_data'),
1079 pyroutes.url('users_and_groups_data'),
1078 {
1080 {
1079 query: query,
1081 query: query,
1080 types: 'users'
1082 types: 'users'
1081 },
1083 },
1082 function(data) {
1084 function(data) {
1083 callback(data.results)
1085 callback(data.results)
1084 }
1086 }
1085 );
1087 );
1086 },
1088 },
1087 sorter: function(query, items) {
1089 sorter: function(query, items) {
1088 return items;
1090 return items;
1089 }
1091 }
1090 },
1092 },
1091 displayTpl: function(item) {
1093 displayTpl: function(item) {
1092 return "<li>" +
1094 return "<li>" +
1093 autocompleteGravatar(
1095 autocompleteGravatar(
1094 "{0} {1} ({2})".format(item.fname, item.lname, item.nname).html_escape(),
1096 "{0} {1} ({2})".format(item.fname, item.lname, item.nname).html_escape(),
1095 '${gravatar_lnk}', 16) +
1097 '${gravatar_lnk}', 16) +
1096 "</li>";
1098 "</li>";
1097 },
1099 },
1098 insertTpl: "${atwho-at}${nname}"
1100 insertTpl: "${atwho-at}${nname}"
1099 });
1101 });
1100 }
1102 }
1101
1103
1102
1104
1103 function addReviewMember(id,fname,lname,nname,gravatar_link,gravatar_size){
1105 function addReviewMember(id,fname,lname,nname,gravatar_link,gravatar_size){
1104 var displayname = nname;
1106 var displayname = nname;
1105 if ((fname != "") && (lname != "")) {
1107 if ((fname != "") && (lname != "")) {
1106 displayname = "{0} {1} ({2})".format(fname, lname, nname);
1108 displayname = "{0} {1} ({2})".format(fname, lname, nname);
1107 }
1109 }
1108 var gravatarelm = gravatar(gravatar_link, gravatar_size, "");
1110 var gravatarelm = gravatar(gravatar_link, gravatar_size, "");
1109 // WARNING: the HTML below is duplicate with
1111 // WARNING: the HTML below is duplicate with
1110 // kallithea/templates/pullrequests/pullrequest_show.html
1112 // kallithea/templates/pullrequests/pullrequest_show.html
1111 // If you change something here it should be reflected in the template too.
1113 // If you change something here it should be reflected in the template too.
1112 var element = (
1114 var element = (
1113 ' <li id="reviewer_{2}">\n'+
1115 ' <li id="reviewer_{2}">\n'+
1114 ' <span class="reviewers_member">\n'+
1116 ' <span class="reviewers_member">\n'+
1115 ' <input type="hidden" value="{2}" name="review_members" />\n'+
1117 ' <input type="hidden" value="{2}" name="review_members" />\n'+
1116 ' <span class="reviewer_status" data-toggle="tooltip" title="not_reviewed">\n'+
1118 ' <span class="reviewer_status" data-toggle="tooltip" title="not_reviewed">\n'+
1117 ' <i class="icon-circle changeset-status-not_reviewed"></i>\n'+
1119 ' <i class="icon-circle changeset-status-not_reviewed"></i>\n'+
1118 ' </span>\n'+
1120 ' </span>\n'+
1119 (gravatarelm ?
1121 (gravatarelm ?
1120 ' {0}\n' :
1122 ' {0}\n' :
1121 '')+
1123 '')+
1122 ' <span>{1}</span>\n'+
1124 ' <span>{1}</span>\n'+
1123 ' <a href="#" class="reviewer_member_remove" onclick="removeReviewMember({2})">\n'+
1125 ' <a href="#" class="reviewer_member_remove" onclick="removeReviewMember({2})">\n'+
1124 ' <i class="icon-minus-circled"></i>\n'+
1126 ' <i class="icon-minus-circled"></i>\n'+
1125 ' </a> (add not saved)\n'+
1127 ' </a> (add not saved)\n'+
1126 ' </span>\n'+
1128 ' </span>\n'+
1127 ' </li>\n'
1129 ' </li>\n'
1128 ).format(gravatarelm, displayname.html_escape(), id);
1130 ).format(gravatarelm, displayname.html_escape(), id);
1129 // check if we don't have this ID already in
1131 // check if we don't have this ID already in
1130 var ids = [];
1132 var ids = [];
1131 $('#review_members').find('li').each(function() {
1133 $('#review_members').find('li').each(function() {
1132 ids.push(this.id);
1134 ids.push(this.id);
1133 });
1135 });
1134 if(ids.indexOf('reviewer_'+id) == -1){
1136 if(ids.indexOf('reviewer_'+id) == -1){
1135 //only add if it's not there
1137 //only add if it's not there
1136 $('#review_members').append(element);
1138 $('#review_members').append(element);
1137 }
1139 }
1138 }
1140 }
1139
1141
1140 function removeReviewMember(reviewer_id){
1142 function removeReviewMember(reviewer_id){
1141 var $li = $('#reviewer_{0}'.format(reviewer_id));
1143 var $li = $('#reviewer_{0}'.format(reviewer_id));
1142 $li.find('div div').css("text-decoration", "line-through");
1144 $li.find('div div').css("text-decoration", "line-through");
1143 $li.find('input').prop('name', 'review_members_removed');
1145 $li.find('input').prop('name', 'review_members_removed');
1144 $li.find('.reviewer_member_remove').replaceWith('&nbsp;(remove not saved)');
1146 $li.find('.reviewer_member_remove').replaceWith('&nbsp;(remove not saved)');
1145 }
1147 }
1146
1148
1147 /* activate auto completion of users as PR reviewers */
1149 /* activate auto completion of users as PR reviewers */
1148 function PullRequestAutoComplete($inputElement) {
1150 function PullRequestAutoComplete($inputElement) {
1149 $inputElement.select2(
1151 $inputElement.select2(
1150 {
1152 {
1151 placeholder: $inputElement.attr('placeholder'),
1153 placeholder: $inputElement.attr('placeholder'),
1152 minimumInputLength: 1,
1154 minimumInputLength: 1,
1153 ajax: {
1155 ajax: {
1154 url: pyroutes.url('users_and_groups_data'),
1156 url: pyroutes.url('users_and_groups_data'),
1155 dataType: 'json',
1157 dataType: 'json',
1156 data: function(term){
1158 data: function(term){
1157 return {
1159 return {
1158 query: term
1160 query: term
1159 };
1161 };
1160 },
1162 },
1161 results: function (data){
1163 results: function (data){
1162 return data;
1164 return data;
1163 },
1165 },
1164 cache: true
1166 cache: true
1165 },
1167 },
1166 formatSelection: autocompleteFormatter,
1168 formatSelection: autocompleteFormatter,
1167 formatResult: autocompleteFormatter,
1169 formatResult: autocompleteFormatter,
1168 }).on("select2-selecting", function(e) {
1170 }).on("select2-selecting", function(e) {
1169 addReviewMember(e.choice.id, e.choice.fname, e.choice.lname, e.choice.nname,
1171 addReviewMember(e.choice.id, e.choice.fname, e.choice.lname, e.choice.nname,
1170 e.choice.gravatar_lnk, e.choice.gravatar_size);
1172 e.choice.gravatar_lnk, e.choice.gravatar_size);
1171 $inputElement.select2("close");
1173 $inputElement.select2("close");
1172 e.preventDefault();
1174 e.preventDefault();
1173 });
1175 });
1174 }
1176 }
1175
1177
1176
1178
1177 function addPermAction(perm_type) {
1179 function addPermAction(perm_type) {
1178 var template =
1180 var template =
1179 '<td><input type="radio" value="{1}.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1181 '<td><input type="radio" value="{1}.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1180 '<td><input type="radio" value="{1}.read" checked="checked" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1182 '<td><input type="radio" value="{1}.read" checked="checked" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1181 '<td><input type="radio" value="{1}.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1183 '<td><input type="radio" value="{1}.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1182 '<td><input type="radio" value="{1}.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1184 '<td><input type="radio" value="{1}.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td>' +
1183 '<td>' +
1185 '<td>' +
1184 '<input class="form-control" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text" placeholder="{2}">' +
1186 '<input class="form-control" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text" placeholder="{2}">' +
1185 '<input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden">' +
1187 '<input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden">' +
1186 '</td>' +
1188 '</td>' +
1187 '<td></td>';
1189 '<td></td>';
1188 var $last_node = $('.last_new_member').last(); // empty tr between last and add
1190 var $last_node = $('.last_new_member').last(); // empty tr between last and add
1189 var next_id = $('.new_members').length;
1191 var next_id = $('.new_members').length;
1190 $last_node.before($('<tr class="new_members">').append(template.format(next_id, perm_type, _TM['Type name of user or member to grant permission'])));
1192 $last_node.before($('<tr class="new_members">').append(template.format(next_id, perm_type, _TM['Type name of user or member to grant permission'])));
1191 MembersAutoComplete($("#perm_new_member_name_"+next_id), $("#perm_new_member_type_"+next_id));
1193 MembersAutoComplete($("#perm_new_member_name_"+next_id), $("#perm_new_member_type_"+next_id));
1192 }
1194 }
1193
1195
1194 function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) {
1196 function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) {
1195 function success() {
1197 function success() {
1196 $('#' + field_id).remove();
1198 $('#' + field_id).remove();
1197 }
1199 }
1198 function failure(o) {
1200 function failure(o) {
1199 alert(_TM['Failed to revoke permission'] + ": " + o.status);
1201 alert(_TM['Failed to revoke permission'] + ": " + o.status);
1200 }
1202 }
1201 var query_params = {};
1203 var query_params = {};
1202 // put extra data into POST
1204 // put extra data into POST
1203 if (extra_data !== undefined && (typeof extra_data === 'object')){
1205 if (extra_data !== undefined && (typeof extra_data === 'object')){
1204 for(var k in extra_data){
1206 for(var k in extra_data){
1205 query_params[k] = extra_data[k];
1207 query_params[k] = extra_data[k];
1206 }
1208 }
1207 }
1209 }
1208
1210
1209 if (obj_type=='user'){
1211 if (obj_type=='user'){
1210 query_params['user_id'] = obj_id;
1212 query_params['user_id'] = obj_id;
1211 query_params['obj_type'] = 'user';
1213 query_params['obj_type'] = 'user';
1212 }
1214 }
1213 else if (obj_type=='user_group'){
1215 else if (obj_type=='user_group'){
1214 query_params['user_group_id'] = obj_id;
1216 query_params['user_group_id'] = obj_id;
1215 query_params['obj_type'] = 'user_group';
1217 query_params['obj_type'] = 'user_group';
1216 }
1218 }
1217
1219
1218 ajaxPOST(url, query_params, success, failure);
1220 ajaxPOST(url, query_params, success, failure);
1219 }
1221 }
1220
1222
1221 /* Multi selectors */
1223 /* Multi selectors */
1222
1224
1223 function MultiSelectWidget(selected_id, available_id, form_id){
1225 function MultiSelectWidget(selected_id, available_id, form_id){
1224 var $availableselect = $('#' + available_id);
1226 var $availableselect = $('#' + available_id);
1225 var $selectedselect = $('#' + selected_id);
1227 var $selectedselect = $('#' + selected_id);
1226
1228
1227 //fill available only with those not in selected
1229 //fill available only with those not in selected
1228 var $selectedoptions = $selectedselect.children('option');
1230 var $selectedoptions = $selectedselect.children('option');
1229 $availableselect.children('option').filter(function(i, e){
1231 $availableselect.children('option').filter(function(i, e){
1230 for(var j = 0, node; node = $selectedoptions[j]; j++){
1232 for(var j = 0, node; node = $selectedoptions[j]; j++){
1231 if(node.value == e.value){
1233 if(node.value == e.value){
1232 return true;
1234 return true;
1233 }
1235 }
1234 }
1236 }
1235 return false;
1237 return false;
1236 }).remove();
1238 }).remove();
1237
1239
1238 $('#add_element').click(function(){
1240 $('#add_element').click(function(){
1239 $selectedselect.append($availableselect.children('option:selected'));
1241 $selectedselect.append($availableselect.children('option:selected'));
1240 });
1242 });
1241 $('#remove_element').click(function(){
1243 $('#remove_element').click(function(){
1242 $availableselect.append($selectedselect.children('option:selected'));
1244 $availableselect.append($selectedselect.children('option:selected'));
1243 });
1245 });
1244
1246
1245 $('#'+form_id).submit(function(){
1247 $('#'+form_id).submit(function(){
1246 $selectedselect.children('option').each(function(i, e){
1248 $selectedselect.children('option').each(function(i, e){
1247 e.selected = 'selected';
1249 e.selected = 'selected';
1248 });
1250 });
1249 });
1251 });
1250 }
1252 }
1251
1253
1252
1254
1253 /**
1255 /**
1254 Branch Sorting callback for select2, modifying the filtered result so prefix
1256 Branch Sorting callback for select2, modifying the filtered result so prefix
1255 matches come before matches in the line.
1257 matches come before matches in the line.
1256 **/
1258 **/
1257 function branchSort(results, container, query) {
1259 function branchSort(results, container, query) {
1258 if (query.term) {
1260 if (query.term) {
1259 return results.sort(function (a, b) {
1261 return results.sort(function (a, b) {
1260 // Put closed branches after open ones (a bit of a hack ...)
1262 // Put closed branches after open ones (a bit of a hack ...)
1261 var aClosed = a.text.indexOf("(closed)") > -1,
1263 var aClosed = a.text.indexOf("(closed)") > -1,
1262 bClosed = b.text.indexOf("(closed)") > -1;
1264 bClosed = b.text.indexOf("(closed)") > -1;
1263 if (aClosed && !bClosed) {
1265 if (aClosed && !bClosed) {
1264 return 1;
1266 return 1;
1265 }
1267 }
1266 if (bClosed && !aClosed) {
1268 if (bClosed && !aClosed) {
1267 return -1;
1269 return -1;
1268 }
1270 }
1269
1271
1270 // Put early (especially prefix) matches before later matches
1272 // Put early (especially prefix) matches before later matches
1271 var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
1273 var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
1272 bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
1274 bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
1273 if (aPos < bPos) {
1275 if (aPos < bPos) {
1274 return -1;
1276 return -1;
1275 }
1277 }
1276 if (bPos < aPos) {
1278 if (bPos < aPos) {
1277 return 1;
1279 return 1;
1278 }
1280 }
1279
1281
1280 // Default sorting
1282 // Default sorting
1281 if (a.text > b.text) {
1283 if (a.text > b.text) {
1282 return 1;
1284 return 1;
1283 }
1285 }
1284 if (a.text < b.text) {
1286 if (a.text < b.text) {
1285 return -1;
1287 return -1;
1286 }
1288 }
1287 return 0;
1289 return 0;
1288 });
1290 });
1289 }
1291 }
1290 return results;
1292 return results;
1291 }
1293 }
1292
1294
1293 function prefixFirstSort(results, container, query) {
1295 function prefixFirstSort(results, container, query) {
1294 if (query.term) {
1296 if (query.term) {
1295 return results.sort(function (a, b) {
1297 return results.sort(function (a, b) {
1296 // if parent node, no sorting
1298 // if parent node, no sorting
1297 if (a.children != undefined || b.children != undefined) {
1299 if (a.children != undefined || b.children != undefined) {
1298 return 0;
1300 return 0;
1299 }
1301 }
1300
1302
1301 // Put prefix matches before matches in the line
1303 // Put prefix matches before matches in the line
1302 var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
1304 var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
1303 bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
1305 bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
1304 if (aPos === 0 && bPos !== 0) {
1306 if (aPos === 0 && bPos !== 0) {
1305 return -1;
1307 return -1;
1306 }
1308 }
1307 if (bPos === 0 && aPos !== 0) {
1309 if (bPos === 0 && aPos !== 0) {
1308 return 1;
1310 return 1;
1309 }
1311 }
1310
1312
1311 // Default sorting
1313 // Default sorting
1312 if (a.text > b.text) {
1314 if (a.text > b.text) {
1313 return 1;
1315 return 1;
1314 }
1316 }
1315 if (a.text < b.text) {
1317 if (a.text < b.text) {
1316 return -1;
1318 return -1;
1317 }
1319 }
1318 return 0;
1320 return 0;
1319 });
1321 });
1320 }
1322 }
1321 return results;
1323 return results;
1322 }
1324 }
1323
1325
1324 /* Helper for jQuery DataTables */
1326 /* Helper for jQuery DataTables */
1325
1327
1326 function updateRowCountCallback($elem, onlyDisplayed) {
1328 function updateRowCountCallback($elem, onlyDisplayed) {
1327 return function drawCallback() {
1329 return function drawCallback() {
1328 var info = this.api().page.info(),
1330 var info = this.api().page.info(),
1329 count = onlyDisplayed === true ? info.recordsDisplay : info.recordsTotal;
1331 count = onlyDisplayed === true ? info.recordsDisplay : info.recordsTotal;
1330 $elem.html(count);
1332 $elem.html(count);
1331 }
1333 }
1332 }
1334 }
1333
1335
1334
1336
1335 /**
1337 /**
1336 * activate changeset parent/child navigation links
1338 * activate changeset parent/child navigation links
1337 */
1339 */
1338 function activate_parent_child_links(){
1340 function activate_parent_child_links(){
1339
1341
1340 $('.parent-child-link').on('click', function(e){
1342 $('.parent-child-link').on('click', function(e){
1341 var $this = $(this);
1343 var $this = $(this);
1342 //fetch via ajax what is going to be the next link, if we have
1344 //fetch via ajax what is going to be the next link, if we have
1343 //>1 links show them to user to choose
1345 //>1 links show them to user to choose
1344 if(!$this.hasClass('disabled')){
1346 if(!$this.hasClass('disabled')){
1345 $.ajax({
1347 $.ajax({
1346 url: $this.data('ajax-url'),
1348 url: $this.data('ajax-url'),
1347 success: function(data) {
1349 success: function(data) {
1348 var repo_name = $this.data('reponame');
1350 var repo_name = $this.data('reponame');
1349 if(data.results.length === 0){
1351 if(data.results.length === 0){
1350 $this.addClass('disabled');
1352 $this.addClass('disabled');
1351 $this.text(_TM['No revisions']);
1353 $this.text(_TM['No revisions']);
1352 }
1354 }
1353 if(data.results.length === 1){
1355 if(data.results.length === 1){
1354 var commit = data.results[0];
1356 var commit = data.results[0];
1355 window.location = pyroutes.url('changeset_home', {'repo_name': repo_name, 'revision': commit.raw_id});
1357 window.location = pyroutes.url('changeset_home', {'repo_name': repo_name, 'revision': commit.raw_id});
1356 }
1358 }
1357 else if(data.results.length > 1){
1359 else if(data.results.length > 1){
1358 $this.addClass('disabled');
1360 $this.addClass('disabled');
1359 $this.addClass('double');
1361 $this.addClass('double');
1360 var template =
1362 var template =
1361 ($this.data('linktype') == 'parent' ? '<i class="icon-left-open"/> ' : '') +
1363 ($this.data('linktype') == 'parent' ? '<i class="icon-left-open"/> ' : '') +
1362 '<a title="__title__" href="__url__">__rev__</a>' +
1364 '<a title="__title__" href="__url__">__rev__</a>' +
1363 ($this.data('linktype') == 'child' ? ' <i class="icon-right-open"/>' : '');
1365 ($this.data('linktype') == 'child' ? ' <i class="icon-right-open"/>' : '');
1364 var _html = [];
1366 var _html = [];
1365 for(var i = 0; i < data.results.length; i++){
1367 for(var i = 0; i < data.results.length; i++){
1366 _html.push(template
1368 _html.push(template
1367 .replace('__rev__', 'r{0}:{1}'.format(data.results[i].revision, data.results[i].raw_id.substr(0, 6)))
1369 .replace('__rev__', 'r{0}:{1}'.format(data.results[i].revision, data.results[i].raw_id.substr(0, 6)))
1368 .replace('__title__', data.results[i].message.html_escape())
1370 .replace('__title__', data.results[i].message.html_escape())
1369 .replace('__url__', pyroutes.url('changeset_home', {
1371 .replace('__url__', pyroutes.url('changeset_home', {
1370 'repo_name': repo_name,
1372 'repo_name': repo_name,
1371 'revision': data.results[i].raw_id}))
1373 'revision': data.results[i].raw_id}))
1372 );
1374 );
1373 }
1375 }
1374 $this.html(_html.join('<br/>'));
1376 $this.html(_html.join('<br/>'));
1375 }
1377 }
1376 }
1378 }
1377 });
1379 });
1378 e.preventDefault();
1380 e.preventDefault();
1379 }
1381 }
1380 });
1382 });
1381 }
1383 }
@@ -1,113 +1,113 b''
1 ${h.form(url('edit_user_group_perms_update', id=c.user_group.users_group_id))}
1 ${h.form(url('edit_user_group_perms_update', id=c.user_group.users_group_id))}
2 <div class="form">
2 <div class="form">
3 <div>
3 <div>
4 <div>
4 <div>
5 <table id="permissions_manage" class="table">
5 <table id="permissions_manage" class="table">
6 <tr>
6 <tr>
7 <td>${_('None')}</td>
7 <td>${_('None')}</td>
8 <td>${_('Read')}</td>
8 <td>${_('Read')}</td>
9 <td>${_('Write')}</td>
9 <td>${_('Write')}</td>
10 <td>${_('Admin')}</td>
10 <td>${_('Admin')}</td>
11 <td>${_('User/User Group')}</td>
11 <td>${_('User/User Group')}</td>
12 <td></td>
12 <td></td>
13 </tr>
13 </tr>
14 ## USERS
14 ## USERS
15 %for r2p in c.user_group.user_user_group_to_perm:
15 %for r2p in c.user_group.user_user_group_to_perm:
16 ##forbid revoking permission from yourself, except if you're an super admin
16 ##forbid revoking permission from yourself, except if you're an super admin
17 <tr id="id${id(r2p.user.username)}">
17 <tr id="id${id(r2p.user.username)}">
18 %if request.authuser.user_id != r2p.user.user_id or request.authuser.is_admin:
18 %if request.authuser.user_id != r2p.user.user_id or request.authuser.is_admin:
19 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.none')}</td>
19 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.none')}</td>
20 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.read')}</td>
20 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.read')}</td>
21 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.write')}</td>
21 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.write')}</td>
22 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.admin')}</td>
22 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.admin')}</td>
23 <td>
23 <td>
24 ${h.gravatar(r2p.user.email, cls="perm-gravatar", size=14)}
24 ${h.gravatar(r2p.user.email, cls="perm-gravatar", size=14)}
25 %if h.HasPermissionAny('hg.admin')() and r2p.user.username != 'default':
25 %if h.HasPermissionAny('hg.admin')() and r2p.user.username != 'default':
26 <a href="${h.url('edit_user',id=r2p.user.user_id)}">${r2p.user.username}</a>
26 <a href="${h.url('edit_user',id=r2p.user.user_id)}">${r2p.user.username}</a>
27 %else:
27 %else:
28 ${r2p.user.username if r2p.user.username != 'default' else _('Default')}
28 ${r2p.user.username if r2p.user.username != 'default' else _('Default')}
29 %endif
29 %endif
30 </td>
30 </td>
31 <td>
31 <td>
32 %if r2p.user.username !='default':
32 %if r2p.user.username !='default':
33 <button type="button" class="btn btn-default btn-xs" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}', '${r2p.user.username}')">
33 <button type="button" class="btn btn-default btn-xs" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}', '${r2p.user.username}')">
34 <i class="icon-minus-circled"></i>${_('Revoke')}
34 <i class="icon-minus-circled"></i>${_('Revoke')}
35 </button>
35 </button>
36 %endif
36 %endif
37 </td>
37 </td>
38 %else:
38 %else:
39 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.none', disabled="disabled")}</td>
39 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.none', disabled="disabled")}</td>
40 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.read', disabled="disabled")}</td>
40 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.read', disabled="disabled")}</td>
41 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.write', disabled="disabled")}</td>
41 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.write', disabled="disabled")}</td>
42 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.admin', disabled="disabled")}</td>
42 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.admin', disabled="disabled")}</td>
43 <td>
43 <td>
44 ${h.gravatar(r2p.user.email, cls="perm-gravatar", size=14)}
44 ${h.gravatar(r2p.user.email, cls="perm-gravatar", size=14)}
45 ${r2p.user.username if r2p.user.username != 'default' else _('Default')}
45 ${r2p.user.username if r2p.user.username != 'default' else _('Default')}
46 </td>
46 </td>
47 <td><i class="icon-user"></i>${_('Admin')}</td>
47 <td><i class="icon-user"></i>${_('Admin')}</td>
48 %endif
48 %endif
49 </tr>
49 </tr>
50 %endfor
50 %endfor
51
51
52 ## USER GROUPS
52 ## USER GROUPS
53 %for g2p in c.user_group.user_group_user_group_to_perm:
53 %for g2p in c.user_group.user_group_user_group_to_perm:
54 <tr id="id${id(g2p.user_group.users_group_name)}">
54 <tr id="id${id(g2p.user_group.users_group_name)}">
55 <td>${h.radio('g_perm_%s' % g2p.user_group.users_group_name,'usergroup.none')}</td>
55 <td>${h.radio('g_perm_%s' % g2p.user_group.users_group_name,'usergroup.none')}</td>
56 <td>${h.radio('g_perm_%s' % g2p.user_group.users_group_name,'usergroup.read')}</td>
56 <td>${h.radio('g_perm_%s' % g2p.user_group.users_group_name,'usergroup.read')}</td>
57 <td>${h.radio('g_perm_%s' % g2p.user_group.users_group_name,'usergroup.write')}</td>
57 <td>${h.radio('g_perm_%s' % g2p.user_group.users_group_name,'usergroup.write')}</td>
58 <td>${h.radio('g_perm_%s' % g2p.user_group.users_group_name,'usergroup.admin')}</td>
58 <td>${h.radio('g_perm_%s' % g2p.user_group.users_group_name,'usergroup.admin')}</td>
59 <td>
59 <td>
60 <i class="icon-users"></i>
60 <i class="icon-users"></i>
61 %if h.HasPermissionAny('hg.admin')():
61 %if h.HasPermissionAny('hg.admin')():
62 <a href="${h.url('edit_users_group',id=g2p.user_group.users_group_id)}">
62 <a href="${h.url('edit_users_group',id=g2p.user_group.users_group_id)}">
63 ${g2p.user_group.users_group_name}
63 ${g2p.user_group.users_group_name}
64 </a>
64 </a>
65 %else:
65 %else:
66 ${g2p.user_group.users_group_name}
66 ${g2p.user_group.users_group_name}
67 %endif
67 %endif
68 </td>
68 </td>
69 <td>
69 <td>
70 <button class="btn btn-default btn-xs" onclick="ajaxActionRevoke(${g2p.user_group.users_group_id}, 'user_group', '${'id%s'%id(g2p.user_group.users_group_name)}', '${g2p.user_group.users_group_name}')">
70 <button type="button" class="btn btn-default btn-xs" onclick="ajaxActionRevoke(${g2p.user_group.users_group_id}, 'user_group', '${'id%s'%id(g2p.user_group.users_group_name)}', '${g2p.user_group.users_group_name}')">
71 <i class="icon-minus-circled"></i>${_('Revoke')}
71 <i class="icon-minus-circled"></i>${_('Revoke')}
72 </button>
72 </button>
73 </td>
73 </td>
74 </tr>
74 </tr>
75 %endfor
75 %endfor
76 ## New entries added by addPermAction here.
76 ## New entries added by addPermAction here.
77 <tr class="new_members last_new_member" id="add_perm_input"><td colspan="6"></td></tr>
77 <tr class="new_members last_new_member" id="add_perm_input"><td colspan="6"></td></tr>
78 <tr>
78 <tr>
79 <td colspan="6">
79 <td colspan="6">
80 <button type="button" id="add_perm" class="btn btn-link btn-xs">
80 <button type="button" id="add_perm" class="btn btn-link btn-xs">
81 <i class="icon-plus"></i>${_('Add new')}
81 <i class="icon-plus"></i>${_('Add new')}
82 </button>
82 </button>
83 </td>
83 </td>
84 </tr>
84 </tr>
85 </table>
85 </table>
86 </div>
86 </div>
87 <div class="buttons">
87 <div class="buttons">
88 ${h.submit('save',_('Save'),class_="btn btn-default")}
88 ${h.submit('save',_('Save'),class_="btn btn-default")}
89 ${h.reset('reset',_('Reset'),class_="btn btn-default")}
89 ${h.reset('reset',_('Reset'),class_="btn btn-default")}
90 </div>
90 </div>
91 </div>
91 </div>
92 </div>
92 </div>
93 ${h.end_form()}
93 ${h.end_form()}
94
94
95 <script>
95 <script>
96 'use strict';
96 'use strict';
97 function ajaxActionRevoke(obj_id, obj_type, field_id, obj_name) {
97 function ajaxActionRevoke(obj_id, obj_type, field_id, obj_name) {
98 let url = ${h.js(h.url('edit_user_group_perms_delete', id=c.user_group.users_group_id))};
98 let url = ${h.js(h.url('edit_user_group_perms_delete', id=c.user_group.users_group_id))};
99 var revoke_msg = _TM['Confirm to revoke permission for {0}: {1}?'].format(obj_type.replace('_', ' '), obj_name);
99 var revoke_msg = _TM['Confirm to revoke permission for {0}: {1}?'].format(obj_type.replace('_', ' '), obj_name);
100 if (confirm(revoke_msg)){
100 if (confirm(revoke_msg)){
101 ajaxActionRevokePermission(url, obj_id, obj_type, field_id);
101 ajaxActionRevokePermission(url, obj_id, obj_type, field_id);
102 }
102 }
103 }
103 }
104
104
105 $(document).ready(function () {
105 $(document).ready(function () {
106 if (!$('#perm_new_member_name').hasClass('error')) {
106 if (!$('#perm_new_member_name').hasClass('error')) {
107 $('#add_perm_input').hide();
107 $('#add_perm_input').hide();
108 }
108 }
109 $('#add_perm').click(function () {
109 $('#add_perm').click(function () {
110 addPermAction('usergroup');
110 addPermAction('usergroup');
111 });
111 });
112 });
112 });
113 </script>
113 </script>
@@ -1,150 +1,150 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <%def name="diff_block(a_repo_name, a_ref_type, a_ref_name, a_rev,
3 <%def name="diff_block(a_repo_name, a_ref_type, a_ref_name, a_rev,
4 cs_repo_name, cs_ref_name, cs_ref_type, cs_rev,
4 cs_repo_name, cs_ref_name, cs_ref_type, cs_rev,
5 file_diff_data)">
5 file_diff_data)">
6 <div class="diff-collapse">
6 <div class="diff-collapse">
7 <button data-target="${'diff-container-%s' % (id(file_diff_data))}" class="diff-collapse-button btn btn-link btn-sm">&uarr; ${_('Collapse Diff')} &uarr;</button>
7 <button type='button', data-target="${'diff-container-%s' % (id(file_diff_data))}" class="diff-collapse-button btn btn-link btn-sm">&uarr; ${_('Collapse Diff')} &uarr;</button>
8 </div>
8 </div>
9 %for id_fid, url_fid, op, a_filename, cs_filename, diff, stats in file_diff_data:
9 %for id_fid, url_fid, op, a_filename, cs_filename, diff, stats in file_diff_data:
10 ${diff_block_diffblock(id_fid, url_fid, op, diff,
10 ${diff_block_diffblock(id_fid, url_fid, op, diff,
11 a_repo_name, a_rev, a_ref_type, a_ref_name, a_filename,
11 a_repo_name, a_rev, a_ref_type, a_ref_name, a_filename,
12 cs_repo_name, cs_rev, cs_ref_type, cs_ref_name, cs_filename,
12 cs_repo_name, cs_rev, cs_ref_type, cs_ref_name, cs_filename,
13 'diff-container-%s' % id(file_diff_data))}
13 'diff-container-%s' % id(file_diff_data))}
14 %endfor
14 %endfor
15 </%def>
15 </%def>
16
16
17 <%def name="diff_block_diffblock(id_fid, url_fid, op, diff,
17 <%def name="diff_block_diffblock(id_fid, url_fid, op, diff,
18 a_repo_name, a_rev, a_ref_type, a_ref_name, a_filename,
18 a_repo_name, a_rev, a_ref_type, a_ref_name, a_filename,
19 cs_repo_name, cs_rev, cs_ref_type, cs_ref_name, cs_filename, cls)"
19 cs_repo_name, cs_rev, cs_ref_type, cs_ref_name, cs_filename, cls)"
20 >
20 >
21 <div id="${id_fid}_target"></div>
21 <div id="${id_fid}_target"></div>
22 <div id="${id_fid}" class="panel panel-default ${cls}">
22 <div id="${id_fid}" class="panel panel-default ${cls}">
23 <div class="panel-heading clearfix">
23 <div class="panel-heading clearfix">
24 <div class="pull-left">
24 <div class="pull-left">
25 ${cs_filename}
25 ${cs_filename}
26 </div>
26 </div>
27 <div class="pull-left diff-actions">
27 <div class="pull-left diff-actions">
28 <span>
28 <span>
29 %if op == 'A':
29 %if op == 'A':
30 <span class="no-file" data-toggle="tooltip" title="${_('No file before')}">
30 <span class="no-file" data-toggle="tooltip" title="${_('No file before')}">
31 <i class="icon-minus-circled"></i></span>
31 <i class="icon-minus-circled"></i></span>
32 %else:
32 %else:
33 <a href="${h.url('files_home', repo_name=a_repo_name, f_path=a_filename, revision=a_rev)}" data-toggle="tooltip" title="${_('File before')}">
33 <a href="${h.url('files_home', repo_name=a_repo_name, f_path=a_filename, revision=a_rev)}" data-toggle="tooltip" title="${_('File before')}">
34 <i class="icon-doc"></i></a>
34 <i class="icon-doc"></i></a>
35 %endif
35 %endif
36
36
37 %if op == 'A':
37 %if op == 'A':
38 <span class="arrow" data-toggle="tooltip" title="${_('Added')}">&#10142;</span>
38 <span class="arrow" data-toggle="tooltip" title="${_('Added')}">&#10142;</span>
39 %elif op == 'M':
39 %elif op == 'M':
40 <span class="arrow" data-toggle="tooltip" title="${_('Modified')}">&#10142;</span>
40 <span class="arrow" data-toggle="tooltip" title="${_('Modified')}">&#10142;</span>
41 %elif op == 'D':
41 %elif op == 'D':
42 <span class="arrow" data-toggle="tooltip" title="${_('Deleted')}">&#10142;</span>
42 <span class="arrow" data-toggle="tooltip" title="${_('Deleted')}">&#10142;</span>
43 %elif op == 'R':
43 %elif op == 'R':
44 <span class="arrow" data-toggle="tooltip" title="${_('Renamed')}">&#10142;</span>
44 <span class="arrow" data-toggle="tooltip" title="${_('Renamed')}">&#10142;</span>
45 %elif op is None:
45 %elif op is None:
46 <span class="arrow" data-toggle="tooltip" title="${_('No change')}">&#10142;</span>
46 <span class="arrow" data-toggle="tooltip" title="${_('No change')}">&#10142;</span>
47 %else:
47 %else:
48 <span class="arrow" data-toggle="tooltip" title="${_('Unknown operation: %r') % op}">&#10142;</span>
48 <span class="arrow" data-toggle="tooltip" title="${_('Unknown operation: %r') % op}">&#10142;</span>
49 %endif
49 %endif
50
50
51 %if op == 'D':
51 %if op == 'D':
52 <span class="no-file" data-toggle="tooltip" title="${_('No file after')}">
52 <span class="no-file" data-toggle="tooltip" title="${_('No file after')}">
53 <i class="icon-minus-circled"></i></span>
53 <i class="icon-minus-circled"></i></span>
54 %else:
54 %else:
55 <a href="${h.url('files_home', repo_name=cs_repo_name, f_path=cs_filename, revision=cs_rev)}" data-toggle="tooltip" title="${_('File after')}">
55 <a href="${h.url('files_home', repo_name=cs_repo_name, f_path=cs_filename, revision=cs_rev)}" data-toggle="tooltip" title="${_('File after')}">
56 <i class="icon-doc"></i></a>
56 <i class="icon-doc"></i></a>
57 %endif
57 %endif
58 </span>
58 </span>
59
59
60 <a href="${h.url('files_diff_home',repo_name=cs_repo_name,f_path=cs_filename,diff2=cs_rev,diff1=a_rev,diff='diff',fulldiff=1)}" data-toggle="tooltip" title="${_('Show full diff for this file')}">
60 <a href="${h.url('files_diff_home',repo_name=cs_repo_name,f_path=cs_filename,diff2=cs_rev,diff1=a_rev,diff='diff',fulldiff=1)}" data-toggle="tooltip" title="${_('Show full diff for this file')}">
61 <i class="icon-file-code"></i></a>
61 <i class="icon-file-code"></i></a>
62 <a href="${h.url('files_diff_2way_home',repo_name=cs_repo_name,f_path=cs_filename,diff2=cs_rev,diff1=a_rev,diff='diff',fulldiff=1)}" data-toggle="tooltip" title="${_('Show full side-by-side diff for this file')}">
62 <a href="${h.url('files_diff_2way_home',repo_name=cs_repo_name,f_path=cs_filename,diff2=cs_rev,diff1=a_rev,diff='diff',fulldiff=1)}" data-toggle="tooltip" title="${_('Show full side-by-side diff for this file')}">
63 <i class="icon-docs"></i></a>
63 <i class="icon-docs"></i></a>
64 <a href="${h.url('files_diff_home',repo_name=cs_repo_name,f_path=cs_filename,diff2=cs_rev,diff1=a_rev,diff='raw')}" data-toggle="tooltip" title="${_('Raw diff for this file')}">
64 <a href="${h.url('files_diff_home',repo_name=cs_repo_name,f_path=cs_filename,diff2=cs_rev,diff1=a_rev,diff='raw')}" data-toggle="tooltip" title="${_('Raw diff for this file')}">
65 <i class="icon-diff"></i></a>
65 <i class="icon-diff"></i></a>
66 <a href="${h.url('files_diff_home',repo_name=cs_repo_name,f_path=cs_filename,diff2=cs_rev,diff1=a_rev,diff='download')}" data-toggle="tooltip" title="${_('Download diff for this file')}">
66 <a href="${h.url('files_diff_home',repo_name=cs_repo_name,f_path=cs_filename,diff2=cs_rev,diff1=a_rev,diff='download')}" data-toggle="tooltip" title="${_('Download diff for this file')}">
67 <i class="icon-floppy"></i></a>
67 <i class="icon-floppy"></i></a>
68 ${h.ignore_whitespace_link(request.GET, id_fid)}
68 ${h.ignore_whitespace_link(request.GET, id_fid)}
69 ${h.increase_context_link(request.GET, id_fid)}
69 ${h.increase_context_link(request.GET, id_fid)}
70 </div>
70 </div>
71 <div class="pull-right">
71 <div class="pull-right">
72 ${_('Show inline comments')}
72 ${_('Show inline comments')}
73 ${h.checkbox('checkbox-show-inline-' + id_fid, checked="checked",class_="show-inline-comments",**{'data-for':id_fid})}
73 ${h.checkbox('checkbox-show-inline-' + id_fid, checked="checked",class_="show-inline-comments",**{'data-for':id_fid})}
74 </div>
74 </div>
75 </div>
75 </div>
76 <div class="no-padding panel-body" data-f_path="${cs_filename}">
76 <div class="no-padding panel-body" data-f_path="${cs_filename}">
77 ${diff|n}
77 ${diff|n}
78 %if op and cs_filename.rsplit('.')[-1] in ['png', 'gif', 'jpg', 'bmp']:
78 %if op and cs_filename.rsplit('.')[-1] in ['png', 'gif', 'jpg', 'bmp']:
79 <div class="btn btn-image-diff-show">Show images</div>
79 <div class="btn btn-image-diff-show">Show images</div>
80 %if op == 'M':
80 %if op == 'M':
81 <div id="${id_fid}_image-diff" class="btn btn-image-diff-swap" style="display:none">Press to swap images</div>
81 <div id="${id_fid}_image-diff" class="btn btn-image-diff-swap" style="display:none">Press to swap images</div>
82 %endif
82 %endif
83 <div>
83 <div>
84 %if op in 'DM':
84 %if op in 'DM':
85 <img id="${id_fid}_image-diff-img-a" class="img-diff img-diff-swapable" style="display:none"
85 <img id="${id_fid}_image-diff-img-a" class="img-diff img-diff-swapable" style="display:none"
86 realsrc="${h.url('files_raw_home',repo_name=a_repo_name,revision=a_rev,f_path=a_filename)}" />
86 realsrc="${h.url('files_raw_home',repo_name=a_repo_name,revision=a_rev,f_path=a_filename)}" />
87 %endif
87 %endif
88 %if op in 'AM':
88 %if op in 'AM':
89 <img id="${id_fid}_image-diff-img-b" class="img-diff img-diff-swapable" style="display:none"
89 <img id="${id_fid}_image-diff-img-b" class="img-diff img-diff-swapable" style="display:none"
90 realsrc="${h.url('files_raw_home',repo_name=cs_repo_name,revision=cs_rev,f_path=cs_filename)}" />
90 realsrc="${h.url('files_raw_home',repo_name=cs_repo_name,revision=cs_rev,f_path=cs_filename)}" />
91 %endif
91 %endif
92 </div>
92 </div>
93 %endif
93 %endif
94 </div>
94 </div>
95 </div>
95 </div>
96 </%def>
96 </%def>
97
97
98 <%def name="diff_block_js()">
98 <%def name="diff_block_js()">
99 <script>
99 <script>
100 'use strict';
100 'use strict';
101 $(document).ready(function(){
101 $(document).ready(function(){
102 $('.btn-image-diff-show').click(function(){
102 $('.btn-image-diff-show').click(function(){
103 $('.btn-image-diff-show').hide();
103 $('.btn-image-diff-show').hide();
104 $('.btn-image-diff-swap').show();
104 $('.btn-image-diff-swap').show();
105 $('.img-diff-swapable')
105 $('.img-diff-swapable')
106 .each(function(i,e){
106 .each(function(i,e){
107 $(e).prop('src', $(e).attr('realsrc'));
107 $(e).prop('src', $(e).attr('realsrc'));
108 })
108 })
109 .show();
109 .show();
110 });
110 });
111
111
112 $('.btn-image-diff-swap').mousedown(function(e){
112 $('.btn-image-diff-swap').mousedown(function(e){
113 $('#'+e.currentTarget.id+'-img-a.img-diff-swapable')
113 $('#'+e.currentTarget.id+'-img-a.img-diff-swapable')
114 .before($('#'+e.currentTarget.id+'-img-b.img-diff-swapable'));
114 .before($('#'+e.currentTarget.id+'-img-b.img-diff-swapable'));
115 });
115 });
116 function reset(e){
116 function reset(e){
117 $('#'+e.currentTarget.id+'-img-a.img-diff-swapable')
117 $('#'+e.currentTarget.id+'-img-a.img-diff-swapable')
118 .after($('#'+e.currentTarget.id+'-img-b.img-diff-swapable'));
118 .after($('#'+e.currentTarget.id+'-img-b.img-diff-swapable'));
119 }
119 }
120 $('.btn-image-diff-swap').mouseup(reset);
120 $('.btn-image-diff-swap').mouseup(reset);
121 $('.btn-image-diff-swap').mouseleave(reset);
121 $('.btn-image-diff-swap').mouseleave(reset);
122
122
123 $('.diff-collapse-button').click(function(e) {
123 $('.diff-collapse-button').click(function(e) {
124 $('.diff_block').toggleClass('hidden');
124 $('.diff_block').toggleClass('hidden');
125 var $button = $(e.currentTarget);
125 var $button = $(e.currentTarget);
126 var $target = $('.' + $button.data('target'));
126 var $target = $('.' + $button.data('target'));
127 if($target.hasClass('hidden')){
127 if($target.hasClass('hidden')){
128 $target.removeClass('hidden');
128 $target.removeClass('hidden');
129 $button.html("&uarr; {0} &uarr;".format(_TM['Collapse Diff']));
129 $button.html("&uarr; {0} &uarr;".format(_TM['Collapse Diff']));
130 }
130 }
131 else if(!$target.hasClass('hidden')){
131 else if(!$target.hasClass('hidden')){
132 $target.addClass('hidden');
132 $target.addClass('hidden');
133 $button.html("&darr; {0} &darr;".format(_TM['Expand Diff']));
133 $button.html("&darr; {0} &darr;".format(_TM['Expand Diff']));
134 }
134 }
135 });
135 });
136 $('.show-inline-comments').change(function(e){
136 $('.show-inline-comments').change(function(e){
137 var target = e.currentTarget;
137 var target = e.currentTarget;
138 if(target == null){
138 if(target == null){
139 target = this;
139 target = this;
140 }
140 }
141 var boxid = $(target).data('for');
141 var boxid = $(target).data('for');
142 if(target.checked){
142 if(target.checked){
143 $('#{0} .inline-comments'.format(boxid)).show();
143 $('#{0} .inline-comments'.format(boxid)).show();
144 }else{
144 }else{
145 $('#{0} .inline-comments'.format(boxid)).hide();
145 $('#{0} .inline-comments'.format(boxid)).hide();
146 }
146 }
147 });
147 });
148 });
148 });
149 </script>
149 </script>
150 </%def>
150 </%def>
@@ -1,149 +1,149 b''
1 ## DATA TABLE RE USABLE ELEMENTS
1 ## DATA TABLE RE USABLE ELEMENTS
2 ## usage:
2 ## usage:
3 ## <%namespace name="dt" file="/data_table/_dt_elements.html"/>
3 ## <%namespace name="dt" file="/data_table/_dt_elements.html"/>
4
4
5 <%namespace name="base" file="/base/base.html"/>
5 <%namespace name="base" file="/base/base.html"/>
6
6
7 <%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False)">
7 <%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False)">
8 <%
8 <%
9 def get_name(name,short_name=short_name):
9 def get_name(name,short_name=short_name):
10 if short_name:
10 if short_name:
11 return name.split('/')[-1]
11 return name.split('/')[-1]
12 else:
12 else:
13 return name
13 return name
14 %>
14 %>
15 <div class="dt_repo ${'dt_repo_pending' if rstate == 'repo_state_pending' else ''}">
15 <div class="dt_repo ${'dt_repo_pending' if rstate == 'repo_state_pending' else ''}">
16 ${base.repolabel(rtype)}
16 ${base.repolabel(rtype)}
17 <a href="${webutils.url('summary_home', repo_name=name)}">
17 <a href="${webutils.url('summary_home', repo_name=name)}">
18 ${get_name(name)}
18 ${get_name(name)}
19 </a>
19 </a>
20 %if private and c.visual.show_private_icon:
20 %if private and c.visual.show_private_icon:
21 <i class="icon-lock" title="${_('Private repository')}"></i>
21 <i class="icon-lock" title="${_('Private repository')}"></i>
22 %elif not private and c.visual.show_public_icon:
22 %elif not private and c.visual.show_public_icon:
23 <i class="icon-globe" title="${_('Public repository')}"></i>
23 <i class="icon-globe" title="${_('Public repository')}"></i>
24 %endif
24 %endif
25 %if fork_of:
25 %if fork_of:
26 <a href="${webutils.url('summary_home',repo_name=fork_of.repo_name)}"><i class="icon-fork"></i></a>
26 <a href="${webutils.url('summary_home',repo_name=fork_of.repo_name)}"><i class="icon-fork"></i></a>
27 %endif
27 %endif
28 %if rstate == 'repo_state_pending':
28 %if rstate == 'repo_state_pending':
29 <i class="icon-wrench" title="${_('Repository creation in progress...')}"></i>
29 <i class="icon-wrench" title="${_('Repository creation in progress...')}"></i>
30 %endif
30 %endif
31 </div>
31 </div>
32 </%def>
32 </%def>
33
33
34 <%def name="following(repo_id, repo_following)">
34 <%def name="following(repo_id, repo_following)">
35 %if request.authuser.username != 'default':
35 %if request.authuser.username != 'default':
36 <a href="#" class="${'following' if repo_following else 'follow'}" onclick="return toggleFollowingRepo(this, ${repo_id});"><i class="list-extra icon-heart-empty show-follow" title="${_('Follow')}"></i><i class="list-extra icon-heart show-following" title="${_('Unfollow')}"></i></a>
36 <a href="#" class="${'following' if repo_following else 'follow'}" onclick="return toggleFollowingRepo(this, ${repo_id});"><i class="list-extra icon-heart-empty show-follow" title="${_('Follow')}"></i><i class="list-extra icon-heart show-following" title="${_('Unfollow')}"></i></a>
37 %endif
37 %endif
38 </%def>
38 </%def>
39
39
40 <%def name="last_change(last_change)">
40 <%def name="last_change(last_change)">
41 <span data-toggle="tooltip" title="${webutils.fmt_date(last_change)}" date="${last_change}">${webutils.age(last_change)}</span>
41 <span data-toggle="tooltip" title="${webutils.fmt_date(last_change)}" date="${last_change}">${webutils.age(last_change)}</span>
42 </%def>
42 </%def>
43
43
44 <%def name="revision(name,rev,tip,author,last_msg)">
44 <%def name="revision(name,rev,tip,author,last_msg)">
45 %if rev >= 0:
45 %if rev >= 0:
46 <a data-toggle="popover" title="${author | entity}" data-content="${last_msg | entity}" class="changeset_hash" href="${webutils.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,webutils.short_id(tip))}</a>
46 <a data-toggle="popover" title="${author | entity}" data-content="${last_msg | entity}" class="changeset_hash" href="${webutils.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,webutils.short_id(tip))}</a>
47 %else:
47 %else:
48 ${_('No changesets yet')}
48 ${_('No changesets yet')}
49 %endif
49 %endif
50 </%def>
50 </%def>
51
51
52 <%def name="rss(name)">
52 <%def name="rss(name)">
53 %if request.authuser.username != 'default':
53 %if request.authuser.username != 'default':
54 <a title="${_('Subscribe to %s rss feed')% name}" href="${webutils.url('rss_feed_home',repo_name=name,api_key=request.authuser.api_key)}"><i class="icon-rss-squared"></i></a>
54 <a title="${_('Subscribe to %s rss feed')% name}" href="${webutils.url('rss_feed_home',repo_name=name,api_key=request.authuser.api_key)}"><i class="icon-rss-squared"></i></a>
55 %else:
55 %else:
56 <a title="${_('Subscribe to %s rss feed')% name}" href="${webutils.url('rss_feed_home',repo_name=name)}"><i class="icon-rss-squared"></i></a>
56 <a title="${_('Subscribe to %s rss feed')% name}" href="${webutils.url('rss_feed_home',repo_name=name)}"><i class="icon-rss-squared"></i></a>
57 %endif
57 %endif
58 </%def>
58 </%def>
59
59
60 <%def name="atom(name)">
60 <%def name="atom(name)">
61 %if request.authuser.username != 'default':
61 %if request.authuser.username != 'default':
62 <a title="${_('Subscribe to %s atom feed')% name}" href="${webutils.url('atom_feed_home',repo_name=name,api_key=request.authuser.api_key)}"><i class="icon-rss-squared"></i></a>
62 <a title="${_('Subscribe to %s atom feed')% name}" href="${webutils.url('atom_feed_home',repo_name=name,api_key=request.authuser.api_key)}"><i class="icon-rss-squared"></i></a>
63 %else:
63 %else:
64 <a title="${_('Subscribe to %s atom feed')% name}" href="${webutils.url('atom_feed_home',repo_name=name)}"><i class="icon-rss-squared"></i></a>
64 <a title="${_('Subscribe to %s atom feed')% name}" href="${webutils.url('atom_feed_home',repo_name=name)}"><i class="icon-rss-squared"></i></a>
65 %endif
65 %endif
66 </%def>
66 </%def>
67
67
68 <%def name="repo_actions(repo_name)">
68 <%def name="repo_actions(repo_name)">
69 <a href="${webutils.url('edit_repo',repo_name=repo_name)}" title="${_('Edit')}" class="btn btn-default btn-xs">
69 <a href="${webutils.url('edit_repo',repo_name=repo_name)}" title="${_('Edit')}" class="btn btn-default btn-xs">
70 <i class="icon-pencil"></i>${_('Edit')}
70 <i class="icon-pencil"></i>${_('Edit')}
71 </a>
71 </a>
72 ${webutils.form(webutils.url('delete_repo', repo_name=repo_name))}
72 ${webutils.form(webutils.url('delete_repo', repo_name=repo_name))}
73 <button name="${'remove_%s' % repo_name}" class="btn btn-default btn-xs"
73 <button type="submit" name="${'remove_%s' % repo_name}" class="btn btn-default btn-xs"
74 onclick="return confirm('${_('Confirm to delete this repository: %s') % repo_name}');">
74 onclick="return confirm('${_('Confirm to delete this repository: %s') % repo_name}');">
75 <i class="icon-trashcan"></i>${_('Delete')}
75 <i class="icon-trashcan"></i>${_('Delete')}
76 </button>
76 </button>
77 ${webutils.end_form()}
77 ${webutils.end_form()}
78 </%def>
78 </%def>
79
79
80 <%def name="repo_state(repo_state)">
80 <%def name="repo_state(repo_state)">
81 %if repo_state == u'repo_state_pending':
81 %if repo_state == u'repo_state_pending':
82 <div class="label label-info">${_('Creating')}</div>
82 <div class="label label-info">${_('Creating')}</div>
83 %elif repo_state == u'repo_state_created':
83 %elif repo_state == u'repo_state_created':
84 <div class="label label-success">${_('Created')}</div>
84 <div class="label label-success">${_('Created')}</div>
85 %else:
85 %else:
86 <div class="label label-danger" title="${repo_state}">invalid</div>
86 <div class="label label-danger" title="${repo_state}">invalid</div>
87 %endif
87 %endif
88 </%def>
88 </%def>
89
89
90 <%def name="user_actions(user_id, username)">
90 <%def name="user_actions(user_id, username)">
91 <a href="${webutils.url('edit_user',id=user_id)}" title="${_('Edit')}" class="btn btn-default btn-xs">
91 <a href="${webutils.url('edit_user',id=user_id)}" title="${_('Edit')}" class="btn btn-default btn-xs">
92 <i class="icon-pencil"></i>${_('Edit')}
92 <i class="icon-pencil"></i>${_('Edit')}
93 </a>
93 </a>
94 ${webutils.form(webutils.url('delete_user', id=user_id))}
94 ${webutils.form(webutils.url('delete_user', id=user_id))}
95 <button id="${'remove_user_%s' % user_id}" name="${'remove_user_%s' % repo_name}" class="btn btn-default btn-xs" title="${_('Delete')}"
95 <button type="submit" id="${'remove_user_%s' % user_id}" name="${'remove_user_%s' % repo_name}" class="btn btn-default btn-xs" title="${_('Delete')}"
96 onclick="return confirm('${_('Confirm to delete this user: %s') % username}');">
96 onclick="return confirm('${_('Confirm to delete this user: %s') % username}');">
97 <i class="icon-trashcan"></i>${_('Delete')}
97 <i class="icon-trashcan"></i>${_('Delete')}
98 </button>
98 </button>
99 ${webutils.end_form()}
99 ${webutils.end_form()}
100 </%def>
100 </%def>
101
101
102 <%def name="user_group_actions(user_group_id, user_group_name)">
102 <%def name="user_group_actions(user_group_id, user_group_name)">
103 <a href="${webutils.url('edit_users_group', id=user_group_id)}" title="${_('Edit')}" class="btn btn-default btn-xs">
103 <a href="${webutils.url('edit_users_group', id=user_group_id)}" title="${_('Edit')}" class="btn btn-default btn-xs">
104 <i class="icon-pencil"></i>${_('Edit')}
104 <i class="icon-pencil"></i>${_('Edit')}
105 </a>
105 </a>
106 ${webutils.form(webutils.url('delete_users_group', id=user_group_id))}
106 ${webutils.form(webutils.url('delete_users_group', id=user_group_id))}
107 <button id="${'remove_group_%s' % user_group_id}" name="${'remove_user_%s' % repo_name}" class="btn btn-default btn-xs" title="${_('Delete')}"
107 <button type="submit" id="${'remove_group_%s' % user_group_id}" name="${'remove_user_%s' % repo_name}" class="btn btn-default btn-xs" title="${_('Delete')}"
108 onclick="return confirm('${_('Confirm to delete this user group: %s') % user_group_name}');">
108 onclick="return confirm('${_('Confirm to delete this user group: %s') % user_group_name}');">
109 <i class="icon-trashcan"></i>${_('Delete')}
109 <i class="icon-trashcan"></i>${_('Delete')}
110 </button>
110 </button>
111 ${webutils.end_form()}
111 ${webutils.end_form()}
112 </%def>
112 </%def>
113
113
114 <%def name="group_name_html(group_name,name)">
114 <%def name="group_name_html(group_name,name)">
115 <div class="dt_repo">
115 <div class="dt_repo">
116 <i class="icon-folder"></i>
116 <i class="icon-folder"></i>
117 <a href="${webutils.url('repos_group_home',group_name=group_name)}">${name}</a>
117 <a href="${webutils.url('repos_group_home',group_name=group_name)}">${name}</a>
118 </div>
118 </div>
119 </%def>
119 </%def>
120
120
121 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
121 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
122 <a href="${webutils.url('edit_repo_group',group_name=repo_group_name)}" title="${_('Edit')}" class="btn btn-default btn-xs">
122 <a href="${webutils.url('edit_repo_group',group_name=repo_group_name)}" title="${_('Edit')}" class="btn btn-default btn-xs">
123 <i class="icon-pencil"></i>${_('Edit')}
123 <i class="icon-pencil"></i>${_('Edit')}
124 </a>
124 </a>
125 ${webutils.form(webutils.url('delete_repo_group', group_name=repo_group_name))}
125 ${webutils.form(webutils.url('delete_repo_group', group_name=repo_group_name))}
126 <button id="${'remove_%s' % repo_group_name}" name="${'remove_%s' % repo_group_name}" class="btn btn-default btn-xs" title="${_('Delete')}"
126 <button type="submit" id="${'remove_%s' % repo_group_name}" name="${'remove_%s' % repo_group_name}" class="btn btn-default btn-xs" title="${_('Delete')}"
127 onclick="return confirm('${ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)}')">
127 onclick="return confirm('${ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)}')">
128 <i class="icon-trashcan"></i>${_('Delete')}
128 <i class="icon-trashcan"></i>${_('Delete')}
129 </button>
129 </button>
130 ${webutils.end_form()}
130 ${webutils.end_form()}
131 </%def>
131 </%def>
132
132
133 <%def name="user_name(user_id, username)">
133 <%def name="user_name(user_id, username)">
134 ${webutils.link_to(username,webutils.url('edit_user', id=user_id))}
134 ${webutils.link_to(username,webutils.url('edit_user', id=user_id))}
135 </%def>
135 </%def>
136
136
137 <%def name="repo_group_name(repo_group_name, children_groups)">
137 <%def name="repo_group_name(repo_group_name, children_groups)">
138 <div class="text-nowrap">
138 <div class="text-nowrap">
139 <a href="${webutils.url('repos_group_home',group_name=repo_group_name)}">
139 <a href="${webutils.url('repos_group_home',group_name=repo_group_name)}">
140 <i class="icon-folder" title="${_('Repository group')}"></i>${webutils.literal(' &raquo; ').join(children_groups)}</a>
140 <i class="icon-folder" title="${_('Repository group')}"></i>${webutils.literal(' &raquo; ').join(children_groups)}</a>
141 </div>
141 </div>
142 </%def>
142 </%def>
143
143
144 <%def name="user_group_name(user_group_id, user_group_name)">
144 <%def name="user_group_name(user_group_id, user_group_name)">
145 <div class="text-nowrap">
145 <div class="text-nowrap">
146 <a href="${webutils.url('edit_users_group', id=user_group_id)}">
146 <a href="${webutils.url('edit_users_group', id=user_group_id)}">
147 <i class="icon-users" title="${_('User group')}"></i>${user_group_name}</a>
147 <i class="icon-users" title="${_('User group')}"></i>${user_group_name}</a>
148 </div>
148 </div>
149 </%def>
149 </%def>
General Comments 0
You need to be logged in to leave comments. Login now