##// END OF EJS Templates
fixed few issues with autoselection of revisions on pull requests
marcink -
r2849:2b672f04 beta
parent child Browse files
Show More
@@ -1,415 +1,431 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.pullrequests
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 pull requests controller for rhodecode for initializing pull requests
7 7
8 8 :created_on: May 7, 2012
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import logging
26 26 import traceback
27 27 import formencode
28 28
29 29 from webob.exc import HTTPNotFound, HTTPForbidden
30 30 from collections import defaultdict
31 31 from itertools import groupby
32 32
33 33 from pylons import request, response, session, tmpl_context as c, url
34 34 from pylons.controllers.util import abort, redirect
35 35 from pylons.i18n.translation import _
36 36 from pylons.decorators import jsonify
37 37
38 38 from rhodecode.lib.compat import json
39 39 from rhodecode.lib.base import BaseRepoController, render
40 40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
41 41 NotAnonymous
42 42 from rhodecode.lib import helpers as h
43 43 from rhodecode.lib import diffs
44 44 from rhodecode.lib.utils import action_logger
45 45 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
46 46 ChangesetComment
47 47 from rhodecode.model.pull_request import PullRequestModel
48 48 from rhodecode.model.meta import Session
49 49 from rhodecode.model.repo import RepoModel
50 50 from rhodecode.model.comment import ChangesetCommentsModel
51 51 from rhodecode.model.changeset_status import ChangesetStatusModel
52 52 from rhodecode.model.forms import PullRequestForm
53 53
54 54 log = logging.getLogger(__name__)
55 55
56 56
57 57 class PullrequestsController(BaseRepoController):
58 58
59 59 @LoginRequired()
60 60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
61 61 'repository.admin')
62 62 def __before__(self):
63 63 super(PullrequestsController, self).__before__()
64 64 repo_model = RepoModel()
65 65 c.users_array = repo_model.get_users_js()
66 66 c.users_groups_array = repo_model.get_users_groups_js()
67 67
68 68 def _get_repo_refs(self, repo):
69 69 hist_l = []
70 70
71 71 branches_group = ([('branch:%s:%s' % (k, v), k) for
72 72 k, v in repo.branches.iteritems()], _("Branches"))
73 73 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
74 74 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
75 75 tags_group = ([('tag:%s:%s' % (k, v), k) for
76 76 k, v in repo.tags.iteritems()], _("Tags"))
77 77
78 78 hist_l.append(bookmarks_group)
79 79 hist_l.append(branches_group)
80 80 hist_l.append(tags_group)
81 81
82 82 return hist_l
83 83
84 def _get_default_rev(self, repo):
85 """
86 Get's default revision to do compare on pull request
87
88 :param repo:
89 """
90 repo = repo.scm_instance
91 if 'default' in repo.branches:
92 return 'default'
93 else:
94 #if repo doesn't have default branch return first found
95 return repo.branches.keys()[0]
96
84 97 def show_all(self, repo_name):
85 98 c.pull_requests = PullRequestModel().get_all(repo_name)
86 99 c.repo_name = repo_name
87 100 return render('/pullrequests/pullrequest_show_all.html')
88 101
89 102 @NotAnonymous()
90 103 def index(self):
91 104 org_repo = c.rhodecode_db_repo
92 105
93 106 if org_repo.scm_instance.alias != 'hg':
94 107 log.error('Review not available for GIT REPOS')
95 108 raise HTTPNotFound
96 109
97 110 other_repos_info = {}
98 111
99 112 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
100 113 c.org_repos = []
101 114 c.other_repos = []
102 115 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
103 116 org_repo.user.username, c.repo_name))
104 117 )
105 118
106 119 # add org repo to other so we can open pull request agains itself
107 120 c.other_repos.extend(c.org_repos)
108 121
109 c.default_pull_request = org_repo.repo_name
122 c.default_pull_request = org_repo.repo_name # repo name pre-selected
123 c.default_pull_request_rev = self._get_default_rev(org_repo) # revision pre-selected
110 124 c.default_revs = self._get_repo_refs(org_repo.scm_instance)
111 125 #add orginal repo
112 126 other_repos_info[org_repo.repo_name] = {
113 127 'gravatar': h.gravatar_url(org_repo.user.email, 24),
114 128 'description': org_repo.description,
115 129 'revs': h.select('other_ref', '', c.default_revs, class_='refs')
116 130 }
117 131
118 132 #gather forks and add to this list
119 133 for fork in org_repo.forks:
120 134 c.other_repos.append((fork.repo_name, '%s/%s' % (
121 135 fork.user.username, fork.repo_name))
122 136 )
123 137 other_repos_info[fork.repo_name] = {
124 138 'gravatar': h.gravatar_url(fork.user.email, 24),
125 139 'description': fork.description,
126 140 'revs': h.select('other_ref', '',
127 141 self._get_repo_refs(fork.scm_instance),
128 142 class_='refs')
129 143 }
130 144 #add parents of this fork also
131 145 if org_repo.parent:
132 146 c.default_pull_request = org_repo.parent.repo_name
147 c.default_pull_request_rev = self._get_default_rev(org_repo.parent)
148 c.default_revs = self._get_repo_refs(org_repo.parent.scm_instance)
133 149 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
134 150 org_repo.parent.user.username,
135 151 org_repo.parent.repo_name))
136 152 )
137 153 other_repos_info[org_repo.parent.repo_name] = {
138 154 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
139 155 'description': org_repo.parent.description,
140 156 'revs': h.select('other_ref', '',
141 157 self._get_repo_refs(org_repo.parent.scm_instance),
142 158 class_='refs')
143 159 }
144 160
145 161 c.other_repos_info = json.dumps(other_repos_info)
146 162 c.review_members = [org_repo.user]
147 163 return render('/pullrequests/pullrequest.html')
148 164
149 165 @NotAnonymous()
150 166 def create(self, repo_name):
151 167 try:
152 168 _form = PullRequestForm()().to_python(request.POST)
153 169 except formencode.Invalid, errors:
154 170 log.error(traceback.format_exc())
155 171 if errors.error_dict.get('revisions'):
156 172 msg = 'Revisions: %s' % errors.error_dict['revisions']
157 173 elif errors.error_dict.get('pullrequest_title'):
158 174 msg = _('Pull request requires a title with min. 3 chars')
159 175 else:
160 176 msg = _('error during creation of pull request')
161 177
162 178 h.flash(msg, 'error')
163 179 return redirect(url('pullrequest_home', repo_name=repo_name))
164 180
165 181 org_repo = _form['org_repo']
166 182 org_ref = _form['org_ref']
167 183 other_repo = _form['other_repo']
168 184 other_ref = _form['other_ref']
169 185 revisions = _form['revisions']
170 186 reviewers = _form['review_members']
171 187
172 188 title = _form['pullrequest_title']
173 189 description = _form['pullrequest_desc']
174 190
175 191 try:
176 192 pull_request = PullRequestModel().create(
177 193 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
178 194 other_ref, revisions, reviewers, title, description
179 195 )
180 196 Session().commit()
181 197 h.flash(_('Successfully opened new pull request'),
182 198 category='success')
183 199 except Exception:
184 200 h.flash(_('Error occurred during sending pull request'),
185 201 category='error')
186 202 log.error(traceback.format_exc())
187 203 return redirect(url('pullrequest_home', repo_name=repo_name))
188 204
189 205 return redirect(url('pullrequest_show', repo_name=other_repo,
190 206 pull_request_id=pull_request.pull_request_id))
191 207
192 208 @NotAnonymous()
193 209 @jsonify
194 210 def update(self, repo_name, pull_request_id):
195 211 pull_request = PullRequest.get_or_404(pull_request_id)
196 212 if pull_request.is_closed():
197 213 raise HTTPForbidden()
198 214 #only owner or admin can update it
199 215 owner = pull_request.author.user_id == c.rhodecode_user.user_id
200 216 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
201 217 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
202 218 request.POST.get('reviewers_ids', '').split(',')))
203 219
204 220 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
205 221 Session.commit()
206 222 return True
207 223 raise HTTPForbidden()
208 224
209 225 @NotAnonymous()
210 226 @jsonify
211 227 def delete(self, repo_name, pull_request_id):
212 228 pull_request = PullRequest.get_or_404(pull_request_id)
213 229 #only owner can delete it !
214 230 if pull_request.author.user_id == c.rhodecode_user.user_id:
215 231 PullRequestModel().delete(pull_request)
216 232 Session().commit()
217 233 h.flash(_('Successfully deleted pull request'),
218 234 category='success')
219 235 return redirect(url('admin_settings_my_account'))
220 236 raise HTTPForbidden()
221 237
222 238 def _load_compare_data(self, pull_request, enable_comments=True):
223 239 """
224 240 Load context data needed for generating compare diff
225 241
226 242 :param pull_request:
227 243 :type pull_request:
228 244 """
229 245
230 246 org_repo = pull_request.org_repo
231 247 (org_ref_type,
232 248 org_ref_name,
233 249 org_ref_rev) = pull_request.org_ref.split(':')
234 250
235 251 other_repo = pull_request.other_repo
236 252 (other_ref_type,
237 253 other_ref_name,
238 254 other_ref_rev) = pull_request.other_ref.split(':')
239 255
240 256 # despite opening revisions for bookmarks/branches/tags, we always
241 257 # convert this to rev to prevent changes after book or branch change
242 258 org_ref = ('rev', org_ref_rev)
243 259 other_ref = ('rev', other_ref_rev)
244 260
245 261 c.org_repo = org_repo
246 262 c.other_repo = other_repo
247 263
248 264 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
249 265 org_repo, org_ref, other_repo, other_ref
250 266 )
251 267
252 268 c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])
253 269 # defines that we need hidden inputs with changesets
254 270 c.as_form = request.GET.get('as_form', False)
255 271
256 272 c.org_ref = org_ref[1]
257 273 c.other_ref = other_ref[1]
258 274 # diff needs to have swapped org with other to generate proper diff
259 275 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
260 276 discovery_data)
261 277 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
262 278 _parsed = diff_processor.prepare()
263 279
264 280 c.files = []
265 281 c.changes = {}
266 282
267 283 for f in _parsed:
268 284 fid = h.FID('', f['filename'])
269 285 c.files.append([fid, f['operation'], f['filename'], f['stats']])
270 286 diff = diff_processor.as_html(enable_comments=enable_comments,
271 287 diff_lines=[f])
272 288 c.changes[fid] = [f['operation'], f['filename'], diff]
273 289
274 290 def show(self, repo_name, pull_request_id):
275 291 repo_model = RepoModel()
276 292 c.users_array = repo_model.get_users_js()
277 293 c.users_groups_array = repo_model.get_users_groups_js()
278 294 c.pull_request = PullRequest.get_or_404(pull_request_id)
279 295 c.target_repo = c.pull_request.org_repo.repo_name
280 296
281 297 cc_model = ChangesetCommentsModel()
282 298 cs_model = ChangesetStatusModel()
283 299 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
284 300 pull_request=c.pull_request,
285 301 with_revisions=True)
286 302
287 303 cs_statuses = defaultdict(list)
288 304 for st in _cs_statuses:
289 305 cs_statuses[st.author.username] += [st]
290 306
291 307 c.pull_request_reviewers = []
292 308 c.pull_request_pending_reviewers = []
293 309 for o in c.pull_request.reviewers:
294 310 st = cs_statuses.get(o.user.username, None)
295 311 if st:
296 312 sorter = lambda k: k.version
297 313 st = [(x, list(y)[0])
298 314 for x, y in (groupby(sorted(st, key=sorter), sorter))]
299 315 else:
300 316 c.pull_request_pending_reviewers.append(o.user)
301 317 c.pull_request_reviewers.append([o.user, st])
302 318
303 319 # pull_requests repo_name we opened it against
304 320 # ie. other_repo must match
305 321 if repo_name != c.pull_request.other_repo.repo_name:
306 322 raise HTTPNotFound
307 323
308 324 # load compare data into template context
309 325 enable_comments = not c.pull_request.is_closed()
310 326 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
311 327
312 328 # inline comments
313 329 c.inline_cnt = 0
314 330 c.inline_comments = cc_model.get_inline_comments(
315 331 c.rhodecode_db_repo.repo_id,
316 332 pull_request=pull_request_id)
317 333 # count inline comments
318 334 for __, lines in c.inline_comments:
319 335 for comments in lines.values():
320 336 c.inline_cnt += len(comments)
321 337 # comments
322 338 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
323 339 pull_request=pull_request_id)
324 340
325 341 try:
326 342 cur_status = c.statuses[c.pull_request.revisions[0]][0]
327 343 except:
328 344 log.error(traceback.format_exc())
329 345 cur_status = 'undefined'
330 346 if c.pull_request.is_closed() and 0:
331 347 c.current_changeset_status = cur_status
332 348 else:
333 349 # changeset(pull-request) status calulation based on reviewers
334 350 c.current_changeset_status = cs_model.calculate_status(
335 351 c.pull_request_reviewers,
336 352 )
337 353 c.changeset_statuses = ChangesetStatus.STATUSES
338 354
339 355 return render('/pullrequests/pullrequest_show.html')
340 356
341 357 @NotAnonymous()
342 358 @jsonify
343 359 def comment(self, repo_name, pull_request_id):
344 360 pull_request = PullRequest.get_or_404(pull_request_id)
345 361 if pull_request.is_closed():
346 362 raise HTTPForbidden()
347 363
348 364 status = request.POST.get('changeset_status')
349 365 change_status = request.POST.get('change_changeset_status')
350 366 text = request.POST.get('text')
351 367 if status and change_status:
352 368 text = text or (_('Status change -> %s')
353 369 % ChangesetStatus.get_status_lbl(status))
354 370 comm = ChangesetCommentsModel().create(
355 371 text=text,
356 372 repo=c.rhodecode_db_repo.repo_id,
357 373 user=c.rhodecode_user.user_id,
358 374 pull_request=pull_request_id,
359 375 f_path=request.POST.get('f_path'),
360 376 line_no=request.POST.get('line'),
361 377 status_change=(ChangesetStatus.get_status_lbl(status)
362 378 if status and change_status else None)
363 379 )
364 380
365 381 # get status if set !
366 382 if status and change_status:
367 383 ChangesetStatusModel().set_status(
368 384 c.rhodecode_db_repo.repo_id,
369 385 status,
370 386 c.rhodecode_user.user_id,
371 387 comm,
372 388 pull_request=pull_request_id
373 389 )
374 390 action_logger(self.rhodecode_user,
375 391 'user_commented_pull_request:%s' % pull_request_id,
376 392 c.rhodecode_db_repo, self.ip_addr, self.sa)
377 393
378 394 if request.POST.get('save_close'):
379 395 PullRequestModel().close_pull_request(pull_request_id)
380 396 action_logger(self.rhodecode_user,
381 397 'user_closed_pull_request:%s' % pull_request_id,
382 398 c.rhodecode_db_repo, self.ip_addr, self.sa)
383 399
384 400 Session().commit()
385 401
386 402 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
387 403 return redirect(h.url('pullrequest_show', repo_name=repo_name,
388 404 pull_request_id=pull_request_id))
389 405
390 406 data = {
391 407 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
392 408 }
393 409 if comm:
394 410 c.co = comm
395 411 data.update(comm.get_dict())
396 412 data.update({'rendered_text':
397 413 render('changeset/changeset_comment_block.html')})
398 414
399 415 return data
400 416
401 417 @NotAnonymous()
402 418 @jsonify
403 419 def delete_comment(self, repo_name, comment_id):
404 420 co = ChangesetComment.get(comment_id)
405 421 if co.pull_request.is_closed():
406 422 #don't allow deleting comments on closed pull request
407 423 raise HTTPForbidden()
408 424
409 425 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
410 426 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
411 427 ChangesetCommentsModel().delete(comment=co)
412 428 Session().commit()
413 429 return True
414 430 else:
415 431 raise HTTPForbidden()
@@ -1,1757 +1,1768 b''
1 1 /**
2 2 RhodeCode JS Files
3 3 **/
4 4
5 5 if (typeof console == "undefined" || typeof console.log == "undefined"){
6 6 console = { log: function() {} }
7 7 }
8 8
9 9
10 10 var str_repeat = function(i, m) {
11 11 for (var o = []; m > 0; o[--m] = i);
12 12 return o.join('');
13 13 };
14 14
15 15 /**
16 16 * INJECT .format function into String
17 17 * Usage: "My name is {0} {1}".format("Johny","Bravo")
18 18 * Return "My name is Johny Bravo"
19 19 * Inspired by https://gist.github.com/1049426
20 20 */
21 21 String.prototype.format = function() {
22 22
23 23 function format() {
24 24 var str = this;
25 25 var len = arguments.length+1;
26 26 var safe = undefined;
27 27 var arg = undefined;
28 28
29 29 // For each {0} {1} {n...} replace with the argument in that position. If
30 30 // the argument is an object or an array it will be stringified to JSON.
31 31 for (var i=0; i < len; arg = arguments[i++]) {
32 32 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
33 33 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
34 34 }
35 35 return str;
36 36 }
37 37
38 38 // Save a reference of what may already exist under the property native.
39 39 // Allows for doing something like: if("".format.native) { /* use native */ }
40 40 format.native = String.prototype.format;
41 41
42 42 // Replace the prototype property
43 43 return format;
44 44
45 45 }();
46 46
47 47 String.prototype.strip = function(char) {
48 48 if(char === undefined){
49 49 char = '\\s';
50 50 }
51 51 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
52 52 }
53 53 String.prototype.lstrip = function(char) {
54 54 if(char === undefined){
55 55 char = '\\s';
56 56 }
57 57 return this.replace(new RegExp('^'+char+'+'),'');
58 58 }
59 59 String.prototype.rstrip = function(char) {
60 60 if(char === undefined){
61 61 char = '\\s';
62 62 }
63 63 return this.replace(new RegExp(''+char+'+$'),'');
64 64 }
65 65
66 66
67 67 if(!Array.prototype.indexOf) {
68 68 Array.prototype.indexOf = function(needle) {
69 69 for(var i = 0; i < this.length; i++) {
70 70 if(this[i] === needle) {
71 71 return i;
72 72 }
73 73 }
74 74 return -1;
75 75 };
76 76 }
77 77
78 78 // IE(CRAP) doesn't support previousElementSibling
79 79 var prevElementSibling = function( el ) {
80 80 if( el.previousElementSibling ) {
81 81 return el.previousElementSibling;
82 82 } else {
83 83 while( el = el.previousSibling ) {
84 84 if( el.nodeType === 1 ) return el;
85 85 }
86 86 }
87 87 }
88 88
89
89 var setSelectValue = function(select, val){
90 var selection = YUD.get(select);
91
92 // select element
93 for(var i=0;i<selection.options.length;i++){
94 console.log(selection.options[i].innerHTML);
95 if (selection.options[i].innerHTML == val) {
96 selection.selectedIndex = i;
97 break;
98 }
99 }
100 }
90 101
91 102
92 103 /**
93 104 * SmartColorGenerator
94 105 *
95 106 *usage::
96 107 * var CG = new ColorGenerator();
97 108 * var col = CG.getColor(key); //returns array of RGB
98 109 * 'rgb({0})'.format(col.join(',')
99 110 *
100 111 * @returns {ColorGenerator}
101 112 */
102 113 var ColorGenerator = function(){
103 114 this.GOLDEN_RATIO = 0.618033988749895;
104 115 this.CURRENT_RATIO = 0.22717784590367374 // this can be random
105 116 this.HSV_1 = 0.75;//saturation
106 117 this.HSV_2 = 0.95;
107 118 this.color;
108 119 this.cacheColorMap = {};
109 120 };
110 121
111 122 ColorGenerator.prototype = {
112 123 getColor:function(key){
113 124 if(this.cacheColorMap[key] !== undefined){
114 125 return this.cacheColorMap[key];
115 126 }
116 127 else{
117 128 this.cacheColorMap[key] = this.generateColor();
118 129 return this.cacheColorMap[key];
119 130 }
120 131 },
121 132 _hsvToRgb:function(h,s,v){
122 133 if (s == 0.0)
123 134 return [v, v, v];
124 135 i = parseInt(h * 6.0)
125 136 f = (h * 6.0) - i
126 137 p = v * (1.0 - s)
127 138 q = v * (1.0 - s * f)
128 139 t = v * (1.0 - s * (1.0 - f))
129 140 i = i % 6
130 141 if (i == 0)
131 142 return [v, t, p]
132 143 if (i == 1)
133 144 return [q, v, p]
134 145 if (i == 2)
135 146 return [p, v, t]
136 147 if (i == 3)
137 148 return [p, q, v]
138 149 if (i == 4)
139 150 return [t, p, v]
140 151 if (i == 5)
141 152 return [v, p, q]
142 153 },
143 154 generateColor:function(){
144 155 this.CURRENT_RATIO = this.CURRENT_RATIO+this.GOLDEN_RATIO;
145 156 this.CURRENT_RATIO = this.CURRENT_RATIO %= 1;
146 157 HSV_tuple = [this.CURRENT_RATIO, this.HSV_1, this.HSV_2]
147 158 RGB_tuple = this._hsvToRgb(HSV_tuple[0],HSV_tuple[1],HSV_tuple[2]);
148 159 function toRgb(v){
149 160 return ""+parseInt(v*256)
150 161 }
151 162 return [toRgb(RGB_tuple[0]),toRgb(RGB_tuple[1]),toRgb(RGB_tuple[2])];
152 163
153 164 }
154 165 }
155 166
156 167
157 168
158 169
159 170
160 171 /**
161 172 * GLOBAL YUI Shortcuts
162 173 */
163 174 var YUC = YAHOO.util.Connect;
164 175 var YUD = YAHOO.util.Dom;
165 176 var YUE = YAHOO.util.Event;
166 177 var YUQ = YAHOO.util.Selector.query;
167 178
168 179 // defines if push state is enabled for this browser ?
169 180 var push_state_enabled = Boolean(
170 181 window.history && window.history.pushState && window.history.replaceState
171 182 && !( /* disable for versions of iOS before version 4.3 (8F190) */
172 183 (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent)
173 184 /* disable for the mercury iOS browser, or at least older versions of the webkit engine */
174 185 || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent)
175 186 )
176 187 );
177 188
178 189 var _run_callbacks = function(callbacks){
179 190 if (callbacks !== undefined){
180 191 var _l = callbacks.length;
181 192 for (var i=0;i<_l;i++){
182 193 var func = callbacks[i];
183 194 if(typeof(func)=='function'){
184 195 try{
185 196 func();
186 197 }catch (err){};
187 198 }
188 199 }
189 200 }
190 201 }
191 202
192 203 /**
193 204 * Partial Ajax Implementation
194 205 *
195 206 * @param url: defines url to make partial request
196 207 * @param container: defines id of container to input partial result
197 208 * @param s_call: success callback function that takes o as arg
198 209 * o.tId
199 210 * o.status
200 211 * o.statusText
201 212 * o.getResponseHeader[ ]
202 213 * o.getAllResponseHeaders
203 214 * o.responseText
204 215 * o.responseXML
205 216 * o.argument
206 217 * @param f_call: failure callback
207 218 * @param args arguments
208 219 */
209 220 function ypjax(url,container,s_call,f_call,args){
210 221 var method='GET';
211 222 if(args===undefined){
212 223 args=null;
213 224 }
214 225
215 226 // Set special header for partial ajax == HTTP_X_PARTIAL_XHR
216 227 YUC.initHeader('X-PARTIAL-XHR',true);
217 228
218 229 // wrapper of passed callback
219 230 var s_wrapper = (function(o){
220 231 return function(o){
221 232 YUD.get(container).innerHTML=o.responseText;
222 233 YUD.setStyle(container,'opacity','1.0');
223 234 //execute the given original callback
224 235 if (s_call !== undefined){
225 236 s_call(o);
226 237 }
227 238 }
228 239 })()
229 240 YUD.setStyle(container,'opacity','0.3');
230 241 YUC.asyncRequest(method,url,{
231 242 success:s_wrapper,
232 243 failure:function(o){
233 244 console.log(o);
234 245 YUD.get(container).innerHTML='<span class="error_red">ERROR: {0}</span>'.format(o.status);
235 246 YUD.setStyle(container,'opacity','1.0');
236 247 },
237 248 cache:false
238 249 },args);
239 250
240 251 };
241 252
242 253 var ajaxPOST = function(url,postData,success) {
243 254 // Set special header for ajax == HTTP_X_PARTIAL_XHR
244 255 YUC.initHeader('X-PARTIAL-XHR',true);
245 256
246 257 var toQueryString = function(o) {
247 258 if(typeof o !== 'object') {
248 259 return false;
249 260 }
250 261 var _p, _qs = [];
251 262 for(_p in o) {
252 263 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
253 264 }
254 265 return _qs.join('&');
255 266 };
256 267
257 268 var sUrl = url;
258 269 var callback = {
259 270 success: success,
260 271 failure: function (o) {
261 272 alert("error");
262 273 },
263 274 };
264 275 var postData = toQueryString(postData);
265 276 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
266 277 return request;
267 278 };
268 279
269 280
270 281 /**
271 282 * tooltip activate
272 283 */
273 284 var tooltip_activate = function(){
274 285 function toolTipsId(){
275 286 var ids = [];
276 287 var tts = YUQ('.tooltip');
277 288 for (var i = 0; i < tts.length; i++) {
278 289 // if element doesn't not have and id
279 290 // autogenerate one for tooltip
280 291 if (!tts[i].id){
281 292 tts[i].id='tt'+((i*100)+tts.length);
282 293 }
283 294 ids.push(tts[i].id);
284 295 }
285 296 return ids
286 297 };
287 298 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
288 299 context: [[toolTipsId()],"tl","bl",null,[0,5]],
289 300 monitorresize:false,
290 301 xyoffset :[0,0],
291 302 autodismissdelay:300000,
292 303 hidedelay:5,
293 304 showdelay:20,
294 305 });
295 306 };
296 307
297 308 /**
298 309 * show more
299 310 */
300 311 var show_more_event = function(){
301 312 YUE.on(YUD.getElementsByClassName('show_more'),'click',function(e){
302 313 var el = e.target;
303 314 YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
304 315 YUD.setStyle(el.parentNode,'display','none');
305 316 });
306 317 };
307 318
308 319
309 320 /**
310 321 * Quick filter widget
311 322 *
312 323 * @param target: filter input target
313 324 * @param nodes: list of nodes in html we want to filter.
314 325 * @param display_element function that takes current node from nodes and
315 326 * does hide or show based on the node
316 327 *
317 328 */
318 329 var q_filter = function(target,nodes,display_element){
319 330
320 331 var nodes = nodes;
321 332 var q_filter_field = YUD.get(target);
322 333 var F = YAHOO.namespace(target);
323 334
324 335 YUE.on(q_filter_field,'click',function(){
325 336 q_filter_field.value = '';
326 337 });
327 338
328 339 YUE.on(q_filter_field,'keyup',function(e){
329 340 clearTimeout(F.filterTimeout);
330 341 F.filterTimeout = setTimeout(F.updateFilter,600);
331 342 });
332 343
333 344 F.filterTimeout = null;
334 345
335 346 var show_node = function(node){
336 347 YUD.setStyle(node,'display','')
337 348 }
338 349 var hide_node = function(node){
339 350 YUD.setStyle(node,'display','none');
340 351 }
341 352
342 353 F.updateFilter = function() {
343 354 // Reset timeout
344 355 F.filterTimeout = null;
345 356
346 357 var obsolete = [];
347 358
348 359 var req = q_filter_field.value.toLowerCase();
349 360
350 361 var l = nodes.length;
351 362 var i;
352 363 var showing = 0;
353 364
354 365 for (i=0;i<l;i++ ){
355 366 var n = nodes[i];
356 367 var target_element = display_element(n)
357 368 if(req && n.innerHTML.toLowerCase().indexOf(req) == -1){
358 369 hide_node(target_element);
359 370 }
360 371 else{
361 372 show_node(target_element);
362 373 showing+=1;
363 374 }
364 375 }
365 376
366 377 // if repo_count is set update the number
367 378 var cnt = YUD.get('repo_count');
368 379 if(cnt){
369 380 YUD.get('repo_count').innerHTML = showing;
370 381 }
371 382
372 383 }
373 384 };
374 385
375 386 var tableTr = function(cls, body){
376 387 var _el = document.createElement('div');
377 388 var cont = new YAHOO.util.Element(body);
378 389 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
379 390 var id = 'comment-tr-{0}'.format(comment_id);
380 391 var _html = ('<table><tbody><tr id="{0}" class="{1}">'+
381 392 '<td class="lineno-inline new-inline"></td>'+
382 393 '<td class="lineno-inline old-inline"></td>'+
383 394 '<td>{2}</td>'+
384 395 '</tr></tbody></table>').format(id, cls, body);
385 396 _el.innerHTML = _html;
386 397 return _el.children[0].children[0].children[0];
387 398 };
388 399
389 400 /** comments **/
390 401 var removeInlineForm = function(form) {
391 402 form.parentNode.removeChild(form);
392 403 };
393 404
394 405 var createInlineForm = function(parent_tr, f_path, line) {
395 406 var tmpl = YUD.get('comment-inline-form-template').innerHTML;
396 407 tmpl = tmpl.format(f_path, line);
397 408 var form = tableTr('comment-form-inline',tmpl)
398 409
399 410 // create event for hide button
400 411 form = new YAHOO.util.Element(form);
401 412 var form_hide_button = new YAHOO.util.Element(YUD.getElementsByClassName('hide-inline-form',null,form)[0]);
402 413 form_hide_button.on('click', function(e) {
403 414 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
404 415 if(YUD.hasClass(newtr.nextElementSibling,'inline-comments-button')){
405 416 YUD.setStyle(newtr.nextElementSibling,'display','');
406 417 }
407 418 removeInlineForm(newtr);
408 419 YUD.removeClass(parent_tr, 'form-open');
409 420
410 421 });
411 422
412 423 return form
413 424 };
414 425
415 426 /**
416 427 * Inject inline comment for on given TR this tr should be always an .line
417 428 * tr containing the line. Code will detect comment, and always put the comment
418 429 * block at the very bottom
419 430 */
420 431 var injectInlineForm = function(tr){
421 432 if(!YUD.hasClass(tr, 'line')){
422 433 return
423 434 }
424 435 var submit_url = AJAX_COMMENT_URL;
425 436 var _td = YUD.getElementsByClassName('code',null,tr)[0];
426 437 if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context') || YUD.hasClass(_td,'no-comment')){
427 438 return
428 439 }
429 440 YUD.addClass(tr,'form-open');
430 441 var node = YUD.getElementsByClassName('full_f_path',null,tr.parentNode.parentNode.parentNode)[0];
431 442 var f_path = YUD.getAttribute(node,'path');
432 443 var lineno = getLineNo(tr);
433 444 var form = createInlineForm(tr, f_path, lineno, submit_url);
434 445
435 446 var parent = tr;
436 447 while (1){
437 448 var n = parent.nextElementSibling;
438 449 // next element are comments !
439 450 if(YUD.hasClass(n,'inline-comments')){
440 451 parent = n;
441 452 }
442 453 else{
443 454 break;
444 455 }
445 456 }
446 457 YUD.insertAfter(form,parent);
447 458 var f = YUD.get(form);
448 459 var overlay = YUD.getElementsByClassName('overlay',null,f)[0];
449 460 var _form = YUD.getElementsByClassName('inline-form',null,f)[0];
450 461
451 462 YUE.on(YUD.get(_form), 'submit',function(e){
452 463 YUE.preventDefault(e);
453 464
454 465 //ajax submit
455 466 var text = YUD.get('text_'+lineno).value;
456 467 var postData = {
457 468 'text':text,
458 469 'f_path':f_path,
459 470 'line':lineno
460 471 };
461 472
462 473 if(lineno === undefined){
463 474 alert('missing line !');
464 475 return
465 476 }
466 477 if(f_path === undefined){
467 478 alert('missing file path !');
468 479 return
469 480 }
470 481
471 482 if(text == ""){
472 483 return
473 484 }
474 485
475 486 var success = function(o){
476 487 YUD.removeClass(tr, 'form-open');
477 488 removeInlineForm(f);
478 489 var json_data = JSON.parse(o.responseText);
479 490 renderInlineComment(json_data);
480 491 };
481 492
482 493 if (YUD.hasClass(overlay,'overlay')){
483 494 var w = _form.offsetWidth;
484 495 var h = _form.offsetHeight;
485 496 YUD.setStyle(overlay,'width',w+'px');
486 497 YUD.setStyle(overlay,'height',h+'px');
487 498 }
488 499 YUD.addClass(overlay, 'submitting');
489 500
490 501 ajaxPOST(submit_url, postData, success);
491 502 });
492 503
493 504 setTimeout(function(){
494 505 // callbacks
495 506 tooltip_activate();
496 507 MentionsAutoComplete('text_'+lineno, 'mentions_container_'+lineno,
497 508 _USERS_AC_DATA, _GROUPS_AC_DATA);
498 509 var _e = YUD.get('text_'+lineno);
499 510 if(_e){
500 511 _e.focus();
501 512 }
502 513 },10)
503 514 };
504 515
505 516 var deleteComment = function(comment_id){
506 517 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__',comment_id);
507 518 var postData = {'_method':'delete'};
508 519 var success = function(o){
509 520 var n = YUD.get('comment-tr-'+comment_id);
510 521 var root = prevElementSibling(prevElementSibling(n));
511 522 n.parentNode.removeChild(n);
512 523
513 524 // scann nodes, and attach add button to last one
514 525 placeAddButton(root);
515 526 }
516 527 ajaxPOST(url,postData,success);
517 528 }
518 529
519 530 var updateReviewers = function(reviewers_ids){
520 531 var url = AJAX_UPDATE_PULLREQUEST;
521 532 var postData = {'_method':'put',
522 533 'reviewers_ids': reviewers_ids};
523 534 var success = function(o){
524 535 window.location.reload();
525 536 }
526 537 ajaxPOST(url,postData,success);
527 538 }
528 539
529 540 var createInlineAddButton = function(tr){
530 541
531 542 var label = TRANSLATION_MAP['add another comment'];
532 543
533 544 var html_el = document.createElement('div');
534 545 YUD.addClass(html_el, 'add-comment');
535 546 html_el.innerHTML = '<span class="ui-btn">{0}</span>'.format(label);
536 547
537 548 var add = new YAHOO.util.Element(html_el);
538 549 add.on('click', function(e) {
539 550 injectInlineForm(tr);
540 551 });
541 552 return add;
542 553 };
543 554
544 555 var getLineNo = function(tr) {
545 556 var line;
546 557 var o = tr.children[0].id.split('_');
547 558 var n = tr.children[1].id.split('_');
548 559
549 560 if (n.length >= 2) {
550 561 line = n[n.length-1];
551 562 } else if (o.length >= 2) {
552 563 line = o[o.length-1];
553 564 }
554 565
555 566 return line
556 567 };
557 568
558 569 var placeAddButton = function(target_tr){
559 570 if(!target_tr){
560 571 return
561 572 }
562 573 var last_node = target_tr;
563 574 //scann
564 575 while (1){
565 576 var n = last_node.nextElementSibling;
566 577 // next element are comments !
567 578 if(YUD.hasClass(n,'inline-comments')){
568 579 last_node = n;
569 580 //also remove the comment button from previous
570 581 var comment_add_buttons = YUD.getElementsByClassName('add-comment',null,last_node);
571 582 for(var i=0;i<comment_add_buttons.length;i++){
572 583 var b = comment_add_buttons[i];
573 584 b.parentNode.removeChild(b);
574 585 }
575 586 }
576 587 else{
577 588 break;
578 589 }
579 590 }
580 591
581 592 var add = createInlineAddButton(target_tr);
582 593 // get the comment div
583 594 var comment_block = YUD.getElementsByClassName('comment',null,last_node)[0];
584 595 // attach add button
585 596 YUD.insertAfter(add,comment_block);
586 597 }
587 598
588 599 /**
589 600 * Places the inline comment into the changeset block in proper line position
590 601 */
591 602 var placeInline = function(target_container,lineno,html){
592 603 var lineid = "{0}_{1}".format(target_container,lineno);
593 604 var target_line = YUD.get(lineid);
594 605 var comment = new YAHOO.util.Element(tableTr('inline-comments',html))
595 606
596 607 // check if there are comments already !
597 608 var parent = target_line.parentNode;
598 609 var root_parent = parent;
599 610 while (1){
600 611 var n = parent.nextElementSibling;
601 612 // next element are comments !
602 613 if(YUD.hasClass(n,'inline-comments')){
603 614 parent = n;
604 615 }
605 616 else{
606 617 break;
607 618 }
608 619 }
609 620 // put in the comment at the bottom
610 621 YUD.insertAfter(comment,parent);
611 622
612 623 // scann nodes, and attach add button to last one
613 624 placeAddButton(root_parent);
614 625
615 626 return target_line;
616 627 }
617 628
618 629 /**
619 630 * make a single inline comment and place it inside
620 631 */
621 632 var renderInlineComment = function(json_data){
622 633 try{
623 634 var html = json_data['rendered_text'];
624 635 var lineno = json_data['line_no'];
625 636 var target_id = json_data['target_id'];
626 637 placeInline(target_id, lineno, html);
627 638
628 639 }catch(e){
629 640 console.log(e);
630 641 }
631 642 }
632 643
633 644 /**
634 645 * Iterates over all the inlines, and places them inside proper blocks of data
635 646 */
636 647 var renderInlineComments = function(file_comments){
637 648 for (f in file_comments){
638 649 // holding all comments for a FILE
639 650 var box = file_comments[f];
640 651
641 652 var target_id = YUD.getAttribute(box,'target_id');
642 653 // actually comments with line numbers
643 654 var comments = box.children;
644 655 for(var i=0; i<comments.length; i++){
645 656 var data = {
646 657 'rendered_text': comments[i].outerHTML,
647 658 'line_no': YUD.getAttribute(comments[i],'line'),
648 659 'target_id': target_id
649 660 }
650 661 renderInlineComment(data);
651 662 }
652 663 }
653 664 }
654 665
655 666 var removeReviewer = function(reviewer_id){
656 667 var el = YUD.get('reviewer_{0}'.format(reviewer_id));
657 668 if (el.parentNode !== undefined){
658 669 el.parentNode.removeChild(el);
659 670 }
660 671 }
661 672
662 673 var fileBrowserListeners = function(current_url, node_list_url, url_base){
663 674
664 675 var current_url_branch = +"?branch=__BRANCH__";
665 676 var url = url_base;
666 677 var node_url = node_list_url;
667 678
668 679 YUE.on('stay_at_branch','click',function(e){
669 680 if(e.target.checked){
670 681 var uri = current_url_branch;
671 682 uri = uri.replace('__BRANCH__',e.target.value);
672 683 window.location = uri;
673 684 }
674 685 else{
675 686 window.location = current_url;
676 687 }
677 688 })
678 689
679 690 var n_filter = YUD.get('node_filter');
680 691 var F = YAHOO.namespace('node_filter');
681 692
682 693 F.filterTimeout = null;
683 694 var nodes = null;
684 695
685 696 F.initFilter = function(){
686 697 YUD.setStyle('node_filter_box_loading','display','');
687 698 YUD.setStyle('search_activate_id','display','none');
688 699 YUD.setStyle('add_node_id','display','none');
689 700 YUC.initHeader('X-PARTIAL-XHR',true);
690 701 YUC.asyncRequest('GET',url,{
691 702 success:function(o){
692 703 nodes = JSON.parse(o.responseText).nodes;
693 704 YUD.setStyle('node_filter_box_loading','display','none');
694 705 YUD.setStyle('node_filter_box','display','');
695 706 n_filter.focus();
696 707 if(YUD.hasClass(n_filter,'init')){
697 708 n_filter.value = '';
698 709 YUD.removeClass(n_filter,'init');
699 710 }
700 711 },
701 712 failure:function(o){
702 713 console.log('failed to load');
703 714 }
704 715 },null);
705 716 }
706 717
707 718 F.updateFilter = function(e) {
708 719
709 720 return function(){
710 721 // Reset timeout
711 722 F.filterTimeout = null;
712 723 var query = e.target.value.toLowerCase();
713 724 var match = [];
714 725 var matches = 0;
715 726 var matches_max = 20;
716 727 if (query != ""){
717 728 for(var i=0;i<nodes.length;i++){
718 729
719 730 var pos = nodes[i].name.toLowerCase().indexOf(query)
720 731 if(query && pos != -1){
721 732
722 733 matches++
723 734 //show only certain amount to not kill browser
724 735 if (matches > matches_max){
725 736 break;
726 737 }
727 738
728 739 var n = nodes[i].name;
729 740 var t = nodes[i].type;
730 741 var n_hl = n.substring(0,pos)
731 742 +"<b>{0}</b>".format(n.substring(pos,pos+query.length))
732 743 +n.substring(pos+query.length)
733 744 node_url = node_url.replace('__FPATH__',n);
734 745 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,node_url,n_hl));
735 746 }
736 747 if(match.length >= matches_max){
737 748 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['search truncated']));
738 749 }
739 750 }
740 751 }
741 752 if(query != ""){
742 753 YUD.setStyle('tbody','display','none');
743 754 YUD.setStyle('tbody_filtered','display','');
744 755
745 756 if (match.length==0){
746 757 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['no matching files']));
747 758 }
748 759
749 760 YUD.get('tbody_filtered').innerHTML = match.join("");
750 761 }
751 762 else{
752 763 YUD.setStyle('tbody','display','');
753 764 YUD.setStyle('tbody_filtered','display','none');
754 765 }
755 766
756 767 }
757 768 };
758 769
759 770 YUE.on(YUD.get('filter_activate'),'click',function(){
760 771 F.initFilter();
761 772 })
762 773 YUE.on(n_filter,'click',function(){
763 774 if(YUD.hasClass(n_filter,'init')){
764 775 n_filter.value = '';
765 776 YUD.removeClass(n_filter,'init');
766 777 }
767 778 });
768 779 YUE.on(n_filter,'keyup',function(e){
769 780 clearTimeout(F.filterTimeout);
770 781 F.filterTimeout = setTimeout(F.updateFilter(e),600);
771 782 });
772 783 };
773 784
774 785
775 786 var initCodeMirror = function(textAreadId,resetUrl){
776 787 var myCodeMirror = CodeMirror.fromTextArea(YUD.get(textAreadId),{
777 788 mode: "null",
778 789 lineNumbers:true
779 790 });
780 791 YUE.on('reset','click',function(e){
781 792 window.location=resetUrl
782 793 });
783 794
784 795 YUE.on('file_enable','click',function(){
785 796 YUD.setStyle('editor_container','display','');
786 797 YUD.setStyle('upload_file_container','display','none');
787 798 YUD.setStyle('filename_container','display','');
788 799 });
789 800
790 801 YUE.on('upload_file_enable','click',function(){
791 802 YUD.setStyle('editor_container','display','none');
792 803 YUD.setStyle('upload_file_container','display','');
793 804 YUD.setStyle('filename_container','display','none');
794 805 });
795 806 };
796 807
797 808
798 809
799 810 var getIdentNode = function(n){
800 811 //iterate thru nodes untill matched interesting node !
801 812
802 813 if (typeof n == 'undefined'){
803 814 return -1
804 815 }
805 816
806 817 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
807 818 return n
808 819 }
809 820 else{
810 821 return getIdentNode(n.parentNode);
811 822 }
812 823 };
813 824
814 825 var getSelectionLink = function(selection_link_label) {
815 826 return function(){
816 827 //get selection from start/to nodes
817 828 if (typeof window.getSelection != "undefined") {
818 829 s = window.getSelection();
819 830
820 831 from = getIdentNode(s.anchorNode);
821 832 till = getIdentNode(s.focusNode);
822 833
823 834 f_int = parseInt(from.id.replace('L',''));
824 835 t_int = parseInt(till.id.replace('L',''));
825 836
826 837 if (f_int > t_int){
827 838 //highlight from bottom
828 839 offset = -35;
829 840 ranges = [t_int,f_int];
830 841
831 842 }
832 843 else{
833 844 //highligth from top
834 845 offset = 35;
835 846 ranges = [f_int,t_int];
836 847 }
837 848
838 849 if (ranges[0] != ranges[1]){
839 850 if(YUD.get('linktt') == null){
840 851 hl_div = document.createElement('div');
841 852 hl_div.id = 'linktt';
842 853 }
843 854 anchor = '#L'+ranges[0]+'-'+ranges[1];
844 855 hl_div.innerHTML = '';
845 856 l = document.createElement('a');
846 857 l.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
847 858 l.innerHTML = selection_link_label;
848 859 hl_div.appendChild(l);
849 860
850 861 YUD.get('body').appendChild(hl_div);
851 862
852 863 xy = YUD.getXY(till.id);
853 864
854 865 YUD.addClass('linktt','yui-tt');
855 866 YUD.setStyle('linktt','top',xy[1]+offset+'px');
856 867 YUD.setStyle('linktt','left',xy[0]+'px');
857 868 YUD.setStyle('linktt','visibility','visible');
858 869 }
859 870 else{
860 871 YUD.setStyle('linktt','visibility','hidden');
861 872 }
862 873 }
863 874 }
864 875 };
865 876
866 877 var deleteNotification = function(url, notification_id,callbacks){
867 878 var callback = {
868 879 success:function(o){
869 880 var obj = YUD.get(String("notification_"+notification_id));
870 881 if(obj.parentNode !== undefined){
871 882 obj.parentNode.removeChild(obj);
872 883 }
873 884 _run_callbacks(callbacks);
874 885 },
875 886 failure:function(o){
876 887 alert("error");
877 888 },
878 889 };
879 890 var postData = '_method=delete';
880 891 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
881 892 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
882 893 callback, postData);
883 894 };
884 895
885 896 var readNotification = function(url, notification_id,callbacks){
886 897 var callback = {
887 898 success:function(o){
888 899 var obj = YUD.get(String("notification_"+notification_id));
889 900 YUD.removeClass(obj, 'unread');
890 901 var r_button = YUD.getElementsByClassName('read-notification',null,obj.children[0])[0];
891 902
892 903 if(r_button.parentNode !== undefined){
893 904 r_button.parentNode.removeChild(r_button);
894 905 }
895 906 _run_callbacks(callbacks);
896 907 },
897 908 failure:function(o){
898 909 alert("error");
899 910 },
900 911 };
901 912 var postData = '_method=put';
902 913 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
903 914 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
904 915 callback, postData);
905 916 };
906 917
907 918 /** MEMBERS AUTOCOMPLETE WIDGET **/
908 919
909 920 var MembersAutoComplete = function (divid, cont, users_list, groups_list) {
910 921 var myUsers = users_list;
911 922 var myGroups = groups_list;
912 923
913 924 // Define a custom search function for the DataSource of users
914 925 var matchUsers = function (sQuery) {
915 926 // Case insensitive matching
916 927 var query = sQuery.toLowerCase();
917 928 var i = 0;
918 929 var l = myUsers.length;
919 930 var matches = [];
920 931
921 932 // Match against each name of each contact
922 933 for (; i < l; i++) {
923 934 contact = myUsers[i];
924 935 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
925 936 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
926 937 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
927 938 matches[matches.length] = contact;
928 939 }
929 940 }
930 941 return matches;
931 942 };
932 943
933 944 // Define a custom search function for the DataSource of usersGroups
934 945 var matchGroups = function (sQuery) {
935 946 // Case insensitive matching
936 947 var query = sQuery.toLowerCase();
937 948 var i = 0;
938 949 var l = myGroups.length;
939 950 var matches = [];
940 951
941 952 // Match against each name of each contact
942 953 for (; i < l; i++) {
943 954 matched_group = myGroups[i];
944 955 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
945 956 matches[matches.length] = matched_group;
946 957 }
947 958 }
948 959 return matches;
949 960 };
950 961
951 962 //match all
952 963 var matchAll = function (sQuery) {
953 964 u = matchUsers(sQuery);
954 965 g = matchGroups(sQuery);
955 966 return u.concat(g);
956 967 };
957 968
958 969 // DataScheme for members
959 970 var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
960 971 memberDS.responseSchema = {
961 972 fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk"]
962 973 };
963 974
964 975 // DataScheme for owner
965 976 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
966 977 ownerDS.responseSchema = {
967 978 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
968 979 };
969 980
970 981 // Instantiate AutoComplete for perms
971 982 var membersAC = new YAHOO.widget.AutoComplete(divid, cont, memberDS);
972 983 membersAC.useShadow = false;
973 984 membersAC.resultTypeList = false;
974 985 membersAC.animVert = false;
975 986 membersAC.animHoriz = false;
976 987 membersAC.animSpeed = 0.1;
977 988
978 989 // Instantiate AutoComplete for owner
979 990 var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
980 991 ownerAC.useShadow = false;
981 992 ownerAC.resultTypeList = false;
982 993 ownerAC.animVert = false;
983 994 ownerAC.animHoriz = false;
984 995 ownerAC.animSpeed = 0.1;
985 996
986 997 // Helper highlight function for the formatter
987 998 var highlightMatch = function (full, snippet, matchindex) {
988 999 return full.substring(0, matchindex)
989 1000 + "<span class='match'>"
990 1001 + full.substr(matchindex, snippet.length)
991 1002 + "</span>" + full.substring(matchindex + snippet.length);
992 1003 };
993 1004
994 1005 // Custom formatter to highlight the matching letters
995 1006 var custom_formatter = function (oResultData, sQuery, sResultMatch) {
996 1007 var query = sQuery.toLowerCase();
997 1008 var _gravatar = function(res, em, group){
998 1009 if (group !== undefined){
999 1010 em = '/images/icons/group.png'
1000 1011 }
1001 1012 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1002 1013 return tmpl.format(em,res)
1003 1014 }
1004 1015 // group
1005 1016 if (oResultData.grname != undefined) {
1006 1017 var grname = oResultData.grname;
1007 1018 var grmembers = oResultData.grmembers;
1008 1019 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
1009 1020 var grprefix = "{0}: ".format(_TM['Group']);
1010 1021 var grsuffix = " (" + grmembers + " )";
1011 1022 var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
1012 1023
1013 1024 if (grnameMatchIndex > -1) {
1014 1025 return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,true);
1015 1026 }
1016 1027 return _gravatar(grprefix + oResultData.grname + grsuffix, null,true);
1017 1028 // Users
1018 1029 } else if (oResultData.nname != undefined) {
1019 1030 var fname = oResultData.fname || "";
1020 1031 var lname = oResultData.lname || "";
1021 1032 var nname = oResultData.nname;
1022 1033
1023 1034 // Guard against null value
1024 1035 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1025 1036 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1026 1037 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1027 1038 displayfname, displaylname, displaynname;
1028 1039
1029 1040 if (fnameMatchIndex > -1) {
1030 1041 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1031 1042 } else {
1032 1043 displayfname = fname;
1033 1044 }
1034 1045
1035 1046 if (lnameMatchIndex > -1) {
1036 1047 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1037 1048 } else {
1038 1049 displaylname = lname;
1039 1050 }
1040 1051
1041 1052 if (nnameMatchIndex > -1) {
1042 1053 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1043 1054 } else {
1044 1055 displaynname = nname ? "(" + nname + ")" : "";
1045 1056 }
1046 1057
1047 1058 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1048 1059 } else {
1049 1060 return '';
1050 1061 }
1051 1062 };
1052 1063 membersAC.formatResult = custom_formatter;
1053 1064 ownerAC.formatResult = custom_formatter;
1054 1065
1055 1066 var myHandler = function (sType, aArgs) {
1056 1067 var nextId = divid.split('perm_new_member_name_')[1];
1057 1068 var myAC = aArgs[0]; // reference back to the AC instance
1058 1069 var elLI = aArgs[1]; // reference to the selected LI element
1059 1070 var oData = aArgs[2]; // object literal of selected item's result data
1060 1071 //fill the autocomplete with value
1061 1072 if (oData.nname != undefined) {
1062 1073 //users
1063 1074 myAC.getInputEl().value = oData.nname;
1064 1075 YUD.get('perm_new_member_type_'+nextId).value = 'user';
1065 1076 } else {
1066 1077 //groups
1067 1078 myAC.getInputEl().value = oData.grname;
1068 1079 YUD.get('perm_new_member_type_'+nextId).value = 'users_group';
1069 1080 }
1070 1081 };
1071 1082
1072 1083 membersAC.itemSelectEvent.subscribe(myHandler);
1073 1084 if(ownerAC.itemSelectEvent){
1074 1085 ownerAC.itemSelectEvent.subscribe(myHandler);
1075 1086 }
1076 1087
1077 1088 return {
1078 1089 memberDS: memberDS,
1079 1090 ownerDS: ownerDS,
1080 1091 membersAC: membersAC,
1081 1092 ownerAC: ownerAC,
1082 1093 };
1083 1094 }
1084 1095
1085 1096
1086 1097 var MentionsAutoComplete = function (divid, cont, users_list, groups_list) {
1087 1098 var myUsers = users_list;
1088 1099 var myGroups = groups_list;
1089 1100
1090 1101 // Define a custom search function for the DataSource of users
1091 1102 var matchUsers = function (sQuery) {
1092 1103 var org_sQuery = sQuery;
1093 1104 if(this.mentionQuery == null){
1094 1105 return []
1095 1106 }
1096 1107 sQuery = this.mentionQuery;
1097 1108 // Case insensitive matching
1098 1109 var query = sQuery.toLowerCase();
1099 1110 var i = 0;
1100 1111 var l = myUsers.length;
1101 1112 var matches = [];
1102 1113
1103 1114 // Match against each name of each contact
1104 1115 for (; i < l; i++) {
1105 1116 contact = myUsers[i];
1106 1117 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1107 1118 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1108 1119 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1109 1120 matches[matches.length] = contact;
1110 1121 }
1111 1122 }
1112 1123 return matches
1113 1124 };
1114 1125
1115 1126 //match all
1116 1127 var matchAll = function (sQuery) {
1117 1128 u = matchUsers(sQuery);
1118 1129 return u
1119 1130 };
1120 1131
1121 1132 // DataScheme for owner
1122 1133 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1123 1134
1124 1135 ownerDS.responseSchema = {
1125 1136 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1126 1137 };
1127 1138
1128 1139 // Instantiate AutoComplete for mentions
1129 1140 var ownerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1130 1141 ownerAC.useShadow = false;
1131 1142 ownerAC.resultTypeList = false;
1132 1143 ownerAC.suppressInputUpdate = true;
1133 1144 ownerAC.animVert = false;
1134 1145 ownerAC.animHoriz = false;
1135 1146 ownerAC.animSpeed = 0.1;
1136 1147
1137 1148 // Helper highlight function for the formatter
1138 1149 var highlightMatch = function (full, snippet, matchindex) {
1139 1150 return full.substring(0, matchindex)
1140 1151 + "<span class='match'>"
1141 1152 + full.substr(matchindex, snippet.length)
1142 1153 + "</span>" + full.substring(matchindex + snippet.length);
1143 1154 };
1144 1155
1145 1156 // Custom formatter to highlight the matching letters
1146 1157 ownerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1147 1158 var org_sQuery = sQuery;
1148 1159 if(this.dataSource.mentionQuery != null){
1149 1160 sQuery = this.dataSource.mentionQuery;
1150 1161 }
1151 1162
1152 1163 var query = sQuery.toLowerCase();
1153 1164 var _gravatar = function(res, em, group){
1154 1165 if (group !== undefined){
1155 1166 em = '/images/icons/group.png'
1156 1167 }
1157 1168 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1158 1169 return tmpl.format(em,res)
1159 1170 }
1160 1171 if (oResultData.nname != undefined) {
1161 1172 var fname = oResultData.fname || "";
1162 1173 var lname = oResultData.lname || "";
1163 1174 var nname = oResultData.nname;
1164 1175
1165 1176 // Guard against null value
1166 1177 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1167 1178 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1168 1179 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1169 1180 displayfname, displaylname, displaynname;
1170 1181
1171 1182 if (fnameMatchIndex > -1) {
1172 1183 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1173 1184 } else {
1174 1185 displayfname = fname;
1175 1186 }
1176 1187
1177 1188 if (lnameMatchIndex > -1) {
1178 1189 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1179 1190 } else {
1180 1191 displaylname = lname;
1181 1192 }
1182 1193
1183 1194 if (nnameMatchIndex > -1) {
1184 1195 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1185 1196 } else {
1186 1197 displaynname = nname ? "(" + nname + ")" : "";
1187 1198 }
1188 1199
1189 1200 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1190 1201 } else {
1191 1202 return '';
1192 1203 }
1193 1204 };
1194 1205
1195 1206 if(ownerAC.itemSelectEvent){
1196 1207 ownerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1197 1208
1198 1209 var myAC = aArgs[0]; // reference back to the AC instance
1199 1210 var elLI = aArgs[1]; // reference to the selected LI element
1200 1211 var oData = aArgs[2]; // object literal of selected item's result data
1201 1212 //fill the autocomplete with value
1202 1213 if (oData.nname != undefined) {
1203 1214 //users
1204 1215 //Replace the mention name with replaced
1205 1216 var re = new RegExp();
1206 1217 var org = myAC.getInputEl().value;
1207 1218 var chunks = myAC.dataSource.chunks
1208 1219 // replace middle chunk(the search term) with actuall match
1209 1220 chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
1210 1221 '@'+oData.nname+' ');
1211 1222 myAC.getInputEl().value = chunks.join('')
1212 1223 YUD.get(myAC.getInputEl()).focus(); // Y U NO WORK !?
1213 1224 } else {
1214 1225 //groups
1215 1226 myAC.getInputEl().value = oData.grname;
1216 1227 YUD.get('perm_new_member_type').value = 'users_group';
1217 1228 }
1218 1229 });
1219 1230 }
1220 1231
1221 1232 // in this keybuffer we will gather current value of search !
1222 1233 // since we need to get this just when someone does `@` then we do the
1223 1234 // search
1224 1235 ownerAC.dataSource.chunks = [];
1225 1236 ownerAC.dataSource.mentionQuery = null;
1226 1237
1227 1238 ownerAC.get_mention = function(msg, max_pos) {
1228 1239 var org = msg;
1229 1240 var re = new RegExp('(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)$')
1230 1241 var chunks = [];
1231 1242
1232 1243
1233 1244 // cut first chunk until curret pos
1234 1245 var to_max = msg.substr(0, max_pos);
1235 1246 var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
1236 1247 var msg2 = to_max.substr(at_pos);
1237 1248
1238 1249 chunks.push(org.substr(0,at_pos))// prefix chunk
1239 1250 chunks.push(msg2) // search chunk
1240 1251 chunks.push(org.substr(max_pos)) // postfix chunk
1241 1252
1242 1253 // clean up msg2 for filtering and regex match
1243 1254 var msg2 = msg2.lstrip(' ').lstrip('\n');
1244 1255
1245 1256 if(re.test(msg2)){
1246 1257 var unam = re.exec(msg2)[1];
1247 1258 return [unam, chunks];
1248 1259 }
1249 1260 return [null, null];
1250 1261 };
1251 1262
1252 1263 if (ownerAC.textboxKeyUpEvent){
1253 1264 ownerAC.textboxKeyUpEvent.subscribe(function(type, args){
1254 1265
1255 1266 var ac_obj = args[0];
1256 1267 var currentMessage = args[1];
1257 1268 var currentCaretPosition = args[0]._elTextbox.selectionStart;
1258 1269
1259 1270 var unam = ownerAC.get_mention(currentMessage, currentCaretPosition);
1260 1271 var curr_search = null;
1261 1272 if(unam[0]){
1262 1273 curr_search = unam[0];
1263 1274 }
1264 1275
1265 1276 ownerAC.dataSource.chunks = unam[1];
1266 1277 ownerAC.dataSource.mentionQuery = curr_search;
1267 1278
1268 1279 })
1269 1280 }
1270 1281 return {
1271 1282 ownerDS: ownerDS,
1272 1283 ownerAC: ownerAC,
1273 1284 };
1274 1285 }
1275 1286
1276 1287
1277 1288 var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) {
1278 1289 var myUsers = users_list;
1279 1290 var myGroups = groups_list;
1280 1291
1281 1292 // Define a custom search function for the DataSource of users
1282 1293 var matchUsers = function (sQuery) {
1283 1294 // Case insensitive matching
1284 1295 var query = sQuery.toLowerCase();
1285 1296 var i = 0;
1286 1297 var l = myUsers.length;
1287 1298 var matches = [];
1288 1299
1289 1300 // Match against each name of each contact
1290 1301 for (; i < l; i++) {
1291 1302 contact = myUsers[i];
1292 1303 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1293 1304 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1294 1305 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1295 1306 matches[matches.length] = contact;
1296 1307 }
1297 1308 }
1298 1309 return matches;
1299 1310 };
1300 1311
1301 1312 // Define a custom search function for the DataSource of usersGroups
1302 1313 var matchGroups = function (sQuery) {
1303 1314 // Case insensitive matching
1304 1315 var query = sQuery.toLowerCase();
1305 1316 var i = 0;
1306 1317 var l = myGroups.length;
1307 1318 var matches = [];
1308 1319
1309 1320 // Match against each name of each contact
1310 1321 for (; i < l; i++) {
1311 1322 matched_group = myGroups[i];
1312 1323 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1313 1324 matches[matches.length] = matched_group;
1314 1325 }
1315 1326 }
1316 1327 return matches;
1317 1328 };
1318 1329
1319 1330 //match all
1320 1331 var matchAll = function (sQuery) {
1321 1332 u = matchUsers(sQuery);
1322 1333 return u
1323 1334 };
1324 1335
1325 1336 // DataScheme for owner
1326 1337 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1327 1338
1328 1339 ownerDS.responseSchema = {
1329 1340 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1330 1341 };
1331 1342
1332 1343 // Instantiate AutoComplete for mentions
1333 1344 var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1334 1345 reviewerAC.useShadow = false;
1335 1346 reviewerAC.resultTypeList = false;
1336 1347 reviewerAC.suppressInputUpdate = true;
1337 1348 reviewerAC.animVert = false;
1338 1349 reviewerAC.animHoriz = false;
1339 1350 reviewerAC.animSpeed = 0.1;
1340 1351
1341 1352 // Helper highlight function for the formatter
1342 1353 var highlightMatch = function (full, snippet, matchindex) {
1343 1354 return full.substring(0, matchindex)
1344 1355 + "<span class='match'>"
1345 1356 + full.substr(matchindex, snippet.length)
1346 1357 + "</span>" + full.substring(matchindex + snippet.length);
1347 1358 };
1348 1359
1349 1360 // Custom formatter to highlight the matching letters
1350 1361 reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1351 1362 var org_sQuery = sQuery;
1352 1363 if(this.dataSource.mentionQuery != null){
1353 1364 sQuery = this.dataSource.mentionQuery;
1354 1365 }
1355 1366
1356 1367 var query = sQuery.toLowerCase();
1357 1368 var _gravatar = function(res, em, group){
1358 1369 if (group !== undefined){
1359 1370 em = '/images/icons/group.png'
1360 1371 }
1361 1372 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1362 1373 return tmpl.format(em,res)
1363 1374 }
1364 1375 if (oResultData.nname != undefined) {
1365 1376 var fname = oResultData.fname || "";
1366 1377 var lname = oResultData.lname || "";
1367 1378 var nname = oResultData.nname;
1368 1379
1369 1380 // Guard against null value
1370 1381 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1371 1382 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1372 1383 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1373 1384 displayfname, displaylname, displaynname;
1374 1385
1375 1386 if (fnameMatchIndex > -1) {
1376 1387 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1377 1388 } else {
1378 1389 displayfname = fname;
1379 1390 }
1380 1391
1381 1392 if (lnameMatchIndex > -1) {
1382 1393 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1383 1394 } else {
1384 1395 displaylname = lname;
1385 1396 }
1386 1397
1387 1398 if (nnameMatchIndex > -1) {
1388 1399 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1389 1400 } else {
1390 1401 displaynname = nname ? "(" + nname + ")" : "";
1391 1402 }
1392 1403
1393 1404 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1394 1405 } else {
1395 1406 return '';
1396 1407 }
1397 1408 };
1398 1409
1399 1410 //members cache to catch duplicates
1400 1411 reviewerAC.dataSource.cache = [];
1401 1412 // hack into select event
1402 1413 if(reviewerAC.itemSelectEvent){
1403 1414 reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1404 1415
1405 1416 var myAC = aArgs[0]; // reference back to the AC instance
1406 1417 var elLI = aArgs[1]; // reference to the selected LI element
1407 1418 var oData = aArgs[2]; // object literal of selected item's result data
1408 1419 var members = YUD.get('review_members');
1409 1420 //fill the autocomplete with value
1410 1421
1411 1422 if (oData.nname != undefined) {
1412 1423 if (myAC.dataSource.cache.indexOf(oData.id) != -1){
1413 1424 return
1414 1425 }
1415 1426
1416 1427 var tmpl = '<li id="reviewer_{2}">'+
1417 1428 '<div class="reviewers_member">'+
1418 1429 '<div class="gravatar"><img alt="gravatar" src="{0}"/> </div>'+
1419 1430 '<div style="float:left">{1}</div>'+
1420 1431 '<input type="hidden" value="{2}" name="review_members" />'+
1421 1432 '<span class="delete_icon action_button" onclick="removeReviewer({2})"></span>'+
1422 1433 '</div>'+
1423 1434 '</li>'
1424 1435
1425 1436 var displayname = "{0} {1} ({2})".format(oData.fname,oData.lname,oData.nname);
1426 1437 var element = tmpl.format(oData.gravatar_lnk,displayname,oData.id);
1427 1438 members.innerHTML += element;
1428 1439 myAC.dataSource.cache.push(oData.id);
1429 1440 YUD.get('user').value = ''
1430 1441 }
1431 1442 });
1432 1443 }
1433 1444 return {
1434 1445 ownerDS: ownerDS,
1435 1446 reviewerAC: reviewerAC,
1436 1447 };
1437 1448 }
1438 1449
1439 1450
1440 1451 /**
1441 1452 * QUICK REPO MENU
1442 1453 */
1443 1454 var quick_repo_menu = function(){
1444 1455 YUE.on(YUQ('.quick_repo_menu'),'mouseenter',function(e){
1445 1456 var menu = e.currentTarget.firstElementChild.firstElementChild;
1446 1457 if(YUD.hasClass(menu,'hidden')){
1447 1458 YUD.replaceClass(e.currentTarget,'hidden', 'active');
1448 1459 YUD.replaceClass(menu, 'hidden', 'active');
1449 1460 }
1450 1461 })
1451 1462 YUE.on(YUQ('.quick_repo_menu'),'mouseleave',function(e){
1452 1463 var menu = e.currentTarget.firstElementChild.firstElementChild;
1453 1464 if(YUD.hasClass(menu,'active')){
1454 1465 YUD.replaceClass(e.currentTarget, 'active', 'hidden');
1455 1466 YUD.replaceClass(menu, 'active', 'hidden');
1456 1467 }
1457 1468 })
1458 1469 };
1459 1470
1460 1471
1461 1472 /**
1462 1473 * TABLE SORTING
1463 1474 */
1464 1475
1465 1476 // returns a node from given html;
1466 1477 var fromHTML = function(html){
1467 1478 var _html = document.createElement('element');
1468 1479 _html.innerHTML = html;
1469 1480 return _html;
1470 1481 }
1471 1482 var get_rev = function(node){
1472 1483 var n = node.firstElementChild.firstElementChild;
1473 1484
1474 1485 if (n===null){
1475 1486 return -1
1476 1487 }
1477 1488 else{
1478 1489 out = n.firstElementChild.innerHTML.split(':')[0].replace('r','');
1479 1490 return parseInt(out);
1480 1491 }
1481 1492 }
1482 1493
1483 1494 var get_name = function(node){
1484 1495 var name = node.firstElementChild.children[2].innerHTML;
1485 1496 return name
1486 1497 }
1487 1498 var get_group_name = function(node){
1488 1499 var name = node.firstElementChild.children[1].innerHTML;
1489 1500 return name
1490 1501 }
1491 1502 var get_date = function(node){
1492 1503 var date_ = YUD.getAttribute(node.firstElementChild,'date');
1493 1504 return date_
1494 1505 }
1495 1506
1496 1507 var get_age = function(node){
1497 1508 return node
1498 1509 }
1499 1510
1500 1511 var get_link = function(node){
1501 1512 return node.firstElementChild.text;
1502 1513 }
1503 1514
1504 1515 var revisionSort = function(a, b, desc, field) {
1505 1516
1506 1517 var a_ = fromHTML(a.getData(field));
1507 1518 var b_ = fromHTML(b.getData(field));
1508 1519
1509 1520 // extract revisions from string nodes
1510 1521 a_ = get_rev(a_)
1511 1522 b_ = get_rev(b_)
1512 1523
1513 1524 var comp = YAHOO.util.Sort.compare;
1514 1525 var compState = comp(a_, b_, desc);
1515 1526 return compState;
1516 1527 };
1517 1528 var ageSort = function(a, b, desc, field) {
1518 1529 var a_ = fromHTML(a.getData(field));
1519 1530 var b_ = fromHTML(b.getData(field));
1520 1531
1521 1532 // extract name from table
1522 1533 a_ = get_date(a_)
1523 1534 b_ = get_date(b_)
1524 1535
1525 1536 var comp = YAHOO.util.Sort.compare;
1526 1537 var compState = comp(a_, b_, desc);
1527 1538 return compState;
1528 1539 };
1529 1540
1530 1541 var lastLoginSort = function(a, b, desc, field) {
1531 1542 var a_ = a.getData('last_login_raw') || 0;
1532 1543 var b_ = b.getData('last_login_raw') || 0;
1533 1544
1534 1545 var comp = YAHOO.util.Sort.compare;
1535 1546 var compState = comp(a_, b_, desc);
1536 1547 return compState;
1537 1548 };
1538 1549
1539 1550 var nameSort = function(a, b, desc, field) {
1540 1551 var a_ = fromHTML(a.getData(field));
1541 1552 var b_ = fromHTML(b.getData(field));
1542 1553
1543 1554 // extract name from table
1544 1555 a_ = get_name(a_)
1545 1556 b_ = get_name(b_)
1546 1557
1547 1558 var comp = YAHOO.util.Sort.compare;
1548 1559 var compState = comp(a_, b_, desc);
1549 1560 return compState;
1550 1561 };
1551 1562
1552 1563 var permNameSort = function(a, b, desc, field) {
1553 1564 var a_ = fromHTML(a.getData(field));
1554 1565 var b_ = fromHTML(b.getData(field));
1555 1566 // extract name from table
1556 1567
1557 1568 a_ = a_.children[0].innerHTML;
1558 1569 b_ = b_.children[0].innerHTML;
1559 1570
1560 1571 var comp = YAHOO.util.Sort.compare;
1561 1572 var compState = comp(a_, b_, desc);
1562 1573 return compState;
1563 1574 };
1564 1575
1565 1576 var groupNameSort = function(a, b, desc, field) {
1566 1577 var a_ = fromHTML(a.getData(field));
1567 1578 var b_ = fromHTML(b.getData(field));
1568 1579
1569 1580 // extract name from table
1570 1581 a_ = get_group_name(a_)
1571 1582 b_ = get_group_name(b_)
1572 1583
1573 1584 var comp = YAHOO.util.Sort.compare;
1574 1585 var compState = comp(a_, b_, desc);
1575 1586 return compState;
1576 1587 };
1577 1588 var dateSort = function(a, b, desc, field) {
1578 1589 var a_ = fromHTML(a.getData(field));
1579 1590 var b_ = fromHTML(b.getData(field));
1580 1591
1581 1592 // extract name from table
1582 1593 a_ = get_date(a_)
1583 1594 b_ = get_date(b_)
1584 1595
1585 1596 var comp = YAHOO.util.Sort.compare;
1586 1597 var compState = comp(a_, b_, desc);
1587 1598 return compState;
1588 1599 };
1589 1600
1590 1601 var linkSort = function(a, b, desc, field) {
1591 1602 var a_ = fromHTML(a.getData(field));
1592 1603 var b_ = fromHTML(a.getData(field));
1593 1604
1594 1605 // extract url text from string nodes
1595 1606 a_ = get_link(a_)
1596 1607 b_ = get_link(b_)
1597 1608
1598 1609 var comp = YAHOO.util.Sort.compare;
1599 1610 var compState = comp(a_, b_, desc);
1600 1611 return compState;
1601 1612 }
1602 1613
1603 1614 var addPermAction = function(_html, users_list, groups_list){
1604 1615 var elmts = YUD.getElementsByClassName('last_new_member');
1605 1616 var last_node = elmts[elmts.length-1];
1606 1617 if (last_node){
1607 1618 var next_id = (YUD.getElementsByClassName('new_members')).length;
1608 1619 _html = _html.format(next_id);
1609 1620 last_node.innerHTML = _html;
1610 1621 YUD.setStyle(last_node, 'display', '');
1611 1622 YUD.removeClass(last_node, 'last_new_member');
1612 1623 MembersAutoComplete("perm_new_member_name_"+next_id,
1613 1624 "perm_container_"+next_id, users_list, groups_list);
1614 1625 //create new last NODE
1615 1626 var el = document.createElement('tr');
1616 1627 el.id = 'add_perm_input';
1617 1628 YUD.addClass(el,'last_new_member');
1618 1629 YUD.addClass(el,'new_members');
1619 1630 YUD.insertAfter(el, last_node);
1620 1631 }
1621 1632 }
1622 1633
1623 1634 /* Multi selectors */
1624 1635
1625 1636 var MultiSelectWidget = function(selected_id, available_id, form_id){
1626 1637
1627 1638
1628 1639 //definition of containers ID's
1629 1640 var selected_container = selected_id;
1630 1641 var available_container = available_id;
1631 1642
1632 1643 //temp container for selected storage.
1633 1644 var cache = new Array();
1634 1645 var av_cache = new Array();
1635 1646 var c = YUD.get(selected_container);
1636 1647 var ac = YUD.get(available_container);
1637 1648
1638 1649 //get only selected options for further fullfilment
1639 1650 for(var i = 0;node =c.options[i];i++){
1640 1651 if(node.selected){
1641 1652 //push selected to my temp storage left overs :)
1642 1653 cache.push(node);
1643 1654 }
1644 1655 }
1645 1656
1646 1657 //get all available options to cache
1647 1658 for(var i = 0;node =ac.options[i];i++){
1648 1659 //push selected to my temp storage left overs :)
1649 1660 av_cache.push(node);
1650 1661 }
1651 1662
1652 1663 //fill available only with those not in choosen
1653 1664 ac.options.length=0;
1654 1665 tmp_cache = new Array();
1655 1666
1656 1667 for(var i = 0;node = av_cache[i];i++){
1657 1668 var add = true;
1658 1669 for(var i2 = 0;node_2 = cache[i2];i2++){
1659 1670 if(node.value == node_2.value){
1660 1671 add=false;
1661 1672 break;
1662 1673 }
1663 1674 }
1664 1675 if(add){
1665 1676 tmp_cache.push(new Option(node.text, node.value, false, false));
1666 1677 }
1667 1678 }
1668 1679
1669 1680 for(var i = 0;node = tmp_cache[i];i++){
1670 1681 ac.options[i] = node;
1671 1682 }
1672 1683
1673 1684 function prompts_action_callback(e){
1674 1685
1675 1686 var choosen = YUD.get(selected_container);
1676 1687 var available = YUD.get(available_container);
1677 1688
1678 1689 //get checked and unchecked options from field
1679 1690 function get_checked(from_field){
1680 1691 //temp container for storage.
1681 1692 var sel_cache = new Array();
1682 1693 var oth_cache = new Array();
1683 1694
1684 1695 for(var i = 0;node = from_field.options[i];i++){
1685 1696 if(node.selected){
1686 1697 //push selected fields :)
1687 1698 sel_cache.push(node);
1688 1699 }
1689 1700 else{
1690 1701 oth_cache.push(node)
1691 1702 }
1692 1703 }
1693 1704
1694 1705 return [sel_cache,oth_cache]
1695 1706 }
1696 1707
1697 1708 //fill the field with given options
1698 1709 function fill_with(field,options){
1699 1710 //clear firtst
1700 1711 field.options.length=0;
1701 1712 for(var i = 0;node = options[i];i++){
1702 1713 field.options[i]=new Option(node.text, node.value,
1703 1714 false, false);
1704 1715 }
1705 1716
1706 1717 }
1707 1718 //adds to current field
1708 1719 function add_to(field,options){
1709 1720 for(var i = 0;node = options[i];i++){
1710 1721 field.appendChild(new Option(node.text, node.value,
1711 1722 false, false));
1712 1723 }
1713 1724 }
1714 1725
1715 1726 // add action
1716 1727 if (this.id=='add_element'){
1717 1728 var c = get_checked(available);
1718 1729 add_to(choosen,c[0]);
1719 1730 fill_with(available,c[1]);
1720 1731 }
1721 1732 // remove action
1722 1733 if (this.id=='remove_element'){
1723 1734 var c = get_checked(choosen);
1724 1735 add_to(available,c[0]);
1725 1736 fill_with(choosen,c[1]);
1726 1737 }
1727 1738 // add all elements
1728 1739 if(this.id=='add_all_elements'){
1729 1740 for(var i=0; node = available.options[i];i++){
1730 1741 choosen.appendChild(new Option(node.text,
1731 1742 node.value, false, false));
1732 1743 }
1733 1744 available.options.length = 0;
1734 1745 }
1735 1746 //remove all elements
1736 1747 if(this.id=='remove_all_elements'){
1737 1748 for(var i=0; node = choosen.options[i];i++){
1738 1749 available.appendChild(new Option(node.text,
1739 1750 node.value, false, false));
1740 1751 }
1741 1752 choosen.options.length = 0;
1742 1753 }
1743 1754
1744 1755 }
1745 1756
1746 1757 YUE.addListener(['add_element','remove_element',
1747 1758 'add_all_elements','remove_all_elements'],'click',
1748 1759 prompts_action_callback)
1749 1760 if (form_id !== undefined) {
1750 1761 YUE.addListener(form_id,'submit',function(){
1751 1762 var choosen = YUD.get(selected_container);
1752 1763 for (var i = 0; i < choosen.options.length; i++) {
1753 1764 choosen.options[i].selected = 'selected';
1754 1765 }
1755 1766 });
1756 1767 }
1757 1768 }
@@ -1,196 +1,202 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 ${h.link_to(_(u'Home'),h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('New pull request')}
13 13 </%def>
14 14
15 15 <%def name="main()">
16 16
17 17 <div class="box">
18 18 <!-- box / title -->
19 19 <div class="title">
20 20 ${self.breadcrumbs()}
21 21 </div>
22 22 ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
23 23 <div style="float:left;padding:0px 30px 30px 30px">
24 24 <div style="padding:0px 5px 5px 5px">
25 25 <span>
26 26 <a id="refresh" href="#">
27 27 <img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/>
28 28 ${_('refresh overview')}
29 29 </a>
30 30 </span>
31 31 </div>
32 32 ##ORG
33 33 <div style="float:left">
34 34 <div class="fork_user">
35 35 <div class="gravatar">
36 36 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
37 37 </div>
38 38 <span style="font-size: 20px">
39 39 ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref','',c.org_refs,class_='refs')}
40 40 </span>
41 41 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
42 42 </div>
43 43 <div style="clear:both;padding-top: 10px"></div>
44 44 </div>
45 45 <div style="float:left;font-size:24px;padding:0px 20px">
46 46 <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/>
47 47 </div>
48 48
49 49 ##OTHER, most Probably the PARENT OF THIS FORK
50 50 <div style="float:left">
51 51 <div class="fork_user">
52 52 <div class="gravatar">
53 53 <img id="other_repo_gravatar" alt="gravatar" src=""/>
54 54 </div>
55 55 <span style="font-size: 20px">
56 ${h.select('other_repo',c.default_pull_request ,c.other_repos,class_='refs')}:${h.select('other_ref','',c.default_revs,class_='refs')}
56 ${h.select('other_repo',c.default_pull_request ,c.other_repos,class_='refs')}:${h.select('other_ref',c.default_pull_request_rev,c.default_revs,class_='refs')}
57 57 </span>
58 58 <div id="other_repo_desc" style="padding:5px 3px 3px 42px;"></div>
59 59 </div>
60 60 <div style="clear:both;padding-top: 10px"></div>
61 61 </div>
62 62 <div style="clear:both;padding-top: 10px"></div>
63 63 ## overview pulled by ajax
64 64 <div style="float:left" id="pull_request_overview"></div>
65 65 <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none">
66 66 <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a>
67 67 </div>
68 68 </div>
69 69 <div style="float:left; border-left:1px dashed #eee">
70 70 <h4>${_('Pull request reviewers')}</h4>
71 71 <div id="reviewers" style="padding:0px 0px 0px 15px">
72 72 ## members goes here !
73 73 <div class="group_members_wrap">
74 74 <ul id="review_members" class="group_members">
75 75 %for member in c.review_members:
76 76 <li id="reviewer_${member.user_id}">
77 77 <div class="reviewers_member">
78 78 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
79 79 <div style="float:left">${member.full_name} (${_('owner')})</div>
80 80 <input type="hidden" value="${member.user_id}" name="review_members" />
81 81 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
82 82 </div>
83 83 </li>
84 84 %endfor
85 85 </ul>
86 86 </div>
87 87
88 88 <div class='ac'>
89 89 <div class="reviewer_ac">
90 90 ${h.text('user', class_='yui-ac-input')}
91 91 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
92 92 <div id="reviewers_container"></div>
93 93 </div>
94 94 </div>
95 95 </div>
96 96 </div>
97 97 <h3>${_('Create new pull request')}</h3>
98 98
99 99 <div class="form">
100 100 <!-- fields -->
101 101
102 102 <div class="fields">
103 103
104 104 <div class="field">
105 105 <div class="label">
106 106 <label for="pullrequest_title">${_('Title')}:</label>
107 107 </div>
108 108 <div class="input">
109 109 ${h.text('pullrequest_title',size=30)}
110 110 </div>
111 111 </div>
112 112
113 113 <div class="field">
114 114 <div class="label label-textarea">
115 115 <label for="pullrequest_desc">${_('description')}:</label>
116 116 </div>
117 117 <div class="textarea text-area editor">
118 118 ${h.textarea('pullrequest_desc',size=30)}
119 119 </div>
120 120 </div>
121 121
122 122 <div class="buttons">
123 123 ${h.submit('save',_('Send pull request'),class_="ui-btn large")}
124 124 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
125 125 </div>
126 126 </div>
127 127 </div>
128 128 ${h.end_form()}
129 129
130 130 </div>
131 131
132 132 <script type="text/javascript">
133 133 var _USERS_AC_DATA = ${c.users_array|n};
134 134 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
135 135 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
136 136
137 137 var other_repos_info = ${c.other_repos_info|n};
138 138
139 139 var loadPreview = function(){
140 140 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none');
141 141 var url = "${h.url('compare_url',
142 142 repo_name='org_repo',
143 143 org_ref_type='org_ref_type', org_ref='org_ref',
144 144 other_ref_type='other_ref_type', other_ref='other_ref',
145 145 repo='other_repo',
146 146 as_form=True)}";
147 147
148 148 var select_refs = YUQ('#pull_request_form select.refs')
149
149 var rev_data = {}; // gather the org/other ref and repo here
150 150 for(var i=0;i<select_refs.length;i++){
151 151 var select_ref = select_refs[i];
152 152 var select_ref_data = select_ref.value.split(':');
153 153 var key = null;
154 154 var val = null;
155
155 156 if(select_ref_data.length>1){
156 157 key = select_ref.name+"_type";
157 158 val = select_ref_data[0];
158 159 url = url.replace(key,val);
159
160 rev_data[key] = val;
161
160 162 key = select_ref.name;
161 163 val = select_ref_data[1];
162 164 url = url.replace(key,val);
163
165 rev_data[key] = val;
166
164 167 }else{
165 168 key = select_ref.name;
166 169 val = select_ref.value;
167 170 url = url.replace(key,val);
171 rev_data[key] = val;
168 172 }
169 173 }
170 174
171 175 YUE.on('other_repo', 'change', function(e){
172 176 var repo_name = e.currentTarget.value;
173 177 // replace the <select> of changed repo
174 178 YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs'];
175 179 });
176 180
177 181 ypjax(url,'pull_request_overview', function(data){
178 182 var sel_box = YUQ('#pull_request_form #other_repo')[0];
179 183 var repo_name = sel_box.options[sel_box.selectedIndex].value;
180 184 YUD.get('pull_request_overview_url').href = url;
181 185 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
182 186 YUD.get('other_repo_gravatar').src = other_repos_info[repo_name]['gravatar'];
183 187 YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description'];
184 188 YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs'];
189 // select back the revision that was just compared
190 setSelectValue(YUD.get('other_ref'), rev_data['other_ref']);
185 191 })
186 192 }
187 193 YUE.on('refresh','click',function(e){
188 194 loadPreview()
189 195 })
190 196
191 197 //lazy load overview after 0.5s
192 198 setTimeout(loadPreview, 500)
193 199
194 200 </script>
195 201
196 202 </%def>
General Comments 0
You need to be logged in to leave comments. Login now