/*global SelectBox, interpolate*/ // Handles related-objects functionality: lookup link for raw_id_fields // and Add Another links. "use strict"; { const $ = django.jQuery; let popupIndex = 0; const relatedWindows = []; function dismissChildPopups() { relatedWindows.forEach(function (win) { if (!win.closed) { win.dismissChildPopups(); win.close(); } }); } function setPopupIndex() { if (document.getElementsByName("_popup").length > 0) { const index = window.name.lastIndexOf("__") + 2; popupIndex = parseInt(window.name.substring(index)); } else { popupIndex = 0; } } function addPopupIndex(name) { return name + "__" + (popupIndex + 1); } function removePopupIndex(name) { return name.replace(new RegExp("__" + (popupIndex + 1) + "$"), ""); } function showAdminPopup(triggeringLink, name_regexp, add_popup) { const name = addPopupIndex(triggeringLink.id.replace(name_regexp, "")); const href = new URL(triggeringLink.href); if (add_popup) { href.searchParams.set("_popup", 1); } const win = window.open( href, name, "height=768,width=1024,resizable=yes,scrollbars=yes" ); relatedWindows.push(win); win.focus(); return false; } function showRelatedObjectLookupPopup(triggeringLink) { return showAdminPopup(triggeringLink, /^lookup_/, true); } function dismissRelatedLookupPopup(win, chosenId) { const name = removePopupIndex(win.name); const elem = document.getElementById(name); if (elem.classList.contains("vManyToManyRawIdAdminField") && elem.value) { elem.value += "," + chosenId; } else { document.getElementById(name).value = chosenId; } const index = relatedWindows.indexOf(win); if (index > -1) { relatedWindows.splice(index, 1); } win.close(); } function showRelatedObjectPopup(triggeringLink) { return showAdminPopup(triggeringLink, /^(change|add|delete)_/, false); } function updateRelatedObjectLinks(triggeringLink) { const $this = $(triggeringLink); const siblings = $this.nextAll( ".view-related, .change-related, .delete-related" ); if (!siblings.length) { return; } const value = $this.val(); if (value) { siblings.each(function () { const elm = $(this); elm.attr( "href", elm.attr("data-href-template").replace("__fk__", value) ); elm.removeAttr("aria-disabled"); }); } else { siblings.removeAttr("href"); siblings.attr("aria-disabled", true); } } function updateRelatedSelectsOptions( currentSelect, win, objId, newRepr, newId, skipIds = [] ) { // After create/edit a model from the options next to the current // select (+ or :pencil:) update ForeignKey PK of the rest of selects // in the page. const path = win.location.pathname; // Extract the model from the popup url '...//add/' or // '...///change/' depending the action (add or change). const modelName = path.split("/")[path.split("/").length - (objId ? 4 : 3)]; // Select elements with a specific model reference and context of "available-source". const selectsRelated = document.querySelectorAll( `[data-model-ref="${modelName}"] [data-context="available-source"]` ); selectsRelated.forEach(function (select) { if ( currentSelect === select || (skipIds && skipIds.includes(select.id)) ) { return; } let option = select.querySelector(`option[value="${objId}"]`); if (!option) { option = new Option(newRepr, newId); select.options.add(option); // Update SelectBox cache for related fields. if ( window.SelectBox !== undefined && !SelectBox.cache[currentSelect.id] ) { SelectBox.add_to_cache(select.id, option); SelectBox.redisplay(select.id); } return; } option.textContent = newRepr; option.value = newId; }); } function dismissAddRelatedObjectPopup(win, newId, newRepr) { const name = removePopupIndex(win.name); const elem = document.getElementById(name); if (elem) { const elemName = elem.nodeName.toUpperCase(); if (elemName === "SELECT") { elem.options[elem.options.length] = new Option( newRepr, newId, true, true ); updateRelatedSelectsOptions(elem, win, null, newRepr, newId); } else if (elemName === "INPUT") { if ( elem.classList.contains("vManyToManyRawIdAdminField") && elem.value ) { elem.value += "," + newId; } else { elem.value = newId; } } // Trigger a change event to update related links if required. $(elem).trigger("change"); } else { const toId = name + "_to"; const toElem = document.getElementById(toId); const o = new Option(newRepr, newId); SelectBox.add_to_cache(toId, o); SelectBox.redisplay(toId); if (toElem && toElem.nodeName.toUpperCase() === "SELECT") { const skipIds = [name + "_from"]; updateRelatedSelectsOptions(toElem, win, null, newRepr, newId, skipIds); } } const index = relatedWindows.indexOf(win); if (index > -1) { relatedWindows.splice(index, 1); } win.close(); } function dismissChangeRelatedObjectPopup(win, objId, newRepr, newId) { const id = removePopupIndex(win.name.replace(/^edit_/, "")); const selectsSelector = interpolate("#%s, #%s_from, #%s_to", [id, id, id]); const selects = $(selectsSelector); selects .find("option") .each(function () { if (this.value === objId) { this.textContent = newRepr; this.value = newId; } }) .trigger("change"); updateRelatedSelectsOptions(selects[0], win, objId, newRepr, newId); selects .next() .find(".select2-selection__rendered") .each(function () { // The element can have a clear button as a child. // Use the lastChild to modify only the displayed value. this.lastChild.textContent = newRepr; this.title = newRepr; }); const index = relatedWindows.indexOf(win); if (index > -1) { relatedWindows.splice(index, 1); } win.close(); } function dismissDeleteRelatedObjectPopup(win, objId) { const id = removePopupIndex(win.name.replace(/^delete_/, "")); const selectsSelector = interpolate("#%s, #%s_from, #%s_to", [id, id, id]); const selects = $(selectsSelector); selects .find("option") .each(function () { if (this.value === objId) { $(this).remove(); } }) .trigger("change"); const index = relatedWindows.indexOf(win); if (index > -1) { relatedWindows.splice(index, 1); } win.close(); } window.showRelatedObjectLookupPopup = showRelatedObjectLookupPopup; window.dismissRelatedLookupPopup = dismissRelatedLookupPopup; window.showRelatedObjectPopup = showRelatedObjectPopup; window.updateRelatedObjectLinks = updateRelatedObjectLinks; window.dismissAddRelatedObjectPopup = dismissAddRelatedObjectPopup; window.dismissChangeRelatedObjectPopup = dismissChangeRelatedObjectPopup; window.dismissDeleteRelatedObjectPopup = dismissDeleteRelatedObjectPopup; window.dismissChildPopups = dismissChildPopups; // Kept for backward compatibility window.showAddAnotherPopup = showRelatedObjectPopup; window.dismissAddAnotherPopup = dismissAddRelatedObjectPopup; window.addEventListener("unload", function (evt) { window.dismissChildPopups(); }); $(document).ready(function () { setPopupIndex(); $("a[data-popup-opener]").on("click", function (event) { event.preventDefault(); opener.dismissRelatedLookupPopup(window, $(this).data("popup-opener")); }); $("body").on( "click", '.related-widget-wrapper-link[data-popup="yes"]', function (e) { e.preventDefault(); if (this.href) { const event = $.Event("django:show-related", { href: this.href }); $(this).trigger(event); if (!event.isDefaultPrevented()) { showRelatedObjectPopup(this); } } } ); $("body").on("change", ".related-widget-wrapper select", function (e) { const event = $.Event("django:update-related"); $(this).trigger(event); if (!event.isDefaultPrevented()) { updateRelatedObjectLinks(this); } }); $(".related-widget-wrapper select").trigger("change"); $("body").on("click", ".related-lookup", function (e) { e.preventDefault(); const event = $.Event("django:lookup-related"); $(this).trigger(event); if (!event.isDefaultPrevented()) { showRelatedObjectLookupPopup(this); } }); }); }