##// END OF EJS Templates
Load generated revs while switching to other sources of pull-requests....
marcink -
r2720:0f7355d3 beta
parent child Browse files
Show More
@@ -1,382 +1,388 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 84 def show_all(self, repo_name):
85 85 c.pull_requests = PullRequestModel().get_all(repo_name)
86 86 c.repo_name = repo_name
87 87 return render('/pullrequests/pullrequest_show_all.html')
88 88
89 89 @NotAnonymous()
90 90 def index(self):
91 91 org_repo = c.rhodecode_db_repo
92 92
93 93 if org_repo.scm_instance.alias != 'hg':
94 94 log.error('Review not available for GIT REPOS')
95 95 raise HTTPNotFound
96 96
97 97 other_repos_info = {}
98 98
99 99 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
100 100 c.org_repos = []
101 101 c.other_repos = []
102 102 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
103 103 org_repo.user.username, c.repo_name))
104 104 )
105 105
106 c.other_refs = c.org_refs
106 # add org repo to other so we can open pull request agains itself
107 107 c.other_repos.extend(c.org_repos)
108 108
109 c.default_pull_request = org_repo.repo_name
110 c.default_revs = self._get_repo_refs(org_repo.scm_instance)
109 111 #add orginal repo
110 112 other_repos_info[org_repo.repo_name] = {
111 113 'gravatar': h.gravatar_url(org_repo.user.email, 24),
112 'description': org_repo.description
114 'description': org_repo.description,
115 'revs': h.select('other_ref', '', c.default_revs, class_='refs')
113 116 }
114 117
115 c.default_pull_request = org_repo.repo_name
116 118 #gather forks and add to this list
117 119 for fork in org_repo.forks:
118 120 c.other_repos.append((fork.repo_name, '%s/%s' % (
119 121 fork.user.username, fork.repo_name))
120 122 )
121 123 other_repos_info[fork.repo_name] = {
122 124 'gravatar': h.gravatar_url(fork.user.email, 24),
123 'description': fork.description
125 'description': fork.description,
126 'revs': h.select('other_ref', '',
127 self._get_repo_refs(fork.scm_instance),
128 class_='refs')
124 129 }
125 130 #add parents of this fork also
126 131 if org_repo.parent:
127 132 c.default_pull_request = org_repo.parent.repo_name
128 133 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
129 134 org_repo.parent.user.username,
130 135 org_repo.parent.repo_name))
131 136 )
132 137 other_repos_info[org_repo.parent.repo_name] = {
133 138 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
134 'description': org_repo.parent.description
139 'description': org_repo.parent.description,
140 'revs': h.select('other_ref', '',
141 self._get_repo_refs(org_repo.parent.scm_instance),
142 class_='refs')
135 143 }
136 144
137 145 c.other_repos_info = json.dumps(other_repos_info)
138 146 c.review_members = [org_repo.user]
139 147 return render('/pullrequests/pullrequest.html')
140 148
141 149 @NotAnonymous()
142 150 def create(self, repo_name):
143
144 151 try:
145 152 _form = PullRequestForm()().to_python(request.POST)
146 153 except formencode.Invalid, errors:
147 154 log.error(traceback.format_exc())
148 155 if errors.error_dict.get('revisions'):
149 msg = _('Cannot open a pull request with '
150 'empty list of changesets')
156 msg = 'Revisions: %s' % errors.error_dict['revisions']
151 157 elif errors.error_dict.get('pullrequest_title'):
152 158 msg = _('Pull request requires a title with min. 3 chars')
153 159 else:
154 160 msg = _('error during creation of pull request')
155 161
156 162 h.flash(msg, 'error')
157 163 return redirect(url('pullrequest_home', repo_name=repo_name))
158 164
159 165 org_repo = _form['org_repo']
160 166 org_ref = _form['org_ref']
161 167 other_repo = _form['other_repo']
162 168 other_ref = _form['other_ref']
163 169 revisions = _form['revisions']
164 170 reviewers = _form['review_members']
165 171
166 172 title = _form['pullrequest_title']
167 173 description = _form['pullrequest_desc']
168 174
169 175 try:
170 176 pull_request = PullRequestModel().create(
171 177 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
172 178 other_ref, revisions, reviewers, title, description
173 179 )
174 180 Session().commit()
175 181 h.flash(_('Successfully opened new pull request'),
176 182 category='success')
177 183 except Exception:
178 184 h.flash(_('Error occurred during sending pull request'),
179 185 category='error')
180 186 log.error(traceback.format_exc())
181 187 return redirect(url('pullrequest_home', repo_name=repo_name))
182 188
183 189 return redirect(url('pullrequest_show', repo_name=other_repo,
184 190 pull_request_id=pull_request.pull_request_id))
185 191
186 192 @NotAnonymous()
187 193 @jsonify
188 194 def update(self, repo_name, pull_request_id):
189 195 pull_request = PullRequest.get_or_404(pull_request_id)
190 196 if pull_request.is_closed():
191 197 raise HTTPForbidden()
192 198
193 199 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
194 200 request.POST.get('reviewers_ids', '').split(',')))
195 201
196 202 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
197 203 Session.commit()
198 204 return True
199 205
200 206 def _load_compare_data(self, pull_request, enable_comments=True):
201 207 """
202 208 Load context data needed for generating compare diff
203 209
204 210 :param pull_request:
205 211 :type pull_request:
206 212 """
207 213
208 214 org_repo = pull_request.org_repo
209 215 (org_ref_type,
210 216 org_ref_name,
211 217 org_ref_rev) = pull_request.org_ref.split(':')
212 218
213 219 other_repo = pull_request.other_repo
214 220 (other_ref_type,
215 221 other_ref_name,
216 222 other_ref_rev) = pull_request.other_ref.split(':')
217 223
218 # dispite opening revisions for bookmarks/branches/tags, we always
224 # despite opening revisions for bookmarks/branches/tags, we always
219 225 # convert this to rev to prevent changes after book or branch change
220 226 org_ref = ('rev', org_ref_rev)
221 227 other_ref = ('rev', other_ref_rev)
222 228
223 229 c.org_repo = org_repo
224 230 c.other_repo = other_repo
225 231
226 232 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
227 233 org_repo, org_ref, other_repo, other_ref
228 234 )
229 235
230 236 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
231 237 c.cs_ranges])
232 238 # defines that we need hidden inputs with changesets
233 239 c.as_form = request.GET.get('as_form', False)
234 240
235 241 c.org_ref = org_ref[1]
236 242 c.other_ref = other_ref[1]
237 243 # diff needs to have swapped org with other to generate proper diff
238 244 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
239 245 discovery_data)
240 246 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
241 247 _parsed = diff_processor.prepare()
242 248
243 249 c.files = []
244 250 c.changes = {}
245 251
246 252 for f in _parsed:
247 253 fid = h.FID('', f['filename'])
248 254 c.files.append([fid, f['operation'], f['filename'], f['stats']])
249 255 diff = diff_processor.as_html(enable_comments=enable_comments,
250 256 diff_lines=[f])
251 257 c.changes[fid] = [f['operation'], f['filename'], diff]
252 258
253 259 def show(self, repo_name, pull_request_id):
254 260 repo_model = RepoModel()
255 261 c.users_array = repo_model.get_users_js()
256 262 c.users_groups_array = repo_model.get_users_groups_js()
257 263 c.pull_request = PullRequest.get_or_404(pull_request_id)
258 264
259 265 cc_model = ChangesetCommentsModel()
260 266 cs_model = ChangesetStatusModel()
261 267 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
262 268 pull_request=c.pull_request,
263 269 with_revisions=True)
264 270
265 271 cs_statuses = defaultdict(list)
266 272 for st in _cs_statuses:
267 273 cs_statuses[st.author.username] += [st]
268 274
269 275 c.pull_request_reviewers = []
270 276 c.pull_request_pending_reviewers = []
271 277 for o in c.pull_request.reviewers:
272 278 st = cs_statuses.get(o.user.username, None)
273 279 if st:
274 280 sorter = lambda k: k.version
275 281 st = [(x, list(y)[0])
276 282 for x, y in (groupby(sorted(st, key=sorter), sorter))]
277 283 else:
278 284 c.pull_request_pending_reviewers.append(o.user)
279 285 c.pull_request_reviewers.append([o.user, st])
280 286
281 287 # pull_requests repo_name we opened it against
282 288 # ie. other_repo must match
283 289 if repo_name != c.pull_request.other_repo.repo_name:
284 290 raise HTTPNotFound
285 291
286 292 # load compare data into template context
287 293 enable_comments = not c.pull_request.is_closed()
288 294 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
289 295
290 296 # inline comments
291 297 c.inline_cnt = 0
292 298 c.inline_comments = cc_model.get_inline_comments(
293 299 c.rhodecode_db_repo.repo_id,
294 300 pull_request=pull_request_id)
295 301 # count inline comments
296 302 for __, lines in c.inline_comments:
297 303 for comments in lines.values():
298 304 c.inline_cnt += len(comments)
299 305 # comments
300 306 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
301 307 pull_request=pull_request_id)
302 308
303 309 # changeset(pull-request) status
304 310 c.current_changeset_status = cs_model.calculate_status(
305 311 c.pull_request_reviewers
306 312 )
307 313 c.changeset_statuses = ChangesetStatus.STATUSES
308 314 c.target_repo = c.pull_request.org_repo.repo_name
309 315 return render('/pullrequests/pullrequest_show.html')
310 316
311 317 @NotAnonymous()
312 318 @jsonify
313 319 def comment(self, repo_name, pull_request_id):
314 320 pull_request = PullRequest.get_or_404(pull_request_id)
315 321 if pull_request.is_closed():
316 322 raise HTTPForbidden()
317 323
318 324 status = request.POST.get('changeset_status')
319 325 change_status = request.POST.get('change_changeset_status')
320 326
321 327 comm = ChangesetCommentsModel().create(
322 328 text=request.POST.get('text'),
323 329 repo=c.rhodecode_db_repo.repo_id,
324 330 user=c.rhodecode_user.user_id,
325 331 pull_request=pull_request_id,
326 332 f_path=request.POST.get('f_path'),
327 333 line_no=request.POST.get('line'),
328 334 status_change=(ChangesetStatus.get_status_lbl(status)
329 335 if status and change_status else None)
330 336 )
331 337
332 338 # get status if set !
333 339 if status and change_status:
334 340 ChangesetStatusModel().set_status(
335 341 c.rhodecode_db_repo.repo_id,
336 342 status,
337 343 c.rhodecode_user.user_id,
338 344 comm,
339 345 pull_request=pull_request_id
340 346 )
341 347 action_logger(self.rhodecode_user,
342 348 'user_commented_pull_request:%s' % pull_request_id,
343 349 c.rhodecode_db_repo, self.ip_addr, self.sa)
344 350
345 351 if request.POST.get('save_close'):
346 352 PullRequestModel().close_pull_request(pull_request_id)
347 353 action_logger(self.rhodecode_user,
348 354 'user_closed_pull_request:%s' % pull_request_id,
349 355 c.rhodecode_db_repo, self.ip_addr, self.sa)
350 356
351 357 Session().commit()
352 358
353 359 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
354 360 return redirect(h.url('pullrequest_show', repo_name=repo_name,
355 361 pull_request_id=pull_request_id))
356 362
357 363 data = {
358 364 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
359 365 }
360 366 if comm:
361 367 c.co = comm
362 368 data.update(comm.get_dict())
363 369 data.update({'rendered_text':
364 370 render('changeset/changeset_comment_block.html')})
365 371
366 372 return data
367 373
368 374 @NotAnonymous()
369 375 @jsonify
370 376 def delete_comment(self, repo_name, comment_id):
371 377 co = ChangesetComment.get(comment_id)
372 378 if co.pull_request.is_closed():
373 379 #don't allow deleting comments on closed pull request
374 380 raise HTTPForbidden()
375 381
376 382 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
377 383 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
378 384 ChangesetCommentsModel().delete(comment=co)
379 385 Session().commit()
380 386 return True
381 387 else:
382 388 raise HTTPForbidden()
@@ -1,188 +1,189 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.other_refs,class_='refs')}
56 ${h.select('other_repo',c.default_pull_request ,c.other_repos,class_='refs')}:${h.select('other_ref','',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 var loadPreview = function(){
139 139 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none');
140 140 var url = "${h.url('compare_url',
141 141 repo_name='org_repo',
142 142 org_ref_type='org_ref_type', org_ref='org_ref',
143 143 other_ref_type='other_ref_type', other_ref='other_ref',
144 144 repo='other_repo',
145 145 as_form=True)}";
146 146
147 147 var select_refs = YUQ('#pull_request_form select.refs')
148 148
149 149 for(var i=0;i<select_refs.length;i++){
150 150 var select_ref = select_refs[i];
151 151 var select_ref_data = select_ref.value.split(':');
152 152 var key = null;
153 153 var val = null;
154 154 if(select_ref_data.length>1){
155 155 key = select_ref.name+"_type";
156 156 val = select_ref_data[0];
157 157 url = url.replace(key,val);
158 158
159 159 key = select_ref.name;
160 160 val = select_ref_data[1];
161 161 url = url.replace(key,val);
162 162
163 163 }else{
164 164 key = select_ref.name;
165 165 val = select_ref.value;
166 166 url = url.replace(key,val);
167 167 }
168 168 }
169 169
170 170 ypjax(url,'pull_request_overview', function(data){
171 171 var sel_box = YUQ('#pull_request_form #other_repo')[0];
172 172 var repo_name = sel_box.options[sel_box.selectedIndex].value;
173 173 YUD.get('pull_request_overview_url').href = url;
174 174 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
175 175 YUD.get('other_repo_gravatar').src = other_repos_info[repo_name]['gravatar'];
176 176 YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description'];
177 YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs'];
177 178 })
178 179 }
179 180 YUE.on('refresh','click',function(e){
180 181 loadPreview()
181 182 })
182 183
183 184 //lazy load overview after 0.5s
184 185 setTimeout(loadPreview, 500)
185 186
186 187 </script>
187 188
188 189 </%def>
General Comments 0
You need to be logged in to leave comments. Login now