1/** 2 * @DealerAlchemist Modal â DAPopUp 3 * 4 * Config-driven modal for Nexus studio srpConfig actions. DVPopUp (dv-modal-disclaimer.js) 5 * stays exactly as-is for its ~20 existing callers; this is a separate entry point so the 6 * two never fight. 7 * 8 * Why not extend DVPopUp: its contract is disclaimer-first and positional 9 * (obj, id, isDisclaimer, modalType, dataObj, customClass), it branches on modalType for 10 * 360/video/shareVDP/shareSpecial, and â the blocker â it discards data-modal-content the 11 * moment a form id is present, swapping in a spinner and then replacing the whole text 12 * container with the form. A studio modal needs title + description + message + 13 * customContent + a form to coexist. 14 * 15 * Deliberately reuses the .dv-global-modal-* class names so all existing modal CSS applies 16 * and no new stylesheet is needed. 17 * 18 * @param {Object} options 19 * @param {string} [options.title] Header label 20 * @param {string} [options.description] Muted line under the title 21 * @param {string} [options.message] Body copy 22 * @param {string} [options.customContent] Raw HTML, rendered after the message 23 * @param {string} [options.size] sm | md | lg | xl 24 * @param {string|number} [options.formId] Numeric Gravity Form id, loaded over AJAX 25 * @param {Object} [options.vehicle] { title, vin, stockNumber } for showVehicleInfo 26 * @param {boolean} [options.showVehicleInfo] 27 * @param {string} [options.customClass] 28 * @param {Function} [options.onOpen] Called with the modal element once rendered 29 * @returns {HTMLElement|null} the modal element 30 */ 31(function (window, document) { 32 "use strict"; 33 34 var MAX_WIDTHS = { sm: "420px", md: "600px", lg: "800px", xl: "1040px" }; 35 36 function escapeHtml(value) { 37 return String(value == null ? "" : value) 38 .replace(/&/g, "&") 39 .replace(/</g, "<") 40 .replace(/>/g, ">") 41 .replace(/"/g, """); 42 } 43 44 function closeModal(modal) { 45 if (!modal || !modal.parentNode) return; 46 47 modal.parentNode.removeChild(modal); 48 49 // Only release the scroll lock once the last modal is gone 50 if (!document.querySelector(".dv-global-modal")) { 51 document.body.classList.remove("dv-global-modal-open"); 52 } 53 } 54 55 window.DAPopUp = function (options) { 56 var opts = options || {}; 57 var vehicle = opts.vehicle || {}; 58 59 var modal = document.createElement("div"); 60 modal.className = "dv-global-modal da-modal" + (opts.customClass ? " " + opts.customClass : ""); 61 62 var header = ""; 63 if (opts.title) { 64 header = '<div class="dv-global-modal-header">' 65 + '<p class="dv-global-modal-header-label">' + escapeHtml(opts.title) + "</p>" 66 + "</div>"; 67 } 68 69 var intro = ""; 70 71 if (opts.showVehicleInfo && (vehicle.title || vehicle.vin || vehicle.stockNumber)) { 72 var details = []; 73 if (vehicle.stockNumber) details.push("Stock # " + escapeHtml(vehicle.stockNumber)); 74 if (vehicle.vin) details.push("VIN " + escapeHtml(vehicle.vin)); 75 76 // Each detail is nowrap so a narrow modal wraps between Stock and VIN, never inside one 77 intro += '<div class="da-modal-vehicle">' 78 + (vehicle.title ? '<p class="da-modal-vehicle-title">' + escapeHtml(vehicle.title) + "</p>" : "") 79 + (details.length 80 ? '<p class="da-modal-vehicle-details"><span class="da-modal-vehicle-detail">' 81 + details.join('</span> · <span class="da-modal-vehicle-detail">') 82 + "</span></p>" 83 : "") 84 + "</div>"; 85 } 86 87 if (opts.description) intro += '<p class="da-modal-description">' + escapeHtml(opts.description) + "</p>"; 88 if (opts.message) intro += '<p class="da-modal-message">' + escapeHtml(opts.message) + "</p>"; 89 // Raw HTML by design â dealer-authored through the studio, same as custom containers 90 if (opts.customContent) intro += '<div class="da-modal-custom">' + opts.customContent + "</div>"; 91 92 var formBlock = opts.formId 93 ? '<div class="dv-global-modal-text da-modal-form">' 94 + '<div class="dv-form-loading" style="text-align:center;padding:20px;">' 95 + '<i class="fas fa-spinner fa-spin"></i> Loading Form...</div></div>' 96 : ""; 97 98 modal.innerHTML = 99 '<div class="dv-global-modal-mask"></div>' 100 + '<div class="dv-global-modal-dialog">' 101 + '<span id="dv-close-modal" class="da-modal-close"><i class="fal fa-times"></i></span>' 102 + '<div class="dv-global-modal-inner">' 103 + header 104 + '<div class="dv-global-modal-body">' 105 + (intro ? '<div class="da-modal-intro">' + intro + "</div>" : "") 106 + formBlock 107 + "</div></div></div>"; 108 109 if (opts.size && MAX_WIDTHS[opts.size]) { 110 modal.querySelector(".dv-global-modal-dialog").style.maxWidth = MAX_WIDTHS[opts.size]; 111 } 112 113 document.body.appendChild(modal); 114 document.body.classList.add("dv-global-modal-open"); 115 116 modal.querySelector(".da-modal-close").addEventListener("click", function () { closeModal(modal); }); 117 modal.querySelector(".dv-global-modal-mask").addEventListener("click", function () { closeModal(modal); }); 118 119 function onKeydown(event) { 120 if (event.key !== "Escape") return; 121 closeModal(modal); 122 document.removeEventListener("keydown", onKeydown); 123 } 124 document.addEventListener("keydown", onKeydown); 125 126 if (opts.formId) { 127 // Shared loader (da-gform-loader.js): get_gform fetch, honeypot re-init, 128 // gform_post_render â the hooks anti-spam and conditional logic depend on. 129 window.daGformLoad(modal.querySelector(".da-modal-form"), opts.formId, { vin: vehicle.vin }); 130 } 131 132 if (typeof opts.onOpen === "function") { 133 opts.onOpen(modal); 134 } 135 136 return modal; 137 }; 138 139 window.DAPopUpClose = closeModal; 140})(window, document);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.