PluginProbe
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits / 3.1.9
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits v3.1.9
3.2.2 3.2.3 3.2.1 3.2.0 3.1.9 3.1.8 3.1.7 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.9 trunk 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.3 1.1.4 1.1.5 All 174 releases
master-addons / assets / js / admin / widget-admin.js

widget-admin.js in Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits 3.1.9, at assets/js/admin/widget-admin.js

744 lines 30.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function(){
2 //#region dev/js/common/dialog.js
3 /**
4 * Master Addons — shared confirm/alert/loading dialog.
5 *
6 * Framework-agnostic vanilla-JS modal. Works identically from React apps
7 * (template-library, template-kits-app, admin-settings, setup-wizard) and
8 * from jQuery bundles (popup-builder, widget-builder, theme-builder,
9 * master-addons-scripts, etc.).
10 *
11 * Usage:
12 * // ES module
13 * import { confirmDialog, alertDialog, loadingDialog } from '../../../common/dialog';
14 * const ok = await confirmDialog({ title, message, tone: 'danger' });
15 * await alertDialog({ title, message, tone: 'success' });
16 * const loader = loadingDialog({ message: 'Deleting…' });
17 * // ... async work ...
18 * loader.close();
19 *
20 * // Global (jQuery / vanilla / legacy bundles)
21 * const ok = await window.JLTMA_Dialog.confirm({ title, message });
22 * await window.JLTMA_Dialog.alert({ title, message });
23 * const loader = window.JLTMA_Dialog.loading({ message: 'Saving…' });
24 *
25 * Styles are auto-injected on first use. Colors reference the admin
26 * design-token CSS variables (--jltma-primary, --jltma-destructive,
27 * etc.) with hex fallbacks so the dialog renders correctly in contexts
28 * where the design tokens aren't loaded (legacy admin pages, generic
29 * frontend previews, non-React bundles).
30 *
31 * Tones (button color + focus ring hue):
32 * primary | danger | success | warning
33 */
34 var STYLE_ID = "jltma-dialog-styles";
35 var PORTAL_ID = "jltma-dialog-portal";
36 var CSS = [
37 ".jltma-dialog{position:fixed;inset:0;z-index:9999999;display:flex;align-items:center;justify-content:center;animation:jltma-dialog-fade-in 0.15s ease-out;}",
38 ".jltma-dialog__overlay{position:absolute;inset:0;background:rgba(15,23,42,0.55);backdrop-filter:blur(1.5px);-webkit-backdrop-filter:blur(1.5px);}",
39 ".jltma-dialog__content{position:relative;z-index:1;width:min(460px,calc(100% - 32px));background:var(--jltma-popover,#ffffff);color:var(--jltma-popover-foreground,#0f172a);border:1px solid var(--jltma-border,#e2e8f0);border-radius:var(--jltma-radius,10px);box-shadow:0 20px 48px rgba(15,23,42,0.24),0 8px 16px rgba(15,23,42,0.12);padding:24px;box-sizing:border-box;animation:jltma-dialog-scale-in 0.18s cubic-bezier(0.16,1,0.3,1);font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial,sans-serif;}",
40 ".jltma-dialog__title{margin:0 0 10px;font-size:18px;font-weight:600;line-height:1.35;letter-spacing:-0.01em;color:var(--jltma-foreground,#0f172a);}",
41 ".jltma-dialog__message{margin:0 0 24px;font-size:14px;line-height:1.55;color:var(--jltma-muted-foreground,#475569);white-space:pre-line;}",
42 ".jltma-dialog__actions{display:flex;justify-content:flex-end;gap:10px;flex-wrap:wrap;}",
43 ".jltma-dialog__btn{appearance:none;border:1px solid transparent;border-radius:calc(var(--jltma-radius,10px) - 2px);height:36px;padding:0 16px;font-size:13px;font-weight:600;line-height:1;cursor:pointer;transition:background-color 0.15s ease,border-color 0.15s ease,color 0.15s ease,box-shadow 0.15s ease,opacity 0.15s ease;font-family:inherit;display:inline-flex;align-items:center;justify-content:center;gap:6px;}",
44 ".jltma-dialog__btn:focus-visible{outline:2px solid var(--jltma-ring,rgba(104,20,205,0.45));outline-offset:2px;}",
45 ".jltma-dialog__btn:disabled{opacity:0.6;cursor:not-allowed;}",
46 ".jltma-dialog__btn--secondary{background:var(--jltma-background,#ffffff);border-color:var(--jltma-border,#e2e8f0);color:var(--jltma-foreground,#334155);}",
47 ".jltma-dialog__btn--secondary:hover:not(:disabled){background:var(--jltma-accent,#f1f5f9);border-color:var(--jltma-border,#cbd5e1);}",
48 ".jltma-dialog__btn--primary{background:var(--jltma-primary,#6814cd);color:var(--jltma-primary-foreground,#ffffff);}",
49 ".jltma-dialog__btn--primary:hover:not(:disabled){opacity:0.9;}",
50 ".jltma-dialog__btn--danger{background:var(--jltma-destructive,#dc2626);color:#ffffff;}",
51 ".jltma-dialog__btn--danger:hover:not(:disabled){opacity:0.9;}",
52 ".jltma-dialog__btn--success{background:#16a34a;color:#ffffff;}",
53 ".jltma-dialog__btn--success:hover:not(:disabled){opacity:0.9;}",
54 ".jltma-dialog__btn--warning{background:#d97706;color:#ffffff;}",
55 ".jltma-dialog__btn--warning:hover:not(:disabled){opacity:0.9;}",
56 ".jltma-dialog--loading .jltma-dialog__content{padding:32px 28px;width:min(340px,calc(100% - 32px));text-align:center;}",
57 ".jltma-dialog--loading .jltma-dialog__message{margin:0;color:var(--jltma-foreground,#0f172a);font-weight:500;font-size:14px;}",
58 ".jltma-dialog__spinner{width:36px;height:36px;border:3px solid var(--jltma-border,#e2e8f0);border-top-color:var(--jltma-primary,#6814cd);border-radius:50%;margin:0 auto 16px;animation:jltma-dialog-spin 0.8s linear infinite;}",
59 "@keyframes jltma-dialog-fade-in{from{opacity:0;}to{opacity:1;}}",
60 "@keyframes jltma-dialog-scale-in{from{opacity:0;transform:scale(0.94) translateY(4px);}to{opacity:1;transform:scale(1) translateY(0);}}",
61 "@keyframes jltma-dialog-spin{to{transform:rotate(360deg);}}"
62 ].join("");
63 function ensureStyles() {
64 if (typeof document === "undefined") return;
65 if (document.getElementById(STYLE_ID)) return;
66 var style = document.createElement("style");
67 style.id = STYLE_ID;
68 style.textContent = CSS;
69 document.head.appendChild(style);
70 }
71 function ensurePortal() {
72 var portal = document.getElementById(PORTAL_ID);
73 if (!portal) {
74 portal = document.createElement("div");
75 portal.id = PORTAL_ID;
76 document.body.appendChild(portal);
77 }
78 return portal;
79 }
80 function openDialog(opts) {
81 return new Promise(function(resolve) {
82 ensureStyles();
83 var portal = ensurePortal();
84 var tone = opts.tone || "primary";
85 var showCancel = !!opts.showCancel;
86 var root = document.createElement("div");
87 root.className = "jltma-dialog";
88 var overlay = document.createElement("div");
89 overlay.className = "jltma-dialog__overlay";
90 var content = document.createElement("div");
91 content.className = "jltma-dialog__content jltma-dialog-tone-" + tone;
92 content.setAttribute("role", "alertdialog");
93 content.setAttribute("aria-modal", "true");
94 if (opts.title) {
95 var titleEl = document.createElement("h3");
96 titleEl.className = "jltma-dialog__title";
97 titleEl.textContent = opts.title;
98 content.appendChild(titleEl);
99 }
100 if (opts.message) {
101 var msgEl = document.createElement("p");
102 msgEl.className = "jltma-dialog__message";
103 msgEl.textContent = opts.message;
104 content.appendChild(msgEl);
105 }
106 var actions = document.createElement("div");
107 actions.className = "jltma-dialog__actions";
108 var cleanup = function(value) {
109 document.removeEventListener("keydown", onKey);
110 overlay.removeEventListener("click", onOverlay);
111 if (root.parentNode) root.parentNode.removeChild(root);
112 resolve(value);
113 };
114 var onKey = function(e) {
115 if (e.key === "Escape") {
116 e.preventDefault();
117 cleanup(false);
118 } else if (e.key === "Enter") {
119 e.preventDefault();
120 cleanup(true);
121 }
122 };
123 document.addEventListener("keydown", onKey);
124 var onOverlay = function() {
125 cleanup(false);
126 };
127 overlay.addEventListener("click", onOverlay);
128 if (showCancel) {
129 var cancelBtn = document.createElement("button");
130 cancelBtn.type = "button";
131 cancelBtn.className = "jltma-dialog__btn jltma-dialog__btn--secondary";
132 cancelBtn.textContent = opts.cancelText || "Cancel";
133 cancelBtn.addEventListener("click", function() {
134 cleanup(false);
135 });
136 actions.appendChild(cancelBtn);
137 }
138 var confirmBtn = document.createElement("button");
139 confirmBtn.type = "button";
140 confirmBtn.className = "jltma-dialog__btn jltma-dialog__btn--" + tone;
141 confirmBtn.textContent = opts.confirmText || "OK";
142 confirmBtn.addEventListener("click", function() {
143 cleanup(true);
144 });
145 actions.appendChild(confirmBtn);
146 content.appendChild(actions);
147 root.appendChild(overlay);
148 root.appendChild(content);
149 portal.appendChild(root);
150 try {
151 confirmBtn.focus({ preventScroll: true });
152 } catch (e) {
153 confirmBtn.focus();
154 }
155 });
156 }
157 function confirmDialog(options) {
158 var opts = options || {};
159 return openDialog({
160 title: opts.title || "Are you sure?",
161 message: opts.message || "",
162 confirmText: opts.confirmText || "Confirm",
163 cancelText: opts.cancelText || "Cancel",
164 tone: opts.tone || "primary",
165 showCancel: true
166 });
167 }
168 function alertDialog(options) {
169 var opts = options || {};
170 return openDialog({
171 title: opts.title || "Notice",
172 message: opts.message || "",
173 confirmText: opts.confirmText || "OK",
174 tone: opts.tone || "primary",
175 showCancel: false
176 });
177 }
178 /**
179 * Blocking loading overlay without action buttons.
180 *
181 * Returns `{ close }` — call `close()` when the async work completes.
182 * Intended for "Deleting…", "Saving…", "Importing…" transitions where
183 * reload follows success and a second success modal would be redundant.
184 *
185 * const loader = loadingDialog({ message: 'Deleting…' });
186 * try {
187 * await doTheWork();
188 * window.location.reload();
189 * } catch (err) {
190 * loader.close();
191 * await alertDialog({ title: 'Failed', message: err.message, tone: 'danger' });
192 * }
193 */
194 function loadingDialog(options) {
195 var opts = options || {};
196 ensureStyles();
197 var portal = ensurePortal();
198 var root = document.createElement("div");
199 root.className = "jltma-dialog jltma-dialog--loading";
200 var overlay = document.createElement("div");
201 overlay.className = "jltma-dialog__overlay";
202 var content = document.createElement("div");
203 content.className = "jltma-dialog__content";
204 content.setAttribute("role", "status");
205 content.setAttribute("aria-live", "polite");
206 var spinner = document.createElement("div");
207 spinner.className = "jltma-dialog__spinner";
208 spinner.setAttribute("aria-hidden", "true");
209 content.appendChild(spinner);
210 var msgEl = document.createElement("p");
211 msgEl.className = "jltma-dialog__message";
212 msgEl.textContent = opts.message || "Working…";
213 content.appendChild(msgEl);
214 root.appendChild(overlay);
215 root.appendChild(content);
216 portal.appendChild(root);
217 var closed = false;
218 return {
219 close: function() {
220 if (closed) return;
221 closed = true;
222 if (root.parentNode) root.parentNode.removeChild(root);
223 },
224 setMessage: function(next) {
225 msgEl.textContent = next;
226 }
227 };
228 }
229 if (typeof window !== "undefined") {
230 window.JLTMA_Dialog = window.JLTMA_Dialog || {};
231 window.JLTMA_Dialog.confirm = confirmDialog;
232 window.JLTMA_Dialog.alert = alertDialog;
233 window.JLTMA_Dialog.loading = loadingDialog;
234 }
235 //#endregion
236 (function($) {
237 "use strict";
238 var JLTMA_Toaster = {
239 container: null,
240 init: function() {
241 if (!this.container) {
242 this.container = $("<div class=\"jltma-toaster-container\"></div>");
243 $("body").append(this.container);
244 }
245 },
246 show: function(message, type = "success", duration = 3e3) {
247 this.init();
248 var toaster = $(`
249 <div class="jltma-toaster ${type}">
250 <span class="jltma-toaster-icon ${type}-icon"></span>
251 <span class="jltma-toaster-content">${message}</span>
252 <button class="jltma-toaster-close"></button>
253 <div class="jltma-toaster-progress"></div>
254 </div>
255 `);
256 this.container.append(toaster);
257 toaster.find(".jltma-toaster-close").on("click", function() {
258 JLTMA_Toaster.dismiss(toaster);
259 });
260 if (duration > 0) setTimeout(function() {
261 JLTMA_Toaster.dismiss(toaster);
262 }, duration);
263 return toaster;
264 },
265 dismiss: function(toaster) {
266 toaster.addClass("jltma-toaster-exit");
267 setTimeout(function() {
268 toaster.remove();
269 }, 300);
270 },
271 success: function(message, duration) {
272 return this.show(message, "success", duration);
273 },
274 error: function(message, duration) {
275 return this.show(message, "error", duration);
276 },
277 warning: function(message, duration) {
278 return this.show(message, "warning", duration);
279 },
280 info: function(message, duration) {
281 return this.show(message, "info", duration);
282 }
283 };
284 const WidgetAdmin = {
285 init: function() {
286 this.bindEvents();
287 this.setupNativeCPTList();
288 },
289 /**
290 * Setup native CPT list table interception
291 * Intercept WP's native "Add New" button and inject extra buttons
292 */
293 setupNativeCPTList: function() {
294 var self = this;
295 $(document).on("click", ".wrap .page-title-action:not(.jltma-cpt-btn)", function(e) {
296 e.preventDefault();
297 self.openModal(e);
298 });
299 var $addNewBtn = $(".wrap .page-title-action").first();
300 if ($addNewBtn.length) {
301 $addNewBtn.text("Add New Widget");
302 if (!$(".jltma-import-widget").length) $addNewBtn.after("<a href=\"#\" class=\"jltma-cpt-btn jltma-cpt-btn-secondary jltma-import-widget\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\"/><polyline points=\"17 8 12 3 7 8\"/><line x1=\"12\" y1=\"3\" x2=\"12\" y2=\"15\"/></svg> Import Widget</a><a href=\"https://master-addons.com/widget-builder/\" target=\"_blank\" class=\"jltma-cpt-btn jltma-cpt-btn-youtube\"><svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"#ff0000\"><path d=\"M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.546 12 3.546 12 3.546s-7.505 0-9.377.504A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.504 9.376.504 9.376.504s7.505 0 9.377-.504a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z\"/></svg> Video Tutorial</a>");
303 }
304 },
305 bindEvents: function() {
306 $(document).on("click", ".jltma-add-new-widget", this.openModal.bind(this));
307 $(document).on("change", "#jltma_widget_category", this.handleCategoryChange.bind(this));
308 $(document).on("click", ".jltma-cancel-inline-category", this.hideInlineCategoryAdd.bind(this));
309 $(document).on("click", ".jltma-save-inline-category", this.saveInlineCategory.bind(this));
310 $(document).on("click", ".jltma-pop-close, .close-btn, .jltma-modal-backdrop", this.closeModal.bind(this));
311 $(document).on("click", ".jltma-import-widget", this.openImportModal.bind(this));
312 $(document).on("click", ".jltma-import-close, .jltma-import-backdrop", this.closeImportModal.bind(this));
313 $(document).on("change", "#jltma-import-file", this.handleFileSelect.bind(this));
314 $(document).on("keydown", function(e) {
315 if (e.key === "Escape" || e.keyCode === 27) {
316 if ($("#jltma_import_widget_modal").is(":visible")) {
317 $("#jltma_import_widget_modal").fadeOut(300);
318 $("body").removeClass("jltma-modal-open");
319 WidgetAdmin.resetImportModal();
320 }
321 }
322 });
323 $(document).on("dragover", "#jltma-dropzone", this.handleDragOver.bind(this));
324 $(document).on("dragleave", "#jltma-dropzone", this.handleDragLeave.bind(this));
325 $(document).on("drop", "#jltma-dropzone", this.handleDrop.bind(this));
326 $(document).on("keypress", "#jltma_new_category_title", function(e) {
327 if (e.which === 13) {
328 e.preventDefault();
329 $(".jltma-save-inline-category").trigger("click");
330 }
331 });
332 $(document).on("keydown", "#jltma_new_category_title", function(e) {
333 if (e.which === 27) {
334 e.preventDefault();
335 $(".jltma-cancel-inline-category").trigger("click");
336 }
337 });
338 $(document).on("submit", "#jltma-widget-form", this.saveWidget.bind(this));
339 $(document).on("click", ".jltma-save-widget", function(e) {
340 e.preventDefault();
341 $("#jltma-widget-form").trigger("submit");
342 });
343 $(document).on("click", ".jltma-delete-widget", this.deleteWidget.bind(this));
344 $(document).on("click", ".jltma-copy-shortcode-widget", this.copyShortcode.bind(this));
345 $(document).on("click", ".jltma-widget-edit-category, .jltma-widget-edit-cond", this.openModalFromConditions.bind(this));
346 },
347 openModal: function(e) {
348 e.preventDefault();
349 var widgetId = $(e.currentTarget).data("widget-id") || 0;
350 var modal = $("#jltma_widget_builder_modal");
351 modal.addClass("loading");
352 modal.addClass("active");
353 $("body").addClass("jltma-modal-open");
354 if (widgetId > 0) {
355 modal.find(".jltma-modal-title").text("Edit Widget");
356 $("#jltma_widget_id").val(widgetId);
357 this.loadWidgetData(widgetId);
358 } else {
359 modal.find(".jltma-modal-title").text("Create New Widget");
360 this.resetForm();
361 modal.removeClass("loading");
362 }
363 modal.find("form").attr("data-widget-id", widgetId);
364 },
365 closeModal: function(e) {
366 if ($(e.target).hasClass("jltma-modal-backdrop") || $(e.target).hasClass("close-btn") || $(e.target).closest(".jltma-pop-close").length) {
367 $("#jltma_widget_builder_modal").removeClass("active");
368 $("#jltma_category_modal").removeClass("active");
369 $("body").removeClass("jltma-modal-open");
370 e.preventDefault();
371 }
372 },
373 resetForm: function() {
374 $("#jltma-widget-form")[0].reset();
375 $("#jltma_widget_id").val("");
376 var categorySelect = $("select#jltma_widget_category");
377 var initialValue = categorySelect.val() || "general";
378 categorySelect.data("previous-value", initialValue);
379 $("#jltma-inline-category-add").hide();
380 $("#jltma_new_category_title").val("");
381 },
382 loadWidgetData: function(widgetId) {
383 var modal = $("#jltma_widget_builder_modal");
384 $.ajax({
385 url: jltmaWidgetAdmin.ajax_url,
386 type: "GET",
387 data: {
388 action: "jltma_widget_get_data",
389 widget_id: widgetId,
390 _nonce: jltmaWidgetAdmin.widget_nonce
391 },
392 success: function(response) {
393 if (response.success) {
394 $("#jltma_widget_id").val(widgetId);
395 $("#jltma_widget_title").val(response.data.title);
396 var categoryDropdown = $("select#jltma_widget_category");
397 var categoryValue = response.data.category || "general";
398 categoryDropdown.val(categoryValue);
399 categoryDropdown.data("previous-value", categoryValue);
400 $("#jltma-inline-category-add").hide();
401 $("#jltma_new_category_title").val("");
402 }
403 modal.removeClass("loading");
404 },
405 error: function(xhr, status, error) {
406 modal.removeClass("loading");
407 JLTMA_Toaster.error(jltmaWidgetAdmin.strings.error);
408 }
409 });
410 },
411 handleCategoryChange: function(e) {
412 var $select = $(e.currentTarget);
413 var selectedValue = $select.val();
414 if (selectedValue === "__add_new__") {
415 var previousValue = $select.data("previous-value") || "general";
416 $select.data("previous-value", previousValue);
417 $("#jltma-inline-category-add").slideDown(200);
418 $("#jltma_new_category_title").val("").focus();
419 } else $select.data("previous-value", selectedValue);
420 },
421 showInlineCategoryAdd: function(e) {
422 e.preventDefault();
423 $("#jltma-inline-category-add").slideDown(200);
424 $("#jltma_new_category_title").focus();
425 },
426 hideInlineCategoryAdd: function(e) {
427 e.preventDefault();
428 var categorySelect = $("select#jltma_widget_category");
429 var previousValue = categorySelect.data("previous-value") || "general";
430 $("#jltma-inline-category-add").slideUp(200);
431 $("#jltma_new_category_title").val("");
432 categorySelect.val(previousValue);
433 },
434 saveInlineCategory: function(e) {
435 e.preventDefault();
436 var title = $("#jltma_new_category_title").val().trim();
437 var categorySelect = $("select#jltma_widget_category");
438 var saveBtn = $(".jltma-save-inline-category");
439 if (!title) {
440 JLTMA_Toaster.error("Category title is required.");
441 return;
442 }
443 var originalText = saveBtn.text();
444 saveBtn.prop("disabled", true).text("Saving...");
445 var slug = title.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").trim();
446 $.ajax({
447 url: jltmaWidgetAdmin.admin_url + "admin-ajax.php?rest_route=/jltma/v1/categories",
448 type: "POST",
449 headers: { "X-WP-Nonce": jltmaWidgetAdmin.widget_nonce },
450 contentType: "application/json",
451 data: JSON.stringify({
452 name: title,
453 slug
454 }),
455 success: function(response) {
456 if (response.success && response.data) {
457 var newSlug = response.data.slug;
458 var newTitle = response.data.title;
459 if (categorySelect.find("option[value=\"" + newSlug + "\"]").length === 0) categorySelect.find("option[value=\"__add_new__\"]").before("<option value=\"" + newSlug + "\">" + newTitle + "</option>");
460 categorySelect.val(newSlug);
461 categorySelect.data("previous-value", newSlug);
462 $("#jltma-inline-category-add").slideUp(200);
463 $("#jltma_new_category_title").val("");
464 JLTMA_Toaster.success(jltmaWidgetAdmin.strings.category_added);
465 } else {
466 JLTMA_Toaster.error("Failed to create category. Please try again.");
467 categorySelect.val(categorySelect.data("previous-value") || "general");
468 }
469 saveBtn.prop("disabled", false).text(originalText);
470 },
471 error: function(xhr, status, error) {
472 JLTMA_Toaster.error("Failed to create category. Please try again.");
473 categorySelect.val(categorySelect.data("previous-value") || "general");
474 saveBtn.prop("disabled", false).text(originalText);
475 }
476 });
477 },
478 saveWidget: function(e) {
479 e.preventDefault();
480 $("#jltma-widget-form");
481 var submitBtn = $(".jltma-save-widget");
482 var widgetId = $("#jltma_widget_id").val();
483 var title = $("#jltma_widget_title").val().trim();
484 var category = $("select#jltma_widget_category").val();
485 if (!title) {
486 JLTMA_Toaster.error(jltmaWidgetAdmin.strings.widget_title_required);
487 return;
488 }
489 var originalText = submitBtn.text();
490 submitBtn.prop("disabled", true).text(jltmaWidgetAdmin.strings.saving);
491 $.ajax({
492 url: jltmaWidgetAdmin.ajax_url,
493 type: "POST",
494 data: {
495 action: "jltma_widget_save_data",
496 widget_id: widgetId,
497 widget_title: title,
498 widget_category: category,
499 _nonce: jltmaWidgetAdmin.widget_nonce
500 },
501 success: function(response) {
502 if (response.success) if (widgetId) {
503 var categoryName = $("select#jltma_widget_category option:selected").text();
504 $("#jltma_widget_builder_modal").removeClass("active");
505 $("body").removeClass("jltma-modal-open");
506 $("#post-" + widgetId).find("td.column-jltma_widget_category").html("<span class=\"jltma-widget-category\">" + categoryName + "</span><br><a href=\"#\" class=\"jltma-widget-edit-category\" data-post-id=\"" + widgetId + "\" data-category=\"" + category + "\"><span class=\"dashicons dashicons-edit\"></span> Edit Category</a>");
507 JLTMA_Toaster.success(jltmaWidgetAdmin.strings.saved);
508 } else window.location.href = response.data.edit_url;
509 else {
510 JLTMA_Toaster.error(response.data.message || jltmaWidgetAdmin.strings.error);
511 submitBtn.prop("disabled", false).text(originalText);
512 }
513 },
514 error: function(xhr, status, error) {
515 JLTMA_Toaster.error(jltmaWidgetAdmin.strings.error);
516 submitBtn.prop("disabled", false).text(originalText);
517 }
518 });
519 },
520 deleteWidget: function(e) {
521 e.preventDefault();
522 var widgetId = $(e.currentTarget).data("widget-id");
523 if (!confirm(jltmaWidgetAdmin.strings.confirm_delete)) return;
524 $.ajax({
525 url: jltmaWidgetAdmin.ajax_url,
526 type: "POST",
527 data: {
528 action: "jltma_widget_delete",
529 widget_id: widgetId,
530 _nonce: jltmaWidgetAdmin.widget_nonce
531 },
532 success: function(response) {
533 if (response.success) location.reload();
534 else JLTMA_Toaster.error(response.data.message || jltmaWidgetAdmin.strings.error);
535 },
536 error: function() {
537 JLTMA_Toaster.error(jltmaWidgetAdmin.strings.error);
538 }
539 });
540 },
541 copyShortcode: function(e) {
542 e.preventDefault();
543 var shortcode = $(e.currentTarget).data("shortcode");
544 var button = $(e.currentTarget);
545 var temp = $("<textarea>");
546 $("body").append(temp);
547 temp.val(shortcode).select();
548 document.execCommand("copy");
549 temp.remove();
550 var originalHtml = button.html();
551 button.html("<span class=\"dashicons dashicons-yes\"></span> " + jltmaWidgetAdmin.strings.copied);
552 setTimeout(function() {
553 button.html(originalHtml);
554 }, 2e3);
555 },
556 openModalFromConditions: function(e) {
557 e.preventDefault();
558 var widgetId = $(e.currentTarget).data("post-id") || $(e.currentTarget).attr("id");
559 var modal = $("#jltma_widget_builder_modal");
560 modal.addClass("loading");
561 modal.addClass("active");
562 $("body").addClass("jltma-modal-open");
563 if (widgetId > 0) {
564 modal.find(".jltma-modal-title").text("Edit Widget");
565 $("#jltma_widget_id").val(widgetId);
566 this.loadWidgetData(widgetId);
567 }
568 modal.find("form").attr("data-widget-id", widgetId);
569 },
570 openImportModal: function(e) {
571 e.preventDefault();
572 $("#jltma_import_widget_modal").fadeIn(300);
573 $("body").addClass("jltma-modal-open");
574 },
575 closeImportModal: function(e) {
576 if ($(e.target).hasClass("jltma-import-backdrop") || $(e.target).hasClass("jltma-import-close") || $(e.target).closest(".jltma-import-close").length) {
577 $("#jltma_import_widget_modal").fadeOut(300);
578 $("body").removeClass("jltma-modal-open");
579 this.resetImportModal();
580 e.preventDefault();
581 }
582 },
583 resetImportModal: function() {
584 $("#jltma-dropzone").removeClass("dragging");
585 $(".jltma-dropzone-content").show();
586 $(".jltma-importing").hide();
587 $("#jltma-import-file").val("");
588 },
589 handleDragOver: function(e) {
590 e.preventDefault();
591 e.stopPropagation();
592 $("#jltma-dropzone").addClass("dragging");
593 },
594 handleDragLeave: function(e) {
595 e.preventDefault();
596 e.stopPropagation();
597 $("#jltma-dropzone").removeClass("dragging");
598 },
599 handleDrop: function(e) {
600 e.preventDefault();
601 e.stopPropagation();
602 $("#jltma-dropzone").removeClass("dragging");
603 var files = e.originalEvent.dataTransfer.files;
604 if (files.length > 0) this.processImportFile(files[0]);
605 },
606 handleFileSelect: function(e) {
607 var file = e.target.files[0];
608 if (file) this.processImportFile(file);
609 },
610 processImportFile: function(file) {
611 var self = this;
612 if (!file.name.endsWith(".json")) {
613 JLTMA_Toaster.error("Please select a valid JSON file.", 5e3);
614 return;
615 }
616 $(".jltma-dropzone-content").hide();
617 $(".jltma-importing").show();
618 var reader = new FileReader();
619 reader.onload = function(event) {
620 try {
621 var jsonData = JSON.parse(event.target.result);
622 var isValid = true;
623 if (window.jltmaWidgetBuilder && window.jltmaWidgetBuilder.hooks) isValid = window.jltmaWidgetBuilder.hooks.applyFilters("jltma_cwb_import_validate", isValid, jsonData);
624 if (!jsonData.widget || !jsonData.version) throw new Error("Invalid widget file format");
625 if (!isValid) throw new Error("Import validation failed");
626 var importedWidget = jsonData.widget;
627 if (window.jltmaWidgetBuilder && window.jltmaWidgetBuilder.hooks) window.jltmaWidgetBuilder.hooks.doAction("jltma_cwb_before_import", importedWidget);
628 var processedWidget = importedWidget;
629 if (window.jltmaWidgetBuilder && window.jltmaWidgetBuilder.hooks) processedWidget = window.jltmaWidgetBuilder.hooks.applyFilters("jltma_cwb_import_data", importedWidget, jsonData);
630 self.createWidgetFromImport(processedWidget, jsonData);
631 } catch (parseError) {
632 console.error("Failed to parse widget file:", parseError);
633 JLTMA_Toaster.error("Failed to import widget. Invalid JSON file format.", 5e3);
634 self.resetImportModal();
635 }
636 };
637 reader.onerror = function() {
638 console.error("Failed to read file");
639 JLTMA_Toaster.error("Failed to read file. Please try again.", 5e3);
640 self.resetImportModal();
641 };
642 reader.readAsText(file);
643 },
644 createWidgetFromImport: function(widgetData, originalJsonData) {
645 var self = this;
646 var CUSTOM_CATEGORY_PREFIX = "jltma_cwb_custom_";
647 var categorySlug = widgetData.category || "general";
648 var isCustomCategory = widgetData.isCustomCategory || categorySlug.startsWith(CUSTOM_CATEGORY_PREFIX);
649 var createWidget = function() {
650 console.log("Creating widget via REST API with data:", widgetData);
651 $.ajax({
652 url: jltmaWidgetAdmin.rest_url + "/widgets",
653 type: "POST",
654 headers: { "X-WP-Nonce": jltmaWidgetAdmin.rest_nonce },
655 contentType: "application/json",
656 data: JSON.stringify(widgetData),
657 success: function(response) {
658 console.log("Widget created successfully:", response);
659 if (window.jltmaWidgetBuilder && window.jltmaWidgetBuilder.hooks) window.jltmaWidgetBuilder.hooks.doAction("jltma_cwb_imported", widgetData, originalJsonData);
660 JLTMA_Toaster.success("Widget imported successfully! Redirecting...", 3e3);
661 setTimeout(function() {
662 $("#jltma_import_widget_modal").fadeOut(300);
663 $("body").removeClass("jltma-modal-open");
664 setTimeout(function() {
665 var newId = response && response.data ? response.data.id : null;
666 var editUrl = response && response.data && response.data.edit_url ? response.data.edit_url : jltmaWidgetAdmin.admin_url + "admin.php?page=jltma-widget-editor&widget_id=" + newId;
667 window.location.href = editUrl;
668 }, 300);
669 }, 1500);
670 },
671 error: function(xhr, status, error) {
672 console.error("Failed to create widget:", error);
673 console.error("XHR Response:", xhr);
674 console.error("Status:", xhr.status);
675 console.error("Response Text:", xhr.responseText);
676 console.error("Request Data:", widgetData);
677 var errorMessage = "Failed to import widget. Please try again.";
678 try {
679 var errorData = JSON.parse(xhr.responseText);
680 if (errorData.message) errorMessage = errorData.message;
681 } catch (e) {}
682 JLTMA_Toaster.error(errorMessage + " Check console for details.", 5e3);
683 self.resetImportModal();
684 }
685 });
686 };
687 if (isCustomCategory) $.ajax({
688 url: jltmaWidgetAdmin.rest_url + "/categories",
689 type: "GET",
690 headers: { "X-WP-Nonce": jltmaWidgetAdmin.rest_nonce },
691 success: function(categories) {
692 if (!categories.some(function(cat) {
693 return cat.slug === categorySlug;
694 })) self.createCategoryIfNotExists(categorySlug, createWidget);
695 else createWidget();
696 },
697 error: function(xhr, status, error) {
698 console.warn("Failed to check categories, proceeding with widget creation:", error);
699 createWidget();
700 }
701 });
702 else createWidget();
703 },
704 createCategoryIfNotExists: function(categorySlug, callback) {
705 var CUSTOM_CATEGORY_PREFIX = "jltma_cwb_custom_";
706 var categoryName = categorySlug.replace(CUSTOM_CATEGORY_PREFIX, "").split("_").map(function(word) {
707 return word.charAt(0).toUpperCase() + word.slice(1);
708 }).join(" ");
709 var shouldCreate = true;
710 if (window.jltmaWidgetBuilder && window.jltmaWidgetBuilder.hooks) shouldCreate = window.jltmaWidgetBuilder.hooks.applyFilters("jltma_cwb_before_create_category", shouldCreate, categorySlug, categoryName);
711 if (!shouldCreate) {
712 if (callback) callback();
713 return;
714 }
715 $.ajax({
716 url: jltmaWidgetAdmin.rest_url + "/categories",
717 type: "POST",
718 headers: { "X-WP-Nonce": jltmaWidgetAdmin.rest_nonce },
719 contentType: "application/json",
720 data: JSON.stringify({
721 name: categoryName,
722 slug: categorySlug,
723 custom_prefix: CUSTOM_CATEGORY_PREFIX,
724 auto_created: true
725 }),
726 success: function(result) {
727 if (window.jltmaWidgetBuilder && window.jltmaWidgetBuilder.hooks) window.jltmaWidgetBuilder.hooks.doAction("jltma_cwb_category_created", result.data, categorySlug, categoryName);
728 if (callback) callback();
729 },
730 error: function(xhr, status, error) {
731 console.error("Failed to create category:", categorySlug, error);
732 JLTMA_Toaster.warning("Could not create category \"" + categoryName + "\". Widget will use \"general\" category.", 5e3);
733 if (callback) callback();
734 }
735 });
736 }
737 };
738 $(document).ready(function() {
739 WidgetAdmin.init();
740 });
741 })(jQuery);
742 //#endregion
743 })();
744