diff --git a/lib/private/search.php b/lib/private/search.php index 4629d52b40e..22f92534cbd 100644 --- a/lib/private/search.php +++ b/lib/private/search.php @@ -64,8 +64,10 @@ class Search implements ISearch { $providerResults = $provider->search($query); if ($size > 0) { $slicedResults = array_slice($providerResults, $page * $size, $size); + $results = array_merge($results, $slicedResults); + } else { + $results = array_merge($results, $providerResults); } - $results = array_merge($results, $slicedResults); } else { \OC::$server->getLogger()->warning('Ignoring Unknown search provider', array('provider' => $provider)); } diff --git a/search/ajax/search.php b/search/ajax/search.php index 90771084659..e26432d1eb2 100644 --- a/search/ajax/search.php +++ b/search/ajax/search.php @@ -46,7 +46,7 @@ if (isset($_GET['page'])) { if (isset($_GET['size'])) { $size = (int)$_GET['size']; } else { - $size = 0; + $size = 30; } if($query) { $result = \OC::$server->getSearch()->search($query, $inApps, $page, $size); diff --git a/search/css/results.css b/search/css/results.css index 78b60e65e45..5dbfa2bd50e 100644 --- a/search/css/results.css +++ b/search/css/results.css @@ -14,6 +14,9 @@ box-sizing: border-box; z-index:75; } +#searchresults * { + box-sizing: content-box; +} #searchresults table { border-spacing:0; @@ -23,6 +26,9 @@ } #searchresults td { + padding: 0 15px; + font-style: normal; + vertical-align: middle; border-top: 20px solid white; border-bottom: none; } diff --git a/search/js/result.js b/search/js/result.js index 95526749c53..217d66dc1e3 100644 --- a/search/js/result.js +++ b/search/js/result.js @@ -8,171 +8,76 @@ * */ -OC.Search.hide = function(){ - $('#searchresults').hide(); - if($('#searchbox').val().length>2){ - $('#searchbox').val(''); - if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system - FileList.unfilter(); - } - }; - if ($('#searchbox').val().length === 0) { - if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system - FileList.unfilter(); - } - } -}; -OC.Search.showResults = function(results){ - if(results.length === 0){ - return; - } - if (!OC.Search.showResults.loaded){ - var parent = $('
'); - $('#app-content').append(parent); - parent.load(OC.filePath('search','templates','part.results.php'),function(){ - OC.Search.showResults.loaded = true; - $('#searchresults').click(function(event){ - OC.Search.hide(); - event.stopPropagation(); - }); - $(document).click(function(event){ - OC.Search.hide(); - if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system - FileList.unfilter(); - } - }); - OC.Search.lastResults=results; - OC.Search.showResults(results); - }); - } else { - $('#searchresults tr.result').remove(); - $('#searchresults').show(); - jQuery.each(results, function(i, result) { - var $row = $('#searchresults tr.template').clone(); - $row.removeClass('template'); - $row.addClass('result'); +//FIXME move to files? +$(document).ready(function() { + // wait for other apps/extensions to register their event handlers and file actions + // in the "ready" clause + _.defer(function() { + OC.Search.setFormatter('file', function ($row, result) { + // backward compatibility: + if (typeof result.mime !== 'undefined') { + result.mime_type = result.mime; + } else if (typeof result.mime_type !== 'undefined') { + result.mime = result.mime_type; + } - $row.data('result', result); + $pathDiv = $('
').text(result.path); + $row.find('td.info div.name').after($pathDiv).text(result.name); - // generic results only have four attributes - $row.find('td.info div.name').text(result.name); - $row.find('td.info a').attr('href', result.link); + $row.find('td.result a').attr('href', result.link); - $row.find('td.icon').css('background-image', 'url(' + OC.imagePath('core', 'places/link') + ')'); - /** - * Give plugins the ability to customize the search results. For example: - * OC.search.customResults.file = function (row, item){ FIXME - * if(item.name.search('.json') >= 0) ... - * }; - */ - if (OC.Search.hasFormatter(result.type)) { - OC.Search.getFormatter(result.type)($row, result); + if (OCA.Files) { + OCA.Files.App.fileList.lazyLoadPreview({ + path: result.path, + mime: result.mime, + callback: function (url) { + $row.find('td.icon').css('background-image', 'url(' + url + ')'); + } + }); } else { - // for backward compatibility add text div - $row.find('td.info div.name').addClass('result') - $row.find('td.result div.name').after('
'); - $row.find('td.result div.text').text(result.name); - if(OC.search.customResults && OC.search.customResults[result.type]) { - OC.search.customResults[result.type]($row, result); + // FIXME how to get mime icon if not in files app + var mimeicon = result.mime.replace('/', '-'); + $row.find('td.icon').css('background-image', 'url(' + OC.imagePath('core', 'filetypes/' + mimeicon) + ')'); + var dir = OC.dirname(result.path); + if (dir === '') { + dir = '/'; } + $row.find('td.info a').attr('href', + OC.generateUrl('/apps/files/?dir={dir}&scrollto={scrollto}', {dir: dir, scrollto: result.name}) + ); } - $('#searchresults tbody').append($row); }); - - $('#searchresults').on('click', 'tr.result', function (event) { - var $row = $(this); - var result = $row.data('result'); - if(OC.Search.hasHandler(result.type)){ - var result = OC.Search.getHandler(result.type)($row, result, event); - OC.Search.hide(); - event.stopPropagation(); - return result; + OC.Search.setHandler('file', function ($row, result, event) { + if (OCA.Files) { + OCA.Files.App.fileList.changeDirectory(OC.dirname(result.path)); + OCA.Files.App.fileList.scrollTo(result.name); + return false; + } else { + return true; } }); - } -}; -OC.Search.showResults.loaded = false; -OC.Search.renderCurrent = function(){ - var $resultsContainer = $('#searchresults'); - var result = $resultsContainer.find('tr.result')[OC.Search.currentResult] - if (result) { - var $result = $(result); - var currentOffset = $resultsContainer.scrollTop(); - $resultsContainer.animate({ - // Scrolling to the top of the new result - scrollTop: currentOffset + $result.offset().top - $result.height() * 2 - }, { - duration: 100 - }); - $resultsContainer.find('tr.result.current').removeClass('current'); - $result.addClass('current'); - } -}; - -OC.Search.setFormatter('file', function ($row, result) { - // backward compatibility: - if (typeof result.mime !== 'undefined') { - result.mime_type = result.mime; - } else if (typeof result.mime_type !== 'undefined') { - result.mime = result.mime_type; - } - - $pathDiv = $('
').text(result.path); - $row.find('td.info div.name').after($pathDiv).text(result.name); + OC.Search.setFormatter('folder', function ($row, result) { + // backward compatibility: + if (typeof result.mime !== 'undefined') { + result.mime_type = result.mime; + } else if (typeof result.mime_type !== 'undefined') { + result.mime = result.mime_type; + } - $row.find('td.result a').attr('href', result.link); + var $pathDiv = $('
').text(result.path) + $row.find('td.info div.name').after($pathDiv).text(result.name); - if (OCA.Files) { - OCA.Files.App.fileList.lazyLoadPreview({ - path: result.path, - mime: result.mime, - callback: function (url) { - $row.find('td.icon').css('background-image', 'url(' + url + ')'); + $row.find('td.result a').attr('href', result.link); + $row.find('td.icon').css('background-image', 'url(' + OC.imagePath('core', 'filetypes/folder') + ')'); + }); + OC.Search.setHandler('folder', function ($row, result, event) { + if (OCA.Files) { + OCA.Files.App.fileList.changeDirectory(result.path); + return false; + } else { + return true; } }); - } else { - // FIXME how to get mime icon if not in files app - var mimeicon = result.mime.replace('/','-'); - $row.find('td.icon').css('background-image', 'url(' + OC.imagePath('core', 'filetypes/'+mimeicon) + ')'); - var dir = OC.dirname(result.path); - if (dir === '') { - dir = '/'; - } - $row.find('td.info a').attr('href', - OC.generateUrl('/apps/files/?dir={dir}&scrollto={scrollto}', {dir:dir, scrollto:result.name}) - ); - } -}); -OC.Search.setHandler('file', function ($row, result, event) { - if (OCA.Files) { - OCA.Files.App.fileList.changeDirectory(OC.dirname(result.path)); - OCA.Files.App.fileList.scrollTo(result.name); - return false; - } else { - return true; - } -}); - -OC.Search.setFormatter('folder', function ($row, result) { - // backward compatibility: - if (typeof result.mime !== 'undefined') { - result.mime_type = result.mime; - } else if (typeof result.mime_type !== 'undefined') { - result.mime = result.mime_type; - } - - var $pathDiv = $('
').text(result.path) - $row.find('td.info div.name').after($pathDiv).text(result.name); - - $row.find('td.result a').attr('href', result.link); - $row.find('td.icon').css('background-image', 'url(' + OC.imagePath('core', 'filetypes/folder') + ')'); -}); -OC.Search.setHandler('folder', function ($row, result, event) { - if (OCA.Files) { - OCA.Files.App.fileList.changeDirectory(result.path); - return false; - } else { - return true; - } + }); }); diff --git a/search/js/search.js b/search/js/search.js index 06a96fd582b..7a6428bce33 100644 --- a/search/js/search.js +++ b/search/js/search.js @@ -4,113 +4,257 @@ * This file is licensed under the Affero General Public License version 3 or * later. See the COPYING file. * - * @author Bernhard Posselt - * @copyright Bernhard Posselt 2014 + * @author Jörn Friedrich Dreyer + * @copyright Jörn Friedrich Dreyer 2014 */ -(function (exports) { - - 'use strict'; +(function () { + /** + * @class OCA.Search + * @classdesc + * + * The Search class manages a search queries and their results + * + * @param $searchBox container element with existing markup for the #searchbox form + */ + var Search = function($searchBox) { + this.initialize($searchBox); + }; + /** + * @memberof OC + */ + Search.prototype = { - exports.Search = { - /** - * contains closures that are called to format search results - */ - formatter:{}, - setFormatter: function(type, formatter) { - this.formatter[type] = formatter; - }, - hasFormatter: function(type) { - return typeof this.formatter[type] !== 'undefined'; - }, - getFormatter: function(type) { - return this.formatter[type]; - }, - /** - * contains closures that are called when a search result has been clicked - */ - handler:{}, - setHandler: function(type, handler) { - this.handler[type] = handler; - }, - hasHandler: function(type) { - return typeof this.handler[type] !== 'undefined'; - }, - getHandler: function(type) { - return this.handler[type]; - }, - currentResult:-1, - lastQuery:'', - lastResults:{}, /** - * Do a search query and display the results - * @param {string} query the search query + * Initialize the search box and results + * + * @param $searchBox container element with existing markup for the #searchbox form + * @private */ - search: _.debounce(function(query, page, size) { - if(query) { - exports.addStyle('search','results'); - if (typeof page !== 'number') { - page = 0; + initialize: function($searchBox) { + + var that = this; + + /** + * contains closures that are called to format search results + */ + var formatters = {}; + this.setFormatter = function(type, formatter) { + formatters[type] = formatter; + }; + this.hasFormatter = function(type) { + return typeof formatters[type] !== 'undefined'; + }; + this.getFormatter = function(type) { + return formatters[type]; + }; + + /** + * contains closures that are called when a search result has been clicked + */ + var handlers = {}; + this.setHandler = function(type, handler) { + handlers[type] = handler; + }; + this.hasHandler = function(type) { + return typeof handlers[type] !== 'undefined'; + }; + this.getHandler = function(type) { + return handlers[type]; + }; + + var currentResult = -1; + var lastQuery = ''; + var lastPage = 0; + var lastSize = 30; + var lastResults = {}; + + /** + * Do a search query and display the results + * @param {string} query the search query + */ + this.search = _.debounce(function(query, page, size) { + if(query) { + OC.addStyle('search','results'); + if (typeof page !== 'number') { + page = 0; + } + if (typeof size !== 'number') { + size = 30; + } + // prevent double pages + if (query === lastPage && page === lastPage && currentResult !== -1) { + return; + } + $.getJSON(OC.generateUrl('search/ajax/search.php'), {query:query, page:page, size:size }, function(results) { + lastQuery = query; + lastPage = page; + lastSize = size; + lastResults = results; + if (page === 0) { + showResults(results); + } else { + addResults(results); + } + }); + } + }, 500); + var $searchResults = false; + + function showResults(results) { + if (results.length === 0) { + return; } - if (typeof size !== 'number') { - size = 30; + if (!$searchResults) { + var $parent = $('
'); + $('#app-content').append($parent); + $parent.load(OC.webroot + '/search/templates/part.results.html', function () { + $searchResults = $parent.find('#searchresults'); + $searchResults.click(function (event) { + that.hideResults(); + event.stopPropagation(); + }); + $(document).click(function (event) { + that.hideResults(); + if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system + FileList.unfilter(); + } + }); + $searchResults.on('scroll', _.bind(onScroll, this)); + lastResults = results; + showResults(results); + }); + } else { + $searchResults.find('tr.result').remove(); + $searchResults.show(); + addResults(results); } - $.getJSON(OC.generateUrl('search/ajax/search.php'), {query:query, page:page, size:size }, function(results) { - exports.Search.lastResults = results; - exports.Search.showResults(results); - }); } - }, 500) - }; + function addResults(results) { + var $template = $searchResults.find('tr.template'); + jQuery.each(results, function (i, result) { + var $row = $template.clone(); + $row.removeClass('template'); + $row.addClass('result'); + $row.data('result', result); - $(document).ready(function () { - $('form.searchbox').submit(function(event) { - event.preventDefault(); - }); - $('#searchbox').keyup(function(event) { - if (event.keyCode === 13) { //enter - if(exports.Search.currentResult > -1) { - var result = $('#searchresults tr.result a')[exports.Search.currentResult]; - window.location = $(result).attr('href'); - } - } else if(event.keyCode === 38) { //up - if(exports.Search.currentResult > 0) { - exports.Search.currentResult--; - exports.Search.renderCurrent(); + // generic results only have four attributes + $row.find('td.info div.name').text(result.name); + $row.find('td.info a').attr('href', result.link); + $row.find('td.icon').css('background-image', 'url(' + OC.imagePath('core', 'places/link') + ')'); + /** + * Give plugins the ability to customize the search results. see result.js for examples + */ + if (that.hasFormatter(result.type)) { + that.getFormatter(result.type)($row, result); + } else { + // for backward compatibility add text div + $row.find('td.info div.name').addClass('result'); + $row.find('td.result div.name').after('
'); + $row.find('td.result div.text').text(result.name); + if (OC.search.customResults && OC.search.customResults[result.type]) { + OC.search.customResults[result.type]($row, result); + } + } + $searchResults.find('tbody').append($row); + }); + } + function renderCurrent() { + var result = $searchResults.find('tr.result')[currentResult]; + if (result) { + var $result = $(result); + var currentOffset = $searchResults.scrollTop(); + $searchResults.animate({ + // Scrolling to the top of the new result + scrollTop: currentOffset + $result.offset().top - $result.height() * 2 + }, { + duration: 100 + }); + $searchResults.find('tr.result.current').removeClass('current'); + $result.addClass('current'); } - } else if(event.keyCode === 40) { //down - if(exports.Search.lastResults.length > exports.Search.currentResult + 1){ - exports.Search.currentResult++; - exports.Search.renderCurrent(); - } - } else if(event.keyCode === 27) { //esc - exports.Search.hide(); - if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system - FileList.unfilter(); + } + this.hideResults = function() { + if ($searchResults) { + $searchResults.hide(); + if ($searchBox.val().length > 2) { + $searchBox.val(''); + if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system + FileList.unfilter(); + } + } + if ($searchBox.val().length === 0) { + if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system + FileList.unfilter(); + } + } } - } else { - var query = $('#searchbox').val(); - if (exports.Search.lastQuery !== query) { - exports.Search.lastQuery = query; - exports.Search.currentResult = -1; - if (FileList && typeof FileList.filter === 'function') { //TODO add hook system - FileList.filter(query); + }; + + $searchBox.keyup(function(event) { + if (event.keyCode === 13) { //enter + if(currentResult > -1) { + var result = $searchResults.find('tr.result a')[currentResult]; + window.location = $(result).attr('href'); } - if (query.length > 2) { - exports.Search.search(query); - } else { - if (exports.Search.hide) { - exports.Search.hide(); + } else if(event.keyCode === 38) { //up + if(currentResult > 0) { + currentResult--; + renderCurrent(); + + } + } else if(event.keyCode === 40) { //down + if(lastResults.length > currentResult + 1){ + currentResult++; + renderCurrent(); + } + } else if(event.keyCode === 27) { //esc + that.hideResults(); + if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system + FileList.unfilter(); + } + } else { + var query = $searchBox.val(); + if (lastQuery !== query) { + lastQuery = query; + currentResult = -1; + if (FileList && typeof FileList.filter === 'function') { //TODO add hook system + FileList.filter(query); + } + if (query.length > 2) { + that.search(query); + } else { + if (that.hideResults) { + that.hideResults(); + } } } } + }); + + /** + * Event handler for when scrolling the list container. + * This appends/renders the next page of entries when reaching the bottom. + */ + function onScroll(e) { + if ( $searchResults.scrollTop() + $searchResults.height() > $searchResults.find('table').height() - 300 ) { + that.search(lastQuery, lastPage + 1); + } } - }); - }); -}(OC)); + $('form.searchbox').submit(function(event) { + event.preventDefault(); + }); + } + }; + OCA.Search = Search; +})(); + +$(document).ready(function() { + OC.Search = new OCA.Search($('#searchbox')); +}); /** * @deprecated use get/setFormatter() instead diff --git a/search/templates/part.results.php b/search/templates/part.results.html similarity index 100% rename from search/templates/part.results.php rename to search/templates/part.results.html