PageSourceSearch

http://kwartz.lyceejeanbart.fr:8080/owncloud/core/search/js/search.js?v=ff0de99913ec549c4e34d726896dcac1

js lyceejeanbart.fr collected 2026-10-01 23:47:12 UTC 11,797 bytes, 415 lines download raw bytes

1/**
2 * ownCloud - core
3 *
4 * This file is licensed under the Affero General Public License version 3 or
5 * later. See the COPYING file.
6 *
7 * @author Jörn Friedrich Dreyer <[email protected]>
8 * @copyright Jörn Friedrich Dreyer 2014
9 */
10
11(function () {
12	/**
13	 * @class OCA.Search
14	 * @classdesc
15	 *
16	 * The Search class manages a search queries and their results
17	 *
18	 * @param $searchBox container element with existing markup for the #searchbox form
19	 * @param $searchResults container element for results und status message
20	 */
21	var Search = function($searchBox, $searchResults) {
22		this.initialize($searchBox, $searchResults);
23	};
24	/**
25	 * @memberof OC
26	 */
27	Search.prototype = {
28
29		/**
30		 * Initialize the search box
31		 *
32		 * @param $searchBox container element with existing markup for the #searchbox form
33		 * @param $searchResults container element for results und status message
34		 * @private
35		 */
36		initialize: function($searchBox, $searchResults) {
37
38			var self = this;
39
40			/**
41			 * contains closures that are called to filter the current content
42			 */
43			var filters = {};
44			this.setFilter = function(type, filter) {
45				filters[type] = filter;
46			};
47			this.hasFilter = function(type) {
48				return typeof filters[type] !== 'undefined';
49			};
50			this.getFilter = function(type) {
51				return filters[type];
52			};
53
54			/**
55			 * contains closures that are called to render search results
56			 */
57			var renderers = {};
58			this.setRenderer = function(type, renderer) {
59				renderers[type] = renderer;
60			};
61			this.hasRenderer = function(type) {
62				return typeof renderers[type] !== 'undefined';
63			};
64			this.getRenderer = function(type) {
65				return renderers[type];
66			};
67
68			/**
69			 * contains closures that are called when a search result has been clicked
70			 */
71			var handlers = {};
72			this.setHandler = function(type, handler) {
73				handlers[type] = handler;
74			};
75			this.hasHandler = function(type) {
76				return typeof handlers[type] !== 'undefined';
77			};
78			this.getHandler = function(type) {
79				return handlers[type];
80			};
81
82			var currentResult = -1;
83			var lastQuery = '';
84			var lastInApps = [];
85			var lastPage = 0;
86			var lastSize = 30;
87			var lastResults = [];
88			var timeoutID = null;
89
90			this.getLastQuery = function() {
91				return lastQuery;
92			};
93
94			/**
95			 * Do a search query and display the results
96			 * @param {string} query the search query
97			 * @param inApps
98			 * @param page
99			 * @param size
100			 */
101			this.search = function(query, inApps, page, size) {
102				if (query) {
103					OC.addStyle('core/search','results');
104					if (typeof page !== 'number') {
105						page = 1;
106					}
107					if (typeof size !== 'number') {
108						size = 30;
109					}
110					if (typeof inApps !== 'object') {
111						var currentApp = getCurrentApp();
112						if(currentApp) {
113							inApps = [currentApp];
114						} else {
115							inApps = [];
116						}
117					}
118					// prevent double pages
119					if ($searchResults && query === lastQuery && page === lastPage && size === lastSize) {
120						return;
121					}
122					window.clearTimeout(timeoutID);
123					timeoutID = window.setTimeout(function() {
124						lastQuery = query;
125						lastInApps = inApps;
126						lastPage = page;
127						lastSize = size;
128
129						//show spinner
130						$searchResults.removeClass('hidden');
131						$status.addClass('status');
132						$status.html(t('core', 'Searching other places')+'<img class="spinner" alt="search in progress" src="'+OC.webroot+'/core/img/loading.gif" />');
133
134						// do the actual search query
135						$.getJSON(OC.generateUrl('core/search'), {query:query, inApps:inApps, page:page, size:size }, function(results) {
136							lastResults = results;
137							if (page === 1) {
138								showResults(results);
139							} else {
140								addResults(results);
141							}
142						});
143					}, 500);
144				}
145			};
146
147			//TODO should be a core method, see https://github.com/owncloud/core/issues/12557
148			function getCurrentApp() {
149				var content = document.getElementById('content');
150				if (content) {
151					var classList = document.getElementById('content').className.split(/\s+/);
152					for (var i = 0; i < classList.length; i++) {
153						if (classList[i].indexOf('app-') === 0) {
154							return classList[i].substr(4);
155						}
156					}
157				}
158				return false;
159			}
160
161			var $status = $searchResults.find('#status');
162			// summaryAndStatusHeight is a constant
163			var summaryAndStatusHeight = 118;
164
165			function isStatusOffScreen() {
166				return $searchResults.position() &&
167					($searchResults.position().top + summaryAndStatusHeight > window.innerHeight);
168			}
169
170			function placeStatus() {
171				if (isStatusOffScreen()) {
172					$status.addClass('fixed');
173				} else {
174					$status.removeClass('fixed');
175				}
176			}
177			function showResults(results) {
178				lastResults = results;
179				$searchResults.find('tr.result').remove();
180				$searchResults.removeClass('hidden');
181				addResults(results);
182			}
183			function addResults(results) {
184				var $template = $searchResults.find('tr.template');
185				jQuery.each(results, function (i, result) {
186					var $row = $template.clone();
187					$row.removeClass('template');
188					$row.addClass('result');
189
190					$row.data('result', result);
191
192					// generic results only have four attributes
193					$row.find('td.info div.name').text(result.name);
194					$row.find('td.info a').attr('href', result.link);
195
196					/**
197					 * Give plugins the ability to customize the search results. see result.js for examples
198					 */
199					if (self.hasRenderer(result.type)) {
200						$row = self.getRenderer(result.type)($row, result);
201					} else {
202						// for backward compatibility add text div
203						$row.find('td.info div.name').addClass('result');
204						$row.find('td.result div.name').after('<div class="text"></div>');
205						$row.find('td.result div.text').text(result.name);
206						if (OC.search.customResults && OC.search.customResults[result.type]) {
207							OC.search.customResults[result.type]($row, result);
208						}
209					}
210					if ($row) {
211						$searchResults.find('tbody').append($row);
212					}
213				});
214				var count = $searchResults.find('tr.result').length;
215				$status.data('count', count);
216				if (count === 0) {
217					$status.addClass('emptycontent').removeClass('status');
218					$status.html('');
219					$status.append('<div class="icon-search"></div>');
220					$status.append('<h2>' + t('core', 'No search results in other folders') + '</h2>');
221				} else {
222					$status.removeClass('emptycontent').addClass('status');
223					$status.text(n('core', '{count} search result in another folder', '{count} search results in other folders', count, {count:count}));
224				}
225			}
226			function renderCurrent() {
227				var result = $searchResults.find('tr.result')[currentResult];
228				if (result) {
229					var $result = $(result);
230					var currentOffset = $('#app-content').scrollTop();
231					$('#app-content').animate({
232						// Scrolling to the top of the new result
233						scrollTop: currentOffset + $result.offset().top - $result.height() * 2
234					}, {
235						duration: 100
236					});
237					$searchResults.find('tr.result.current').removeClass('current');
238					$result.addClass('current');
239				}
240			}
241			this.hideResults = function() {
242				$searchResults.addClass('hidden');
243				$searchResults.find('tr.result').remove();
244				lastQuery = false;
245			};
246			this.clear = function() {
247				self.hideResults();
248				if(self.hasFilter(getCurrentApp())) {
249					self.getFilter(getCurrentApp())('');
250				}
251				$searchBox.val('');
252				$searchBox.blur();
253			};
254
255			/**
256			 * Event handler for when scrolling the list container.
257			 * This appends/renders the next page of entries when reaching the bottom.
258			 */
259			function onScroll() {
260				if ($searchResults && lastQuery !== false && lastResults.length > 0) {
261					var resultsBottom = $searchResults.offset().top + $searchResults.height();
262					var containerBottom = $searchResults.offsetParent().offset().top +
263						$searchResults.offsetParent().height();
264					if ( resultsBottom < containerBottom * 1.2 ) {
265						self.search(lastQuery, lastInApps, lastPage + 1);
266					}
267					placeStatus();
268				}
269			}
270
271			$('#app-content').on('scroll', _.bind(onScroll, this));
272
273			/**
274			 * scrolls the search results to the top
275			 */
276			function scrollToResults() {
277				setTimeout(function() {
278					if (isStatusOffScreen()) {
279						var newScrollTop = $('#app-content').prop('scrollHeight') - $searchResults.height();
280						console.log('scrolling to ' + newScrollTop);
281						$('#app-content').animate({
282							scrollTop: newScrollTop
283						}, {
284							duration: 100,
285							complete: function () {
286								scrollToResults();
287							}
288						});
289					}
290				}, 150);
291			}
292
293			$('form.searchbox').submit(function(event) {
294				event.preventDefault();
295			});
296
297			$searchBox.on('search', function () {
298				if($searchBox.val() === '') {
299					if(self.hasFilter(getCurrentApp())) {
300						self.getFilter(getCurrentApp())('');
301					}
302					self.hideResults();
303				}
304			});
305			$searchBox.keyup(function(event) {
306				if (event.keyCode === 13) { //enter
307					if(currentResult > -1) {
308						var result = $searchResults.find('tr.result a')[currentResult];
309						window.location = $(result).attr('href');
310					}
311				} else if(event.keyCode === 38) { //up
312					if(currentResult > 0) {
313						currentResult--;
314						renderCurrent();
315					}
316				} else if(event.keyCode === 40) { //down
317					if(lastResults.length > currentResult + 1){
318						currentResult++;
319						renderCurrent();
320					}
321				} else {
322					var query = $searchBox.val();
323					if (lastQuery !== query) {
324						currentResult = -1;
325						if (query.length > 2) {
326							self.search(query);
327						} else {
328							self.hideResults();
329						}
330						if(self.hasFilter(getCurrentApp())) {
331							self.getFilter(getCurrentApp())(query);
332						}
333					}
334				}
335			});
336			$(document).keyup(function(event) {
337				if(event.keyCode === 27) { //esc
338					$searchBox.val('');
339					if(self.hasFilter(getCurrentApp())) {
340						self.getFilter(getCurrentApp())('');
341					}
342					self.hideResults();
343				}
344			});
345
346			$(document).keydown(function(event) {
347				if ((event.ctrlKey || event.metaKey) && // Ctrl or Command (OSX)
348					!event.shiftKey &&
349					event.keyCode === 70 && // F
350					self.hasFilter(getCurrentApp()) && // Search is enabled
351					!$searchBox.is(':focus') // if searchbox is already focused do nothing (fallback to browser default)
352				) {
353					$searchBox.focus();
354					event.preventDefault();
355				}
356			});
357
358			$searchResults.on('click', 'tr.result', function (event) {
359				var $row = $(this);
360				var item = $row.data('result');
361				if(self.hasHandler(item.type)){
362					var result = self.getHandler(item.type)($row, item, event);
363					$searchBox.val('');
364					if(self.hasFilter(getCurrentApp())) {
365						self.getFilter(getCurrentApp())('');
366					}
367					self.hideResults();
368					return result;
369				}
370			});
371			$searchResults.on('click', '#status', function (event) {
372				event.preventDefault();
373				scrollToResults();
374				return false;
375			});
376			placeStatus();
377
378			OC.Plugins.attach('OCA.Search', this);
379
380			// hide search file if search is not enabled
381			if(self.hasFilter(getCurrentApp())) {
382				return;
383			}
384			if ($searchResults.length === 0) {
385				$searchBox.hide();
386			}
387		}
388	};
389	OCA.Search = Search;
390})();
391
392$(document).ready(function() {
393	var $searchResults = $('#searchresults');
394	if ($searchResults.length > 0) {
395		$searchResults.addClass('hidden');
396		$('#app-content')
397			.find('.viewcontainer').css('min-height', 'initial');
398		$searchResults.load(OC.webroot + '/core/search/templates/part.results.html', function () {
399			OC.Search = new OCA.Search($('#searchbox'), $('#searchresults'));
400		});
401	} else {
402		_.defer(function() {
403			OC.Search = new OCA.Search($('#searchbox'), $('#searchresults'));
404		});
405	}
406});
407
408/**
409 * @deprecated use get/setRenderer() instead
410 */
411OC.search.customResults = {};
412/**
413 * @deprecated use get/setRenderer() instead
414 */
415OC.search.resultTypes = {};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.