##// END OF EJS Templates
files: made file filter an active input instead of a button.
marcink -
r3694:4aa7e6be new-ui
parent child Browse files
Show More
@@ -1951,7 +1951,7 b' def get_visual_attr(tmpl_context_var, at'
1951
1951
1952 def get_last_path_part(file_node):
1952 def get_last_path_part(file_node):
1953 if not file_node.path:
1953 if not file_node.path:
1954 return u''
1954 return u'/'
1955
1955
1956 path = safe_unicode(file_node.path.split('/')[-1])
1956 path = safe_unicode(file_node.path.split('/')[-1])
1957 return u'../' + path
1957 return u'../' + path
@@ -2221,25 +2221,41 b' h3.files_location{'
2221 margin: -25px 0px 5px 0px;
2221 margin: -25px 0px 5px 0px;
2222 }
2222 }
2223
2223
2224 .node-filter {
2224 .files-quick-filter {
2225 font-size: @repo-title-fontsize;
2225 float: right;
2226 padding: 4px 0px 0px 0px;
2226 margin: 0 25px;
2227
2228 .node-filter-path {
2229 float: left;
2230 color: @grey4;
2231 }
2232 .node-filter-input {
2233 float: left;
2234 margin: -2px 0px 0px 2px;
2235 input {
2236 padding: 2px;
2237 border: none;
2238 font-size: @repo-title-fontsize;
2239 }
2240 }
2241 }
2227 }
2242
2228
2229 .files-filter-box {
2230 display: flex;
2231 padding: 0px;
2232 border-radius: 3px;
2233 border: 1px solid @grey3;
2234
2235 a {
2236 border: none !important;
2237 }
2238
2239 li {
2240 list-style-type: none
2241 }
2242 }
2243
2244
2245 .files-filter-box-path {
2246 line-height: 28px;
2247 padding: 0 5px;
2248
2249 }
2250
2251 .files-filter-box-input {
2252 background-color: @grey3 !important;
2253 margin-right: 0;
2254
2255 input {
2256 border: none
2257 }
2258 }
2243
2259
2244 .browser-result{
2260 .browser-result{
2245 td a{
2261 td a{
@@ -19,234 +19,254 b''
19 /**
19 /**
20 * Search file list
20 * Search file list
21 */
21 */
22 // global reference to file-node filter
22
23 var _NODEFILTER = {};
23 var NodeFilter = {};
24
24
25 var fileBrowserListeners = function(node_list_url, url_base){
25 var fileBrowserListeners = function (node_list_url, url_base) {
26 var n_filter = $('#node_filter').get(0);
26 var $filterInput = $('#node_filter');
27
27 var n_filter = $filterInput.get(0);
28 _NODEFILTER.filterTimeout = null;
29 var nodes = null;
30
28
31 _NODEFILTER.fetchNodes = function(callback) {
29 NodeFilter.filterTimeout = null;
32 $.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': true}})
30 var nodes = null;
33 .done(function(data){
34 nodes = data.nodes;
35 if (callback) {
36 callback();
37 }
38 })
39 .fail(function(data){
40 console.log('failed to load');
41 });
42 };
43
31
44 _NODEFILTER.fetchNodesCallback = function() {
32 NodeFilter.focus = function () {
45 $('#node_filter_box_loading').hide();
33 $filterInput.focus()
46 $('#node_filter_box').removeClass('hidden').show();
34 };
47 n_filter.focus();
48 if ($('#node_filter').hasClass('init')){
49 n_filter.value = '';
50 $('#node_filter').removeClass('init');
51 }
52 };
53
35
54 _NODEFILTER.initFilter = function(){
36 NodeFilter.fetchNodes = function (callback) {
55 $('#node_filter_box_loading').removeClass('hidden').show();
37 $.ajax(
56 $('#search_activate_id').hide();
38 {url: node_list_url, headers: {'X-PARTIAL-XHR': true}})
57 $('#search_deactivate_id').removeClass('hidden').show();
39 .done(function (data) {
58 $('#add_node_id').hide();
40 nodes = data.nodes;
59 _NODEFILTER.fetchNodes(_NODEFILTER.fetchNodesCallback);
41 if (callback) {
60 };
42 callback();
43 }
44 })
45 .fail(function (data) {
46 console.log('failed to load');
47 });
48 };
61
49
62 _NODEFILTER.resetFilter = function(){
50 NodeFilter.initFilter = function (e) {
63 $('#node_filter_box_loading').hide();
51 if ($filterInput.hasClass('loading')) {
64 $('#node_filter_box').hide();
52 return
65 $('#search_activate_id').show();
66 $('#search_deactivate_id').hide();
67 $('#add_node_id').show();
68 $('#tbody').show();
69 $('#tbody_filtered').hide();
70 $('#node_filter').val('');
71 };
72
73 _NODEFILTER.fuzzy_match = function(filepath, query) {
74 var highlight = [];
75 var order = 0;
76 for (var i = 0; i < query.length; i++) {
77 var match_position = filepath.indexOf(query[i]);
78 if (match_position !== -1) {
79 var prev_match_position = highlight[highlight.length-1];
80 if (prev_match_position === undefined) {
81 highlight.push(match_position);
82 } else {
83 var current_match_position = prev_match_position + match_position + 1;
84 highlight.push(current_match_position);
85 order = order + current_match_position - prev_match_position;
86 }
53 }
87 filepath = filepath.substring(match_position+1);
88 } else {
89 return false;
90 }
91 }
92 return {'order': order,
93 'highlight': highlight};
94 };
95
54
96 _NODEFILTER.sortPredicate = function(a, b) {
55 // in case we are already loaded, do nothing
97 if (a.order < b.order) return -1;
56 if (!$filterInput.hasClass('init')) {
98 if (a.order > b.order) return 1;
57 return NodeFilter.handleKey(e);
99 if (a.filepath < b.filepath) return -1;
100 if (a.filepath > b.filepath) return 1;
101 return 0;
102 };
103
104 _NODEFILTER.updateFilter = function(elem, e) {
105 return function(){
106 // Reset timeout
107 _NODEFILTER.filterTimeout = null;
108 var query = elem.value.toLowerCase();
109 var match = [];
110 var matches_max = 20;
111 if (query !== ""){
112 var results = [];
113 for(var k=0;k<nodes.length;k++){
114 var result = _NODEFILTER.fuzzy_match(
115 nodes[k].name.toLowerCase(), query);
116 if (result) {
117 result.type = nodes[k].type;
118 result.filepath = nodes[k].name;
119 results.push(result);
120 }
121 }
122 results = results.sort(_NODEFILTER.sortPredicate);
123 var limit = matches_max;
124 if (results.length < matches_max) {
125 limit = results.length;
126 }
58 }
127 for (var i=0; i<limit; i++){
59
128 if(query && results.length > 0){
60 var org = $('.files-filter-box-path .tag').html();
129 var n = results[i].filepath;
61 $('.files-filter-box-path .tag').html('loading...');
130 var t = results[i].type;
62 $filterInput.addClass('loading');
131 var n_hl = n.split("");
132 var pos = results[i].highlight;
133 for (var j = 0; j < pos.length; j++) {
134 n_hl[pos[j]] = "<em>" + n_hl[pos[j]] + "</em>";
135 }
136 n_hl = n_hl.join("");
137 var new_url = url_base.replace('__FPATH__',n);
138
63
139 var typeObj = {
64 var callback = function (org) {
140 dir: 'icon-directory browser-dir',
65 return function () {
141 file: 'icon-file-text browser-file'
66 if ($filterInput.hasClass('init')) {
142 };
67 $filterInput.removeClass('init');
68 $filterInput.removeClass('loading');
69 }
70 $('.files-filter-box-path .tag').html(org);
143
71
144 var typeIcon = '<i class="{0}"></i>'.format(typeObj[t]);
72 // auto re-filter if we filled in the input
145 match.push('<tr class="browser-result"><td><a class="match-link" href="{0}">{1}{2}</a></td><td colspan="5"></td></tr>'.format(new_url,typeIcon, n_hl));
73 if (n_filter.value !== "") {
146 }
74 NodeFilter.updateFilter(n_filter, e)()
147 }
75 }
148 if(results.length > limit){
149 var truncated_count = results.length - matches_max;
150 if (truncated_count === 1) {
151 match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated result')));
152 } else {
153 match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated results')));
154 }
155 }
156 }
157 if (query !== ""){
158 $('#tbody').hide();
159 $('#tbody_filtered').show();
160
76
161 if (match.length === 0){
77 }
162 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_gettext('No matching files')));
78 };
163 }
79 // load node data
164 $('#tbody_filtered').html(match.join(""));
80 NodeFilter.fetchNodes(callback(org));
165 }
81
166 else{
82 };
83
84 NodeFilter.resetFilter = function () {
167 $('#tbody').show();
85 $('#tbody').show();
168 $('#tbody_filtered').hide();
86 $('#tbody_filtered').hide();
169 }
87 $filterInput.val('');
88 };
89
90 NodeFilter.handleKey = function (e) {
91 var scrollDown = function (element) {
92 var elementBottom = element.offset().top + $(element).outerHeight();
93 var windowBottom = window.innerHeight + $(window).scrollTop();
94 if (elementBottom > windowBottom) {
95 var offset = elementBottom - window.innerHeight;
96 $('html,body').scrollTop(offset);
97 return false;
98 }
99 return true;
100 };
101
102 var scrollUp = function (element) {
103 if (element.offset().top < $(window).scrollTop()) {
104 $('html,body').scrollTop(element.offset().top);
105 return false;
106 }
107 return true;
108 };
109 var $hlElem = $('.browser-highlight');
110
111 if (e.keyCode === 40) { // Down
112 if ($hlElem.length === 0) {
113 $('.browser-result').first().addClass('browser-highlight');
114 } else {
115 var next = $hlElem.next();
116 if (next.length !== 0) {
117 $hlElem.removeClass('browser-highlight');
118 next.addClass('browser-highlight');
119 }
120 }
121
122 if ($hlElem.get(0) !== undefined){
123 scrollDown($hlElem);
124 }
125 }
126 if (e.keyCode === 38) { // Up
127 e.preventDefault();
128 if ($hlElem.length !== 0) {
129 var next = $hlElem.prev();
130 if (next.length !== 0) {
131 $('.browser-highlight').removeClass('browser-highlight');
132 next.addClass('browser-highlight');
133 }
134 }
135
136 if ($hlElem.get(0) !== undefined){
137 scrollUp($hlElem);
138 }
139
140 }
141 if (e.keyCode === 13) { // Enter
142 if ($('.browser-highlight').length !== 0) {
143 var url = $('.browser-highlight').find('.match-link').attr('href');
144 window.location = url;
145 }
146 }
147 if (e.keyCode === 27) { // Esc
148 NodeFilter.resetFilter();
149 $('html,body').scrollTop(0);
150 }
151
152 var capture_keys = [
153 40, // ArrowDown
154 38, // ArrowUp
155 39, // ArrowRight
156 37, // ArrowLeft
157 13, // Enter
158 27 // Esc
159 ];
160
161 if ($.inArray(e.keyCode, capture_keys) === -1) {
162 clearTimeout(NodeFilter.filterTimeout);
163 NodeFilter.filterTimeout = setTimeout(NodeFilter.updateFilter(n_filter, e), 200);
164 }
170
165
171 };
166 };
172 };
173
174 var scrollDown = function(element){
175 var elementBottom = element.offset().top + $(element).outerHeight();
176 var windowBottom = window.innerHeight + $(window).scrollTop();
177 if (elementBottom > windowBottom) {
178 var offset = elementBottom - window.innerHeight;
179 $('html,body').scrollTop(offset);
180 return false;
181 }
182 return true;
183 };
184
167
185 var scrollUp = function(element){
168 NodeFilter.fuzzy_match = function (filepath, query) {
186 if (element.offset().top < $(window).scrollTop()) {
169 var highlight = [];
187 $('html,body').scrollTop(element.offset().top);
170 var order = 0;
188 return false;
171 for (var i = 0; i < query.length; i++) {
189 }
172 var match_position = filepath.indexOf(query[i]);
190 return true;
173 if (match_position !== -1) {
191 };
174 var prev_match_position = highlight[highlight.length - 1];
175 if (prev_match_position === undefined) {
176 highlight.push(match_position);
177 } else {
178 var current_match_position = prev_match_position + match_position + 1;
179 highlight.push(current_match_position);
180 order = order + current_match_position - prev_match_position;
181 }
182 filepath = filepath.substring(match_position + 1);
183 } else {
184 return false;
185 }
186 }
187 return {
188 'order': order,
189 'highlight': highlight
190 };
191 };
192
192
193 $('#filter_activate').click(function() {
193 NodeFilter.sortPredicate = function (a, b) {
194 _NODEFILTER.initFilter();
194 if (a.order < b.order) return -1;
195 });
195 if (a.order > b.order) return 1;
196
196 if (a.filepath < b.filepath) return -1;
197 $('#filter_deactivate').click(function() {
197 if (a.filepath > b.filepath) return 1;
198 _NODEFILTER.resetFilter();
198 return 0;
199 });
199 };
200
201 $(n_filter).click(function() {
202 if ($('#node_filter').hasClass('init')){
203 n_filter.value = '';
204 $('#node_filter').removeClass('init');
205 }
206 });
207
200
208 $(n_filter).keydown(function(e) {
201 NodeFilter.updateFilter = function (elem, e) {
209 if (e.keyCode === 40){ // Down
202 return function () {
210 if ($('.browser-highlight').length === 0){
203 // Reset timeout
211 $('.browser-result').first().addClass('browser-highlight');
204 NodeFilter.filterTimeout = null;
212 } else {
205 var query = elem.value.toLowerCase();
213 var next = $('.browser-highlight').next();
206 var match = [];
214 if (next.length !== 0) {
207 var matches_max = 20;
215 $('.browser-highlight').removeClass('browser-highlight');
208 if (query !== "") {
216 next.addClass('browser-highlight');
209 var results = [];
217 }
210 for (var k = 0; k < nodes.length; k++) {
218 }
211 var result = NodeFilter.fuzzy_match(
219 scrollDown($('.browser-highlight'));
212 nodes[k].name.toLowerCase(), query);
220 }
213 if (result) {
221 if (e.keyCode === 38){ // Up
214 result.type = nodes[k].type;
222 e.preventDefault();
215 result.filepath = nodes[k].name;
223 if ($('.browser-highlight').length !== 0){
216 results.push(result);
224 var next = $('.browser-highlight').prev();
217 }
225 if (next.length !== 0) {
218 }
226 $('.browser-highlight').removeClass('browser-highlight');
219 results = results.sort(NodeFilter.sortPredicate);
227 next.addClass('browser-highlight');
220 var limit = matches_max;
228 }
221 if (results.length < matches_max) {
229 }
222 limit = results.length;
230 scrollUp($('.browser-highlight'));
223 }
231 }
224 for (var i = 0; i < limit; i++) {
232 if (e.keyCode === 13){ // Enter
225 if (query && results.length > 0) {
233 if ($('.browser-highlight').length !== 0){
226 var n = results[i].filepath;
234 var url = $('.browser-highlight').find('.match-link').attr('href');
227 var t = results[i].type;
235 window.location = url;
228 var n_hl = n.split("");
236 }
229 var pos = results[i].highlight;
237 }
230 for (var j = 0; j < pos.length; j++) {
238 if (e.keyCode === 27){ // Esc
231 n_hl[pos[j]] = "<em>" + n_hl[pos[j]] + "</em>";
239 _NODEFILTER.resetFilter();
232 }
240 $('html,body').scrollTop(0);
233 n_hl = n_hl.join("");
241 }
234 var new_url = url_base.replace('__FPATH__', n);
242 });
235
243 var capture_keys = [40, 38, 39, 37, 13, 27];
236 var typeObj = {
244 $(n_filter).keyup(function(e) {
237 dir: 'icon-directory browser-dir',
245 if ($.inArray(e.keyCode, capture_keys) === -1){
238 file: 'icon-file-text browser-file'
246 clearTimeout(_NODEFILTER.filterTimeout);
239 };
247 _NODEFILTER.filterTimeout = setTimeout(_NODEFILTER.updateFilter(n_filter, e),200);
240
248 }
241 var typeIcon = '<i class="{0}"></i>'.format(typeObj[t]);
249 });
242 match.push('<tr class="browser-result"><td><a class="match-link" href="{0}">{1}{2}</a></td><td colspan="5"></td></tr>'.format(new_url, typeIcon, n_hl));
243 }
244 }
245 if (results.length > limit) {
246 var truncated_count = results.length - matches_max;
247 if (truncated_count === 1) {
248 match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated result')));
249 } else {
250 match.push('<tr><td>{0} {1}</td><td colspan="5"></td></tr>'.format(truncated_count, _gettext('truncated results')));
251 }
252 }
253 }
254 if (query !== "") {
255 $('#tbody').hide();
256 $('#tbody_filtered').show();
257
258 if (match.length === 0) {
259 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_gettext('No matching files')));
260 }
261 $('#tbody_filtered').html(match.join(""));
262 } else {
263 $('#tbody').show();
264 $('#tbody_filtered').hide();
265 }
266
267 };
268 };
269
250 };
270 };
251
271
252 var getIdentNode = function(n){
272 var getIdentNode = function(n){
@@ -383,7 +383,8 b''
383 callbacks();
383 callbacks();
384 var search_GET = "${request.GET.get('search','')}";
384 var search_GET = "${request.GET.get('search','')}";
385 if (search_GET === "1") {
385 if (search_GET === "1") {
386 _NODEFILTER.initFilter();
386 NodeFilter.initFilter();
387 NodeFilter.focus();
387 }
388 }
388 });
389 });
389
390
@@ -3,25 +3,37 b''
3 <div class="browser-header">
3 <div class="browser-header">
4 <div class="browser-nav">
4 <div class="browser-nav">
5
5
6 <div class="files-quick-filter">
7
8 <ul class="files-filter-box">
9
10 <li class="files-filter-box-path">
11 <span class="tag">
12 ${h.get_last_path_part(c.file)}
13 </span>
14 </li>
15
16 <li class="files-filter-box-input">
17 <input onkeydown="NodeFilter.initFilter(event)" class="init" type="text" name="filter" size="25" id="node_filter" autocomplete="off">
18 </li>
19
20 </ul>
21
22 </div>
23
6 <div class="info_box">
24 <div class="info_box">
7
25
8 <div class="info_box_elem previous">
26 <div class="info_box_elem previous">
9 <a id="prev_commit_link" data-commit-id="${c.prev_commit.raw_id}" class=" ${'disabled' if c.url_prev == '#' else ''}" href="${c.url_prev}" title="${_('Previous commit')}"><i class="icon-left"></i></a>
27 <a id="prev_commit_link" data-commit-id="${c.prev_commit.raw_id}" class=" ${('disabled' if c.url_prev == '#' else '')}" href="${c.url_prev}" title="${_('Previous commit')}"><i class="icon-left"></i></a>
10 </div>
28 </div>
11
29
12 ${h.hidden('refs_filter')}
30 ${h.hidden('refs_filter')}
13
31
14 <div class="info_box_elem next">
32 <div class="info_box_elem next">
15 <a id="next_commit_link" data-commit-id="${c.next_commit.raw_id}" class=" ${'disabled' if c.url_next == '#' else ''}" href="${c.url_next}" title="${_('Next commit')}"><i class="icon-right"></i></a>
33 <a id="next_commit_link" data-commit-id="${c.next_commit.raw_id}" class=" ${('disabled' if c.url_next == '#' else '')}" href="${c.url_next}" title="${_('Next commit')}"><i class="icon-right"></i></a>
16 </div>
34 </div>
17 </div>
35 </div>
18
36
19 <div id="search_activate_id" class="search_activate">
20 <a class="btn btn-default" id="filter_activate" href="javascript:void(0)">${_('Search File List')}</a>
21 </div>
22 <div id="search_deactivate_id" class="search_activate hidden">
23 <a class="btn btn-default" id="filter_deactivate" href="javascript:void(0)">${_('Close File List')}</a>
24 </div>
25 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
37 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
26 <div title="${_('Add New File')}" class="btn btn-primary new-file">
38 <div title="${_('Add New File')}" class="btn btn-primary new-file">
27 <a href="${h.route_path('repo_files_add_file',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path, _anchor='edit')}">
39 <a href="${h.route_path('repo_files_add_file',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path, _anchor='edit')}">
@@ -36,19 +48,9 b''
36 </a>
48 </a>
37 </div>
49 </div>
38 % endif
50 % endif
51
39 </div>
52 </div>
40
53
41 <div class="browser-search">
42 <div class="node-filter">
43 <div class="node_filter_box hidden" id="node_filter_box_loading" >${_('Loading file list...')}</div>
44 <div class="node_filter_box hidden" id="node_filter_box" >
45 <div class="node-filter-path">${h.get_last_path_part(c.file)}/</div>
46 <div class="node-filter-input">
47 <input class="init" type="text" name="filter" size="25" id="node_filter" autocomplete="off">
48 </div>
49 </div>
50 </div>
51 </div>
52 </div>
54 </div>
53 ## file tree is computed from caches, and filled in
55 ## file tree is computed from caches, and filled in
54 <div id="file-tree">
56 <div id="file-tree">
General Comments 0
You need to be logged in to leave comments. Login now