View | Details | Raw Unified | Return to bug 14567
Collapse All | Expand All

(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-browse.tt (+83 lines)
Line 0 Link Here
1
[% USE Koha %]
2
[% INCLUDE 'doc-head-open.inc' %][% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog ›  Browse our catalog
3
[% INCLUDE 'doc-head-close.inc' %]
4
[% BLOCK cssinclude %][% END %]
5
</head>
6
[% INCLUDE 'bodytag.inc' bodyid='opac-browser' %]
7
[% INCLUDE 'masthead.inc' %]
8
9
    <div class="main">
10
        <ul class="breadcrumb">
11
            <li><a href="/cgi-bin/koha/opac-main.pl">Home</a> <span class="divider">&rsaquo;</span></li>
12
            <li><a href="#">Browse search</a></li>
13
        </ul>
14
15
        <div class="container-fluid">
16
            <div class="row-fluid">
17
                [% IF ( OpacNav || OpacNavBottom ) %]
18
                    <div class="span2">
19
                        <div id="navigation">
20
                            [% INCLUDE 'navigation.inc' %]
21
                        </div>
22
                    </div>
23
                [% END %]
24
25
                [% IF ( OpacNav ) %]<div class="span10">[% ELSE %]<div class="span12">[% END %]
26
27
                <div id="browse-search">
28
                    <h1>Browse search</h1>
29
30
                    <form>
31
                        <label for="browse-searchterm">Search for: </label><input type="search" id="browse-searchterm" name="searchterm" value="" />
32
33
                        <label for="browse-searchfield" class="hide-text">Search type: </label>
34
                        <select id="browse-searchfield" name="searchfield">
35
                            <option value="author">Author</option>
36
                            <option value="subject">Subject</option>
37
                            <option value="title">Title</option>
38
                        </select>
39
40
                        <div id="browse-searchfuzziness">
41
                            <label for="exact" class="radio inline"><input type="radio" name="browse-searchfuzziness" id="exact" value="0" /> Exact</label>
42
43
                            <label for="fuzzy" class="radio inline"><input type="radio" name="browse-searchfuzziness" id="fuzzy" value="1" checked="checked" /> Fuzzy</label>
44
45
                            <label for="reallyfuzzy" class="radio inline"><input type="radio" name="browse-searchfuzziness" id="reallyfuzzy" value="2" /> Really Fuzzy</label>
46
                        </div>
47
48
                        <button class="btn btn-success" type="submit"  accesskey="s">Search</button>
49
                    </form>
50
51
                    <p id="browse-suggestionserror" class="error hidden">An error occured, please try again.</p>
52
53
                    <div id="browse-resultswrapper" class="hidden">
54
55
                      <ol id="browse-searchresults" class="span3" start="-1" aria-live="polite">
56
                          <li class="loading hidden"><img src="[% interface %]/[% theme %]/images/loading.gif" alt="" /> Loading</li>
57
                          <li class="no-results hidden">Sorry, there are no results, try a different search term.</li>
58
                      </ol>
59
60
                      <h3 id="browse-selection" class="span9"></h3>
61
                      <div id="browse-selectionsearch" class="span9 hidden">
62
63
                          <div class="loading hidden">
64
                            <img src="[% interface %]/[% theme %]/images/loading.gif" alt="" /> Loading
65
                          </div>
66
67
                          <div class="no-results hidden">
68
                            No results
69
                          </div>
70
71
                          <ol aria-live="polite">
72
                          </ol>
73
                        </div>
74
                      </div><!-- / .results-wrapper -->
75
76
                    </div> <!-- / .userbrowser -->
77
                </div> <!-- / .span10 -->
78
            </div> <!-- / .row-fluid -->
79
        </div> <!-- / .container-fluid -->
80
    </div> <!-- / .main -->
81
82
[% INCLUDE 'opac-bottom.inc' %]
83
[% BLOCK jsinclude %]<script type="text/javascript" src="[% interface %]/[% theme %]/js/browse.js"></script>[% END %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/js/browse.js (+176 lines)
Line 0 Link Here
1
jQuery.fn.overflowScrollReset = function() {
2
    $(this).scrollTop($(this).scrollTop() - $(this).offset().top);
3
    return this;
4
};
5
6
$(document).ready(function(){
7
    var xhrGetSuggestions, xhrGetResults;
8
9
    $('#browse-search form').submit(function(event) {
10
        // if there is an in progress request, abort it so we
11
        // don't end up with  a race condition
12
        if(xhrGetSuggestions && xhrGetSuggestions.readyState != 4){
13
            xhrGetSuggestions.abort();
14
        }
15
16
        var userInput = $('#browse-searchterm').val().trim();
17
        var userField = $('#browse-searchfield').val();
18
        var userFuzziness = $('input[name=browse-searchfuzziness]:checked', '#browse-searchfuzziness').val();
19
        var leftPaneResults = $('#browse-searchresults li').not('.loading, .no-results');
20
        var rightPaneResults = $('#browse-selectionsearch ol li');
21
22
        event.preventDefault();
23
24
        if(!userInput) {
25
            return;
26
        }
27
28
        // remove any error states and show the results area (except right pane)
29
        $('#browse-suggestionserror').addClass('hidden');
30
        $('#browse-searchresults .no-results').addClass('hidden');
31
        $('#browse-resultswrapper').removeClass('hidden');
32
        $('#browse-selection').addClass('hidden').text("");
33
        $('#browse-selectionsearch').addClass('hidden');
34
35
        // clear any results from left and right panes
36
        leftPaneResults.remove();
37
        rightPaneResults.remove();
38
39
        // show the spinner in the left pane
40
        $('#browse-searchresults .loading').removeClass('hidden');
41
42
        xhrGetSuggestions = $.get(window.location.pathname, {api: "GetSuggestions", field: userField, prefix: userInput, fuzziness: userFuzziness})
43
            .always(function() {
44
                // hide spinner
45
                $('#browse-searchresults .loading').addClass('hidden');
46
            })
47
            .done(function(data) {
48
                var fragment = document.createDocumentFragment();
49
50
                if (data.length === 0) {
51
                    $('#browse-searchresults .no-results').removeClass('hidden');
52
53
                    return;
54
                }
55
56
                // scroll to top of container again
57
                $("#browse-searchresults").overflowScrollReset();
58
59
                // store the type of search that was performed as an attrib
60
                $('#browse-searchresults').data('field', userField);
61
62
                $.each(data, function(index, object) {
63
                    // use a document fragment so we don't need to nest the elems
64
                    // or append during each iteration (which would be slow)
65
                    var elem = document.createElement("li");
66
                    var link = document.createElement("a");
67
                    link.textContent = object.text;
68
                    link.setAttribute("href", "#");
69
                    elem.appendChild(link);
70
                    fragment.appendChild(elem);
71
                });
72
73
                $('#browse-searchresults').append(fragment.cloneNode(true));
74
            })
75
            .fail(function(jqXHR) {
76
                //if 500 or 404 (abort is okay though)
77
                if (jqXHR.statusText !== "abort") {
78
                    $('#browse-resultswrapper').addClass('hidden');
79
                    $('#browse-suggestionserror').removeClass('hidden');
80
                }
81
            });
82
    });
83
84
    $('#browse-searchresults').on("click", 'a', function(event) {
85
        // if there is an in progress request, abort it so we
86
        // don't end up with  a race condition
87
        if(xhrGetResults && xhrGetResults.readyState != 4){
88
            xhrGetResults.abort();
89
        }
90
91
        var term = $(this).text();
92
        var field = $('#browse-searchresults').data('field');
93
        var rightPaneResults = $('#browse-selectionsearch ol li');
94
95
        event.preventDefault();
96
97
        // clear any current selected classes and add a new one
98
        $(this).parent().siblings().children().removeClass('selected');
99
        $(this).addClass('selected');
100
101
        // copy in the clicked text
102
        $('#browse-selection').removeClass('hidden').text(term);
103
104
        // show the right hand pane if it is not shown already
105
        $('#browse-selectionsearch').removeClass('hidden');
106
107
        // hide the no results element
108
        $('#browse-selectionsearch .no-results').addClass('hidden');
109
110
        // clear results
111
        rightPaneResults.remove();
112
113
        // turn the spinner on
114
        $('#browse-selectionsearch .loading').removeClass('hidden');
115
116
        // do the query for the term
117
        xhrGetResults = $.get(window.location.pathname, {api: "GetResults", field: field, term: term})
118
            .always(function() {
119
                // hide spinner
120
                $('#browse-selectionsearch .loading').addClass('hidden');
121
            })
122
            .done(function(data) {
123
                var fragment = document.createDocumentFragment();
124
125
                if (data.length === 0) {
126
                    $('#browse-selectionsearch .no-results').removeClass('hidden');
127
128
                    return;
129
                }
130
131
                // scroll to top of container again
132
                $("#browse-selectionsearch").overflowScrollReset();
133
134
                $.each(data, function(index, object) {
135
                    // use a document fragment so we don't need to nest the elems
136
                    // or append during each iteration (which would be slow)
137
                    var elem = document.createElement("li");
138
                    var title = document.createElement("h4");
139
                    var link = document.createElement("a");
140
                    var author = document.createElement("p");
141
                    var subjects = document.createElement("p");
142
                    var destination = window.location.pathname;
143
144
                    destination = destination.replace("browse", "detail");
145
                    destination = destination + "?biblionumber=" + object.id;
146
147
                    author.className = "author";
148
                    subjects.className = "subjects";
149
150
                    link.setAttribute("href", destination);
151
                    link.setAttribute("target", "_blank");
152
                    link.textContent = object.title;
153
                    title.appendChild(link);
154
155
                    author.textContent = object.authors.join(', ');
156
                    subjects.textContent = object.subjects.join(' | ');
157
158
                    elem.appendChild(title);
159
                    elem.appendChild(author);
160
                    elem.appendChild(subjects);
161
                    fragment.appendChild(elem);
162
                });
163
164
                $('#browse-selectionsearch ol').append(fragment.cloneNode(true));
165
            })
166
            .fail(function(jqXHR) {
167
                //if 500 or 404 (abort is okay though)
168
                if (jqXHR.statusText !== "abort") {
169
                    $('#browse-resultswrapper').addClass('hidden');
170
                    $('#browse-suggestionserror').removeClass('hidden');
171
                }
172
            });
173
174
    });
175
176
});
(-)a/koha-tmpl/opac-tmpl/bootstrap/less/opac.less (+101 lines)
Lines 2473-2476 a.reviewlink:visited { Link Here
2473
    padding-left:20px;
2473
    padding-left:20px;
2474
}
2474
}
2475
2475
2476
/*opac browse search*/
2477
#browse-search {
2478
2479
    form {
2480
        label {
2481
            display: inline-block;
2482
            margin-right:5px;
2483
        }
2484
2485
        [type=submit] {
2486
            margin-top: 10px;
2487
        }
2488
    }
2489
2490
    #browse-resultswrapper {
2491
        margin-top: 4em;
2492
2493
        @media (min-width: 768px) and (max-width: 984px) {
2494
            margin-top: 2em;
2495
        }
2496
2497
        @media (max-width: 767px) {
2498
            margin-top: 1em;
2499
        }
2500
    }
2501
    #browse-searchresults, #browse-selectionsearch {
2502
        border: 1px solid #E3E3E3;
2503
        .border-radius-all(4px);
2504
        padding: 0;
2505
        overflow-y: auto;
2506
        max-height: 31em;
2507
        margin-bottom: 2em;
2508
    }
