Koha/koha-tmpl/opac-tmpl/bootstrap/js/browse.js
Robin Sheat 0e7f7ab051
Bug 14567: Add a browse interface to the OPAC
This is an interface for quick and efficient browsing through records.

It presents a page at /cgi-bin/koha/opac-browse.pl that allows you to
enter the prefix of an author, title, or subject and it'll give you a
list of the options that match that. You can then scroll through these
and select the one you're after. Selecting it provides a list of records
that match that particular search.

To Test:
 1 - Apply patches
 2 - Update database (updatedatabase on kohadevbox)
 3 - Compile the CSS
   https://wiki.koha-community.org/wiki/Working_with_SCSS_in_the_OPAC_and_staff_client
   yarn build --view=opac on kohadevbox
 4 - Enable the new syspref OpacBrowseSearch
 5 - Have ES running and some records in it
     SearchEngine syspref set to Elasticsearch
 6 - Browse to opac home, click 'Browse search' link
for your site)
 7 - Test searching for author, title, and subject
 8 - Verify that results are returned in expected order
 9 - Experiment with fuzziness
     https://www.elastic.co/guide/en/elasticsearch/reference/5.6/common-options.html#fuzziness
     Options are: exact (0 edits), fuzzy (1 edit), very fuzzy (2 edits)
10 - Click any result and verify specific titles are correct
11 - Click through title to record and verify it is the correct record
12 - Test that disabling pref removes the link on the opac home

Signed-off-by: David Nind <david@davidnind.com>
Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
2020-02-20 09:07:16 +00:00

172 lines
6.7 KiB
JavaScript

jQuery.fn.overflowScrollReset = function() {
$(this).scrollTop($(this).scrollTop() - $(this).offset().top);
return this;
};
$(document).ready(function(){
var xhrGetSuggestions, xhrGetResults;
$('#browse-search form').submit(function(event) {
// if there is an in progress request, abort it so we
// don't end up with a race condition
if(xhrGetSuggestions && xhrGetSuggestions.readyState != 4){
xhrGetSuggestions.abort();
}
var userInput = $('#browse-searchterm').val().trim();
var userField = $('#browse-searchfield').val();
var userFuzziness = $('input[name=browse-searchfuzziness]:checked', '#browse-searchfuzziness').val();
var leftPaneResults = $('#browse-searchresults li').not('.loading, .no-results');
var rightPaneResults = $('#browse-selectionsearch ol li');
event.preventDefault();
if(!userInput) {
return;
}
// remove any error states and show the results area (except right pane)
$('#browse-suggestionserror').addClass('hidden');
$('#browse-searchresults .no-results').addClass('hidden');
$('#browse-resultswrapper').removeClass('hidden');
$('#browse-selection').addClass('hidden').text("");
$('#browse-selectionsearch').addClass('hidden');
// clear any results from left and right panes
leftPaneResults.remove();
rightPaneResults.remove();
// show the spinner in the left pane
$('#browse-searchresults .loading').removeClass('hidden');
xhrGetSuggestions = $.get(window.location.pathname, {api: "GetSuggestions", field: userField, prefix: userInput, fuzziness: userFuzziness})
.always(function() {
// hide spinner
$('#browse-searchresults .loading').addClass('hidden');
})
.done(function(data) {
var fragment = document.createDocumentFragment();
if (data.length === 0) {
$('#browse-searchresults .no-results').removeClass('hidden');
return;
}
// scroll to top of container again
$("#browse-searchresults").overflowScrollReset();
// store the type of search that was performed as an attrib
$('#browse-searchresults').data('field', userField);
$.each(data, function(index, object) {
// use a document fragment so we don't need to nest the elems
// or append during each iteration (which would be slow)
var elem = document.createElement("li");
var link = document.createElement("a");
link.textContent = object.text;
link.setAttribute("href", "#");
elem.appendChild(link);
fragment.appendChild(elem);
});
$('#browse-searchresults').append(fragment.cloneNode(true));
})
.fail(function(jqXHR) {
//if 500 or 404 (abort is okay though)
if (jqXHR.statusText !== "abort") {
$('#browse-resultswrapper').addClass('hidden');
$('#browse-suggestionserror').removeClass('hidden');
}
});
});
$('#browse-searchresults').on("click", 'a', function(event) {
// if there is an in progress request, abort it so we
// don't end up with a race condition
if(xhrGetResults && xhrGetResults.readyState != 4){
xhrGetResults.abort();
}
var term = $(this).text();
var field = $('#browse-searchresults').data('field');
var rightPaneResults = $('#browse-selectionsearch ol li');
event.preventDefault();
// clear any current selected classes and add a new one
$(this).parent().siblings().children().removeClass('selected');
$(this).addClass('selected');
// copy in the clicked text
$('#browse-selection').removeClass('hidden').text(term);
// show the right hand pane if it is not shown already
$('#browse-selectionsearch').removeClass('hidden');
// hide the no results element
$('#browse-selectionsearch .no-results').addClass('hidden');
// clear results
rightPaneResults.remove();
// turn the spinner on
$('#browse-selectionsearch .loading').removeClass('hidden');
// do the query for the term
xhrGetResults = $.get(window.location.pathname, {api: "GetResults", field: field, term: term})
.always(function() {
// hide spinner
$('#browse-selectionsearch .loading').addClass('hidden');
})
.done(function(data) {
var fragment = document.createDocumentFragment();
if (data.length === 0) {
$('#browse-selectionsearch .no-results').removeClass('hidden');
return;
}
// scroll to top of container again
$("#browse-selectionsearch").overflowScrollReset();
$.each(data, function(index, object) {
// use a document fragment so we don't need to nest the elems
// or append during each iteration (which would be slow)
var elem = document.createElement("li");
var title = document.createElement("h4");
var link = document.createElement("a");
var author = document.createElement("p");
var destination = window.location.pathname;
destination = destination.replace("browse", "detail");
destination = destination + "?biblionumber=" + object.id;
author.className = "author";
link.setAttribute("href", destination);
link.setAttribute("target", "_blank");
link.textContent = object.title;
title.appendChild(link);
author.textContent = object.author;
elem.appendChild(title);
elem.appendChild(author);
fragment.appendChild(elem);
});
$('#browse-selectionsearch ol').append(fragment.cloneNode(true));
})
.fail(function(jqXHR) {
//if 500 or 404 (abort is okay though)
if (jqXHR.statusText !== "abort") {
$('#browse-resultswrapper').addClass('hidden');
$('#browse-suggestionserror').removeClass('hidden');
}
});
});
});