##// END OF EJS Templates
summary: don't load size on container expand, only on manual action....
marcink -
r3334:6301d8bb default
parent child Browse files
Show More
@@ -1,203 +1,203 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2015-2018 RhodeCode GmbH
3 # Copyright (C) 2015-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 import time
20 import time
21 import errno
21 import errno
22 import logging
22 import logging
23
23
24 import gevent
24 import gevent
25
25
26 from dogpile.cache.backends import memory as memory_backend
26 from dogpile.cache.backends import memory as memory_backend
27 from dogpile.cache.backends import file as file_backend
27 from dogpile.cache.backends import file as file_backend
28 from dogpile.cache.backends import redis as redis_backend
28 from dogpile.cache.backends import redis as redis_backend
29 from dogpile.cache.backends.file import NO_VALUE, compat, FileLock
29 from dogpile.cache.backends.file import NO_VALUE, compat, FileLock
30 from dogpile.cache.util import memoized_property
30 from dogpile.cache.util import memoized_property
31
31
32 from rhodecode.lib.memory_lru_dict import LRUDict, LRUDictDebug
32 from rhodecode.lib.memory_lru_dict import LRUDict, LRUDictDebug
33
33
34
34
35 _default_max_size = 1024
35 _default_max_size = 1024
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39
39
40 class LRUMemoryBackend(memory_backend.MemoryBackend):
40 class LRUMemoryBackend(memory_backend.MemoryBackend):
41 pickle_values = False
41 pickle_values = False
42
42
43 def __init__(self, arguments):
43 def __init__(self, arguments):
44 max_size = arguments.pop('max_size', _default_max_size)
44 max_size = arguments.pop('max_size', _default_max_size)
45
45
46 LRUDictClass = LRUDict
46 LRUDictClass = LRUDict
47 if arguments.pop('log_key_count', None):
47 if arguments.pop('log_key_count', None):
48 LRUDictClass = LRUDictDebug
48 LRUDictClass = LRUDictDebug
49
49
50 arguments['cache_dict'] = LRUDictClass(max_size)
50 arguments['cache_dict'] = LRUDictClass(max_size)
51 super(LRUMemoryBackend, self).__init__(arguments)
51 super(LRUMemoryBackend, self).__init__(arguments)
52
52
53 def delete(self, key):
53 def delete(self, key):
54 try:
54 try:
55 del self._cache[key]
55 del self._cache[key]
56 except KeyError:
56 except KeyError:
57 # we don't care if key isn't there at deletion
57 # we don't care if key isn't there at deletion
58 pass
58 pass
59
59
60 def delete_multi(self, keys):
60 def delete_multi(self, keys):
61 for key in keys:
61 for key in keys:
62 self.delete(key)
62 self.delete(key)
63
63
64
64
65 class Serializer(object):
65 class Serializer(object):
66 def _dumps(self, value, safe=False):
66 def _dumps(self, value, safe=False):
67 try:
67 try:
68 return compat.pickle.dumps(value)
68 return compat.pickle.dumps(value)
69 except Exception:
69 except Exception:
70 if safe:
70 if safe:
71 return NO_VALUE
71 return NO_VALUE
72 else:
72 else:
73 raise
73 raise
74
74
75 def _loads(self, value, safe=True):
75 def _loads(self, value, safe=True):
76 try:
76 try:
77 return compat.pickle.loads(value)
77 return compat.pickle.loads(value)
78 except Exception:
78 except Exception:
79 if safe:
79 if safe:
80 return NO_VALUE
80 return NO_VALUE
81 else:
81 else:
82 raise
82 raise
83
83
84
84
85 class CustomLockFactory(FileLock):
85 class CustomLockFactory(FileLock):
86
86
87 @memoized_property
87 @memoized_property
88 def _module(self):
88 def _module(self):
89 import fcntl
89 import fcntl
90 flock_org = fcntl.flock
90 flock_org = fcntl.flock
91
91
92 def gevent_flock(fd, operation):
92 def gevent_flock(fd, operation):
93 """
93 """
94 Gevent compatible flock
94 Gevent compatible flock
95 """
95 """
96 # set non-blocking, this will cause an exception if we cannot acquire a lock
96 # set non-blocking, this will cause an exception if we cannot acquire a lock
97 operation |= fcntl.LOCK_NB
97 operation |= fcntl.LOCK_NB
98 start_lock_time = time.time()
98 start_lock_time = time.time()
99 timeout = 60 * 5 # 5min
99 timeout = 60 * 15 # 15min
100 while True:
100 while True:
101 try:
101 try:
102 flock_org(fd, operation)
102 flock_org(fd, operation)
103 # lock has been acquired
103 # lock has been acquired
104 break
104 break
105 except (OSError, IOError) as e:
105 except (OSError, IOError) as e:
106 # raise on other errors than Resource temporarily unavailable
106 # raise on other errors than Resource temporarily unavailable
107 if e.errno != errno.EAGAIN:
107 if e.errno != errno.EAGAIN:
108 raise
108 raise
109 elif (time.time() - start_lock_time) > timeout:
109 elif (time.time() - start_lock_time) > timeout:
110 # waited to much time on a lock, better fail than loop for ever
110 # waited to much time on a lock, better fail than loop for ever
111 log.error('Failed to acquire lock on `%s` after waiting %ss',
111 log.error('Failed to acquire lock on `%s` after waiting %ss',
112 self.filename, timeout)
112 self.filename, timeout)
113 raise
113 raise
114 wait_timeout = 0.03
114 wait_timeout = 0.03
115 log.debug('Failed to acquire lock on `%s`, retry in %ss',
115 log.debug('Failed to acquire lock on `%s`, retry in %ss',
116 self.filename, wait_timeout)
116 self.filename, wait_timeout)
117 gevent.sleep(wait_timeout)
117 gevent.sleep(wait_timeout)
118
118
119 fcntl.flock = gevent_flock
119 fcntl.flock = gevent_flock
120 return fcntl
120 return fcntl
121
121
122
122
123 class FileNamespaceBackend(Serializer, file_backend.DBMBackend):
123 class FileNamespaceBackend(Serializer, file_backend.DBMBackend):
124
124
125 def __init__(self, arguments):
125 def __init__(self, arguments):
126 arguments['lock_factory'] = CustomLockFactory
126 arguments['lock_factory'] = CustomLockFactory
127 super(FileNamespaceBackend, self).__init__(arguments)
127 super(FileNamespaceBackend, self).__init__(arguments)
128
128
129 def list_keys(self, prefix=''):
129 def list_keys(self, prefix=''):
130 def cond(v):
130 def cond(v):
131 if not prefix:
131 if not prefix:
132 return True
132 return True
133
133
134 if v.startswith(prefix):
134 if v.startswith(prefix):
135 return True
135 return True
136 return False
136 return False
137
137
138 with self._dbm_file(True) as dbm:
138 with self._dbm_file(True) as dbm:
139
139
140 return filter(cond, dbm.keys())
140 return filter(cond, dbm.keys())
141
141
142 def get_store(self):
142 def get_store(self):
143 return self.filename
143 return self.filename
144
144
145 def get(self, key):
145 def get(self, key):
146 with self._dbm_file(False) as dbm:
146 with self._dbm_file(False) as dbm:
147 if hasattr(dbm, 'get'):
147 if hasattr(dbm, 'get'):
148 value = dbm.get(key, NO_VALUE)
148 value = dbm.get(key, NO_VALUE)
149 else:
149 else:
150 # gdbm objects lack a .get method
150 # gdbm objects lack a .get method
151 try:
151 try:
152 value = dbm[key]
152 value = dbm[key]
153 except KeyError:
153 except KeyError:
154 value = NO_VALUE
154 value = NO_VALUE
155 if value is not NO_VALUE:
155 if value is not NO_VALUE:
156 value = self._loads(value)
156 value = self._loads(value)
157 return value
157 return value
158
158
159 def set(self, key, value):
159 def set(self, key, value):
160 with self._dbm_file(True) as dbm:
160 with self._dbm_file(True) as dbm:
161 dbm[key] = self._dumps(value)
161 dbm[key] = self._dumps(value)
162
162
163 def set_multi(self, mapping):
163 def set_multi(self, mapping):
164 with self._dbm_file(True) as dbm:
164 with self._dbm_file(True) as dbm:
165 for key, value in mapping.items():
165 for key, value in mapping.items():
166 dbm[key] = self._dumps(value)
166 dbm[key] = self._dumps(value)
167
167
168
168
169 class RedisPickleBackend(Serializer, redis_backend.RedisBackend):
169 class RedisPickleBackend(Serializer, redis_backend.RedisBackend):
170 def list_keys(self, prefix=''):
170 def list_keys(self, prefix=''):
171 if prefix:
171 if prefix:
172 prefix = prefix + '*'
172 prefix = prefix + '*'
173 return self.client.keys(prefix)
173 return self.client.keys(prefix)
174
174
175 def get_store(self):
175 def get_store(self):
176 return self.client.connection_pool
176 return self.client.connection_pool
177
177
178 def get(self, key):
178 def get(self, key):
179 value = self.client.get(key)
179 value = self.client.get(key)
180 if value is None:
180 if value is None:
181 return NO_VALUE
181 return NO_VALUE
182 return self._loads(value)
182 return self._loads(value)
183
183
184 def set(self, key, value):
184 def set(self, key, value):
185 if self.redis_expiration_time:
185 if self.redis_expiration_time:
186 self.client.setex(key, self.redis_expiration_time,
186 self.client.setex(key, self.redis_expiration_time,
187 self._dumps(value))
187 self._dumps(value))
188 else:
188 else:
189 self.client.set(key, self._dumps(value))
189 self.client.set(key, self._dumps(value))
190
190
191 def set_multi(self, mapping):
191 def set_multi(self, mapping):
192 mapping = dict(
192 mapping = dict(
193 (k, self._dumps(v))
193 (k, self._dumps(v))
194 for k, v in mapping.items()
194 for k, v in mapping.items()
195 )
195 )
196
196
197 if not self.redis_expiration_time:
197 if not self.redis_expiration_time:
198 self.client.mset(mapping)
198 self.client.mset(mapping)
199 else:
199 else:
200 pipe = self.client.pipeline()
200 pipe = self.client.pipeline()
201 for key, value in mapping.items():
201 for key, value in mapping.items():
202 pipe.setex(key, self.redis_expiration_time, value)
202 pipe.setex(key, self.redis_expiration_time, value)
203 pipe.execute()
203 pipe.execute()
@@ -1,578 +1,579 b''
1 // # Copyright (C) 2010-2018 RhodeCode GmbH
1 // # Copyright (C) 2010-2018 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 /**
19 /**
20 RhodeCode JS Files
20 RhodeCode JS Files
21 **/
21 **/
22
22
23 if (typeof console == "undefined" || typeof console.log == "undefined"){
23 if (typeof console == "undefined" || typeof console.log == "undefined"){
24 console = { log: function() {} }
24 console = { log: function() {} }
25 }
25 }
26
26
27 // TODO: move the following function to submodules
27 // TODO: move the following function to submodules
28
28
29 /**
29 /**
30 * show more
30 * show more
31 */
31 */
32 var show_more_event = function(){
32 var show_more_event = function(){
33 $('table .show_more').click(function(e) {
33 $('table .show_more').click(function(e) {
34 var cid = e.target.id.substring(1);
34 var cid = e.target.id.substring(1);
35 var button = $(this);
35 var button = $(this);
36 if (button.hasClass('open')) {
36 if (button.hasClass('open')) {
37 $('#'+cid).hide();
37 $('#'+cid).hide();
38 button.removeClass('open');
38 button.removeClass('open');
39 } else {
39 } else {
40 $('#'+cid).show();
40 $('#'+cid).show();
41 button.addClass('open one');
41 button.addClass('open one');
42 }
42 }
43 });
43 });
44 };
44 };
45
45
46 var compare_radio_buttons = function(repo_name, compare_ref_type){
46 var compare_radio_buttons = function(repo_name, compare_ref_type){
47 $('#compare_action').on('click', function(e){
47 $('#compare_action').on('click', function(e){
48 e.preventDefault();
48 e.preventDefault();
49
49
50 var source = $('input[name=compare_source]:checked').val();
50 var source = $('input[name=compare_source]:checked').val();
51 var target = $('input[name=compare_target]:checked').val();
51 var target = $('input[name=compare_target]:checked').val();
52 if(source && target){
52 if(source && target){
53 var url_data = {
53 var url_data = {
54 repo_name: repo_name,
54 repo_name: repo_name,
55 source_ref: source,
55 source_ref: source,
56 source_ref_type: compare_ref_type,
56 source_ref_type: compare_ref_type,
57 target_ref: target,
57 target_ref: target,
58 target_ref_type: compare_ref_type,
58 target_ref_type: compare_ref_type,
59 merge: 1
59 merge: 1
60 };
60 };
61 window.location = pyroutes.url('repo_compare', url_data);
61 window.location = pyroutes.url('repo_compare', url_data);
62 }
62 }
63 });
63 });
64 $('.compare-radio-button').on('click', function(e){
64 $('.compare-radio-button').on('click', function(e){
65 var source = $('input[name=compare_source]:checked').val();
65 var source = $('input[name=compare_source]:checked').val();
66 var target = $('input[name=compare_target]:checked').val();
66 var target = $('input[name=compare_target]:checked').val();
67 if(source && target){
67 if(source && target){
68 $('#compare_action').removeAttr("disabled");
68 $('#compare_action').removeAttr("disabled");
69 $('#compare_action').removeClass("disabled");
69 $('#compare_action').removeClass("disabled");
70 }
70 }
71 })
71 })
72 };
72 };
73
73
74 var showRepoSize = function(target, repo_name, commit_id, callback) {
74 var showRepoSize = function(target, repo_name, commit_id, callback) {
75 var container = $('#' + target);
75 var container = $('#' + target);
76 var url = pyroutes.url('repo_stats',
76 var url = pyroutes.url('repo_stats',
77 {"repo_name": repo_name, "commit_id": commit_id});
77 {"repo_name": repo_name, "commit_id": commit_id});
78
78
79 container.show();
79 if (!container.hasClass('loaded')) {
80 if (!container.hasClass('loaded')) {
80 $.ajax({url: url})
81 $.ajax({url: url})
81 .complete(function (data) {
82 .complete(function (data) {
82 var responseJSON = data.responseJSON;
83 var responseJSON = data.responseJSON;
83 container.addClass('loaded');
84 container.addClass('loaded');
84 container.html(responseJSON.size);
85 container.html(responseJSON.size);
85 callback(responseJSON.code_stats)
86 callback(responseJSON.code_stats)
86 })
87 })
87 .fail(function (data) {
88 .fail(function (data) {
88 console.log('failed to load repo stats');
89 console.log('failed to load repo stats');
89 });
90 });
90 }
91 }
91
92
92 };
93 };
93
94
94 var showRepoStats = function(target, data){
95 var showRepoStats = function(target, data){
95 var container = $('#' + target);
96 var container = $('#' + target);
96
97
97 if (container.hasClass('loaded')) {
98 if (container.hasClass('loaded')) {
98 return
99 return
99 }
100 }
100
101
101 var total = 0;
102 var total = 0;
102 var no_data = true;
103 var no_data = true;
103 var tbl = document.createElement('table');
104 var tbl = document.createElement('table');
104 tbl.setAttribute('class', 'trending_language_tbl');
105 tbl.setAttribute('class', 'trending_language_tbl');
105
106
106 $.each(data, function(key, val){
107 $.each(data, function(key, val){
107 total += val.count;
108 total += val.count;
108 });
109 });
109
110
110 var sortedStats = [];
111 var sortedStats = [];
111 for (var obj in data){
112 for (var obj in data){
112 sortedStats.push([obj, data[obj]])
113 sortedStats.push([obj, data[obj]])
113 }
114 }
114 var sortedData = sortedStats.sort(function (a, b) {
115 var sortedData = sortedStats.sort(function (a, b) {
115 return b[1].count - a[1].count
116 return b[1].count - a[1].count
116 });
117 });
117 var cnt = 0;
118 var cnt = 0;
118 $.each(sortedData, function(idx, val){
119 $.each(sortedData, function(idx, val){
119 cnt += 1;
120 cnt += 1;
120 no_data = false;
121 no_data = false;
121
122
122 var hide = cnt > 2;
123 var hide = cnt > 2;
123 var tr = document.createElement('tr');
124 var tr = document.createElement('tr');
124 if (hide) {
125 if (hide) {
125 tr.setAttribute('style', 'display:none');
126 tr.setAttribute('style', 'display:none');
126 tr.setAttribute('class', 'stats_hidden');
127 tr.setAttribute('class', 'stats_hidden');
127 }
128 }
128
129
129 var key = val[0];
130 var key = val[0];
130 var obj = {"desc": val[1].desc, "count": val[1].count};
131 var obj = {"desc": val[1].desc, "count": val[1].count};
131
132
132 var percentage = Math.round((obj.count / total * 100), 2);
133 var percentage = Math.round((obj.count / total * 100), 2);
133
134
134 var td1 = document.createElement('td');
135 var td1 = document.createElement('td');
135 td1.width = 300;
136 td1.width = 300;
136 var trending_language_label = document.createElement('div');
137 var trending_language_label = document.createElement('div');
137 trending_language_label.innerHTML = obj.desc + " (.{0})".format(key);
138 trending_language_label.innerHTML = obj.desc + " (.{0})".format(key);
138 td1.appendChild(trending_language_label);
139 td1.appendChild(trending_language_label);
139
140
140 var td2 = document.createElement('td');
141 var td2 = document.createElement('td');
141 var trending_language = document.createElement('div');
142 var trending_language = document.createElement('div');
142 var nr_files = obj.count +" "+ _ngettext('file', 'files', obj.count);
143 var nr_files = obj.count +" "+ _ngettext('file', 'files', obj.count);
143
144
144 trending_language.title = key + " " + nr_files;
145 trending_language.title = key + " " + nr_files;
145
146
146 trending_language.innerHTML = "<span>" + percentage + "% " + nr_files
147 trending_language.innerHTML = "<span>" + percentage + "% " + nr_files
147 + "</span><b>" + percentage + "% " + nr_files + "</b>";
148 + "</span><b>" + percentage + "% " + nr_files + "</b>";
148
149
149 trending_language.setAttribute("class", 'trending_language');
150 trending_language.setAttribute("class", 'trending_language');
150 $('b', trending_language)[0].style.width = percentage + "%";
151 $('b', trending_language)[0].style.width = percentage + "%";
151 td2.appendChild(trending_language);
152 td2.appendChild(trending_language);
152
153
153 tr.appendChild(td1);
154 tr.appendChild(td1);
154 tr.appendChild(td2);
155 tr.appendChild(td2);
155 tbl.appendChild(tr);
156 tbl.appendChild(tr);
156 if (cnt == 3) {
157 if (cnt == 3) {
157 var show_more = document.createElement('tr');
158 var show_more = document.createElement('tr');
158 var td = document.createElement('td');
159 var td = document.createElement('td');
159 lnk = document.createElement('a');
160 lnk = document.createElement('a');
160
161
161 lnk.href = '#';
162 lnk.href = '#';
162 lnk.innerHTML = _gettext('Show more');
163 lnk.innerHTML = _gettext('Show more');
163 lnk.id = 'code_stats_show_more';
164 lnk.id = 'code_stats_show_more';
164 td.appendChild(lnk);
165 td.appendChild(lnk);
165
166
166 show_more.appendChild(td);
167 show_more.appendChild(td);
167 show_more.appendChild(document.createElement('td'));
168 show_more.appendChild(document.createElement('td'));
168 tbl.appendChild(show_more);
169 tbl.appendChild(show_more);
169 }
170 }
170 });
171 });
171
172
172 $(container).html(tbl);
173 $(container).html(tbl);
173 $(container).addClass('loaded');
174 $(container).addClass('loaded');
174
175
175 $('#code_stats_show_more').on('click', function (e) {
176 $('#code_stats_show_more').on('click', function (e) {
176 e.preventDefault();
177 e.preventDefault();
177 $('.stats_hidden').each(function (idx) {
178 $('.stats_hidden').each(function (idx) {
178 $(this).css("display", "");
179 $(this).css("display", "");
179 });
180 });
180 $('#code_stats_show_more').hide();
181 $('#code_stats_show_more').hide();
181 });
182 });
182
183
183 };
184 };
184
185
185 // returns a node from given html;
186 // returns a node from given html;
186 var fromHTML = function(html){
187 var fromHTML = function(html){
187 var _html = document.createElement('element');
188 var _html = document.createElement('element');
188 _html.innerHTML = html;
189 _html.innerHTML = html;
189 return _html;
190 return _html;
190 };
191 };
191
192
192 // Toggle Collapsable Content
193 // Toggle Collapsable Content
193 function collapsableContent() {
194 function collapsableContent() {
194
195
195 $('.collapsable-content').not('.no-hide').hide();
196 $('.collapsable-content').not('.no-hide').hide();
196
197
197 $('.btn-collapse').unbind(); //in case we've been here before
198 $('.btn-collapse').unbind(); //in case we've been here before
198 $('.btn-collapse').click(function() {
199 $('.btn-collapse').click(function() {
199 var button = $(this);
200 var button = $(this);
200 var togglename = $(this).data("toggle");
201 var togglename = $(this).data("toggle");
201 $('.collapsable-content[data-toggle='+togglename+']').toggle();
202 $('.collapsable-content[data-toggle='+togglename+']').toggle();
202 if ($(this).html()=="Show Less")
203 if ($(this).html()=="Show Less")
203 $(this).html("Show More");
204 $(this).html("Show More");
204 else
205 else
205 $(this).html("Show Less");
206 $(this).html("Show Less");
206 });
207 });
207 };
208 };
208
209
209 var timeagoActivate = function() {
210 var timeagoActivate = function() {
210 $("time.timeago").timeago();
211 $("time.timeago").timeago();
211 };
212 };
212
213
213
214
214 var clipboardActivate = function() {
215 var clipboardActivate = function() {
215 /*
216 /*
216 *
217 *
217 * <i class="tooltip icon-plus clipboard-action" data-clipboard-text="${commit.raw_id}" title="${_('Copy the full commit id')}"></i>
218 * <i class="tooltip icon-plus clipboard-action" data-clipboard-text="${commit.raw_id}" title="${_('Copy the full commit id')}"></i>
218 * */
219 * */
219 var clipboard = new ClipboardJS('.clipboard-action');
220 var clipboard = new ClipboardJS('.clipboard-action');
220
221
221 clipboard.on('success', function(e) {
222 clipboard.on('success', function(e) {
222 var callback = function () {
223 var callback = function () {
223 $(e.trigger).animate({'opacity': 1.00}, 200)
224 $(e.trigger).animate({'opacity': 1.00}, 200)
224 };
225 };
225 $(e.trigger).animate({'opacity': 0.15}, 200, callback);
226 $(e.trigger).animate({'opacity': 0.15}, 200, callback);
226 e.clearSelection();
227 e.clearSelection();
227 });
228 });
228 };
229 };
229
230
230
231
231 // Formatting values in a Select2 dropdown of commit references
232 // Formatting values in a Select2 dropdown of commit references
232 var formatSelect2SelectionRefs = function(commit_ref){
233 var formatSelect2SelectionRefs = function(commit_ref){
233 var tmpl = '';
234 var tmpl = '';
234 if (!commit_ref.text || commit_ref.type === 'sha'){
235 if (!commit_ref.text || commit_ref.type === 'sha'){
235 return commit_ref.text;
236 return commit_ref.text;
236 }
237 }
237 if (commit_ref.type === 'branch'){
238 if (commit_ref.type === 'branch'){
238 tmpl = tmpl.concat('<i class="icon-branch"></i> ');
239 tmpl = tmpl.concat('<i class="icon-branch"></i> ');
239 } else if (commit_ref.type === 'tag'){
240 } else if (commit_ref.type === 'tag'){
240 tmpl = tmpl.concat('<i class="icon-tag"></i> ');
241 tmpl = tmpl.concat('<i class="icon-tag"></i> ');
241 } else if (commit_ref.type === 'book'){
242 } else if (commit_ref.type === 'book'){
242 tmpl = tmpl.concat('<i class="icon-bookmark"></i> ');
243 tmpl = tmpl.concat('<i class="icon-bookmark"></i> ');
243 }
244 }
244 return tmpl.concat(escapeHtml(commit_ref.text));
245 return tmpl.concat(escapeHtml(commit_ref.text));
245 };
246 };
246
247
247 // takes a given html element and scrolls it down offset pixels
248 // takes a given html element and scrolls it down offset pixels
248 function offsetScroll(element, offset) {
249 function offsetScroll(element, offset) {
249 setTimeout(function() {
250 setTimeout(function() {
250 var location = element.offset().top;
251 var location = element.offset().top;
251 // some browsers use body, some use html
252 // some browsers use body, some use html
252 $('html, body').animate({ scrollTop: (location - offset) });
253 $('html, body').animate({ scrollTop: (location - offset) });
253 }, 100);
254 }, 100);
254 }
255 }
255
256
256 // scroll an element `percent`% from the top of page in `time` ms
257 // scroll an element `percent`% from the top of page in `time` ms
257 function scrollToElement(element, percent, time) {
258 function scrollToElement(element, percent, time) {
258 percent = (percent === undefined ? 25 : percent);
259 percent = (percent === undefined ? 25 : percent);
259 time = (time === undefined ? 100 : time);
260 time = (time === undefined ? 100 : time);
260
261
261 var $element = $(element);
262 var $element = $(element);
262 if ($element.length == 0) {
263 if ($element.length == 0) {
263 throw('Cannot scroll to {0}'.format(element))
264 throw('Cannot scroll to {0}'.format(element))
264 }
265 }
265 var elOffset = $element.offset().top;
266 var elOffset = $element.offset().top;
266 var elHeight = $element.height();
267 var elHeight = $element.height();
267 var windowHeight = $(window).height();
268 var windowHeight = $(window).height();
268 var offset = elOffset;
269 var offset = elOffset;
269 if (elHeight < windowHeight) {
270 if (elHeight < windowHeight) {
270 offset = elOffset - ((windowHeight / (100 / percent)) - (elHeight / 2));
271 offset = elOffset - ((windowHeight / (100 / percent)) - (elHeight / 2));
271 }
272 }
272 setTimeout(function() {
273 setTimeout(function() {
273 $('html, body').animate({ scrollTop: offset});
274 $('html, body').animate({ scrollTop: offset});
274 }, time);
275 }, time);
275 }
276 }
276
277
277 /**
278 /**
278 * global hooks after DOM is loaded
279 * global hooks after DOM is loaded
279 */
280 */
280 $(document).ready(function() {
281 $(document).ready(function() {
281 firefoxAnchorFix();
282 firefoxAnchorFix();
282
283
283 $('.navigation a.menulink').on('click', function(e){
284 $('.navigation a.menulink').on('click', function(e){
284 var menuitem = $(this).parent('li');
285 var menuitem = $(this).parent('li');
285 if (menuitem.hasClass('open')) {
286 if (menuitem.hasClass('open')) {
286 menuitem.removeClass('open');
287 menuitem.removeClass('open');
287 } else {
288 } else {
288 menuitem.addClass('open');
289 menuitem.addClass('open');
289 $(document).on('click', function(event) {
290 $(document).on('click', function(event) {
290 if (!$(event.target).closest(menuitem).length) {
291 if (!$(event.target).closest(menuitem).length) {
291 menuitem.removeClass('open');
292 menuitem.removeClass('open');
292 }
293 }
293 });
294 });
294 }
295 }
295 });
296 });
296
297
297 $('body').on('click', '.cb-lineno a', function(event) {
298 $('body').on('click', '.cb-lineno a', function(event) {
298 function sortNumber(a,b) {
299 function sortNumber(a,b) {
299 return a - b;
300 return a - b;
300 }
301 }
301
302
302 var lineNo = $(this).data('lineNo');
303 var lineNo = $(this).data('lineNo');
303 var lineName = $(this).attr('name');
304 var lineName = $(this).attr('name');
304
305
305 if (lineNo) {
306 if (lineNo) {
306 var prevLine = $('.cb-line-selected a').data('lineNo');
307 var prevLine = $('.cb-line-selected a').data('lineNo');
307
308
308 // on shift, we do a range selection, if we got previous line
309 // on shift, we do a range selection, if we got previous line
309 if (event.shiftKey && prevLine !== undefined) {
310 if (event.shiftKey && prevLine !== undefined) {
310 var prevLine = parseInt(prevLine);
311 var prevLine = parseInt(prevLine);
311 var nextLine = parseInt(lineNo);
312 var nextLine = parseInt(lineNo);
312 var pos = [prevLine, nextLine].sort(sortNumber);
313 var pos = [prevLine, nextLine].sort(sortNumber);
313 var anchor = '#L{0}-{1}'.format(pos[0], pos[1]);
314 var anchor = '#L{0}-{1}'.format(pos[0], pos[1]);
314
315
315 // single click
316 // single click
316 } else {
317 } else {
317 var nextLine = parseInt(lineNo);
318 var nextLine = parseInt(lineNo);
318 var pos = [nextLine, nextLine];
319 var pos = [nextLine, nextLine];
319 var anchor = '#L{0}'.format(pos[0]);
320 var anchor = '#L{0}'.format(pos[0]);
320
321
321 }
322 }
322 // highlight
323 // highlight
323 var range = [];
324 var range = [];
324 for (var i = pos[0]; i <= pos[1]; i++) {
325 for (var i = pos[0]; i <= pos[1]; i++) {
325 range.push(i);
326 range.push(i);
326 }
327 }
327 // clear old selected lines
328 // clear old selected lines
328 $('.cb-line-selected').removeClass('cb-line-selected');
329 $('.cb-line-selected').removeClass('cb-line-selected');
329
330
330 $.each(range, function (i, lineNo) {
331 $.each(range, function (i, lineNo) {
331 var line_td = $('td.cb-lineno#L' + lineNo);
332 var line_td = $('td.cb-lineno#L' + lineNo);
332
333
333 if (line_td.length) {
334 if (line_td.length) {
334 line_td.addClass('cb-line-selected'); // line number td
335 line_td.addClass('cb-line-selected'); // line number td
335 line_td.prev().addClass('cb-line-selected'); // line data
336 line_td.prev().addClass('cb-line-selected'); // line data
336 line_td.next().addClass('cb-line-selected'); // line content
337 line_td.next().addClass('cb-line-selected'); // line content
337 }
338 }
338 });
339 });
339
340
340 } else if (lineName !== undefined) { // lineName only occurs in diffs
341 } else if (lineName !== undefined) { // lineName only occurs in diffs
341 // clear old selected lines
342 // clear old selected lines
342 $('td.cb-line-selected').removeClass('cb-line-selected');
343 $('td.cb-line-selected').removeClass('cb-line-selected');
343 var anchor = '#{0}'.format(lineName);
344 var anchor = '#{0}'.format(lineName);
344 var diffmode = templateContext.session_attrs.diffmode || "sideside";
345 var diffmode = templateContext.session_attrs.diffmode || "sideside";
345
346
346 if (diffmode === "unified") {
347 if (diffmode === "unified") {
347 $(this).closest('tr').find('td').addClass('cb-line-selected');
348 $(this).closest('tr').find('td').addClass('cb-line-selected');
348 } else {
349 } else {
349 var activeTd = $(this).closest('td');
350 var activeTd = $(this).closest('td');
350 activeTd.addClass('cb-line-selected');
351 activeTd.addClass('cb-line-selected');
351 activeTd.next('td').addClass('cb-line-selected');
352 activeTd.next('td').addClass('cb-line-selected');
352 }
353 }
353
354
354 }
355 }
355
356
356 // Replace URL without jumping to it if browser supports.
357 // Replace URL without jumping to it if browser supports.
357 // Default otherwise
358 // Default otherwise
358 if (history.pushState && anchor !== undefined) {
359 if (history.pushState && anchor !== undefined) {
359 var new_location = location.href.rstrip('#');
360 var new_location = location.href.rstrip('#');
360 if (location.hash) {
361 if (location.hash) {
361 // location without hash
362 // location without hash
362 new_location = new_location.replace(location.hash, "");
363 new_location = new_location.replace(location.hash, "");
363 }
364 }
364
365
365 // Make new anchor url
366 // Make new anchor url
366 new_location = new_location + anchor;
367 new_location = new_location + anchor;
367 history.pushState(true, document.title, new_location);
368 history.pushState(true, document.title, new_location);
368
369
369 return false;
370 return false;
370 }
371 }
371
372
372 });
373 });
373
374
374 $('.collapse_file').on('click', function(e) {
375 $('.collapse_file').on('click', function(e) {
375 e.stopPropagation();
376 e.stopPropagation();
376 if ($(e.target).is('a')) { return; }
377 if ($(e.target).is('a')) { return; }
377 var node = $(e.delegateTarget).first();
378 var node = $(e.delegateTarget).first();
378 var icon = $($(node.children().first()).children().first());
379 var icon = $($(node.children().first()).children().first());
379 var id = node.attr('fid');
380 var id = node.attr('fid');
380 var target = $('#'+id);
381 var target = $('#'+id);
381 var tr = $('#tr_'+id);
382 var tr = $('#tr_'+id);
382 var diff = $('#diff_'+id);
383 var diff = $('#diff_'+id);
383 if(node.hasClass('expand_file')){
384 if(node.hasClass('expand_file')){
384 node.removeClass('expand_file');
385 node.removeClass('expand_file');
385 icon.removeClass('expand_file_icon');
386 icon.removeClass('expand_file_icon');
386 node.addClass('collapse_file');
387 node.addClass('collapse_file');
387 icon.addClass('collapse_file_icon');
388 icon.addClass('collapse_file_icon');
388 diff.show();
389 diff.show();
389 tr.show();
390 tr.show();
390 target.show();
391 target.show();
391 } else {
392 } else {
392 node.removeClass('collapse_file');
393 node.removeClass('collapse_file');
393 icon.removeClass('collapse_file_icon');
394 icon.removeClass('collapse_file_icon');
394 node.addClass('expand_file');
395 node.addClass('expand_file');
395 icon.addClass('expand_file_icon');
396 icon.addClass('expand_file_icon');
396 diff.hide();
397 diff.hide();
397 tr.hide();
398 tr.hide();
398 target.hide();
399 target.hide();
399 }
400 }
400 });
401 });
401
402
402 $('#expand_all_files').click(function() {
403 $('#expand_all_files').click(function() {
403 $('.expand_file').each(function() {
404 $('.expand_file').each(function() {
404 var node = $(this);
405 var node = $(this);
405 var icon = $($(node.children().first()).children().first());
406 var icon = $($(node.children().first()).children().first());
406 var id = $(this).attr('fid');
407 var id = $(this).attr('fid');
407 var target = $('#'+id);
408 var target = $('#'+id);
408 var tr = $('#tr_'+id);
409 var tr = $('#tr_'+id);
409 var diff = $('#diff_'+id);
410 var diff = $('#diff_'+id);
410 node.removeClass('expand_file');
411 node.removeClass('expand_file');
411 icon.removeClass('expand_file_icon');
412 icon.removeClass('expand_file_icon');
412 node.addClass('collapse_file');
413 node.addClass('collapse_file');
413 icon.addClass('collapse_file_icon');
414 icon.addClass('collapse_file_icon');
414 diff.show();
415 diff.show();
415 tr.show();
416 tr.show();
416 target.show();
417 target.show();
417 });
418 });
418 });
419 });
419
420
420 $('#collapse_all_files').click(function() {
421 $('#collapse_all_files').click(function() {
421 $('.collapse_file').each(function() {
422 $('.collapse_file').each(function() {
422 var node = $(this);
423 var node = $(this);
423 var icon = $($(node.children().first()).children().first());
424 var icon = $($(node.children().first()).children().first());
424 var id = $(this).attr('fid');
425 var id = $(this).attr('fid');
425 var target = $('#'+id);
426 var target = $('#'+id);
426 var tr = $('#tr_'+id);
427 var tr = $('#tr_'+id);
427 var diff = $('#diff_'+id);
428 var diff = $('#diff_'+id);
428 node.removeClass('collapse_file');
429 node.removeClass('collapse_file');
429 icon.removeClass('collapse_file_icon');
430 icon.removeClass('collapse_file_icon');
430 node.addClass('expand_file');
431 node.addClass('expand_file');
431 icon.addClass('expand_file_icon');
432 icon.addClass('expand_file_icon');
432 diff.hide();
433 diff.hide();
433 tr.hide();
434 tr.hide();
434 target.hide();
435 target.hide();
435 });
436 });
436 });
437 });
437
438
438 // Mouse over behavior for comments and line selection
439 // Mouse over behavior for comments and line selection
439
440
440 // Select the line that comes from the url anchor
441 // Select the line that comes from the url anchor
441 // At the time of development, Chrome didn't seem to support jquery's :target
442 // At the time of development, Chrome didn't seem to support jquery's :target
442 // element, so I had to scroll manually
443 // element, so I had to scroll manually
443
444
444 if (location.hash) {
445 if (location.hash) {
445 var result = splitDelimitedHash(location.hash);
446 var result = splitDelimitedHash(location.hash);
446 var loc = result.loc;
447 var loc = result.loc;
447 if (loc.length > 1) {
448 if (loc.length > 1) {
448
449
449 var highlightable_line_tds = [];
450 var highlightable_line_tds = [];
450
451
451 // source code line format
452 // source code line format
452 var page_highlights = loc.substring(
453 var page_highlights = loc.substring(
453 loc.indexOf('#') + 1).split('L');
454 loc.indexOf('#') + 1).split('L');
454
455
455 if (page_highlights.length > 1) {
456 if (page_highlights.length > 1) {
456 var highlight_ranges = page_highlights[1].split(",");
457 var highlight_ranges = page_highlights[1].split(",");
457 var h_lines = [];
458 var h_lines = [];
458 for (var pos in highlight_ranges) {
459 for (var pos in highlight_ranges) {
459 var _range = highlight_ranges[pos].split('-');
460 var _range = highlight_ranges[pos].split('-');
460 if (_range.length === 2) {
461 if (_range.length === 2) {
461 var start = parseInt(_range[0]);
462 var start = parseInt(_range[0]);
462 var end = parseInt(_range[1]);
463 var end = parseInt(_range[1]);
463 if (start < end) {
464 if (start < end) {
464 for (var i = start; i <= end; i++) {
465 for (var i = start; i <= end; i++) {
465 h_lines.push(i);
466 h_lines.push(i);
466 }
467 }
467 }
468 }
468 }
469 }
469 else {
470 else {
470 h_lines.push(parseInt(highlight_ranges[pos]));
471 h_lines.push(parseInt(highlight_ranges[pos]));
471 }
472 }
472 }
473 }
473 for (pos in h_lines) {
474 for (pos in h_lines) {
474 var line_td = $('td.cb-lineno#L' + h_lines[pos]);
475 var line_td = $('td.cb-lineno#L' + h_lines[pos]);
475 if (line_td.length) {
476 if (line_td.length) {
476 highlightable_line_tds.push(line_td);
477 highlightable_line_tds.push(line_td);
477 }
478 }
478 }
479 }
479 }
480 }
480
481
481 // now check a direct id reference (diff page)
482 // now check a direct id reference (diff page)
482 if ($(loc).length && $(loc).hasClass('cb-lineno')) {
483 if ($(loc).length && $(loc).hasClass('cb-lineno')) {
483 highlightable_line_tds.push($(loc));
484 highlightable_line_tds.push($(loc));
484 }
485 }
485 $.each(highlightable_line_tds, function (i, $td) {
486 $.each(highlightable_line_tds, function (i, $td) {
486 $td.addClass('cb-line-selected'); // line number td
487 $td.addClass('cb-line-selected'); // line number td
487 $td.prev().addClass('cb-line-selected'); // line data
488 $td.prev().addClass('cb-line-selected'); // line data
488 $td.next().addClass('cb-line-selected'); // line content
489 $td.next().addClass('cb-line-selected'); // line content
489 });
490 });
490
491
491 if (highlightable_line_tds.length) {
492 if (highlightable_line_tds.length) {
492 var $first_line_td = highlightable_line_tds[0];
493 var $first_line_td = highlightable_line_tds[0];
493 scrollToElement($first_line_td);
494 scrollToElement($first_line_td);
494 $.Topic('/ui/plugins/code/anchor_focus').prepareOrPublish({
495 $.Topic('/ui/plugins/code/anchor_focus').prepareOrPublish({
495 td: $first_line_td,
496 td: $first_line_td,
496 remainder: result.remainder
497 remainder: result.remainder
497 });
498 });
498 }
499 }
499 }
500 }
500 }
501 }
501 collapsableContent();
502 collapsableContent();
502 });
503 });
503
504
504 var feedLifetimeOptions = function(query, initialData){
505 var feedLifetimeOptions = function(query, initialData){
505 var data = {results: []};
506 var data = {results: []};
506 var isQuery = typeof query.term !== 'undefined';
507 var isQuery = typeof query.term !== 'undefined';
507
508
508 var section = _gettext('Lifetime');
509 var section = _gettext('Lifetime');
509 var children = [];
510 var children = [];
510
511
511 //filter results
512 //filter results
512 $.each(initialData.results, function(idx, value) {
513 $.each(initialData.results, function(idx, value) {
513
514
514 if (!isQuery || query.term.length === 0 || value.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
515 if (!isQuery || query.term.length === 0 || value.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0) {
515 children.push({
516 children.push({
516 'id': this.id,
517 'id': this.id,
517 'text': this.text
518 'text': this.text
518 })
519 })
519 }
520 }
520
521
521 });
522 });
522 data.results.push({
523 data.results.push({
523 'text': section,
524 'text': section,
524 'children': children
525 'children': children
525 });
526 });
526
527
527 if (isQuery) {
528 if (isQuery) {
528
529
529 var now = moment.utc();
530 var now = moment.utc();
530
531
531 var parseQuery = function(entry, now){
532 var parseQuery = function(entry, now){
532 var fmt = 'DD/MM/YYYY H:mm';
533 var fmt = 'DD/MM/YYYY H:mm';
533 var parsed = moment.utc(entry, fmt);
534 var parsed = moment.utc(entry, fmt);
534 var diffInMin = parsed.diff(now, 'minutes');
535 var diffInMin = parsed.diff(now, 'minutes');
535
536
536 if (diffInMin > 0){
537 if (diffInMin > 0){
537 return {
538 return {
538 id: diffInMin,
539 id: diffInMin,
539 text: parsed.format(fmt)
540 text: parsed.format(fmt)
540 }
541 }
541 } else {
542 } else {
542 return {
543 return {
543 id: undefined,
544 id: undefined,
544 text: parsed.format('DD/MM/YYYY') + ' ' + _gettext('date not in future')
545 text: parsed.format('DD/MM/YYYY') + ' ' + _gettext('date not in future')
545 }
546 }
546 }
547 }
547
548
548
549
549 };
550 };
550
551
551 data.results.push({
552 data.results.push({
552 'text': _gettext('Specified expiration date'),
553 'text': _gettext('Specified expiration date'),
553 'children': [{
554 'children': [{
554 'id': parseQuery(query.term, now).id,
555 'id': parseQuery(query.term, now).id,
555 'text': parseQuery(query.term, now).text
556 'text': parseQuery(query.term, now).text
556 }]
557 }]
557 });
558 });
558 }
559 }
559
560
560 query.callback(data);
561 query.callback(data);
561 };
562 };
562
563
563
564
564 var storeUserSessionAttr = function (key, val) {
565 var storeUserSessionAttr = function (key, val) {
565
566
566 var postData = {
567 var postData = {
567 'key': key,
568 'key': key,
568 'val': val,
569 'val': val,
569 'csrf_token': CSRF_TOKEN
570 'csrf_token': CSRF_TOKEN
570 };
571 };
571
572
572 var success = function(o) {
573 var success = function(o) {
573 return true
574 return true
574 };
575 };
575
576
576 ajaxPOST(pyroutes.url('store_user_session_value'), postData, success);
577 ajaxPOST(pyroutes.url('store_user_session_value'), postData, success);
577 return false;
578 return false;
578 };
579 };
@@ -1,217 +1,231 b''
1 <%def name="refs_counters(branches, closed_branches, tags, bookmarks)">
1 <%def name="refs_counters(branches, closed_branches, tags, bookmarks)">
2 <span class="branchtag tag">
2 <span class="branchtag tag">
3 <a href="${h.route_path('branches_home',repo_name=c.repo_name)}" class="childs">
3 <a href="${h.route_path('branches_home',repo_name=c.repo_name)}" class="childs">
4 <i class="icon-branch"></i>${_ungettext(
4 <i class="icon-branch"></i>${_ungettext(
5 '%(num)s Branch','%(num)s Branches', len(branches)) % {'num': len(branches)}}</a>
5 '%(num)s Branch','%(num)s Branches', len(branches)) % {'num': len(branches)}}</a>
6 </span>
6 </span>
7
7
8 %if closed_branches:
8 %if closed_branches:
9 <span class="branchtag tag">
9 <span class="branchtag tag">
10 <a href="${h.route_path('branches_home',repo_name=c.repo_name)}" class="childs">
10 <a href="${h.route_path('branches_home',repo_name=c.repo_name)}" class="childs">
11 <i class="icon-branch"></i>${_ungettext(
11 <i class="icon-branch"></i>${_ungettext(
12 '%(num)s Closed Branch', '%(num)s Closed Branches', len(closed_branches)) % {'num': len(closed_branches)}}</a>
12 '%(num)s Closed Branch', '%(num)s Closed Branches', len(closed_branches)) % {'num': len(closed_branches)}}</a>
13 </span>
13 </span>
14 %endif
14 %endif
15
15
16 <span class="tagtag tag">
16 <span class="tagtag tag">
17 <a href="${h.route_path('tags_home',repo_name=c.repo_name)}" class="childs">
17 <a href="${h.route_path('tags_home',repo_name=c.repo_name)}" class="childs">
18 <i class="icon-tag"></i>${_ungettext(
18 <i class="icon-tag"></i>${_ungettext(
19 '%(num)s Tag', '%(num)s Tags', len(tags)) % {'num': len(tags)}}</a>
19 '%(num)s Tag', '%(num)s Tags', len(tags)) % {'num': len(tags)}}</a>
20 </span>
20 </span>
21
21
22 %if bookmarks:
22 %if bookmarks:
23 <span class="booktag tag">
23 <span class="booktag tag">
24 <a href="${h.route_path('bookmarks_home',repo_name=c.repo_name)}" class="childs">
24 <a href="${h.route_path('bookmarks_home',repo_name=c.repo_name)}" class="childs">
25 <i class="icon-bookmark"></i>${_ungettext(
25 <i class="icon-bookmark"></i>${_ungettext(
26 '%(num)s Bookmark', '%(num)s Bookmarks', len(bookmarks)) % {'num': len(bookmarks)}}</a>
26 '%(num)s Bookmark', '%(num)s Bookmarks', len(bookmarks)) % {'num': len(bookmarks)}}</a>
27 </span>
27 </span>
28 %endif
28 %endif
29 </%def>
29 </%def>
30
30
31 <%def name="summary_detail(breadcrumbs_links, show_downloads=True)">
31 <%def name="summary_detail(breadcrumbs_links, show_downloads=True)">
32 <% summary = lambda n:{False:'summary-short'}.get(n) %>
32 <% summary = lambda n:{False:'summary-short'}.get(n) %>
33
33
34 <div id="summary-menu-stats" class="summary-detail">
34 <div id="summary-menu-stats" class="summary-detail">
35 <div class="summary-detail-header">
35 <div class="summary-detail-header">
36 <div class="breadcrumbs files_location">
36 <div class="breadcrumbs files_location">
37 <h4>
37 <h4>
38 ${breadcrumbs_links}
38 ${breadcrumbs_links}
39 </h4>
39 </h4>
40 </div>
40 </div>
41 <div id="summary_details_expand" class="btn-collapse" data-toggle="summary-details">
41 <div id="summary_details_expand" class="btn-collapse" data-toggle="summary-details">
42 ${_('Show More')}
42 ${_('Show More')}
43 </div>
43 </div>
44 </div>
44 </div>
45
45
46 <div class="fieldset">
46 <div class="fieldset">
47
47
48 <div class="left-clone">
48 <div class="left-clone">
49 <select id="clone_option" name="clone_option">
49 <select id="clone_option" name="clone_option">
50 <option value="http" selected="selected">HTTP</option>
50 <option value="http" selected="selected">HTTP</option>
51 <option value="http_id">HTTP UID</option>
51 <option value="http_id">HTTP UID</option>
52 % if c.ssh_enabled:
52 % if c.ssh_enabled:
53 <option value="ssh">SSH</option>
53 <option value="ssh">SSH</option>
54 % endif
54 % endif
55 </select>
55 </select>
56 </div>
56 </div>
57 <div class="right-clone">
57 <div class="right-clone">
58 <%
58 <%
59 maybe_disabled = ''
59 maybe_disabled = ''
60 if h.is_svn_without_proxy(c.rhodecode_db_repo):
60 if h.is_svn_without_proxy(c.rhodecode_db_repo):
61 maybe_disabled = 'disabled'
61 maybe_disabled = 'disabled'
62 %>
62 %>
63
63
64 <span id="clone_option_http">
64 <span id="clone_option_http">
65 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url}"/>
65 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url}"/>
66 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url}" title="${_('Copy the clone url')}"></i>
66 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url}" title="${_('Copy the clone url')}"></i>
67 </span>
67 </span>
68
68
69 <span style="display: none;" id="clone_option_http_id">
69 <span style="display: none;" id="clone_option_http_id">
70 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url_id}"/>
70 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url_id}"/>
71 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url_id}" title="${_('Copy the clone by id url')}"></i>
71 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url_id}" title="${_('Copy the clone by id url')}"></i>
72 </span>
72 </span>
73
73
74 <span style="display: none;" id="clone_option_ssh">
74 <span style="display: none;" id="clone_option_ssh">
75 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url_ssh}"/>
75 <input type="text" class="input-monospace clone_url_input" ${maybe_disabled} readonly="readonly" value="${c.clone_repo_url_ssh}"/>
76 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url_ssh}" title="${_('Copy the clone by ssh url')}"></i>
76 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${c.clone_repo_url_ssh}" title="${_('Copy the clone by ssh url')}"></i>
77 </span>
77 </span>
78
78
79 % if maybe_disabled:
79 % if maybe_disabled:
80 <p class="help-block">${_('SVN Protocol is disabled. To enable it, see the')} <a href="${h.route_url('enterprise_svn_setup')}" target="_blank">${_('documentation here')}</a>.</p>
80 <p class="help-block">${_('SVN Protocol is disabled. To enable it, see the')} <a href="${h.route_url('enterprise_svn_setup')}" target="_blank">${_('documentation here')}</a>.</p>
81 % endif
81 % endif
82
82
83 </div>
83 </div>
84 </div>
84 </div>
85
85
86 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
86 <div class="fieldset">
87 <div class="left-label-summary">
87 <div class="left-label-summary">
88 ${_('Information')}:
88 &nbsp;
89 </div>
89 </div>
90 <div class="right-content">
90 <div class="right-content">
91 <div class="commit-info">
91 <div class="commit-info">
92 <div class="tags">
92 <div class="tags">
93 <% commit_rev = c.rhodecode_db_repo.changeset_cache.get('revision') %>
93 <% commit_rev = c.rhodecode_db_repo.changeset_cache.get('revision') %>
94 % if c.rhodecode_repo:
94 % if c.rhodecode_repo:
95 ${refs_counters(
95 ${refs_counters(
96 c.rhodecode_repo.branches,
96 c.rhodecode_repo.branches,
97 c.rhodecode_repo.branches_closed,
97 c.rhodecode_repo.branches_closed,
98 c.rhodecode_repo.tags,
98 c.rhodecode_repo.tags,
99 c.rhodecode_repo.bookmarks)}
99 c.rhodecode_repo.bookmarks)}
100 % else:
100 % else:
101 ## missing requirements can make c.rhodecode_repo None
101 ## missing requirements can make c.rhodecode_repo None
102 ${refs_counters([], [], [], [])}
102 ${refs_counters([], [], [], [])}
103 % endif
103 % endif
104
104
105 ## commits
105 ## commits
106 <span class="tag">
106 <span class="tag">
107 % if commit_rev == -1:
107 % if commit_rev == -1:
108 ${_ungettext('%(num)s Commit', '%(num)s Commits', 0) % {'num': 0}},
108 ${_ungettext('%(num)s Commit', '%(num)s Commits', 0) % {'num': 0}}
109 % else:
109 % else:
110 <a href="${h.route_path('repo_changelog', repo_name=c.repo_name)}">
110 <a href="${h.route_path('repo_changelog', repo_name=c.repo_name)}">
111 ${_ungettext('%(num)s Commit', '%(num)s Commits', commit_rev) % {'num': commit_rev}}</a>,
111 ${_ungettext('%(num)s Commit', '%(num)s Commits', commit_rev) % {'num': commit_rev}}</a>
112 % endif
112 % endif
113 </span>
113 </span>
114
114
115 ## forks
115 ## forks
116 <span class="tag">
116 <span class="tag">
117 <a title="${_('Number of Repository Forks')}" href="${h.route_path('repo_forks_show_all', repo_name=c.repo_name)}">
117 <a title="${_('Number of Repository Forks')}" href="${h.route_path('repo_forks_show_all', repo_name=c.repo_name)}">
118 ${c.repository_forks} ${_ungettext('Fork', 'Forks', c.repository_forks)}</a>,
118 ${c.repository_forks} ${_ungettext('Fork', 'Forks', c.repository_forks)}</a>
119 </span>
119 </span>
120
120
121 ## repo size
121 </div>
122 % if commit_rev == -1:
122 </div>
123 <span class="stats-bullet">0 B</span>
123 </div>
124 % else:
124 </div>
125 <span class="stats-bullet" id="repo_size_container">
126 ${_('Calculating Repository Size...')}
127 </span>
128 % endif
129
125
126 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
127 <div class="left-label-summary">
128 ${_('Repository size')}:
129 </div>
130 <div class="right-content">
131 <div class="commit-info">
132 <div class="tags">
133 ## repo size
134 % if commit_rev == -1:
135 <span class="stats-bullet">0 B</span>
136 % else:
137 <span>
138 <a href="#showSize" onclick="calculateSize(); $(this).hide(); return false" id="show-repo-size">Show repository size</a>
139 </span>
140 <span class="stats-bullet" id="repo_size_container" style="display:none">
141 ${_('Calculating Repository Size...')}
142 </span>
143 % endif
130 </div>
144 </div>
131 </div>
145 </div>
132 </div>
146 </div>
133 </div>
147 </div>
134
148
135 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
149 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
136 <div class="left-label-summary">
150 <div class="left-label-summary">
137 ${_('Description')}:
151 ${_('Description')}:
138 </div>
152 </div>
139 <div class="right-content">
153 <div class="right-content">
140 <div class="input ${summary(c.show_stats)}">
154 <div class="input ${summary(c.show_stats)}">
141 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
155 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
142 ${dt.repo_desc(c.rhodecode_db_repo.description_safe, c.visual.stylify_metatags)}
156 ${dt.repo_desc(c.rhodecode_db_repo.description_safe, c.visual.stylify_metatags)}
143 </div>
157 </div>
144 </div>
158 </div>
145 </div>
159 </div>
146
160
147 % if show_downloads:
161 % if show_downloads:
148 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
162 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
149 <div class="left-label-summary">
163 <div class="left-label-summary">
150 ${_('Downloads')}:
164 ${_('Downloads')}:
151 </div>
165 </div>
152 <div class="right-content">
166 <div class="right-content">
153 <div class="input ${summary(c.show_stats)} downloads">
167 <div class="input ${summary(c.show_stats)} downloads">
154 % if c.rhodecode_repo and len(c.rhodecode_repo.commit_ids) == 0:
168 % if c.rhodecode_repo and len(c.rhodecode_repo.commit_ids) == 0:
155 <span class="disabled">
169 <span class="disabled">
156 ${_('There are no downloads yet')}
170 ${_('There are no downloads yet')}
157 </span>
171 </span>
158 % elif not c.enable_downloads:
172 % elif not c.enable_downloads:
159 <span class="disabled">
173 <span class="disabled">
160 ${_('Downloads are disabled for this repository')}.
174 ${_('Downloads are disabled for this repository')}.
161 </span>
175 </span>
162 % if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
176 % if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
163 ${h.link_to(_('Enable downloads'),h.route_path('edit_repo',repo_name=c.repo_name, _anchor='repo_enable_downloads'))}
177 ${h.link_to(_('Enable downloads'),h.route_path('edit_repo',repo_name=c.repo_name, _anchor='repo_enable_downloads'))}
164 % endif
178 % endif
165 % else:
179 % else:
166 <span class="enabled">
180 <span class="enabled">
167 <a id="archive_link" class="btn btn-small" href="${h.route_path('repo_archivefile',repo_name=c.rhodecode_db_repo.repo_name,fname='tip.zip')}">
181 <a id="archive_link" class="btn btn-small" href="${h.route_path('repo_archivefile',repo_name=c.rhodecode_db_repo.repo_name,fname='tip.zip')}">
168 <i class="icon-archive"></i> tip.zip
182 <i class="icon-archive"></i> tip.zip
169 ## replaced by some JS on select
183 ## replaced by some JS on select
170 </a>
184 </a>
171 </span>
185 </span>
172 ${h.hidden('download_options')}
186 ${h.hidden('download_options')}
173 % endif
187 % endif
174 </div>
188 </div>
175 </div>
189 </div>
176 </div>
190 </div>
177 % endif
191 % endif
178
192
179 ## Statistics
193 ## Statistics
180 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
194 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
181 <div class="left-label-summary">
195 <div class="left-label-summary">
182 ${_('Statistics')}:
196 ${_('Statistics')}:
183 </div>
197 </div>
184 <div class="right-content">
198 <div class="right-content">
185 <div class="input ${summary(c.show_stats)} statistics">
199 <div class="input ${summary(c.show_stats)} statistics">
186 % if c.show_stats:
200 % if c.show_stats:
187 <div id="lang_stats" class="enabled">
201 <div id="lang_stats" class="enabled">
188 ${_('Calculating Code Statistics...')}
202 ${_('Calculating Code Statistics...')}
189 </div>
203 </div>
190 % else:
204 % else:
191 <span class="disabled">
205 <span class="disabled">
192 ${_('Statistics are disabled for this repository')}.
206 ${_('Statistics are disabled for this repository')}.
193 </span>
207 </span>
194 % if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
208 % if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
195 ${h.link_to(_('Enable statistics'),h.route_path('edit_repo',repo_name=c.repo_name, _anchor='repo_enable_statistics'))}
209 ${h.link_to(_('Enable statistics'),h.route_path('edit_repo',repo_name=c.repo_name, _anchor='repo_enable_statistics'))}
196 % endif
210 % endif
197 % endif
211 % endif
198 </div>
212 </div>
199
213
200 </div>
214 </div>
201 </div>
215 </div>
202
216
203 </div><!--end summary-detail-->
217 </div><!--end summary-detail-->
204 </%def>
218 </%def>
205
219
206 <%def name="summary_stats(gravatar_function)">
220 <%def name="summary_stats(gravatar_function)">
207 <div class="sidebar-right">
221 <div class="sidebar-right">
208 <div class="summary-detail-header">
222 <div class="summary-detail-header">
209 <h4 class="item">
223 <h4 class="item">
210 ${_('Owner')}
224 ${_('Owner')}
211 </h4>
225 </h4>
212 </div>
226 </div>
213 <div class="sidebar-right-content">
227 <div class="sidebar-right-content">
214 ${gravatar_function(c.rhodecode_db_repo.user.email, 16)}
228 ${gravatar_function(c.rhodecode_db_repo.user.email, 16)}
215 </div>
229 </div>
216 </div><!--end sidebar-right-->
230 </div><!--end sidebar-right-->
217 </%def>
231 </%def>
@@ -1,129 +1,125 b''
1 <%inherit file="/summary/summary_base.mako"/>
1 <%inherit file="/summary/summary_base.mako"/>
2
2
3 <%namespace name="components" file="/summary/components.mako"/>
3 <%namespace name="components" file="/summary/components.mako"/>
4
4
5
5
6 <%def name="menu_bar_subnav()">
6 <%def name="menu_bar_subnav()">
7 ${self.repo_menu(active='summary')}
7 ${self.repo_menu(active='summary')}
8 </%def>
8 </%def>
9
9
10 <%def name="main()">
10 <%def name="main()">
11
11
12 <div class="title">
12 <div class="title">
13 ${self.repo_page_title(c.rhodecode_db_repo)}
13 ${self.repo_page_title(c.rhodecode_db_repo)}
14 <ul class="links icon-only-links block-right">
14 <ul class="links icon-only-links block-right">
15 <li>
15 <li>
16 %if c.rhodecode_user.username != h.DEFAULT_USER:
16 %if c.rhodecode_user.username != h.DEFAULT_USER:
17 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}"><i class="icon-rss-sign"></i></a>
17 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}"><i class="icon-rss-sign"></i></a>
18 %else:
18 %else:
19 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name)}" title="${_('RSS Feed')}"><i class="icon-rss-sign"></i></a>
19 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_name)}" title="${_('RSS Feed')}"><i class="icon-rss-sign"></i></a>
20 %endif
20 %endif
21 </li>
21 </li>
22 </ul>
22 </ul>
23 </div>
23 </div>
24
24
25 <div id="repo-summary" class="summary">
25 <div id="repo-summary" class="summary">
26 ${components.summary_detail(breadcrumbs_links=self.breadcrumbs_links(), show_downloads=True)}
26 ${components.summary_detail(breadcrumbs_links=self.breadcrumbs_links(), show_downloads=True)}
27 ${components.summary_stats(gravatar_function=self.gravatar_with_user)}
27 ${components.summary_stats(gravatar_function=self.gravatar_with_user)}
28 </div><!--end repo-summary-->
28 </div><!--end repo-summary-->
29
29
30
30
31 <div class="box" >
31 <div class="box" >
32 %if not c.repo_commits:
32 %if not c.repo_commits:
33 <div class="title">
33 <div class="title">
34 <h3>${_('Quick start')}</h3>
34 <h3>${_('Quick start')}</h3>
35 </div>
35 </div>
36 %endif
36 %endif
37 <div class="table">
37 <div class="table">
38 <div id="shortlog_data">
38 <div id="shortlog_data">
39 <%include file='summary_commits.mako'/>
39 <%include file='summary_commits.mako'/>
40 </div>
40 </div>
41 </div>
41 </div>
42 </div>
42 </div>
43
43
44 %if c.readme_data:
44 %if c.readme_data:
45 <div id="readme" class="anchor">
45 <div id="readme" class="anchor">
46 <div class="box" >
46 <div class="box" >
47 <div class="title" title="${h.tooltip(_('Readme file from commit %s:%s') % (c.rhodecode_db_repo.landing_rev[0], c.rhodecode_db_repo.landing_rev[1]))}">
47 <div class="title" title="${h.tooltip(_('Readme file from commit %s:%s') % (c.rhodecode_db_repo.landing_rev[0], c.rhodecode_db_repo.landing_rev[1]))}">
48 <h3 class="breadcrumbs">
48 <h3 class="breadcrumbs">
49 <a href="${h.route_path('repo_files',repo_name=c.repo_name,commit_id=c.rhodecode_db_repo.landing_rev[1],f_path=c.readme_file)}">${c.readme_file}</a>
49 <a href="${h.route_path('repo_files',repo_name=c.repo_name,commit_id=c.rhodecode_db_repo.landing_rev[1],f_path=c.readme_file)}">${c.readme_file}</a>
50 </h3>
50 </h3>
51 </div>
51 </div>
52 <div class="readme codeblock">
52 <div class="readme codeblock">
53 <div class="readme_box">
53 <div class="readme_box">
54 ${c.readme_data|n}
54 ${c.readme_data|n}
55 </div>
55 </div>
56 </div>
56 </div>
57 </div>
57 </div>
58 </div>
58 </div>
59 %endif
59 %endif
60
60
61 <script type="text/javascript">
61 <script type="text/javascript">
62 $(document).ready(function(){
62 $(document).ready(function(){
63
63
64 var showCloneField = function(clone_url_format){
64 var showCloneField = function(clone_url_format){
65 $.each(['http', 'http_id', 'ssh'], function (idx, val) {
65 $.each(['http', 'http_id', 'ssh'], function (idx, val) {
66 if(val === clone_url_format){
66 if(val === clone_url_format){
67 $('#clone_option_' + val).show();
67 $('#clone_option_' + val).show();
68 $('#clone_option').val(val)
68 $('#clone_option').val(val)
69 } else {
69 } else {
70 $('#clone_option_' + val).hide();
70 $('#clone_option_' + val).hide();
71 }
71 }
72 });
72 });
73 };
73 };
74 // default taken from session
74 // default taken from session
75 showCloneField(templateContext.session_attrs.clone_url_format);
75 showCloneField(templateContext.session_attrs.clone_url_format);
76
76
77 $('#clone_option').on('change', function(e) {
77 $('#clone_option').on('change', function(e) {
78 var selected = $(this).val();
78 var selected = $(this).val();
79
79
80 storeUserSessionAttr('rc_user_session_attr.clone_url_format', selected);
80 storeUserSessionAttr('rc_user_session_attr.clone_url_format', selected);
81 showCloneField(selected)
81 showCloneField(selected)
82 });
82 });
83
83
84 var initialCommitData = {
84 var initialCommitData = {
85 id: null,
85 id: null,
86 text: 'tip',
86 text: 'tip',
87 type: 'tag',
87 type: 'tag',
88 raw_id: null,
88 raw_id: null,
89 files_url: null
89 files_url: null
90 };
90 };
91
91
92 select2RefSwitcher('#download_options', initialCommitData);
92 select2RefSwitcher('#download_options', initialCommitData);
93
93
94 // on change of download options
94 // on change of download options
95 $('#download_options').on('change', function(e) {
95 $('#download_options').on('change', function(e) {
96 // format of Object {text: "v0.0.3", type: "tag", id: "rev"}
96 // format of Object {text: "v0.0.3", type: "tag", id: "rev"}
97 var ext = '.zip';
97 var ext = '.zip';
98 var selected_cs = e.added;
98 var selected_cs = e.added;
99 var fname = e.added.raw_id + ext;
99 var fname = e.added.raw_id + ext;
100 var href = pyroutes.url('repo_archivefile', {'repo_name': templateContext.repo_name, 'fname':fname});
100 var href = pyroutes.url('repo_archivefile', {'repo_name': templateContext.repo_name, 'fname':fname});
101 // set new label
101 // set new label
102 $('#archive_link').html('<i class="icon-archive"></i> {0}{1}'.format(escapeHtml(e.added.text), ext));
102 $('#archive_link').html('<i class="icon-archive"></i> {0}{1}'.format(escapeHtml(e.added.text), ext));
103
103
104 // set new url to button,
104 // set new url to button,
105 $('#archive_link').attr('href', href)
105 $('#archive_link').attr('href', href)
106 });
106 });
107
107
108
108
109 // load details on summary page expand
109 // calculate size of repository
110 $('#summary_details_expand').on('click', function() {
110 calculateSize = function () {
111
111
112 var callback = function (data) {
112 var callback = function (data) {
113 % if c.show_stats:
113 % if c.show_stats:
114 showRepoStats('lang_stats', data);
114 showRepoStats('lang_stats', data);
115 % endif
115 % endif
116 };
116 };
117
117
118 showRepoSize(
118 showRepoSize('repo_size_container', templateContext.repo_name, templateContext.repo_landing_commit, callback);
119 'repo_size_container',
120 templateContext.repo_name,
121 templateContext.repo_landing_commit,
122 callback);
123
119
124 })
120 }
125
121
126 })
122 })
127 </script>
123 </script>
128
124
129 </%def>
125 </%def>
General Comments 0
You need to be logged in to leave comments. Login now