2509
    #browse-searchresults {
2510
        max-height: 31em;
2511
        list-style: none;
2512
        padding: 10px;
2513
2514
        a {
2515
            display: block;
2516
            margin-bottom: 5px;
2517
2518
            &.selected {
2519
                background-color:#EEE;
2520
            }
2521
        }
2522
2523
        li:last-child a {
2524
            margin-bottom: 0;
2525
        }
2526
2527
        @media (max-width: 767px) {
2528
            max-height: 13em;
2529
        }
2530
    }
2531
    #browse-selection {
2532
        margin-top: -40px;
2533
        padding-top: 0;
2534
2535
        @media (max-width: 767px) {
2536
            margin-top: 0;
2537
        }
2538
    }
2539
    #browse-selectionsearch ol {
2540
        list-style: none;
2541
        margin: 0;
2542
2543
        li {
2544
            padding: 1em;
2545
2546
            &:nth-child(odd) {
2547
                background-color: #F4F4F4;
2548
            }
2549
        }
2550
    }
2551
    #browse-selectionsearch p.subjects {
2552
        font-size: 0.9em;
2553
        margin-bottom: 0;
2554
    }
2555
    #browse-selectionsearch h4 {
2556
        margin: 0;
2557
    }
2558
    .error, .no-results {
2559
        background-color: #EEE;
2560
        border: 1px solid #E8E8E8;
2561
        text-align: left;
2562
        padding: 0.5em;
2563
        .border-radius-all(3px);
2564
    }
