'use strict'; // TODO A: Improve readability and documentation. // TODO B: Implement an autocomplete functionality for the text input. // TODO C: Preview file content when clicking on an autocomplete suggestion. // TODO D: Display autocomplete suggestions only when the input is in focus. // TODO E: Add error handling for the autocomplete suggestions in case of a network failure. (() => { const submitButton = document.querySelector('.submit-button'); const form = document.querySelector('form'); const articleTitle = document.querySelector('input'); const articleBody = document.querySelector('textarea'); const autoCompleteList = document.querySelector('.articles-list'); const errorMessage = document.querySelector('.error-message'); const delay = 200; /** * Create child element for the autocomplete list. * * @function createChildOption * @param {string} articleName * @returns {HTMLElement} The created child option element wrapped */ const createChildOption = (articleName) => { const childOption = document.createElement('div'); childOption.textContent = articleName; childOption.addEventListener('click', () => { articleTitle.value = articleName; fetchArticleContent(articleName); clearAutoCompleteList(); }); return childOption; }; /** * Clear autocomplete list removing all child elements and hides the list * from view. * * @function clearAutoCompleteList */ const clearAutoCompleteList = () => { autoCompleteList.innerHTML = ''; autoCompleteList.style.display = 'none'; } /** * Handles errors that occur during data fetching. * * @function handleFetchError * @param {Error} error - The error object thrown during the fetch operation */ const handleFetchError = (error) => { console.error('Error fetching data:', error); errorMessage.textContent = 'Unable load content. Please try again.'; errorMessage.style.display = 'block'; }; /** * Fetch an existing title of an article based on a search query * and fill the autocomplete list. * * @function fetchArticleTitle * @param {string} query */ const fetchArticleTitle = (query) => { fetch(`/api.php?search=${encodeURIComponent(query)}`) .then(response => { if (!response.ok) { throw new Error(`HTTP error: ${response.status}`); } return response.json(); }) .then(data => { autoCompleteList.innerHTML = ''; if (data.content && data.content.length > 0) { data.content.forEach(articleName => { const childOption = createChildOption(articleName); autoCompleteList.appendChild(childOption); }); autoCompleteList.style.display = 'block'; } else { autoCompleteList.style.display = 'none'; } }) .catch(error => { clearAutoCompleteList(); handleFetchError(error); }); } /** * Fetch the content of an article based on its title and fill textarea. * * @function fetchArticleContent * @param {string} title */ const fetchArticleContent = (title) => { fetch(`/api.php?title=${encodeURIComponent(title)}`) .then(response => { if (!response.ok) { throw new Error(`HTTP error: ${response.status}`); } return response.json(); }) .then(data => { articleBody.value = data.content; errorMessage.style.display = 'none'; }) .catch(error => { handleFetchError(error); }); }; /** * Initializes the autocomplete functionality for the article title input. * * This function set up event listeners to provide autocomplete suggestions * as the user types in the input field. Handle hiding the autocomplete list * when the input loses focus. * * The autocomplete data are fetched after a delay to avoid too many * requests in short time. * * @function autoComplete */ const autoComplete = () => { let autoCompleteTimeout = null; articleTitle.addEventListener('input', function () { const query = this.value.trim(); clearTimeout(autoCompleteTimeout); clearAutoCompleteList(); if (query.length === 0) return; autoCompleteTimeout = setTimeout(() => { fetchArticleTitle(query); }, delay); }); /** * Hide the autocomplete list with a delay when the input loses focus. */ articleTitle.addEventListener('blur', () => { setTimeout(() => { clearAutoCompleteList(); }, delay); }); } /** * Validate the form fields before submitting the form. * * @function submitArticle */ const submitArticle = () => { submitButton.addEventListener('click', (e) => { if (articleTitle.value.trim() === '' || articleBody.value.trim() === '') { e.preventDefault(); errorMessage.innerHTML = 'Fields shouldn\'t be empty.'; errorMessage.style.display = 'block'; } else { form.submit(); } }); }; /** * Calls submitArticle function on DOM loaded to set up the form submission. * * @event DOMContentLoaded */ document.addEventListener('DOMContentLoaded', function() { autoComplete(); submitArticle(); }); })();