Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

const moreValue = 'more';
const triggerTimeout = 500;
const listCssClass = 'c-form__autocomplete';

/**
*
Expand Down Expand Up @@ -79,11 +80,14 @@ function buildListElement(label, value, id) {

/**
* @param {HTMLElement} inputField
* @param {Object} config
* @returns {void}
*/
function removeList(inputField) {
if (inputField.nextElementSibling?.nodeName === 'UL') {
inputField.nextElementSibling.remove();
function removeList(inputField, config) {
if (config.appendTo) {
document.querySelector(config.appendTo)?.querySelector(`.${listCssClass}`)?.remove();
} else {
inputField.parentNode.querySelector(`.${listCssClass}`)?.remove();
}
}

Expand Down Expand Up @@ -113,55 +117,76 @@ async function fetchListItemsAndBuildSelector(fullUrl, inputField, config) {
const items = buildItems(responseJson);

if (items.length === 0) {
removeList(inputField);
removeList(inputField, config);
return;
}

const list = document.createElement('ul');
list.style.left = `${inputField.offsetLeft}px`;
if (typeof config.appendTo !== 'string') {
list.style.left = `${inputField.offsetLeft}px`;
}
list.style.minWidth = `${inputField.offsetWidth}px`;
list.classList.add('c-form__autocomplete');
list.classList.add(listCssClass);
items.forEach((elem) => {
if (inputField.value !== elem.value && inputField.value.includes(elem.value)) {
return;
}
list.appendChild(buildListElement(elem.label, elem.value, elem.id));
list.appendChild(buildListElement(elem.label || elem.value, elem.value, elem.id));
});
if (responseJson.hasMoreResults) {
list.appendChild(buildListElement(config.moreText, moreValue));
}
if (list.children.length === 0) {
return;
}
list.addEventListener('keydown', (e) => { keyHandler(e, config); });
list.addEventListener('click', (e) => { onSelectHandler(e, config); });
list.addEventListener('keydown', (e) => { keyHandler(inputField, e, config); });
list.addEventListener('click', (e) => { onSelectHandler(inputField, e, config); });
const activeElementValue = document.activeElement.dataset.value;
removeList(inputField);
inputField.parentNode.insertBefore(list, inputField.nextElementSibling);
if (typeof activeElementValue !== 'undefined') {
inputField.parentNode.querySelector(`[data-value="${activeElementValue}"]`).focus();
removeList(inputField, config);
if (config.appendTo) {
document.querySelector(config.appendTo).appendChild(list);
if (typeof activeElementValue !== 'undefined') {
document.querySelector(config.appendTo)
.querySelector(`[data-value="${activeElementValue}"]`)
.focus();
}
} else {
inputField.parentNode.insertBefore(list, inputField.nextElementSibling);
if (typeof activeElementValue !== 'undefined') {
inputField.parentNode.querySelector(`[data-value="${activeElementValue}"]`).focus();
}
}
if (config.open) {
config.open(list);
}
} catch (e) {
// nothing to do
}
}

/**
* @param {HTMLInputElement} inputField
* @param {Event} e
* @param {Object} config
* @returns {void}
*/
function keyHandler(e, config) {
function keyHandler(inputField, e, config) {
if (e.key === 'Enter' && e.target.nodeName === 'LI') {
e.preventDefault();
onSelectHandler(e, config);
onSelectHandler(inputField, e, config);
}

if (e.key === 'ArrowDown') {
e.stopImmediatePropagation();
e.preventDefault();
if (e.target.nextElementSibling?.nodeName === 'UL') {
e.target.nextElementSibling.firstElementChild.focus();
if (e.target === inputField) {
if (config.appendTo) {
document.querySelector(config.appendTo)
.querySelector(`.${listCssClass}`)
.firstElementChild
.focus();
} else {
e.target.parentNode.querySelector(`.${listCssClass}`)?.firstElementChild?.focus();
}
}

if (e.target.nodeName === 'LI' && e.target.nextElementSibling !== null) {
Expand All @@ -172,8 +197,9 @@ function keyHandler(e, config) {
if (e.key === 'ArrowUp' && e.target.nodeName === 'LI') {
e.stopImmediatePropagation();
e.preventDefault();

if (e.target.previousElementSibling === null) {
e.target.parentElement.previousElementSibling.focus();
inputField.focus();
} else {
e.target.previousElementSibling.focus();
}
Expand All @@ -193,7 +219,7 @@ function onChangeHandler(e, config) {

if (e.target.value.length < config.autocompleteLength) {
clearTimeout();
removeList(e.target);
removeList(e.target, config);
return;
}

Expand Down Expand Up @@ -227,37 +253,39 @@ function getTermFromSelectedValue(value, delimiter) {
}

/**
* @param {HTMLInputElement} inputField
* @param {Event} e
* @param {Object} config
* @returns {void}
*/
function onSelectHandler(e, config) {
function onSelectHandler(inputField, e, config) {
controller.abort();
controller = new AbortController();
let { value } = e.target.dataset;
if (value === moreValue) {
const term = getTermFromSelectedValue(
e.target.parentNode.previousElementSibling.value,
inputField.value,
config.delimiter,
);
fetchListItemsAndBuildSelector(
`${config.dataSource}&term=${encodeURIComponent(term)}&fetchall=1`,
e.target.parentNode.previousElementSibling,
inputField,
config,
);
return;
}

if (config.delimiter !== null) {
const currentValueArray = e.target.parentNode.previousElementSibling.value
const currentValueArray = inputField.value
.split(config.delimiter);
let currentValue = '';
if (currentValueArray.length > 1) {
currentValue = `${currentValueArray.slice(0, -1).join(`${config.delimiter} `)}${config.delimiter} `;
}
value = `${currentValue}${value}${config.delimiter} `;
}
e.target.parentNode.previousElementSibling.value = value;
e.target.parentNode.previousElementSibling.focus();
inputField.value = value;
inputField.focus();
e.target.parentNode.remove();

if (config.submitOnSelection && 'id' in e.target.dataset) {
Expand All @@ -268,6 +296,6 @@ function onSelectHandler(e, config) {
export default function autocompleteHandler(autocompleteInput, config) {
controller = new AbortController();
setAccessibilityAttributesToContainer(autocompleteInput.parentElement);
autocompleteInput.addEventListener('keydown', (e) => { keyHandler(e, config); });
autocompleteInput.addEventListener('keydown', (e) => { keyHandler(autocompleteInput, e, config); });
autocompleteInput.addEventListener('keyup', (e) => { onChangeHandler(e, config); });
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class ilMainMenuSearchGUI
protected ilTree $tree;
protected ilCtrl $ctrl;
protected ilObjUser $user;
protected ilGlobalTemplateInterface $global_template;

private GlobalHttpState $http;
private Factory $refinery;
Expand All @@ -56,7 +57,7 @@ public function __construct()

$this->http = $DIC->http();
$this->refinery = $DIC->refinery();
$DIC->ui()->mainTemplate()->addJavascript('assets/js/SearchMainMenu.js');
$this->global_template = $DIC->ui()->mainTemplate();

$this->initRefIdFromQuery();
}
Expand All @@ -76,6 +77,9 @@ public function getHTML(): string
{
iljQueryUtil::initjQuery();

$this->global_template->addJavaScript('assets/js/legacyAutocomplete.js', true, 3);
$this->global_template->addJavascript('assets/js/SearchMainMenu.js');

$this->tpl = new ilTemplate('tpl.main_menu_search.html', true, true, 'components/ILIAS/Search');
if ($this->user->getId() != ANONYMOUS_USER_ID) {
$this->tpl->setVariable('LABEL_SEARCH_OPTIONS', $this->lng->txt("label_search_options"));
Expand Down
67 changes: 38 additions & 29 deletions components/ILIAS/Search/resources/SearchMainMenu.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
/**
* This file is part of ILIAS, a powerful learning management system
* published by ILIAS open source e-Learning e.V.
*
* ILIAS is licensed with the GPL-3.0,
* see https://www.gnu.org/licenses/gpl-3.0.en.html
* You should have received a copy of said license along with the
* source code, too.
*
* If this is not the case or you just want to try ILIAS, you'll find
* us at:
* https://www.ilias.de
* https://github.com/ILIAS-eLearning
*/

/* eslint-env jquery */
il.SearchMainMenu = {
acDatasource: 'ilias.php?baseClass=ilSearchControllerGUI&cmd=autoComplete',

init() {
// we must bind the blur event before the autocomplete item is added
this.suppressBlur();
this.initAutocomplete();
this.initAutocomplete(`${this.acDatasource}&search_type=4`);
this.initChange();
},

Expand All @@ -16,46 +31,40 @@ il.SearchMainMenu = {
);
},

initAutocomplete() {
$('#main_menu_search').autocomplete({
source: `${this.acDatasource}&search_type=4`,
appendTo: '#mm_search_menu_ac',
open() {
$('.ui-autocomplete').position({
my: 'left top',
at: 'left top',
of: $('#mm_search_menu_ac'),
});
},
minLength: 3,
/**
* @param {string} dataSource
*/
initAutocomplete(dataSource) {
const autocomplete = document.querySelector('#main_menu_search');
const target = document.getElementById('mm_search_menu_ac');

il.LegacyForm.autocomplete.init(autocomplete, {
delimiter: null,
dataSource,
submitOnSelection: false,
autocompleteLength: 3,
submitUrl: null,
moreText: null,
appendTo: `#${target.id}`,
});
},

initChange() {
$("#ilMMSearchMenu input[type='radio']").change(() => {
/* close current search */
$('#main_menu_search').autocomplete('close');
$('#main_menu_search').autocomplete('enable');

/* append search type */
const checkedInput = $('input[name=root_id]:checked', '#mm_search_form');
const typeVal = checkedInput.val();
/* disabled autocomplete */
const originalInput = document.querySelector('#main_menu_search');
const autocomplete = originalInput.cloneNode(true); // clone attributes, value, etc.
originalInput.replaceWith(autocomplete); // replace the original inpu

/* disable autocomplete for search at current position */
const checkedInput = $('input[name=root_id]:checked', '#mm_search_form');
if (checkedInput[0].id === 'ilmmsc') {
$('#main_menu_search').autocomplete('disable');
return;
}

$('#main_menu_search').autocomplete(
'option',
{
source: `${this.acDatasource}&search_type=${typeVal}`,
},
);
const typeVal = checkedInput.val();

/* start new search */
$('#main_menu_search').autocomplete('search');
this.initAutocomplete(`${this.acDatasource}&search_type=${typeVal}`);
});
},
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<form class="" role="search" id="mm_search_form" action="{FORMACTION}" method="post" target="_top">
<div class="input-group">
<input aria-label="{SEARCH_INPUT_LABEL}" id="main_menu_search" class="form-control" type="text" name="queryString"> <span class="input-group-btn">
<input type="submit" class="btn btn-default" value="{BTN_SEARCH}"/>
<input aria-label="{SEARCH_INPUT_LABEL}" id="main_menu_search" class="form-control" type="text" name="queryString" autocomplete="off"> <span class="input-group-btn">
<input type="submit" class="btn btn-default" value="{BTN_SEARCH}" />
</span>
</div>
<!-- BEGIN ov_head -->
Expand Down