2565
    .loading {
2566
        text-align: center;
2567
2568
        img {
2569
            margin:0.5em 0;
2570
            position: relative;
2571
            left: -5px;
2572
        }
2573
    }
2574
}
2575
/*end browse search*/
2576
2476
@import "responsive.less";
2577
@import "responsive.less";
(-)a/opac/opac-browse.pl (-2 / +12 lines)
Lines 24-29 use CGI qw( -utf8 :standard ); Link Here
24
24
25
use C4::Auth;
25
use C4::Auth;
26
use C4::Context;
26
use C4::Context;
27
use C4::Output;
27
28
28
use Koha::ElasticSearch;
29
use Koha::ElasticSearch;
29
use Koha::SearchEngine::Elasticsearch::Browse;
30
use Koha::SearchEngine::Elasticsearch::Browse;
Lines 43-49 my $api = $query->param('api'); Link Here
43
if ( !$api ) {
44
if ( !$api ) {
44
45
45
    # No parameters, so we just render the template.
46
    # No parameters, so we just render the template.
46
47
    my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
48
        {
49
            template_name   => "opac-browse.tt",
50
            query           => $query,
51
            type            => "opac",
52
            authnotrequired => ( C4::Context->preference("OpacPublic") ? 1 : 0 ),
53
            debug           => 1, # TODO remove
54
        }
55
    );
56
    $template->param();
57
    output_html_with_http_headers $query, $cookie, $template->output;
47
}
58
}
48
elsif ( $api eq 'GetSuggestions' ) {
59
elsif ( $api eq 'GetSuggestions' ) {
49
    my $fuzzie = $query->param('fuzziness');
60
    my $fuzzie = $query->param('fuzziness');
50
- 

Return to bug 14567