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
 * Interface to the Lunr search engines.
18
 *
19
 * @module     tool_componentlibrary/search
20
 * @copyright  2021 Bas Brands <bas@moodle.com>
21
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
 
24
import lunrJs from 'tool_componentlibrary/lunr';
25
import selectors from 'tool_componentlibrary/selectors';
26
import Log from 'core/log';
27
import Notification from 'core/notification';
28
import {enter, escape} from 'core/key_codes';
29
 
30
let lunrIndex = null;
31
let pagesIndex = null;
32
 
33
/**
34
 * Get the jsonFile that is generated when the component library is build.
35
 *
36
 * @method
37
 * @private
38
 * @param {String} jsonFile the URL to the json file.
39
 * @return {Object}
40
 */
41
const fetchJson = async(jsonFile) => {
42
    const response = await fetch(jsonFile);
43
 
44
    if (!response.ok) {
45
        Log.debug(`Error getting Hugo index file: ${response.status}`);
46
    }
47
 
48
    return await response.json();
49
};
50
 
51
/**
52
 * Initiate lunr on the data in the jsonFile and add the jsondata to the pagesIndex
53
 *
54
 * @method
55
 * @private
56
 * @param {String} jsonFile the URL to the json file.
57
 */
58
const initLunr = jsonFile => {
59
    fetchJson(jsonFile).then(jsondata => {
60
        pagesIndex = jsondata;
61
        // Using an arrow function here will break lunr on compile.
62
        lunrIndex = lunrJs(function() {
63
            this.ref('uri');
64
            this.field('title', {boost: 10});
65
            this.field('content');
66
            this.field('tags', {boost: 5});
67
            jsondata.forEach(p => {
68
                this.add(p);
69
            });
70
        });
71
        return null;
72
    }).catch(Notification.exception);
73
};
74
 
75
/**
76
 * Setup the eventlistener to listen on user input on the search field.
77
 *
78
 * @method
79
 * @private
80
 */
81
const initUI = () => {
82
    const searchInput = document.querySelector(selectors.searchinput);
83
    searchInput.addEventListener('keyup', e => {
84
        const query = e.currentTarget.value;
85
        if (query.length < 2) {
86
            document.querySelector(selectors.dropdownmenu).classList.remove('show');
87
            return;
88
        }
89
        renderResults(searchIndex(query));
90
    });
91
    searchInput.addEventListener('keydown', e => {
92
        if (e.keyCode === enter) {
93
            e.preventDefault();
94
        }
95
        if (e.keyCode === escape) {
96
            searchInput.value = '';
97
        }
98
    });
99
};
100
 
101
/**
102
 * Trigger a search in lunr and transform the result.
103
 *
104
 * @method
105
 * @private
106
 * @param  {String} query
107
 * @return {Array} results
108
 */
109
const searchIndex = query => {
110
    // Find the item in our index corresponding to the lunr one to have more info
111
    // Lunr result:
112
    //  {ref: "/section/page1", score: 0.2725657778206127}
113
    // Our result:
114
    //  {title:"Page1", href:"/section/page1", ...}
115
 
116
    return lunrIndex.search(query + ' ' + query + '*').map(result => {
117
        return pagesIndex.filter(page => {
118
            return page.uri === result.ref;
119
        })[0];
120
    });
121
};
122
 
123
/**
124
 * Display the 10 first results
125
 *
126
 * @method
127
 * @private
128
 * @param {Array} results to display
129
 */
130
const renderResults = results => {
131
    const dropdownMenu = document.querySelector(selectors.dropdownmenu);
132
    if (!results.length) {
133
        dropdownMenu.classList.remove('show');
134
        return;
135
    }
136
 
137
    // Clear out the results.
138
    dropdownMenu.innerHTML = '';
139
 
140
    const baseUrl = M.cfg.wwwroot + '/admin/tool/componentlibrary/docspage.php';
141
 
142
    // Only show the ten first results
143
    results.slice(0, 10).forEach(function(result) {
144
        const link = document.createElement("a");
145
        const chapter = result.uri.split('/')[1];
146
        link.appendChild(document.createTextNode(`${chapter} > ${result.title}`));
147
        link.classList.add('dropdown-item');
148
        link.href = baseUrl + result.uri;
149
 
150
        dropdownMenu.appendChild(link);
151
    });
152
 
153
    dropdownMenu.classList.add('show');
154
};
155
 
156
/**
157
 * Initialize module.
158
 *
159
 * @method
160
 * @param {String} jsonFile Full path to the search DB json file.
161
 */
162
export const search = jsonFile => {
163
    initLunr(jsonFile);
164
    initUI();
165
};