ckeditor_taxonomy_glossary-1.0.0-alpha1/js/ckeditor5_plugins/glossaryLink/src/autocomplete.js
js/ckeditor5_plugins/glossaryLink/src/autocomplete.js
(($, Drupal, once) => {
/**
* Glossary autocomplete utilities.
*/
Drupal.glossaryAutocomplete = Drupal.glossaryAutocomplete || {};
/**
* Shows a modal for creating a new term.
*/
Drupal.glossaryAutocomplete.showCreateTermModal = (termName, $input) => {
// Check if site is multilingual and get available languages
const isMultilingual = typeof drupalSettings !== "undefined" &&
drupalSettings.path &&
drupalSettings.path.currentLanguage &&
drupalSettings.language &&
Object.keys(drupalSettings.language).length > 1;
const currentLanguage = (typeof drupalSettings !== "undefined" && drupalSettings.path && drupalSettings.path.currentLanguage)
? drupalSettings.path.currentLanguage
: 'en';
// Build language selector HTML if multilingual
let languageSelectorHtml = '';
if (isMultilingual && drupalSettings.language) {
const languageOptions = Object.keys(drupalSettings.language)
.map(langcode => {
const language = drupalSettings.language[langcode];
const selected = langcode === currentLanguage ? 'selected' : '';
return `<option value="${langcode}" ${selected}>${language.name} (${langcode.toUpperCase()})</option>`;
})
.join('');
languageSelectorHtml = `
<div class="form-item">
<label for="new-term-language">${Drupal.t("Language")}</label>
<select id="new-term-language">
${languageOptions}
</select>
</div>
`;
}
// Create modal HTML
const modalHtml = `
<div id="glossary-create-modal" class="glossary-modal-overlay">
<div class="glossary-modal">
<div class="glossary-modal-header">
<h3>${Drupal.t("Create New Glossary Term")}</h3>
<button type="button" class="glossary-modal-close">×</button>
</div>
<div class="glossary-modal-body">
<div class="form-item">
<label for="new-term-name">${Drupal.t("Term Name")} *</label>
<input type="text" id="new-term-name" value="" required maxlength="255" />
</div>
<div class="form-item">
<label for="new-term-description">${Drupal.t("Description")}</label>
<textarea id="new-term-description" rows="3" maxlength="500" placeholder="${Drupal.t("Optional description for this term")}"></textarea>
</div>
${languageSelectorHtml}
<div class="glossary-modal-messages"></div>
</div>
<div class="glossary-modal-footer">
<button type="button" class="btn btn-primary" id="create-term-submit">${Drupal.t("Create Term")}</button>
<button type="button" class="btn btn-secondary glossary-modal-cancel">${Drupal.t("Cancel")}</button>
</div>
</div>
</div>
`;
// Add modal to page
$("body").append(modalHtml);
const $modal = $("#glossary-create-modal");
// Store current scroll position and prevent body scrolling
const scrollTop = $(window).scrollTop();
$("body").addClass("glossary-modal-open").css("top", `${-scrollTop}px`);
$("body").data("scroll-top", scrollTop);
// Safely set the term name value after DOM creation
$("#new-term-name").val(termName).focus().select();
// Prevent modal from closing when clicking inside the modal content
$modal.find(".glossary-modal").on("click", (e) => {
e.stopPropagation();
});
// Function to close modal and restore body state
const closeModal = () => {
const scrollTop = $("body").data("scroll-top") || 0;
$("body")
.removeClass("glossary-modal-open")
.css("top", "")
.removeData("scroll-top");
$(window).scrollTop(scrollTop);
$modal.remove();
};
// Handle close buttons
$modal
.find(".glossary-modal-close, .glossary-modal-cancel")
.on("click", closeModal);
$modal.on("click", function (e) {
// Only close if clicking directly on the overlay (not on modal content)
if (e.target === this) {
closeModal();
}
});
// Handle form submission
$("#create-term-submit").on("click", function () {
const name = $("#new-term-name").val().trim();
const description = $("#new-term-description").val().trim();
const selectedLanguage = $("#new-term-language").length ? $("#new-term-language").val() : currentLanguage;
if (!name) {
Drupal.glossaryAutocomplete.showModalMessage(
Drupal.t("Term name is required"),
"error",
);
return;
}
// Disable button and show loading
$(this).prop("disabled", true).text(Drupal.t("Creating..."));
// Create the term
$.ajax({
url: "/glossary/create-term",
method: "POST",
contentType: "application/json",
headers: { 'X-CSRF-Token': drupalSettings.csrfToken },
data: JSON.stringify({
name: name,
description: description,
langcode: selectedLanguage,
}),
success: (response) => {
if (response.success && response.term) {
// Set the new term in the input
$input.data("glossary-id", response.term.id);
$input.val(`${response.term.name} (${response.term.id})`);
// Close modal
closeModal();
// Get the CKEditor instance and execute the glossaryLink command
const editor = $input.data("ckeditor-instance");
const uiInstance = $input.data("ui-instance");
if (editor && uiInstance) {
// Execute the glossaryLink command to create the link
editor.execute("glossaryLink", response.term.id);
// Hide the form
uiInstance._hideForm();
}
// Show success message
Drupal.announce(
Drupal.t(
'Glossary term "@name" created and linked successfully.',
{ "@name": response.term.name },
),
);
} else {
Drupal.glossaryAutocomplete.showModalMessage(
Drupal.t("Failed to create term"),
"error",
);
}
},
error: (xhr) => {
let errorMsg = Drupal.t("Failed to create term");
if (xhr.responseJSON?.error) {
errorMsg = xhr.responseJSON.error;
// Handle existing term case
if (xhr.status === 409 && xhr.responseJSON.existing_term) {
const existingTerm = xhr.responseJSON.existing_term;
errorMsg += Drupal.t(". Use existing term?");
const useExistingHtml = `
<div class="existing-term-option">
<button type="button" class="btn btn-link" id="use-existing-term"
data-term-id="${existingTerm.id}" data-term-name="${existingTerm.name}">
${Drupal.t('Use "@name" (ID: @id)', { "@name": existingTerm.name, "@id": existingTerm.id })}
</button>
</div>
`;
$(".glossary-modal-messages").append(useExistingHtml);
$("#use-existing-term").on("click", function () {
const termId = $(this).data("term-id");
const termName = $(this).data("term-name");
$input.data("glossary-id", termId);
$input.val(`${termName} (${termId})`);
closeModal();
// Get the CKEditor instance and execute the glossaryLink command
const editor = $input.data("ckeditor-instance");
const uiInstance = $input.data("ui-instance");
if (editor && uiInstance) {
// Execute the glossaryLink command to create the link
editor.execute("glossaryLink", termId);
// Hide the form
uiInstance._hideForm();
}
});
}
}
Drupal.glossaryAutocomplete.showModalMessage(errorMsg, "error");
$(this).prop("disabled", false).text(Drupal.t("Create Term"));
},
});
});
};
/**
* Shows a message in the modal.
*/
Drupal.glossaryAutocomplete.showModalMessage = (message, type = "info") => {
const $messages = $(".glossary-modal-messages");
$messages.empty();
const alertClass = type === "error" ? "alert-danger" : "alert-success";
const messageHtml = `<div class="alert ${alertClass}">${message}</div>`;
$messages.html(messageHtml);
};
})(jQuery, Drupal, once);
