##// END OF EJS Templates
pull requests: abort pending ajax requests before starting new one...
Mads Kiilerich -
r4556:dbd2f289 default
parent child Browse files
Show More
@@ -1,2173 +1,2175 b''
1 1 /**
2 2 Kallithea JS Files
3 3 **/
4 4 'use strict';
5 5
6 6 if (typeof console == "undefined" || typeof console.log == "undefined"){
7 7 console = { log: function() {} }
8 8 }
9 9
10 10 /**
11 11 * INJECT .format function into String
12 12 * Usage: "My name is {0} {1}".format("Johny","Bravo")
13 13 * Return "My name is Johny Bravo"
14 14 * Inspired by https://gist.github.com/1049426
15 15 */
16 16 String.prototype.format = function() {
17 17 function format() {
18 18 var str = this;
19 19 var len = arguments.length+1;
20 20 var safe = undefined;
21 21 var arg = undefined;
22 22
23 23 // For each {0} {1} {n...} replace with the argument in that position. If
24 24 // the argument is an object or an array it will be stringified to JSON.
25 25 for (var i=0; i < len; arg = arguments[i++]) {
26 26 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
27 27 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
28 28 }
29 29 return str;
30 30 }
31 31
32 32 // Save a reference of what may already exist under the property native.
33 33 // Allows for doing something like: if("".format.native) { /* use native */ }
34 34 format.native = String.prototype.format;
35 35
36 36 // Replace the prototype property
37 37 return format;
38 38
39 39 }();
40 40
41 41 String.prototype.strip = function(char) {
42 42 if(char === undefined){
43 43 char = '\\s';
44 44 }
45 45 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
46 46 }
47 47
48 48 String.prototype.lstrip = function(char) {
49 49 if(char === undefined){
50 50 char = '\\s';
51 51 }
52 52 return this.replace(new RegExp('^'+char+'+'),'');
53 53 }
54 54
55 55 String.prototype.rstrip = function(char) {
56 56 if(char === undefined){
57 57 char = '\\s';
58 58 }
59 59 return this.replace(new RegExp(''+char+'+$'),'');
60 60 }
61 61
62 62 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
63 63 under MIT license / public domain, see
64 64 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
65 65 if(!Array.prototype.indexOf) {
66 66 Array.prototype.indexOf = function (searchElement, fromIndex) {
67 67 if ( this === undefined || this === null ) {
68 68 throw new TypeError( '"this" is null or not defined' );
69 69 }
70 70
71 71 var length = this.length >>> 0; // Hack to convert object.length to a UInt32
72 72
73 73 fromIndex = +fromIndex || 0;
74 74
75 75 if (Math.abs(fromIndex) === Infinity) {
76 76 fromIndex = 0;
77 77 }
78 78
79 79 if (fromIndex < 0) {
80 80 fromIndex += length;
81 81 if (fromIndex < 0) {
82 82 fromIndex = 0;
83 83 }
84 84 }
85 85
86 86 for (;fromIndex < length; fromIndex++) {
87 87 if (this[fromIndex] === searchElement) {
88 88 return fromIndex;
89 89 }
90 90 }
91 91
92 92 return -1;
93 93 };
94 94 }
95 95
96 96 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Compatibility
97 97 under MIT license / public domain, see
98 98 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
99 99 if (!Array.prototype.filter)
100 100 {
101 101 Array.prototype.filter = function(fun /*, thisArg */)
102 102 {
103 103 if (this === void 0 || this === null)
104 104 throw new TypeError();
105 105
106 106 var t = Object(this);
107 107 var len = t.length >>> 0;
108 108 if (typeof fun !== "function")
109 109 throw new TypeError();
110 110
111 111 var res = [];
112 112 var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
113 113 for (var i = 0; i < len; i++)
114 114 {
115 115 if (i in t)
116 116 {
117 117 var val = t[i];
118 118
119 119 // NOTE: Technically this should Object.defineProperty at
120 120 // the next index, as push can be affected by
121 121 // properties on Object.prototype and Array.prototype.
122 122 // But that method's new, and collisions should be
123 123 // rare, so use the more-compatible alternative.
124 124 if (fun.call(thisArg, val, i, t))
125 125 res.push(val);
126 126 }
127 127 }
128 128
129 129 return res;
130 130 };
131 131 }
132 132
133 133 /**
134 134 * A customized version of PyRoutes.JS from https://pypi.python.org/pypi/pyroutes.js/
135 135 * which is copyright Stephane Klein and was made available under the BSD License.
136 136 *
137 137 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
138 138 */
139 139 var pyroutes = (function() {
140 140 // access global map defined in special file pyroutes
141 141 var matchlist = PROUTES_MAP;
142 142 var sprintf = (function() {
143 143 function get_type(variable) {
144 144 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
145 145 }
146 146 function str_repeat(input, multiplier) {
147 147 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
148 148 return output.join('');
149 149 }
150 150
151 151 var str_format = function() {
152 152 if (!str_format.cache.hasOwnProperty(arguments[0])) {
153 153 str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
154 154 }
155 155 return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
156 156 };
157 157
158 158 str_format.format = function(parse_tree, argv) {
159 159 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
160 160 for (i = 0; i < tree_length; i++) {
161 161 node_type = get_type(parse_tree[i]);
162 162 if (node_type === 'string') {
163 163 output.push(parse_tree[i]);
164 164 }
165 165 else if (node_type === 'array') {
166 166 match = parse_tree[i]; // convenience purposes only
167 167 if (match[2]) { // keyword argument
168 168 arg = argv[cursor];
169 169 for (k = 0; k < match[2].length; k++) {
170 170 if (!arg.hasOwnProperty(match[2][k])) {
171 171 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
172 172 }
173 173 arg = arg[match[2][k]];
174 174 }
175 175 }
176 176 else if (match[1]) { // positional argument (explicit)
177 177 arg = argv[match[1]];
178 178 }
179 179 else { // positional argument (implicit)
180 180 arg = argv[cursor++];
181 181 }
182 182
183 183 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
184 184 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
185 185 }
186 186 switch (match[8]) {
187 187 case 'b': arg = arg.toString(2); break;
188 188 case 'c': arg = String.fromCharCode(arg); break;
189 189 case 'd': arg = parseInt(arg, 10); break;
190 190 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
191 191 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
192 192 case 'o': arg = arg.toString(8); break;
193 193 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
194 194 case 'u': arg = Math.abs(arg); break;
195 195 case 'x': arg = arg.toString(16); break;
196 196 case 'X': arg = arg.toString(16).toUpperCase(); break;
197 197 }
198 198 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
199 199 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
200 200 pad_length = match[6] - String(arg).length;
201 201 pad = match[6] ? str_repeat(pad_character, pad_length) : '';
202 202 output.push(match[5] ? arg + pad : pad + arg);
203 203 }
204 204 }
205 205 return output.join('');
206 206 };
207 207
208 208 str_format.cache = {};
209 209
210 210 str_format.parse = function(fmt) {
211 211 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
212 212 while (_fmt) {
213 213 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
214 214 parse_tree.push(match[0]);
215 215 }
216 216 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
217 217 parse_tree.push('%');
218 218 }
219 219 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
220 220 if (match[2]) {
221 221 arg_names |= 1;
222 222 var field_list = [], replacement_field = match[2], field_match = [];
223 223 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
224 224 field_list.push(field_match[1]);
225 225 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
226 226 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
227 227 field_list.push(field_match[1]);
228 228 }
229 229 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
230 230 field_list.push(field_match[1]);
231 231 }
232 232 else {
233 233 throw('[sprintf] huh?');
234 234 }
235 235 }
236 236 }
237 237 else {
238 238 throw('[sprintf] huh?');
239 239 }
240 240 match[2] = field_list;
241 241 }
242 242 else {
243 243 arg_names |= 2;
244 244 }
245 245 if (arg_names === 3) {
246 246 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
247 247 }
248 248 parse_tree.push(match);
249 249 }
250 250 else {
251 251 throw('[sprintf] huh?');
252 252 }
253 253 _fmt = _fmt.substring(match[0].length);
254 254 }
255 255 return parse_tree;
256 256 };
257 257
258 258 return str_format;
259 259 })();
260 260
261 261 var vsprintf = function(fmt, argv) {
262 262 argv.unshift(fmt);
263 263 return sprintf.apply(null, argv);
264 264 };
265 265 return {
266 266 'url': function(route_name, params) {
267 267 var result = route_name;
268 268 if (typeof(params) != 'object'){
269 269 params = {};
270 270 }
271 271 if (matchlist.hasOwnProperty(route_name)) {
272 272 var route = matchlist[route_name];
273 273 // param substitution
274 274 for(var i=0; i < route[1].length; i++) {
275 275 if (!params.hasOwnProperty(route[1][i]))
276 276 throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
277 277 }
278 278 result = sprintf(route[0], params);
279 279
280 280 var ret = [];
281 281 //extra params => GET
282 282 for(var param in params){
283 283 if (route[1].indexOf(param) == -1){
284 284 ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
285 285 }
286 286 }
287 287 var _parts = ret.join("&");
288 288 if(_parts){
289 289 result = result +'?'+ _parts
290 290 }
291 291 }
292 292
293 293 return result;
294 294 },
295 295 'register': function(route_name, route_tmpl, req_params) {
296 296 if (typeof(req_params) != 'object') {
297 297 req_params = [];
298 298 }
299 299 var keys = [];
300 300 for (var i=0; i < req_params.length; i++) {
301 301 keys.push(req_params[i])
302 302 }
303 303 matchlist[route_name] = [
304 304 unescape(route_tmpl),
305 305 keys
306 306 ]
307 307 },
308 308 '_routes': function(){
309 309 return matchlist;
310 310 }
311 311 }
312 312 })();
313 313
314 314
315 315 /**
316 316 * GLOBAL YUI Shortcuts
317 317 */
318 318 var YUC = YAHOO.util.Connect;
319 319 var YUD = YAHOO.util.Dom;
320 320 var YUE = YAHOO.util.Event;
321 321 var YUQ = YAHOO.util.Selector.query;
322 322
323 323 /* Invoke all functions in callbacks */
324 324 var _run_callbacks = function(callbacks){
325 325 if (callbacks !== undefined){
326 326 var _l = callbacks.length;
327 327 for (var i=0;i<_l;i++){
328 328 var func = callbacks[i];
329 329 if(typeof(func)=='function'){
330 330 try{
331 331 func();
332 332 }catch (err){};
333 333 }
334 334 }
335 335 }
336 336 }
337 337
338 338 /**
339 339 * turns objects into GET query string
340 340 */
341 341 var _toQueryString = function(o) {
342 342 if(typeof o !== 'object') {
343 343 return false;
344 344 }
345 345 var _p, _qs = [];
346 346 for(_p in o) {
347 347 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
348 348 }
349 349 return _qs.join('&');
350 350 };
351 351
352 352 /**
353 353 * Load HTML into DOM using Ajax
354 354 *
355 355 * @param $target: load html async and place it (or an error message) here
356 356 * @param success: success callback function
357 357 * @param args: query parameters to pass to url
358 358 */
359 359 function asynchtml(url, $target, success, args){
360 360 if(args===undefined){
361 361 args=null;
362 362 }
363 363 $target.html(_TM['Loading ...']).css('opacity','0.3');
364 364
365 $.ajax({url: url, data: args, headers: {'X-PARTIAL-XHR': '1'}, cache: false, dataType: 'html'})
365 return $.ajax({url: url, data: args, headers: {'X-PARTIAL-XHR': '1'}, cache: false, dataType: 'html'})
366 366 .done(function(html) {
367 367 $target.html(html);
368 368 $target.css('opacity','1.0');
369 369 //execute the given original callback
370 370 if (success !== undefined && success) {
371 371 success();
372 372 }
373 373 })
374 374 .fail(function(jqXHR, textStatus, errorThrown) {
375 if (textStatus == "abort")
376 return;
375 377 console.log('Ajax failure: ' + textStatus);
376 378 $target.html('<span class="error_red">ERROR: {0}</span>'.format(textStatus));
377 379 $target.css('opacity','1.0');
378 380 })
379 381 ;
380 382 };
381 383
382 384 var ajaxGET = function(url,success) {
383 385 // Set special header for ajax == HTTP_X_PARTIAL_XHR
384 386 YUC.initHeader('X-PARTIAL-XHR',true);
385 387
386 388 var sUrl = url;
387 389 var callback = {
388 390 success: success,
389 391 failure: function (o) {
390 392 if (o.status != 0) {
391 393 alert("Ajax GET error: " + o.statusText);
392 394 };
393 395 }
394 396 };
395 397
396 398 var request = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
397 399 return request;
398 400 };
399 401
400 402 var ajaxPOST = function(url,postData,success) {
401 403 // Set special header for ajax == HTTP_X_PARTIAL_XHR
402 404 YUC.initHeader('X-PARTIAL-XHR',true);
403 405
404 406 var sUrl = url;
405 407 var callback = {
406 408 success: success,
407 409 failure: function (o) {
408 410 alert("Ajax POST error: " + o.statusText);
409 411 }
410 412 };
411 413 var postData = _toQueryString(postData);
412 414 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
413 415 return request;
414 416 };
415 417
416 418
417 419 /**
418 420 * activate .show_more links
419 421 * the .show_more must have an id that is the the id of an element to hide prefixed with _
420 422 * the parentnode will be displayed
421 423 */
422 424 var show_more_event = function(){
423 425 $('.show_more').click(function(e){
424 426 var el = e.currentTarget;
425 427 $('#' + el.id.substring(1)).hide();
426 428 $(el.parentNode).show();
427 429 });
428 430 };
429 431
430 432 /**
431 433 * activate .lazy-cs mouseover for showing changeset tooltip
432 434 */
433 435 var show_changeset_tooltip = function(){
434 436 $('.lazy-cs').mouseover(function(e){
435 437 var $target = $(e.currentTarget);
436 438 var rid = $target.attr('raw_id');
437 439 var repo_name = $target.attr('repo_name');
438 440 if(rid && !$target.hasClass('tooltip')){
439 441 _show_tooltip(e, _TM['loading ...']);
440 442 var url = pyroutes.url('changeset_info', {"repo_name": repo_name, "revision": rid});
441 443 ajaxGET(url, function(o){
442 444 var json = JSON.parse(o.responseText);
443 445 $target.addClass('tooltip')
444 446 _show_tooltip(e, json['message']);
445 447 _activate_tooltip($target);
446 448 });
447 449 }
448 450 });
449 451 };
450 452
451 453 var _onSuccessFollow = function(target){
452 454 var $target = $(target);
453 455 var $f_cnt = $('#current_followers_count');
454 456 if($target.hasClass('follow')){
455 457 $target.attr('class', 'following');
456 458 $target.attr('title', _TM['Stop following this repository']);
457 459 if($f_cnt.html()){
458 460 var cnt = Number($f_cnt.html())+1;
459 461 $f_cnt.html(cnt);
460 462 }
461 463 }
462 464 else{
463 465 $target.attr('class', 'follow');
464 466 $target.attr('title', _TM['Start following this repository']);
465 467 if($f_cnt.html()){
466 468 var cnt = Number($f_cnt.html())-1;
467 469 $f_cnt.html(cnt);
468 470 }
469 471 }
470 472 }
471 473
472 474 var toggleFollowingRepo = function(target, follows_repo_id, token, user_id){
473 475 var args = 'follows_repo_id=' + follows_repo_id;
474 476 args += '&amp;auth_token=' + token;
475 477 if(user_id != undefined){
476 478 args +="&amp;user_id=" + user_id;
477 479 }
478 480 $.post(TOGGLE_FOLLOW_URL, args, function(data){
479 481 _onSuccessFollow(target);
480 482 });
481 483 return false;
482 484 };
483 485
484 486 var showRepoSize = function(target, repo_name, token){
485 487 var args = 'auth_token=' + token;
486 488
487 489 if(!$("#" + target).hasClass('loaded')){
488 490 $("#" + target).html(_TM['Loading ...']);
489 491 var url = pyroutes.url('repo_size', {"repo_name":repo_name});
490 492 $.post(url, args, function(data) {
491 493 $("#" + target).html(data);
492 494 $("#" + target).addClass('loaded');
493 495 });
494 496 }
495 497 return false;
496 498 };
497 499
498 500 /**
499 501 * tooltips
500 502 */
501 503
502 504 var tooltip_activate = function(){
503 505 $(document).ready(_init_tooltip);
504 506 };
505 507
506 508 var _activate_tooltip = function($tt){
507 509 $tt.mouseover(_show_tooltip);
508 510 $tt.mousemove(_move_tooltip);
509 511 $tt.mouseout(_close_tooltip);
510 512 };
511 513
512 514 var _init_tooltip = function(){
513 515 var $tipBox = $('#tip-box');
514 516 if(!$tipBox.length){
515 517 $tipBox = $('<div id="tip-box"></div>')
516 518 $(document.body).append($tipBox);
517 519 }
518 520
519 521 $tipBox.hide();
520 522 $tipBox.css('position', 'absolute');
521 523 $tipBox.css('max-width', '600px');
522 524
523 525 _activate_tooltip($('.tooltip'));
524 526 };
525 527
526 528 var _show_tooltip = function(e, tipText){
527 529 e.stopImmediatePropagation();
528 530 var el = e.currentTarget;
529 531 if(tipText){
530 532 // just use it
531 533 } else if(el.tagName.toLowerCase() === 'img'){
532 534 tipText = el.alt ? el.alt : '';
533 535 } else {
534 536 tipText = el.title ? el.title : '';
535 537 }
536 538
537 539 if(tipText !== ''){
538 540 // save org title
539 541 $(el).attr('tt_title', tipText);
540 542 // reset title to not show org tooltips
541 543 $(el).attr('title', '');
542 544
543 545 var $tipBox = $('#tip-box');
544 546 $tipBox.html(tipText);
545 547 $tipBox.css('display', 'block');
546 548 }
547 549 };
548 550
549 551 var _move_tooltip = function(e){
550 552 e.stopImmediatePropagation();
551 553 var $tipBox = $('#tip-box');
552 554 $tipBox.css('top', (e.pageY + 15) + 'px');
553 555 $tipBox.css('left', (e.pageX + 15) + 'px');
554 556 };
555 557
556 558 var _close_tooltip = function(e){
557 559 e.stopImmediatePropagation();
558 560 var $tipBox = $('#tip-box');
559 561 $tipBox.hide();
560 562 var el = e.currentTarget;
561 563 $(el).attr('title', $(el).attr('tt_title'));
562 564 };
563 565
564 566 /**
565 567 * Quick filter widget
566 568 *
567 569 * @param target: filter input target
568 570 * @param nodes: list of nodes in html we want to filter.
569 571 * @param display_element function that takes current node from nodes and
570 572 * does hide or show based on the node
571 573 */
572 574 var q_filter = function(target, nodes, display_element){
573 575 var nodes = nodes;
574 576 var $q_filter_field = $('#' + target);
575 577 var F = YAHOO.namespace(target);
576 578
577 579 $q_filter_field.keyup(function(e){
578 580 clearTimeout(F.filterTimeout);
579 581 F.filterTimeout = setTimeout(F.updateFilter, 600);
580 582 });
581 583
582 584 F.filterTimeout = null;
583 585
584 586 F.updateFilter = function() {
585 587 // Reset timeout
586 588 F.filterTimeout = null;
587 589
588 590 var obsolete = [];
589 591
590 592 var req = $q_filter_field.val().toLowerCase();
591 593
592 594 var l = nodes.length;
593 595 var i;
594 596 var showing = 0;
595 597
596 598 for (i=0; i<l; i++ ){
597 599 var n = nodes[i];
598 600 var target_element = display_element(n)
599 601 if(req && n.innerHTML.toLowerCase().indexOf(req) == -1){
600 602 $(target_element).hide();
601 603 }
602 604 else{
603 605 $(target_element).show();
604 606 showing += 1;
605 607 }
606 608 }
607 609
608 610 $('#repo_count').html(showing); /* FIXME: don't hardcode */
609 611 }
610 612 };
611 613
612 614 /* return jQuery expression with a tr with body in 3rd column and class cls and id named after the body */
613 615 var _table_tr = function(cls, body){
614 616 // like: <div class="comment" id="comment-8" line="o92"><div class="comment-wrapp">...
615 617 // except new inlines which are different ...
616 618 var comment_id = ($(body).attr('id') || 'comment-new').split('comment-')[1];
617 619 var tr_id = 'comment-tr-{0}'.format(comment_id);
618 620 return $(('<tr id="{0}" class="{1}">'+
619 621 '<td class="lineno-inline new-inline"></td>'+
620 622 '<td class="lineno-inline old-inline"></td>'+
621 623 '<td>{2}</td>'+
622 624 '</tr>').format(tr_id, cls, body));
623 625 };
624 626
625 627 /** return jQuery expression with new inline form based on template **/
626 628 var _createInlineForm = function(parent_tr, f_path, line) {
627 629 var $tmpl = $('#comment-inline-form-template').html().format(f_path, line);
628 630 var $form = _table_tr('comment-form-inline', $tmpl)
629 631
630 632 // create event for hide button
631 633 $form.find('.hide-inline-form').click(function(e) {
632 634 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
633 635 if($(newtr).next().hasClass('inline-comments-button')){
634 636 $(newtr).next().show();
635 637 }
636 638 $(newtr).remove();
637 639 $(parent_tr).removeClass('form-open');
638 640 $(parent_tr).removeClass('hl-comment');
639 641 });
640 642
641 643 return $form
642 644 };
643 645
644 646 /**
645 647 * Inject inline comment for an given TR. This tr should always be a .line .
646 648 * The form will be inject after any comments.
647 649 */
648 650 var injectInlineForm = function(tr){
649 651 var $tr = $(tr);
650 652 if(!$tr.hasClass('line')){
651 653 return
652 654 }
653 655 var submit_url = AJAX_COMMENT_URL;
654 656 var $td = $tr.find('.code');
655 657 if($tr.hasClass('form-open') || $tr.hasClass('context') || $td.hasClass('no-comment')){
656 658 return
657 659 }
658 660 $tr.addClass('form-open hl-comment');
659 661 var $node = $tr.parent().parent().parent().find('.full_f_path');
660 662 var f_path = $node.attr('path');
661 663 var lineno = _getLineNo(tr);
662 664 var $form = _createInlineForm(tr, f_path, lineno, submit_url);
663 665
664 666 var $parent = $tr;
665 667 while ($parent.next().hasClass('inline-comments')){
666 668 var $parent = $parent.next();
667 669 }
668 670 $form.insertAfter($parent);
669 671 var $overlay = $form.find('.submitting-overlay');
670 672 var $inlineform = $form.find('.inline-form');
671 673
672 674 $form.submit(function(e){
673 675 e.preventDefault();
674 676
675 677 if(lineno === undefined){
676 678 alert('Error submitting, line ' + lineno + ' not found.');
677 679 return
678 680 }
679 681 if(f_path === undefined){
680 682 alert('Error submitting, file path ' + f_path + ' not found.');
681 683 return
682 684 }
683 685
684 686 var text = $('#text_'+lineno).val();
685 687 if(text == ""){
686 688 return
687 689 }
688 690
689 691 $overlay.show();
690 692
691 693 var success = function(o){
692 694 $tr.removeClass('form-open');
693 695 $form.remove();
694 696 var json_data = JSON.parse(o.responseText);
695 697 _renderInlineComment(json_data);
696 698 };
697 699 var postData = {
698 700 'text': text,
699 701 'f_path': f_path,
700 702 'line': lineno
701 703 };
702 704 ajaxPOST(submit_url, postData, success);
703 705 });
704 706
705 707 $('#preview-btn_'+lineno).click(function(e){
706 708 var text = $('#text_'+lineno).val();
707 709 if(!text){
708 710 return
709 711 }
710 712 $('#preview-box_'+lineno).addClass('unloaded');
711 713 $('#preview-box_'+lineno).html(_TM['Loading ...']);
712 714 $('#edit-container_'+lineno).hide();
713 715 $('#edit-btn_'+lineno).show();
714 716 $('#preview-container_'+lineno).show();
715 717 $('#preview-btn_'+lineno).hide();
716 718
717 719 var url = pyroutes.url('changeset_comment_preview', {'repo_name': REPO_NAME});
718 720 var post_data = {'text': text};
719 721 ajaxPOST(url, post_data, function(o){
720 722 $('#preview-box_'+lineno).html(o.responseText);
721 723 $('#preview-box_'+lineno).removeClass('unloaded');
722 724 })
723 725 })
724 726 $('#edit-btn_'+lineno).click(function(e){
725 727 $('#edit-container_'+lineno).show();
726 728 $('#edit-btn_'+lineno).hide();
727 729 $('#preview-container_'+lineno).hide();
728 730 $('#preview-btn_'+lineno).show();
729 731 })
730 732
731 733 setTimeout(function(){
732 734 // callbacks
733 735 tooltip_activate();
734 736 MentionsAutoComplete('text_'+lineno, 'mentions_container_'+lineno,
735 737 _USERS_AC_DATA, _GROUPS_AC_DATA);
736 738 $('#text_'+lineno).focus();
737 739 },10)
738 740 };
739 741
740 742 var deleteComment = function(comment_id){
741 743 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__',comment_id);
742 744 var postData = {'_method':'delete'};
743 745 var success = function(o){
744 746 var $deleted = $('#comment-tr-'+comment_id);
745 747 var $prev = $deleted.prev('tr');
746 748 $deleted.remove();
747 749 _placeAddButton($prev);
748 750 }
749 751 ajaxPOST(url,postData,success);
750 752 }
751 753
752 754 var _getLineNo = function(tr) {
753 755 var line;
754 756 var o = $(tr).children()[0].id.split('_');
755 757 var n = $(tr).children()[1].id.split('_');
756 758
757 759 if (n.length >= 2) {
758 760 line = n[n.length-1];
759 761 } else if (o.length >= 2) {
760 762 line = o[o.length-1];
761 763 }
762 764
763 765 return line
764 766 };
765 767
766 768 var _placeAddButton = function($line_tr){
767 769 var $tr = $line_tr;
768 770 while ($tr.next().hasClass('inline-comments')){
769 771 $tr.find('.add-comment').remove();
770 772 $tr = $tr.next();
771 773 }
772 774 $tr.find('.add-comment').remove();
773 775 var label = TRANSLATION_MAP['Add Another Comment'];
774 776 var $html_el = $('<div class="add-comment"><span class="btn btn-mini">{0}</span></div>'.format(label));
775 777 $html_el.click(function(e) {
776 778 injectInlineForm($line_tr);
777 779 });
778 780 $tr.find('.comment').after($html_el);
779 781 };
780 782
781 783 /**
782 784 * Places the inline comment into the changeset block in proper line position
783 785 */
784 786 var _placeInline = function(target_id, lineno, html){
785 787 var $td = $("#{0}_{1}".format(target_id, lineno));
786 788 if (!$td.length){
787 789 return false;
788 790 }
789 791
790 792 // check if there are comments already !
791 793 var $line_tr = $td.parent(); // the tr
792 794 var $after_tr = $line_tr;
793 795 while ($after_tr.next().hasClass('inline-comments')){
794 796 $after_tr = $after_tr.next();
795 797 }
796 798 // put in the comment at the bottom
797 799 var $tr = _table_tr('inline-comments', html)
798 800 $tr.find('div.comment').addClass('inline-comment');
799 801 $after_tr.after($tr);
800 802
801 803 // scan nodes, and attach add button to last one
802 804 _placeAddButton($line_tr);
803 805 return true;
804 806 }
805 807
806 808 /**
807 809 * make a single inline comment and place it inside
808 810 */
809 811 var _renderInlineComment = function(json_data){
810 812 var html = json_data['rendered_text'];
811 813 var lineno = json_data['line_no'];
812 814 var target_id = json_data['target_id'];
813 815 return _placeInline(target_id, lineno, html);
814 816 }
815 817
816 818 /**
817 819 * Iterates over all the inlines, and places them inside proper blocks of data
818 820 */
819 821 var renderInlineComments = function(file_comments){
820 822 for (var f in file_comments){
821 823 // holding all comments for a FILE
822 824 var box = file_comments[f];
823 825
824 826 var target_id = $(box).attr('target_id');
825 827 // actual comments with line numbers
826 828 var comments = box.children;
827 829 for(var i=0; i<comments.length; i++){
828 830 var data = {
829 831 'rendered_text': comments[i].outerHTML,
830 832 'line_no': $(comments[i]).attr('line'),
831 833 'target_id': target_id
832 834 }
833 835 if (_renderInlineComment(data)) {
834 836 $(comments[i]).hide();
835 837 }else{
836 838 var txt = document.createTextNode(
837 839 "Comment to " + YUD.getAttribute(comments[i].parentNode,'path') +
838 840 " line " + data.line_no +
839 841 " which is outside the diff context:");
840 842 comments[i].insertBefore(txt, comments[i].firstChild);
841 843 }
842 844 }
843 845 $(box).show();
844 846 }
845 847 }
846 848
847 849 /**
848 850 * Double link comments
849 851 */
850 852 var linkInlineComments = function(firstlinks, comments){
851 853 var $comments = $(comments);
852 854 if ($comments.length > 0) {
853 855 $(firstlinks).html('<a href="#{0}">First comment</a>'.format($comments.attr('id')));
854 856 }
855 857 if ($comments.length <= 1) {
856 858 return;
857 859 }
858 860
859 861 $comments.each(function(i, e){
860 862 var prev = '';
861 863 if (i > 0){
862 864 var prev_anchor = YUD.getAttribute(comments.item(i-1),'id');
863 865 prev = '<a href="#{0}">Previous comment</a>'.format(prev_anchor);
864 866 }
865 867 var next = '';
866 868 if (i+1 < comments.length){
867 869 var next_anchor = YUD.getAttribute(comments.item(i+1),'id');
868 870 next = '<a href="#{0}">Next comment</a>'.format(next_anchor);
869 871 }
870 872 var $div = $(('<div class="prev-next-comment">'+
871 873 '<div class="prev-comment">{0}</div>'+
872 874 '<div class="next-comment">{1}</div>').format(prev, next));
873 875 $div.prependTo(this);
874 876 });
875 877 }
876 878
877 879 /* activate files.html stuff */
878 880 var fileBrowserListeners = function(current_url, node_list_url, url_base){
879 881 var current_url_branch = "?branch=__BRANCH__";
880 882
881 883 $('#stay_at_branch').on('click',function(e){
882 884 if(e.currentTarget.checked){
883 885 var uri = current_url_branch;
884 886 uri = uri.replace('__BRANCH__',e.currentTarget.value);
885 887 window.location = uri;
886 888 }
887 889 else{
888 890 window.location = current_url;
889 891 }
890 892 })
891 893
892 894 var $node_filter = $('#node_filter');
893 895
894 896 var filterTimeout = null;
895 897 var nodes = null;
896 898
897 899 var initFilter = function(){
898 900 $('#node_filter_box_loading').show();
899 901 $('#search_activate_id').hide();
900 902 $('#add_node_id').hide();
901 903 YUC.initHeader('X-PARTIAL-XHR',true);
902 904 YUC.asyncRequest('GET', node_list_url, {
903 905 success:function(o){
904 906 nodes = JSON.parse(o.responseText).nodes;
905 907 $('#node_filter_box_loading').hide();
906 908 $('#node_filter_box').show();
907 909 $node_filter.focus();
908 910 if($node_filter.hasClass('init')){
909 911 $node_filter.val('');
910 912 $node_filter.removeClass('init');
911 913 }
912 914 },
913 915 failure:function(o){
914 916 console.log('failed to load');
915 917 }
916 918 },null);
917 919 }
918 920
919 921 var updateFilter = function(e) {
920 922 return function(){
921 923 // Reset timeout
922 924 filterTimeout = null;
923 925 var query = e.currentTarget.value.toLowerCase();
924 926 var match = [];
925 927 var matches = 0;
926 928 var matches_max = 20;
927 929 if (query != ""){
928 930 for(var i=0;i<nodes.length;i++){
929 931 var pos = nodes[i].name.toLowerCase().indexOf(query)
930 932 if(query && pos != -1){
931 933 matches++
932 934 //show only certain amount to not kill browser
933 935 if (matches > matches_max){
934 936 break;
935 937 }
936 938
937 939 var n = nodes[i].name;
938 940 var t = nodes[i].type;
939 941 var n_hl = n.substring(0,pos)
940 942 +"<b>{0}</b>".format(n.substring(pos,pos+query.length))
941 943 +n.substring(pos+query.length)
942 944 var new_url = url_base.replace('__FPATH__',n);
943 945 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
944 946 }
945 947 if(match.length >= matches_max){
946 948 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
947 949 break;
948 950 }
949 951 }
950 952 }
951 953 if(query != ""){
952 954 $('#tbody').hide();
953 955 $('#tbody_filtered').show();
954 956
955 957 if (match.length==0){
956 958 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
957 959 }
958 960
959 961 $('#tbody_filtered').html(match.join(""));
960 962 }
961 963 else{
962 964 $('#tbody').show();
963 965 $('#tbody_filtered').hide();
964 966 }
965 967 }
966 968 };
967 969
968 970 $('#filter_activate').click(function(){
969 971 initFilter();
970 972 });
971 973 $node_filter.click(function(){
972 974 if($node_filter.hasClass('init')){
973 975 $node_filter.val('');
974 976 $node_filter.removeClass('init');
975 977 }
976 978 });
977 979 $node_filter.keyup(function(e){
978 980 clearTimeout(filterTimeout);
979 981 filterTimeout = setTimeout(updateFilter(e),600);
980 982 });
981 983 };
982 984
983 985
984 986 var initCodeMirror = function(textarea_id, resetUrl){
985 987 var myCodeMirror = CodeMirror.fromTextArea($('#' + textarea_id)[0], {
986 988 mode: "null",
987 989 lineNumbers: true,
988 990 indentUnit: 4,
989 991 autofocus: true
990 992 });
991 993 $('#reset').click(function(e){
992 994 window.location=resetUrl;
993 995 });
994 996
995 997 $('#file_enable').click(function(){
996 998 $('#editor_container').show();
997 999 $('#upload_file_container').hide();
998 1000 $('#filename_container').show();
999 1001 $('#set_mode_header').show();
1000 1002 });
1001 1003
1002 1004 $('#upload_file_enable').click(function(){
1003 1005 $('#editor_container').hide();
1004 1006 $('#upload_file_container').show();
1005 1007 $('#filename_container').hide();
1006 1008 $('#set_mode_header').hide();
1007 1009 });
1008 1010
1009 1011 return myCodeMirror
1010 1012 };
1011 1013
1012 1014 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
1013 1015 codeMirrorInstance.setOption("mode", mode);
1014 1016 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
1015 1017 }
1016 1018
1017 1019
1018 1020 var _getIdentNode = function(n){
1019 1021 //iterate thrugh nodes until matching interesting node
1020 1022
1021 1023 if (typeof n == 'undefined'){
1022 1024 return -1
1023 1025 }
1024 1026
1025 1027 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
1026 1028 return n
1027 1029 }
1028 1030 else{
1029 1031 return _getIdentNode(n.parentNode);
1030 1032 }
1031 1033 };
1032 1034
1033 1035 /* generate links for multi line selects that can be shown by files.html page_highlights.
1034 1036 * This is a mouseup handler for hlcode from CodeHtmlFormatter and pygmentize */
1035 1037 var getSelectionLink = function(e) {
1036 1038 //get selection from start/to nodes
1037 1039 if (typeof window.getSelection != "undefined") {
1038 1040 s = window.getSelection();
1039 1041
1040 1042 var from = _getIdentNode(s.anchorNode);
1041 1043 var till = _getIdentNode(s.focusNode);
1042 1044
1043 1045 var f_int = parseInt(from.id.replace('L',''));
1044 1046 var t_int = parseInt(till.id.replace('L',''));
1045 1047
1046 1048 var yoffset = 35;
1047 1049 var ranges = [parseInt(from.id.replace('L','')), parseInt(till.id.replace('L',''))];
1048 1050 if (ranges[0] > ranges[1]){
1049 1051 //highlight from bottom
1050 1052 yoffset = -yoffset;
1051 1053 ranges = [ranges[1], ranges[0]];
1052 1054 }
1053 1055 var $hl_div = $('div#linktt');
1054 1056 // if we select more than 2 lines
1055 1057 if (ranges[0] != ranges[1]){
1056 1058 if ($hl_div.length) {
1057 1059 $hl_div.html('');
1058 1060 } else {
1059 1061 $hl_div = $('<div id="linktt" class="hl-tip-box">');
1060 1062 $('body').prepend($hl_div);
1061 1063 }
1062 1064
1063 1065 $hl_div.append($('<a>').html(_TM['Selection link']).attr('href', location.href.substring(0, location.href.indexOf('#')) + '#L' + ranges[0] + '-'+ranges[1]));
1064 1066 var xy = $(till).offset();
1065 1067 $hl_div.css('top', (xy.top + yoffset) + 'px').css('left', xy.left + 'px');
1066 1068 $hl_div.show();
1067 1069 }
1068 1070 else{
1069 1071 $hl_div.hide();
1070 1072 }
1071 1073 }
1072 1074 };
1073 1075
1074 1076 var deleteNotification = function(url, notification_id, callbacks){
1075 1077 var callback = {
1076 1078 success:function(o){
1077 1079 $("#notification_"+notification_id).remove();
1078 1080 _run_callbacks(callbacks);
1079 1081 },
1080 1082 failure:function(o){
1081 1083 alert("deleteNotification failure");
1082 1084 }
1083 1085 };
1084 1086 var postData = '_method=delete';
1085 1087 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1086 1088 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
1087 1089 callback, postData);
1088 1090 };
1089 1091
1090 1092 var readNotification = function(url, notification_id, callbacks){
1091 1093 var callback = {
1092 1094 success:function(o){
1093 1095 var $obj = $("#notification_"+notification_id);
1094 1096 $obj.removeClass('unread');
1095 1097 $obj.find('.read-notification').remove();
1096 1098 _run_callbacks(callbacks);
1097 1099 },
1098 1100 failure:function(o){
1099 1101 alert("readNotification failure");
1100 1102 }
1101 1103 };
1102 1104 var postData = '_method=put';
1103 1105 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1104 1106 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
1105 1107 callback, postData);
1106 1108 };
1107 1109
1108 1110 /** MEMBERS AUTOCOMPLETE WIDGET **/
1109 1111
1110 1112 var _MembersAutoComplete = function (divid, cont, users_list, groups_list) {
1111 1113 var myUsers = users_list;
1112 1114 var myGroups = groups_list;
1113 1115
1114 1116 // Define a custom search function for the DataSource of users
1115 1117 var matchUsers = function (sQuery) {
1116 1118 // Case insensitive matching
1117 1119 var query = sQuery.toLowerCase();
1118 1120 var i = 0;
1119 1121 var l = myUsers.length;
1120 1122 var matches = [];
1121 1123
1122 1124 // Match against each name of each contact
1123 1125 for (; i < l; i++) {
1124 1126 var contact = myUsers[i];
1125 1127 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1126 1128 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1127 1129 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1128 1130 matches[matches.length] = contact;
1129 1131 }
1130 1132 }
1131 1133 return matches;
1132 1134 };
1133 1135
1134 1136 // Define a custom search function for the DataSource of userGroups
1135 1137 var matchGroups = function (sQuery) {
1136 1138 // Case insensitive matching
1137 1139 var query = sQuery.toLowerCase();
1138 1140 var i = 0;
1139 1141 var l = myGroups.length;
1140 1142 var matches = [];
1141 1143
1142 1144 // Match against each name of each contact
1143 1145 for (; i < l; i++) {
1144 1146 var matched_group = myGroups[i];
1145 1147 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1146 1148 matches[matches.length] = matched_group;
1147 1149 }
1148 1150 }
1149 1151 return matches;
1150 1152 };
1151 1153
1152 1154 //match all
1153 1155 var matchAll = function (sQuery) {
1154 1156 var u = matchUsers(sQuery);
1155 1157 var g = matchGroups(sQuery);
1156 1158 return u.concat(g);
1157 1159 };
1158 1160
1159 1161 // DataScheme for members
1160 1162 var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
1161 1163 memberDS.responseSchema = {
1162 1164 fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk"]
1163 1165 };
1164 1166
1165 1167 // DataScheme for owner
1166 1168 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1167 1169 ownerDS.responseSchema = {
1168 1170 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1169 1171 };
1170 1172
1171 1173 // Instantiate AutoComplete for perms
1172 1174 var membersAC = new YAHOO.widget.AutoComplete(divid, cont, memberDS);
1173 1175 membersAC.useShadow = false;
1174 1176 membersAC.resultTypeList = false;
1175 1177 membersAC.animVert = false;
1176 1178 membersAC.animHoriz = false;
1177 1179 membersAC.animSpeed = 0.1;
1178 1180
1179 1181 // Instantiate AutoComplete for owner
1180 1182 var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
1181 1183 ownerAC.useShadow = false;
1182 1184 ownerAC.resultTypeList = false;
1183 1185 ownerAC.animVert = false;
1184 1186 ownerAC.animHoriz = false;
1185 1187 ownerAC.animSpeed = 0.1;
1186 1188
1187 1189 // Helper highlight function for the formatter
1188 1190 var highlightMatch = function (full, snippet, matchindex) {
1189 1191 return full.substring(0, matchindex)
1190 1192 + "<span class='match'>"
1191 1193 + full.substr(matchindex, snippet.length)
1192 1194 + "</span>" + full.substring(matchindex + snippet.length);
1193 1195 };
1194 1196
1195 1197 // Custom formatter to highlight the matching letters
1196 1198 var custom_formatter = function (oResultData, sQuery, sResultMatch) {
1197 1199 var query = sQuery.toLowerCase();
1198 1200 var _gravatar = function(res, em, group){
1199 1201 if (group !== undefined){
1200 1202 em = '/images/icons/group.png'
1201 1203 }
1202 1204 var tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1203 1205 return tmpl.format(em,res)
1204 1206 }
1205 1207 // group
1206 1208 if (oResultData.grname != undefined) {
1207 1209 var grname = oResultData.grname;
1208 1210 var grmembers = oResultData.grmembers;
1209 1211 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
1210 1212 var grprefix = "{0}: ".format(_TM['Group']);
1211 1213 var grsuffix = " (" + grmembers + " )";
1212 1214 var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
1213 1215
1214 1216 if (grnameMatchIndex > -1) {
1215 1217 return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,true);
1216 1218 }
1217 1219 return _gravatar(grprefix + oResultData.grname + grsuffix, null,true);
1218 1220 // Users
1219 1221 } else if (oResultData.nname != undefined) {
1220 1222 var fname = oResultData.fname || "";
1221 1223 var lname = oResultData.lname || "";
1222 1224 var nname = oResultData.nname;
1223 1225
1224 1226 // Guard against null value
1225 1227 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1226 1228 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1227 1229 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1228 1230 displayfname, displaylname, displaynname;
1229 1231
1230 1232 if (fnameMatchIndex > -1) {
1231 1233 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1232 1234 } else {
1233 1235 displayfname = fname;
1234 1236 }
1235 1237
1236 1238 if (lnameMatchIndex > -1) {
1237 1239 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1238 1240 } else {
1239 1241 displaylname = lname;
1240 1242 }
1241 1243
1242 1244 if (nnameMatchIndex > -1) {
1243 1245 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1244 1246 } else {
1245 1247 displaynname = nname ? "(" + nname + ")" : "";
1246 1248 }
1247 1249
1248 1250 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1249 1251 } else {
1250 1252 return '';
1251 1253 }
1252 1254 };
1253 1255 membersAC.formatResult = custom_formatter;
1254 1256 ownerAC.formatResult = custom_formatter;
1255 1257
1256 1258 var myHandler = function (sType, aArgs) {
1257 1259 var nextId = divid.split('perm_new_member_name_')[1];
1258 1260 var myAC = aArgs[0]; // reference back to the AC instance
1259 1261 var elLI = aArgs[1]; // reference to the selected LI element
1260 1262 var oData = aArgs[2]; // object literal of selected item's result data
1261 1263 //fill the autocomplete with value
1262 1264 if (oData.nname != undefined) {
1263 1265 //users
1264 1266 myAC.getInputEl().value = oData.nname;
1265 1267 $('#perm_new_member_type_'+nextId).val('user');
1266 1268 } else {
1267 1269 //groups
1268 1270 myAC.getInputEl().value = oData.grname;
1269 1271 $('#perm_new_member_type_'+nextId).val('users_group');
1270 1272 }
1271 1273 };
1272 1274
1273 1275 membersAC.itemSelectEvent.subscribe(myHandler);
1274 1276 if(ownerAC.itemSelectEvent){
1275 1277 ownerAC.itemSelectEvent.subscribe(myHandler);
1276 1278 }
1277 1279
1278 1280 return {
1279 1281 memberDS: memberDS,
1280 1282 ownerDS: ownerDS,
1281 1283 membersAC: membersAC,
1282 1284 ownerAC: ownerAC
1283 1285 };
1284 1286 }
1285 1287
1286 1288 var MentionsAutoComplete = function (divid, cont, users_list, groups_list) {
1287 1289 var myUsers = users_list;
1288 1290 var myGroups = groups_list;
1289 1291
1290 1292 // Define a custom search function for the DataSource of users
1291 1293 var matchUsers = function (sQuery) {
1292 1294 var org_sQuery = sQuery;
1293 1295 if(this.mentionQuery == null){
1294 1296 return []
1295 1297 }
1296 1298 sQuery = this.mentionQuery;
1297 1299 // Case insensitive matching
1298 1300 var query = sQuery.toLowerCase();
1299 1301 var i = 0;
1300 1302 var l = myUsers.length;
1301 1303 var matches = [];
1302 1304
1303 1305 // Match against each name of each contact
1304 1306 for (; i < l; i++) {
1305 1307 var contact = myUsers[i];
1306 1308 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1307 1309 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1308 1310 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1309 1311 matches[matches.length] = contact;
1310 1312 }
1311 1313 }
1312 1314 return matches
1313 1315 };
1314 1316
1315 1317 //match all
1316 1318 var matchAll = function (sQuery) {
1317 1319 return matchUsers(sQuery);
1318 1320 };
1319 1321
1320 1322 // DataScheme for owner
1321 1323 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1322 1324
1323 1325 ownerDS.responseSchema = {
1324 1326 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1325 1327 };
1326 1328
1327 1329 // Instantiate AutoComplete for mentions
1328 1330 var ownerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1329 1331 ownerAC.useShadow = false;
1330 1332 ownerAC.resultTypeList = false;
1331 1333 ownerAC.suppressInputUpdate = true;
1332 1334 ownerAC.animVert = false;
1333 1335 ownerAC.animHoriz = false;
1334 1336 ownerAC.animSpeed = 0.1;
1335 1337
1336 1338 // Helper highlight function for the formatter
1337 1339 var highlightMatch = function (full, snippet, matchindex) {
1338 1340 return full.substring(0, matchindex)
1339 1341 + "<span class='match'>"
1340 1342 + full.substr(matchindex, snippet.length)
1341 1343 + "</span>" + full.substring(matchindex + snippet.length);
1342 1344 };
1343 1345
1344 1346 // Custom formatter to highlight the matching letters
1345 1347 ownerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1346 1348 var org_sQuery = sQuery;
1347 1349 if(this.dataSource.mentionQuery != null){
1348 1350 sQuery = this.dataSource.mentionQuery;
1349 1351 }
1350 1352
1351 1353 var query = sQuery.toLowerCase();
1352 1354 var _gravatar = function(res, em, group){
1353 1355 if (group !== undefined){
1354 1356 em = '/images/icons/group.png'
1355 1357 }
1356 1358 var tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1357 1359 return tmpl.format(em,res)
1358 1360 }
1359 1361 if (oResultData.nname != undefined) {
1360 1362 var fname = oResultData.fname || "";
1361 1363 var lname = oResultData.lname || "";
1362 1364 var nname = oResultData.nname;
1363 1365
1364 1366 // Guard against null value
1365 1367 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1366 1368 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1367 1369 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1368 1370 displayfname, displaylname, displaynname;
1369 1371
1370 1372 if (fnameMatchIndex > -1) {
1371 1373 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1372 1374 } else {
1373 1375 displayfname = fname;
1374 1376 }
1375 1377
1376 1378 if (lnameMatchIndex > -1) {
1377 1379 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1378 1380 } else {
1379 1381 displaylname = lname;
1380 1382 }
1381 1383
1382 1384 if (nnameMatchIndex > -1) {
1383 1385 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1384 1386 } else {
1385 1387 displaynname = nname ? "(" + nname + ")" : "";
1386 1388 }
1387 1389
1388 1390 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1389 1391 } else {
1390 1392 return '';
1391 1393 }
1392 1394 };
1393 1395
1394 1396 if(ownerAC.itemSelectEvent){
1395 1397 ownerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1396 1398 var myAC = aArgs[0]; // reference back to the AC instance
1397 1399 var elLI = aArgs[1]; // reference to the selected LI element
1398 1400 var oData = aArgs[2]; // object literal of selected item's result data
1399 1401 //fill the autocomplete with value
1400 1402 if (oData.nname != undefined) {
1401 1403 //users
1402 1404 //Replace the mention name with replaced
1403 1405 var re = new RegExp();
1404 1406 var org = myAC.getInputEl().value;
1405 1407 var chunks = myAC.dataSource.chunks
1406 1408 // replace middle chunk(the search term) with actuall match
1407 1409 chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
1408 1410 '@'+oData.nname+' ');
1409 1411 myAC.getInputEl().value = chunks.join('')
1410 1412 myAC.getInputEl().focus(); // Y U NO WORK !?
1411 1413 } else {
1412 1414 //groups
1413 1415 myAC.getInputEl().value = oData.grname;
1414 1416 $('#perm_new_member_type').val('users_group');
1415 1417 }
1416 1418 });
1417 1419 }
1418 1420
1419 1421 // in this keybuffer we will gather current value of search !
1420 1422 // since we need to get this just when someone does `@` then we do the
1421 1423 // search
1422 1424 ownerAC.dataSource.chunks = [];
1423 1425 ownerAC.dataSource.mentionQuery = null;
1424 1426
1425 1427 ownerAC.get_mention = function(msg, max_pos) {
1426 1428 var org = msg;
1427 1429 // Must match utils2.py MENTIONS_REGEX.
1428 1430 // Only matching on string up to cursor, so it must end with $
1429 1431 var re = new RegExp('(?:^|[^a-zA-Z0-9])@([a-zA-Z0-9][-_.a-zA-Z0-9]*[a-zA-Z0-9])$')
1430 1432 var chunks = [];
1431 1433
1432 1434 // cut first chunk until current pos
1433 1435 var to_max = msg.substr(0, max_pos);
1434 1436 var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
1435 1437 var msg2 = to_max.substr(at_pos);
1436 1438
1437 1439 chunks.push(org.substr(0,at_pos))// prefix chunk
1438 1440 chunks.push(msg2) // search chunk
1439 1441 chunks.push(org.substr(max_pos)) // postfix chunk
1440 1442
1441 1443 // clean up msg2 for filtering and regex match
1442 1444 var msg2 = msg2.lstrip(' ').lstrip('\n');
1443 1445
1444 1446 if(re.test(msg2)){
1445 1447 var unam = re.exec(msg2)[1];
1446 1448 return [unam, chunks];
1447 1449 }
1448 1450 return [null, null];
1449 1451 };
1450 1452
1451 1453 var $divid = $('#'+divid);
1452 1454 $divid.keyup(function(e){
1453 1455 var currentMessage = $divid.val();
1454 1456 var currentCaretPosition = $divid[0].selectionStart;
1455 1457
1456 1458 var unam = ownerAC.get_mention(currentMessage, currentCaretPosition);
1457 1459 var curr_search = null;
1458 1460 if(unam[0]){
1459 1461 curr_search = unam[0];
1460 1462 }
1461 1463
1462 1464 ownerAC.dataSource.chunks = unam[1];
1463 1465 ownerAC.dataSource.mentionQuery = curr_search;
1464 1466 });
1465 1467 }
1466 1468
1467 1469 var addReviewMember = function(id,fname,lname,nname,gravatar_link){
1468 1470 var displayname = "{0} {1} ({2})".format(fname, lname, nname);
1469 1471 var element = (
1470 1472 ' <li id="reviewer_{2}">\n'+
1471 1473 ' <div class="reviewers_member">\n'+
1472 1474 ' <div class="reviewer_status tooltip" title="not_reviewed">\n'+
1473 1475 ' <img src="/images/icons/flag_status_not_reviewed.png"/>\n'+
1474 1476 ' </div>\n'+
1475 1477 ' <div class="reviewer_gravatar gravatar"><img alt="gravatar" src="{0}"/> </div>\n'+
1476 1478 ' <div style="float:left;">{1}</div>\n'+
1477 1479 ' <input type="hidden" value="{2}" name="review_members" />\n'+
1478 1480 ' <div class="reviewer_member_remove action_button" onclick="removeReviewMember({2})">\n'+
1479 1481 ' <i class="icon-remove-sign" style="color: #FF4444;"></i>\n'+
1480 1482 ' </div> *\n'+
1481 1483 ' </div>\n'+
1482 1484 ' </li>\n'
1483 1485 ).format(gravatar_link, displayname, id);
1484 1486 // check if we don't have this ID already in
1485 1487 var ids = [];
1486 1488 $('#review_members').find('li').each(function() {
1487 1489 ids.push(this.id);
1488 1490 });
1489 1491 if(ids.indexOf('reviewer_'+id) == -1){
1490 1492 //only add if it's not there
1491 1493 $('#review_members').append(element);
1492 1494 }
1493 1495 }
1494 1496
1495 1497 var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
1496 1498 var $li = $('#reviewer_{0}'.format(reviewer_id));
1497 1499 $li.find('div div').css("text-decoration", "line-through");
1498 1500 $li.find('input').remove();
1499 1501 $li.find('.reviewer_member_remove').remove();
1500 1502 }
1501 1503
1502 1504 /* handle "Save Changes" of addReviewMember and removeReviewMember on PR */
1503 1505 var updateReviewers = function(reviewers_ids, repo_name, pull_request_id){
1504 1506 if (reviewers_ids === undefined){
1505 1507 var reviewers_ids = [];
1506 1508 $('#review_members').find('input').each(function(){
1507 1509 reviewers_ids.push(this.value);
1508 1510 });
1509 1511 }
1510 1512 var url = pyroutes.url('pullrequest_update', {"repo_name":repo_name,
1511 1513 "pull_request_id": pull_request_id});
1512 1514 var postData = {'_method':'put',
1513 1515 'reviewers_ids': reviewers_ids};
1514 1516 var success = function(o){
1515 1517 window.location.reload();
1516 1518 }
1517 1519 ajaxPOST(url,postData,success);
1518 1520 }
1519 1521
1520 1522 /* activate auto completion of users and groups ... but only used for users as PR reviewers */
1521 1523 var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) {
1522 1524 var myUsers = users_list;
1523 1525 var myGroups = groups_list;
1524 1526
1525 1527 // Define a custom search function for the DataSource of users
1526 1528 var matchUsers = function (sQuery) {
1527 1529 // Case insensitive matching
1528 1530 var query = sQuery.toLowerCase();
1529 1531 var i = 0;
1530 1532 var l = myUsers.length;
1531 1533 var matches = [];
1532 1534
1533 1535 // Match against each name of each contact
1534 1536 for (; i < l; i++) {
1535 1537 var contact = myUsers[i];
1536 1538 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1537 1539 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1538 1540 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1539 1541 matches[matches.length] = contact;
1540 1542 }
1541 1543 }
1542 1544 return matches;
1543 1545 };
1544 1546
1545 1547 // Define a custom search function for the DataSource of userGroups
1546 1548 var matchGroups = function (sQuery) {
1547 1549 // Case insensitive matching
1548 1550 var query = sQuery.toLowerCase();
1549 1551 var i = 0;
1550 1552 var l = myGroups.length;
1551 1553 var matches = [];
1552 1554
1553 1555 // Match against each name of each contact
1554 1556 for (; i < l; i++) {
1555 1557 matched_group = myGroups[i];
1556 1558 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1557 1559 matches[matches.length] = matched_group;
1558 1560 }
1559 1561 }
1560 1562 return matches;
1561 1563 };
1562 1564
1563 1565 //match all
1564 1566 var matchAll = function (sQuery) {
1565 1567 return matchUsers(sQuery);
1566 1568 };
1567 1569
1568 1570 // DataScheme for owner
1569 1571 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1570 1572
1571 1573 ownerDS.responseSchema = {
1572 1574 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1573 1575 };
1574 1576
1575 1577 // Instantiate AutoComplete for mentions
1576 1578 var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1577 1579 reviewerAC.useShadow = false;
1578 1580 reviewerAC.resultTypeList = false;
1579 1581 reviewerAC.suppressInputUpdate = true;
1580 1582 reviewerAC.animVert = false;
1581 1583 reviewerAC.animHoriz = false;
1582 1584 reviewerAC.animSpeed = 0.1;
1583 1585
1584 1586 // Helper highlight function for the formatter
1585 1587 var highlightMatch = function (full, snippet, matchindex) {
1586 1588 return full.substring(0, matchindex)
1587 1589 + "<span class='match'>"
1588 1590 + full.substr(matchindex, snippet.length)
1589 1591 + "</span>" + full.substring(matchindex + snippet.length);
1590 1592 };
1591 1593
1592 1594 // Custom formatter to highlight the matching letters
1593 1595 reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1594 1596 var org_sQuery = sQuery;
1595 1597 if(this.dataSource.mentionQuery != null){
1596 1598 sQuery = this.dataSource.mentionQuery;
1597 1599 }
1598 1600
1599 1601 var query = sQuery.toLowerCase();
1600 1602 var _gravatar = function(res, em, group){
1601 1603 if (group !== undefined){
1602 1604 em = '/images/icons/group.png'
1603 1605 }
1604 1606 var tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1605 1607 return tmpl.format(em,res)
1606 1608 }
1607 1609 if (oResultData.nname != undefined) {
1608 1610 var fname = oResultData.fname || "";
1609 1611 var lname = oResultData.lname || "";
1610 1612 var nname = oResultData.nname;
1611 1613
1612 1614 // Guard against null value
1613 1615 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1614 1616 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1615 1617 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1616 1618 displayfname, displaylname, displaynname;
1617 1619
1618 1620 if (fnameMatchIndex > -1) {
1619 1621 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1620 1622 } else {
1621 1623 displayfname = fname;
1622 1624 }
1623 1625
1624 1626 if (lnameMatchIndex > -1) {
1625 1627 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1626 1628 } else {
1627 1629 displaylname = lname;
1628 1630 }
1629 1631
1630 1632 if (nnameMatchIndex > -1) {
1631 1633 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1632 1634 } else {
1633 1635 displaynname = nname ? "(" + nname + ")" : "";
1634 1636 }
1635 1637
1636 1638 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1637 1639 } else {
1638 1640 return '';
1639 1641 }
1640 1642 };
1641 1643
1642 1644 //members cache to catch duplicates
1643 1645 reviewerAC.dataSource.cache = [];
1644 1646 // hack into select event
1645 1647 if(reviewerAC.itemSelectEvent){
1646 1648 reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1647 1649
1648 1650 var myAC = aArgs[0]; // reference back to the AC instance
1649 1651 var elLI = aArgs[1]; // reference to the selected LI element
1650 1652 var oData = aArgs[2]; // object literal of selected item's result data
1651 1653
1652 1654 //fill the autocomplete with value
1653 1655 if (oData.nname != undefined) {
1654 1656 addReviewMember(oData.id, oData.fname, oData.lname, oData.nname,
1655 1657 oData.gravatar_lnk);
1656 1658 myAC.dataSource.cache.push(oData.id);
1657 1659 $('#user').val('');
1658 1660 }
1659 1661 });
1660 1662 }
1661 1663 }
1662 1664
1663 1665 /**
1664 1666 * Activate .quick_repo_menu
1665 1667 */
1666 1668 var quick_repo_menu = function(){
1667 1669 $(".quick_repo_menu").mouseenter(function(e) {
1668 1670 var $menu = $(e.currentTarget).children().first().children().first();
1669 1671 if($menu.hasClass('hidden')){
1670 1672 $menu.removeClass('hidden').addClass('active');
1671 1673 $(e.currentTarget).removeClass('hidden').addClass('active');
1672 1674 }
1673 1675 })
1674 1676 $(".quick_repo_menu").mouseleave(function(e) {
1675 1677 var $menu = $(e.currentTarget).children().first().children().first();
1676 1678 if($menu.hasClass('active')){
1677 1679 $menu.removeClass('active').addClass('hidden');
1678 1680 $(e.currentTarget).removeClass('active').addClass('hidden');
1679 1681 }
1680 1682 })
1681 1683 };
1682 1684
1683 1685
1684 1686 /**
1685 1687 * TABLE SORTING
1686 1688 */
1687 1689
1688 1690 var revisionSort = function(a, b, desc, field) {
1689 1691 var a_ = parseInt(a.getData('last_rev_raw') || 0);
1690 1692 var b_ = parseInt(b.getData('last_rev_raw') || 0);
1691 1693
1692 1694 return YAHOO.util.Sort.compare(a_, b_, desc);
1693 1695 };
1694 1696
1695 1697 var ageSort = function(a, b, desc, field) {
1696 1698 // data is like: <span class="tooltip" date="2014-06-04 18:18:55.325474" title="Wed, 04 Jun 2014 18:18:55">1 day and 23 hours ago</span>
1697 1699 var a_ = $(a.getData(field)).attr('date');
1698 1700 var b_ = $(b.getData(field)).attr('date');
1699 1701
1700 1702 return YAHOO.util.Sort.compare(a_, b_, desc);
1701 1703 };
1702 1704
1703 1705 var lastLoginSort = function(a, b, desc, field) {
1704 1706 var a_ = parseFloat(a.getData('last_login_raw') || 0);
1705 1707 var b_ = parseFloat(b.getData('last_login_raw') || 0);
1706 1708
1707 1709 return YAHOO.util.Sort.compare(a_, b_, desc);
1708 1710 };
1709 1711
1710 1712 var nameSort = function(a, b, desc, field) {
1711 1713 var a_ = a.getData('raw_name') || 0;
1712 1714 var b_ = b.getData('raw_name') || 0;
1713 1715
1714 1716 return YAHOO.util.Sort.compare(a_, b_, desc);
1715 1717 };
1716 1718
1717 1719 var dateSort = function(a, b, desc, field) {
1718 1720 var a_ = parseFloat(a.getData('raw_date') || 0);
1719 1721 var b_ = parseFloat(b.getData('raw_date') || 0);
1720 1722
1721 1723 return YAHOO.util.Sort.compare(a_, b_, desc);
1722 1724 };
1723 1725
1724 1726 var addPermAction = function(_html, users_list, groups_list){
1725 1727 var $last_node = $('.last_new_member').last(); // empty tr between last and add
1726 1728 var next_id = $('.new_members').length;
1727 1729 $last_node.before($('<tr class="new_members">').append(_html.format(next_id)));
1728 1730 _MembersAutoComplete("perm_new_member_name_"+next_id,
1729 1731 "perm_container_"+next_id, users_list, groups_list);
1730 1732 }
1731 1733
1732 1734 function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) {
1733 1735 var callback = {
1734 1736 success: function (o) {
1735 1737 $('#' + field_id).remove();
1736 1738 },
1737 1739 failure: function (o) {
1738 1740 alert(_TM['Failed to revoke permission'] + ": " + o.status);
1739 1741 }
1740 1742 };
1741 1743 var query_params = {
1742 1744 '_method': 'delete'
1743 1745 }
1744 1746 // put extra data into POST
1745 1747 if (extra_data !== undefined && (typeof extra_data === 'object')){
1746 1748 for(var k in extra_data){
1747 1749 query_params[k] = extra_data[k];
1748 1750 }
1749 1751 }
1750 1752
1751 1753 if (obj_type=='user'){
1752 1754 query_params['user_id'] = obj_id;
1753 1755 query_params['obj_type'] = 'user';
1754 1756 }
1755 1757 else if (obj_type=='user_group'){
1756 1758 query_params['user_group_id'] = obj_id;
1757 1759 query_params['obj_type'] = 'user_group';
1758 1760 }
1759 1761
1760 1762 var request = YAHOO.util.Connect.asyncRequest('POST', url, callback,
1761 1763 _toQueryString(query_params));
1762 1764 };
1763 1765
1764 1766 /* Multi selectors */
1765 1767
1766 1768 var MultiSelectWidget = function(selected_id, available_id, form_id){
1767 1769 var $availableselect = $('#' + available_id);
1768 1770 var $selectedselect = $('#' + selected_id);
1769 1771
1770 1772 //fill available only with those not in selected
1771 1773 var $selectedoptions = $selectedselect.children('option');
1772 1774 $availableselect.children('option').filter(function(i, e){
1773 1775 for(var j = 0, node; node = $selectedoptions[j]; j++){
1774 1776 if(node.value == e.value){
1775 1777 return true;
1776 1778 }
1777 1779 }
1778 1780 return false;
1779 1781 }).remove();
1780 1782
1781 1783 $('#add_element').click(function(e){
1782 1784 $selectedselect.append($availableselect.children('option:selected'));
1783 1785 });
1784 1786 $('#remove_element').click(function(e){
1785 1787 $availableselect.append($selectedselect.children('option:selected'));
1786 1788 });
1787 1789 $('#add_all_elements').click(function(e){
1788 1790 $selectedselect.append($availableselect.children('option'));
1789 1791 });
1790 1792 $('#remove_all_elements').click(function(e){
1791 1793 $availableselect.append($selectedselect.children('option'));
1792 1794 });
1793 1795
1794 1796 $('#'+form_id).submit(function(){
1795 1797 $selectedselect.children('option').each(function(i, e){
1796 1798 e.selected = 'selected';
1797 1799 });
1798 1800 });
1799 1801 }
1800 1802
1801 1803 // custom paginator
1802 1804 var YUI_paginator = function(links_per_page, containers){
1803 1805
1804 1806 (function () {
1805 1807
1806 1808 var Paginator = YAHOO.widget.Paginator,
1807 1809 l = YAHOO.lang,
1808 1810 setId = YAHOO.util.Dom.generateId;
1809 1811
1810 1812 Paginator.ui.MyFirstPageLink = function (p) {
1811 1813 this.paginator = p;
1812 1814
1813 1815 p.subscribe('recordOffsetChange',this.update,this,true);
1814 1816 p.subscribe('rowsPerPageChange',this.update,this,true);
1815 1817 p.subscribe('totalRecordsChange',this.update,this,true);
1816 1818 p.subscribe('destroy',this.destroy,this,true);
1817 1819
1818 1820 // TODO: make this work
1819 1821 p.subscribe('firstPageLinkLabelChange',this.update,this,true);
1820 1822 p.subscribe('firstPageLinkClassChange',this.update,this,true);
1821 1823 };
1822 1824
1823 1825 Paginator.ui.MyFirstPageLink.init = function (p) {
1824 1826 p.setAttributeConfig('firstPageLinkLabel', {
1825 1827 value : 1,
1826 1828 validator : l.isString
1827 1829 });
1828 1830 p.setAttributeConfig('firstPageLinkClass', {
1829 1831 value : 'yui-pg-first',
1830 1832 validator : l.isString
1831 1833 });
1832 1834 p.setAttributeConfig('firstPageLinkTitle', {
1833 1835 value : 'First Page',
1834 1836 validator : l.isString
1835 1837 });
1836 1838 };
1837 1839
1838 1840 // Instance members and methods
1839 1841 Paginator.ui.MyFirstPageLink.prototype = {
1840 1842 current : null,
1841 1843 leftmost_page: null,
1842 1844 rightmost_page: null,
1843 1845 link : null,
1844 1846 span : null,
1845 1847 dotdot : null,
1846 1848 getPos : function(cur_page, max_page, items){
1847 1849 var edge = parseInt(items / 2) + 1;
1848 1850 if (cur_page <= edge){
1849 1851 var radius = Math.max(parseInt(items / 2), items - cur_page);
1850 1852 }
1851 1853 else if ((max_page - cur_page) < edge) {
1852 1854 var radius = (items - 1) - (max_page - cur_page);
1853 1855 }
1854 1856 else{
1855 1857 var radius = parseInt(items / 2);
1856 1858 }
1857 1859
1858 1860 var left = Math.max(1, (cur_page - (radius)))
1859 1861 var right = Math.min(max_page, cur_page + (radius))
1860 1862 return [left, cur_page, right]
1861 1863 },
1862 1864 render : function (id_base) {
1863 1865 var p = this.paginator,
1864 1866 c = p.get('firstPageLinkClass'),
1865 1867 label = p.get('firstPageLinkLabel'),
1866 1868 title = p.get('firstPageLinkTitle');
1867 1869
1868 1870 this.link = document.createElement('a');
1869 1871 this.span = document.createElement('span');
1870 1872 $(this.span).hide();
1871 1873
1872 1874 var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
1873 1875 this.leftmost_page = _pos[0];
1874 1876 this.rightmost_page = _pos[2];
1875 1877
1876 1878 setId(this.link, id_base + '-first-link');
1877 1879 this.link.href = '#';
1878 1880 this.link.className = c;
1879 1881 this.link.innerHTML = label;
1880 1882 this.link.title = title;
1881 1883 YUE.on(this.link,'click',this.onClick,this,true);
1882 1884
1883 1885 setId(this.span, id_base + '-first-span');
1884 1886 this.span.className = c;
1885 1887 this.span.innerHTML = label;
1886 1888
1887 1889 this.current = p.getCurrentPage() > 1 ? this.link : this.span;
1888 1890 return this.current;
1889 1891 },
1890 1892 update : function (e) {
1891 1893 var p = this.paginator;
1892 1894 var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
1893 1895 this.leftmost_page = _pos[0];
1894 1896 this.rightmost_page = _pos[2];
1895 1897
1896 1898 if (e && e.prevValue === e.newValue) {
1897 1899 return;
1898 1900 }
1899 1901
1900 1902 var par = this.current ? this.current.parentNode : null;
1901 1903 if (this.leftmost_page > 1) {
1902 1904 if (par && this.current === this.span) {
1903 1905 par.replaceChild(this.link,this.current);
1904 1906 this.current = this.link;
1905 1907 }
1906 1908 } else {
1907 1909 if (par && this.current === this.link) {
1908 1910 par.replaceChild(this.span,this.current);
1909 1911 this.current = this.span;
1910 1912 }
1911 1913 }
1912 1914 },
1913 1915 destroy : function () {
1914 1916 YUE.purgeElement(this.link);
1915 1917 this.current.parentNode.removeChild(this.current);
1916 1918 this.link = this.span = null;
1917 1919 },
1918 1920 onClick : function (e) {
1919 1921 YUE.stopEvent(e);
1920 1922 this.paginator.setPage(1);
1921 1923 }
1922 1924 };
1923 1925
1924 1926 })();
1925 1927
1926 1928 (function () {
1927 1929
1928 1930 var Paginator = YAHOO.widget.Paginator,
1929 1931 l = YAHOO.lang,
1930 1932 setId = YAHOO.util.Dom.generateId;
1931 1933
1932 1934 Paginator.ui.MyLastPageLink = function (p) {
1933 1935 this.paginator = p;
1934 1936
1935 1937 p.subscribe('recordOffsetChange',this.update,this,true);
1936 1938 p.subscribe('rowsPerPageChange',this.update,this,true);
1937 1939 p.subscribe('totalRecordsChange',this.update,this,true);
1938 1940 p.subscribe('destroy',this.destroy,this,true);
1939 1941
1940 1942 // TODO: make this work
1941 1943 p.subscribe('lastPageLinkLabelChange',this.update,this,true);
1942 1944 p.subscribe('lastPageLinkClassChange', this.update,this,true);
1943 1945 };
1944 1946
1945 1947 Paginator.ui.MyLastPageLink.init = function (p) {
1946 1948 p.setAttributeConfig('lastPageLinkLabel', {
1947 1949 value : -1,
1948 1950 validator : l.isString
1949 1951 });
1950 1952 p.setAttributeConfig('lastPageLinkClass', {
1951 1953 value : 'yui-pg-last',
1952 1954 validator : l.isString
1953 1955 });
1954 1956 p.setAttributeConfig('lastPageLinkTitle', {
1955 1957 value : 'Last Page',
1956 1958 validator : l.isString
1957 1959 });
1958 1960
1959 1961 };
1960 1962
1961 1963 Paginator.ui.MyLastPageLink.prototype = {
1962 1964
1963 1965 current : null,
1964 1966 leftmost_page: null,
1965 1967 rightmost_page: null,
1966 1968 link : null,
1967 1969 span : null,
1968 1970 dotdot : null,
1969 1971 na : null,
1970 1972 getPos : function(cur_page, max_page, items){
1971 1973 var edge = parseInt(items / 2) + 1;
1972 1974 if (cur_page <= edge){
1973 1975 var radius = Math.max(parseInt(items / 2), items - cur_page);
1974 1976 }
1975 1977 else if ((max_page - cur_page) < edge) {
1976 1978 var radius = (items - 1) - (max_page - cur_page);
1977 1979 }
1978 1980 else{
1979 1981 var radius = parseInt(items / 2);
1980 1982 }
1981 1983
1982 1984 var left = Math.max(1, (cur_page - (radius)))
1983 1985 var right = Math.min(max_page, cur_page + (radius))
1984 1986 return [left, cur_page, right]
1985 1987 },
1986 1988 render : function (id_base) {
1987 1989 var p = this.paginator,
1988 1990 c = p.get('lastPageLinkClass'),
1989 1991 label = p.get('lastPageLinkLabel'),
1990 1992 last = p.getTotalPages(),
1991 1993 title = p.get('lastPageLinkTitle');
1992 1994
1993 1995 var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
1994 1996 this.leftmost_page = _pos[0];
1995 1997 this.rightmost_page = _pos[2];
1996 1998
1997 1999 this.link = document.createElement('a');
1998 2000 this.span = document.createElement('span');
1999 2001 $(this.span).hide();
2000 2002
2001 2003 this.na = this.span.cloneNode(false);
2002 2004
2003 2005 setId(this.link, id_base + '-last-link');
2004 2006 this.link.href = '#';
2005 2007 this.link.className = c;
2006 2008 this.link.innerHTML = label;
2007 2009 this.link.title = title;
2008 2010 YUE.on(this.link,'click',this.onClick,this,true);
2009 2011
2010 2012 setId(this.span, id_base + '-last-span');
2011 2013 this.span.className = c;
2012 2014 this.span.innerHTML = label;
2013 2015
2014 2016 setId(this.na, id_base + '-last-na');
2015 2017
2016 2018 if (this.rightmost_page < p.getTotalPages()){
2017 2019 this.current = this.link;
2018 2020 }
2019 2021 else{
2020 2022 this.current = this.span;
2021 2023 }
2022 2024
2023 2025 this.current.innerHTML = p.getTotalPages();
2024 2026 return this.current;
2025 2027 },
2026 2028
2027 2029 update : function (e) {
2028 2030 var p = this.paginator;
2029 2031
2030 2032 var _pos = this.getPos(p.getCurrentPage(), p.getTotalPages(), 5);
2031 2033 this.leftmost_page = _pos[0];
2032 2034 this.rightmost_page = _pos[2];
2033 2035
2034 2036 if (e && e.prevValue === e.newValue) {
2035 2037 return;
2036 2038 }
2037 2039
2038 2040 var par = this.current ? this.current.parentNode : null,
2039 2041 after = this.link;
2040 2042 if (par) {
2041 2043
2042 2044 // only show the last page if the rightmost one is
2043 2045 // lower, so we don't have doubled entries at the end
2044 2046 if (!(this.rightmost_page < p.getTotalPages())){
2045 2047 after = this.span
2046 2048 }
2047 2049
2048 2050 if (this.current !== after) {
2049 2051 par.replaceChild(after,this.current);
2050 2052 this.current = after;
2051 2053 }
2052 2054 }
2053 2055 this.current.innerHTML = this.paginator.getTotalPages();
2054 2056
2055 2057 },
2056 2058 destroy : function () {
2057 2059 YUE.purgeElement(this.link);
2058 2060 this.current.parentNode.removeChild(this.current);
2059 2061 this.link = this.span = null;
2060 2062 },
2061 2063 onClick : function (e) {
2062 2064 YUE.stopEvent(e);
2063 2065 this.paginator.setPage(this.paginator.getTotalPages());
2064 2066 }
2065 2067 };
2066 2068
2067 2069 })();
2068 2070
2069 2071 var pagi = new YAHOO.widget.Paginator({
2070 2072 rowsPerPage: links_per_page,
2071 2073 alwaysVisible: false,
2072 2074 template : "{PreviousPageLink} {MyFirstPageLink} {PageLinks} {MyLastPageLink} {NextPageLink}",
2073 2075 pageLinks: 5,
2074 2076 containerClass: 'pagination-wh',
2075 2077 currentPageClass: 'pager_curpage',
2076 2078 pageLinkClass: 'pager_link',
2077 2079 nextPageLinkLabel: '&gt;',
2078 2080 previousPageLinkLabel: '&lt;',
2079 2081 containers:containers
2080 2082 })
2081 2083
2082 2084 return pagi
2083 2085 }
2084 2086
2085 2087 var YUI_datatable = function(data, fields, columns, countnode, sortkey, rows){
2086 2088 var myDataSource = new YAHOO.util.DataSource(data);
2087 2089 myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
2088 2090 myDataSource.responseSchema = {
2089 2091 resultsList: "records",
2090 2092 fields: fields
2091 2093 };
2092 2094 myDataSource.doBeforeCallback = function(req, raw, res, cb) {
2093 2095 // This is the filter function
2094 2096 var data = res.results || [],
2095 2097 filtered = [],
2096 2098 i, l;
2097 2099
2098 2100 if (req) {
2099 2101 req = req.toLowerCase();
2100 2102 for (i = 0; i<data.length; i++) {
2101 2103 var pos = data[i].raw_name.toLowerCase().indexOf(req)
2102 2104 if (pos != -1) {
2103 2105 filtered.push(data[i]);
2104 2106 }
2105 2107 }
2106 2108 res.results = filtered;
2107 2109 }
2108 2110 $(countnode).html(res.results.length);
2109 2111 return res;
2110 2112 }
2111 2113
2112 2114 var myDataTable = new YAHOO.widget.DataTable("datatable_list_wrap", columns, myDataSource, {
2113 2115 sortedBy: {key:sortkey, dir:"asc"},
2114 2116 paginator: YUI_paginator(rows !== undefined && rows ? rows : 25, ['user-paginator']),
2115 2117 MSG_SORTASC: _TM['MSG_SORTASC'],
2116 2118 MSG_SORTDESC: _TM['MSG_SORTDESC'],
2117 2119 MSG_EMPTY: _TM['MSG_EMPTY'],
2118 2120 MSG_ERROR: _TM['MSG_ERROR'],
2119 2121 MSG_LOADING: _TM['MSG_LOADING']
2120 2122 });
2121 2123 myDataTable.subscribe('postRenderEvent',function(oArgs) {
2122 2124 tooltip_activate();
2123 2125 quick_repo_menu();
2124 2126 });
2125 2127
2126 2128 var filterTimeout = null;
2127 2129 var $q_filter = $('#q_filter');
2128 2130
2129 2131 var updateFilter = function () {
2130 2132 // Reset timeout
2131 2133 filterTimeout = null;
2132 2134
2133 2135 // Reset sort
2134 2136 var state = myDataTable.getState();
2135 2137 state.sortedBy = {key:sortkey, dir:YAHOO.widget.DataTable.CLASS_ASC};
2136 2138
2137 2139 // Get filtered data
2138 2140 myDataSource.sendRequest($q_filter.val(), {
2139 2141 success : myDataTable.onDataReturnInitializeTable,
2140 2142 failure : myDataTable.onDataReturnInitializeTable,
2141 2143 scope : myDataTable,
2142 2144 argument: state});
2143 2145 };
2144 2146
2145 2147 $q_filter.click(function(){
2146 2148 if(!$q_filter.hasClass('loaded')){
2147 2149 //TODO: load here full list later to do search within groups
2148 2150 $q_filter.addClass('loaded');
2149 2151 }
2150 2152 });
2151 2153
2152 2154 $q_filter.keyup(function (e) {
2153 2155 clearTimeout(filterTimeout);
2154 2156 filterTimeout = setTimeout(updateFilter, 600);
2155 2157 });
2156 2158 }
2157 2159
2158 2160 // global hooks after DOM is loaded
2159 2161
2160 2162 $(document).ready(function(){
2161 2163 $('.diff-collapse-button').click(function(e) {
2162 2164 var $button = $(e.currentTarget);
2163 2165 var $target = $('#' + $button.attr('target'));
2164 2166 if($target.hasClass('hidden')){
2165 2167 $target.removeClass('hidden');
2166 2168 $button.html("&uarr; {0} &uarr;".format(_TM['Collapse Diff']));
2167 2169 }
2168 2170 else if(!$target.hasClass('hidden')){
2169 2171 $target.addClass('hidden');
2170 2172 $button.html("&darr; {0} &darr;".format(_TM['Expand Diff']));
2171 2173 }
2172 2174 });
2173 2175 });
@@ -1,260 +1,271 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('New Pull Request')}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${_('New Pull Request')}
9 9 </%def>
10 10
11 11 <%def name="page_nav()">
12 12 ${self.menu('repositories')}
13 13 </%def>
14 14
15 15 <%def name="main()">
16 16 ${self.repo_context_bar('showpullrequest')}
17 17 <div class="box">
18 18 <!-- box / title -->
19 19 <div class="title">
20 20 ${self.breadcrumbs()}
21 21 </div>
22 22
23 23 ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
24 24 <div class="form">
25 25 <!-- fields -->
26 26
27 27 <div class="fields" style="float:left;width:50%;padding-right:30px;">
28 28
29 29 <div class="field">
30 30 <div class="label">
31 31 <label for="pullrequest_title">${_('Title')}:</label>
32 32 </div>
33 33 <div class="input">
34 34 ${h.text('pullrequest_title',class_="large",placeholder=_('Summarize the changes - or leave empty'))}
35 35 </div>
36 36 </div>
37 37
38 38 <div class="field">
39 39 <div class="label label-textarea">
40 40 <label for="pullrequest_desc">${_('Description')}:</label>
41 41 </div>
42 42 <div class="textarea text-area editor">
43 43 ${h.textarea('pullrequest_desc',size=30,placeholder=_('Write a short description on this pull request'))}
44 44 </div>
45 45 </div>
46 46
47 47 <div class="field">
48 48 <div class="label label-textarea">
49 49 <label for="pullrequest_desc">${_('Changeset flow')}:</label>
50 50 </div>
51 51 <div class="input">
52 52 ##ORG
53 53 <div>
54 54 <div>
55 55 <div style="padding:5px 3px 3px 3px;">
56 56 <b>${_('Origin repository')}:</b> <span id="org_repo_desc">${c.db_repo.description.split('\n')[0]}</span>
57 57 </div>
58 58 <div>
59 59 ${h.select('org_repo','',c.cs_repos,class_='refs')}:${h.select('org_ref',c.default_cs_ref,c.cs_refs,class_='refs')}
60 60 </div>
61 61 <div style="padding:5px 3px 3px 3px;">
62 62 <b>${_('Revision')}:</b> <span id="org_rev_span">-</span>
63 63 </div>
64 64 </div>
65 65 </div>
66 66
67 67 ##OTHER, most Probably the PARENT OF THIS FORK
68 68 <div style="border-top: 1px solid #EEE; margin: 5px 0px 0px 0px">
69 69 <div>
70 70 ## filled with JS
71 71 <div style="padding:5px 3px 3px 3px;">
72 72 <b>${_('Destination repository')}:</b> <span id="other_repo_desc">${c.a_repo.description.split('\n')[0]}</span>
73 73 </div>
74 74 <div>
75 75 ${h.select('other_repo',c.a_repo.repo_name,c.a_repos,class_='refs')}:${h.select('other_ref',c.default_a_ref,c.a_refs,class_='refs')}
76 76 </div>
77 77 <div style="padding:5px 3px 3px 3px;">
78 78 <b>${_('Revision')}:</b> <span id="other_rev_span">-</span>
79 79 </div>
80 80 </div>
81 81 </div>
82 82 <div style="clear:both"></div>
83 83 </div>
84 84 </div>
85 85
86 86 <div class="field">
87 87 <div class="buttons">
88 88 ${h.submit('save',_('Create Pull Request'),class_="btn")}
89 89 ${h.reset('reset',_('Reset'),class_="btn")}
90 90 </div>
91 91 </div>
92 92
93 93 </div>
94 94
95 95 ## Reviewers
96 96 <div style="float:left; border-left:1px dashed #eee">
97 97 <div class="pr-details-title">${_('Pull Request Reviewers')}</div>
98 98 <div id="reviewers" style="padding:0px 0px 0px 15px">
99 99 ## members goes here !
100 100 <div>
101 101 <ul id="review_members" class="group_members">
102 102 %for member in [c.a_repo.user]:
103 103 <li id="reviewer_${member.user_id}">
104 104 <div class="reviewers_member">
105 105 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email, 14)}"/> </div>
106 106 <div style="float:left">${member.firstname} ${member.lastname} (${_('owner')})</div>
107 107 <input type="hidden" value="${member.user_id}" name="review_members" />
108 108 <span class="action_button" style="padding: 3px" onclick="removeReviewMember(${member.user_id})" title="${_('Remove reviewer')}">
109 109 <i class="icon-remove-sign" style="color: #FF4444;"></i>
110 110 </span>
111 111 </div>
112 112 </li>
113 113 %endfor
114 114 </ul>
115 115 </div>
116 116
117 117 <div class='ac'>
118 118 <div class="reviewer_ac">
119 119 ${h.text('user', class_='yui-ac-input',placeholder=_('Type name of reviewer to add'))}
120 120 <div id="reviewers_container"></div>
121 121 </div>
122 122 </div>
123 123 </div>
124 124 </div>
125 125
126 126 <div style="clear:both;padding: 0 0 30px 0;"></div>
127 127
128 128 <h4>${_('Changesets')}</h4>
129 129 <div style="float:left;padding:0px 30px 30px 30px">
130 130 ## overview pulled by ajax
131 131 <div style="float:left" id="pull_request_overview"></div>
132 132 </div>
133 133 <div style="clear:both;"></div>
134 134
135 135 </div>
136 136
137 137 ${h.end_form()}
138 138
139 139 </div>
140 140
141 141 <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
142 142 <script type="text/javascript">
143 143 var _USERS_AC_DATA = ${c.users_array|n};
144 144 var _GROUPS_AC_DATA = ${c.user_groups_array|n};
145 145 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
146 146
147 147 pyroutes.register('pullrequest_repo_info', "${url('pullrequest_repo_info',repo_name='%(repo_name)s')}", ['repo_name']);
148 148
149 var pendingajax = undefined;
149 150 var otherrepoChanged = function(){
150 151 var repo_name = $('#other_repo').val();
151 ajaxGET(pyroutes.url('pullrequest_repo_info', {"repo_name": repo_name}),
152 if (pendingajax) {
153 pendingajax.abort();
154 pendingajax = undefined;
155 }
156 pendingajax = ajaxGET(pyroutes.url('pullrequest_repo_info', {"repo_name": repo_name}),
152 157 function(o){
158 pendingajax = undefined;
153 159 var data = JSON.parse(o.responseText);
154 160 $('#other_repo_desc').html(data.description);
155 161
156 162 // replace options of other_ref with the ones for the current other_repo
157 163 var $other_ref = $('#other_ref');
158 164 $other_ref.empty();
159 165 for(var i = 0; i < data.refs.length; i++)
160 166 {
161 167 var $optgroup = $('<optgroup/>').attr('label', data.refs[i][1]);
162 168 var options = data.refs[i][0];
163 169 var length = options.length;
164 170 for(var j = 0; j < length; j++)
165 171 {
166 172 $optgroup.append($('<option/>').text(options[j][1]).val(options[j][0]));
167 173 }
168 174 $other_ref.append($optgroup);
169 175 }
170 176 $other_ref.val(data.selected_ref);
171 177
172 178 // reset && add the reviewer based on selected repo
173 179 YUD.get('review_members').innerHTML = '';
174 180 addReviewMember(data.user.user_id, data.user.firstname,
175 181 data.user.lastname, data.user.username,
176 182 data.user.gravatar_link);
177 183
178 184 // re-populate the select2 thingie
179 185 $("#other_ref").select2({
180 186 dropdownAutoWidth: true
181 187 });
182 188
183 189 loadPreview();
184 190 });
185 191 };
186 192
187 193 var loadPreview = function(){
188 194 //url template
189 195 var url = "${h.url('compare_url',
190 196 repo_name='__other_repo__',
191 197 org_ref_type='rev',
192 198 org_ref_name='__other_ref_name__',
193 199 other_repo='__org_repo__',
194 200 other_ref_type='rev',
195 201 other_ref_name='__org_ref_name__',
196 202 as_form=True,
197 203 merge=True,
198 204 )}";
199 205 var org_repo = YUQ('#pull_request_form #org_repo')[0].value;
200 206 var org_ref = YUQ('#pull_request_form #org_ref')[0].value.split(':');
201 207 ## TODO: make nice link like link_to_ref() do
202 208 YUD.get('org_rev_span').innerHTML = org_ref[2].substr(0,12);
203 209
204 210 var other_repo = YUQ('#pull_request_form #other_repo')[0].value;
205 211 var other_ref = YUQ('#pull_request_form #other_ref')[0].value.split(':');
206 212 YUD.get('other_rev_span').innerHTML = other_ref[2].substr(0,12);
207 213
208 214 var select_refs = YUQ('#pull_request_form select.refs')
209 215 var rev_data = {
210 216 '__org_repo__': org_repo,
211 217 '__org_ref_name__': org_ref[2],
212 218 '__other_repo__': other_repo,
213 219 '__other_ref_name__': other_ref[2]
214 220 }; // gather the org/other ref and repo here
215 221
216 222 for (k in rev_data){
217 223 url = url.replace(k,rev_data[k]);
218 224 }
219 225
220 asynchtml(url, $('#pull_request_overview'), function(o){
226 if (pendingajax) {
227 pendingajax.abort();
228 pendingajax = undefined;
229 }
230 pendingajax = asynchtml(url, $('#pull_request_overview'), function(o){
231 pendingajax = undefined;
221 232 var jsdata = eval('('+YUD.get('jsdata').innerHTML+')'); // TODO: just get json
222 233 var r = new BranchRenderer('graph_canvas', 'graph_content_pr');
223 234 r.render(jsdata,100);
224 235 });
225 236 }
226 237
227 238 $(document).ready(function(){
228 239 $("#org_repo").select2({
229 240 dropdownAutoWidth: true
230 241 });
231 242 ## (org_repo can't change)
232 243
233 244 $("#org_ref").select2({
234 245 dropdownAutoWidth: true
235 246 });
236 247 $("#org_ref").on("change", function(e){
237 248 loadPreview();
238 249 });
239 250
240 251 $("#other_repo").select2({
241 252 dropdownAutoWidth: true
242 253 });
243 254 $("#other_repo").on("change", function(e){
244 255 otherrepoChanged();
245 256 });
246 257
247 258 $("#other_ref").select2({
248 259 dropdownAutoWidth: true
249 260 });
250 261 $("#other_ref").on("change", function(e){
251 262 loadPreview();
252 263 });
253 264
254 265 //lazy load overview after 0.5s
255 266 setTimeout(loadPreview, 500);
256 267 });
257 268
258 269 </script>
259 270
260 271 </%def>
General Comments 0
You need to be logged in to leave comments. Login now