Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// This file is part of Moodle - http://moodle.org/
2
//
3
// Moodle is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, either version 3 of the License, or
6
// (at your option) any later version.
7
//
8
// Moodle is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
// GNU General Public License for more details.
12
//
13
// You should have received a copy of the GNU General Public License
14
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
15
 
16
/**
17
 * Search methods for finding contents in the content bank.
18
 *
19
 * @module     core_contentbank/search
20
 * @copyright  2020 Sara Arjona <sara@moodle.com>
21
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
 
24
import $ from 'jquery';
25
import selectors from 'core_contentbank/selectors';
26
import {getString} from 'core/str';
27
import Pending from 'core/pending';
28
import {debounce} from 'core/utils';
29
 
30
/**
31
 * Set up the search.
32
 *
33
 * @method init
34
 */
35
export const init = () => {
36
    const pendingPromise = new Pending();
37
 
38
    const root = $(selectors.regions.contentbank);
39
    registerListenerEvents(root);
40
 
41
    pendingPromise.resolve();
42
};
43
 
44
/**
45
 * Register contentbank search related event listeners.
46
 *
47
 * @method registerListenerEvents
48
 * @param {Object} root The root element for the contentbank.
49
 */
50
const registerListenerEvents = (root) => {
51
 
52
    const searchInput = root.find(selectors.elements.searchinput)[0];
53
 
54
    root.on('click', selectors.actions.search, function(e) {
55
        e.preventDefault();
56
        toggleSearchResultsView(root, searchInput.value);
57
    });
58
 
59
    root.on('click', selectors.actions.clearSearch, function(e) {
60
        e.preventDefault();
61
        searchInput.value = "";
62
        searchInput.focus();
63
        toggleSearchResultsView(root, searchInput.value);
64
    });
65
 
66
    // The search input is also triggered.
67
    searchInput.addEventListener('input', debounce(() => {
68
        // Display the search results.
69
        toggleSearchResultsView(root, searchInput.value);
70
    }, 300));
71
 
72
};
73
 
74
/**
75
 * Toggle (display/hide) the search results depending on the value of the search query.
76
 *
77
 * @method toggleSearchResultsView
78
 * @param {HTMLElement} body The root element for the contentbank.
79
 * @param {String} searchQuery The search query.
80
 */
81
const toggleSearchResultsView = async(body, searchQuery) => {
82
    const clearSearchButton = body.find(selectors.actions.clearSearch)[0];
83
 
84
    const navbarBreadcrumb = body.find(selectors.elements.cbnavbarbreadcrumb)[0];
85
    const navbarTotal = body.find(selectors.elements.cbnavbartotalsearch)[0];
86
    // Update the results.
87
    const filteredContents = filterContents(body, searchQuery);
88
    if (searchQuery.length > 0) {
89
        // As the search query is present, search results should be displayed.
90
 
91
        // Display the "clear" search button in the activity chooser search bar.
92
        clearSearchButton.classList.remove('d-none');
93
 
94
        // Change the cb-navbar to display total items found.
95
        navbarBreadcrumb.classList.add('d-none');
96
        navbarTotal.innerHTML = await getString('itemsfound', 'core_contentbank', filteredContents.length);
97
        navbarTotal.classList.remove('d-none');
98
    } else {
99
        // As search query is not present, the search results should be removed.
100
 
101
        // Hide the "clear" search button in the activity chooser search bar.
102
        clearSearchButton.classList.add('d-none');
103
 
104
        // Display again the breadcrumb in the navbar.
105
        navbarBreadcrumb.classList.remove('d-none');
106
        navbarTotal.classList.add('d-none');
107
    }
108
};
109
 
110
/**
111
 * Return the list of contents which have a name that matches the given search term.
112
 *
113
 * @method filterContents
114
 * @param {HTMLElement} body The root element for the contentbank.
115
 * @param {String} searchTerm The search term to match.
116
 * @return {Array}
117
 */
118
const filterContents = (body, searchTerm) => {
119
    const contents = Array.from(body.find(selectors.elements.listitem));
120
    const searchResults = [];
121
    contents.forEach((content) => {
122
        const contentName = content.getAttribute('data-name');
123
        if (searchTerm === '' || contentName.toLowerCase().includes(searchTerm.toLowerCase())) {
124
            // The content matches the search criteria so it should be displayed and hightlighted.
125
            searchResults.push(content);
126
            const contentNameElement = content.querySelector(selectors.regions.cbcontentname);
127
            contentNameElement.innerHTML = highlight(contentName, searchTerm);
128
            content.classList.remove('d-none');
129
        } else {
130
            content.classList.add('d-none');
131
        }
132
    });
133
 
134
    return searchResults;
135
};
136
 
137
/**
138
 * Highlight a given string in a text.
139
 *
140
 * @method highlight
141
 * @param  {String} text The whole text.
142
 * @param  {String} highlightText The piece of text to highlight.
143
 * @return {String}
144
 */
145
const highlight = (text, highlightText) => {
146
    let result = text;
147
    if (highlightText !== '') {
148
        const pos = text.toLowerCase().indexOf(highlightText.toLowerCase());
149
        if (pos > -1) {
150
            result = text.substr(0, pos) + '<span class="matchtext">' + text.substr(pos, highlightText.length) + '</span>' +
151
                text.substr(pos + highlightText.length);
152
        }
153
    }
154
 
155
    return result;
156
};