1/*!
2 * WHMCS Ajax Driven Modal Framework
3 *
4 * @copyright Copyright (c) WHMCS Limited 2005-2021
5 * @license https://www.whmcs.com/license/ WHMCS Eula
6 */
7var ajaxModalSubmitEvents = [],
8 ajaxModalPostSubmitEvents = [];
9jQuery(document).ready(function(){
10 jQuery(document).on('click', '.open-modal', function(e) {
11 e.preventDefault();
12 var url = jQuery(this).attr('href'),
13 modalSize = jQuery(this).data('modal-size'),
14 modalClass = jQuery(this).data('modal-class'),
15 modalTitle = jQuery(this).data('modal-title'),
16 submitId = jQuery(this).data('btn-submit-id'),
17 submitLabel = jQuery(this).data('btn-submit-label'),
18 submitColor = jQuery(this).data('btn-submit-color'),
19 hideClose = jQuery(this).data('btn-close-hide'),
20 disabled = jQuery(this).attr('disabled'),
21 successDataTable = jQuery(this).data('datatable-reload-success');
22
23 var postData = '';
24 if (csrfToken) {
25 postData = {token: csrfToken};
26 }
27 if (!disabled) {
28 openModal(url, postData, modalTitle, modalSize, modalClass, submitLabel, submitId, submitColor, hideClose, successDataTable);
29 }
30 });
31
32 // define modal close reset action
33 jQuery('#modalAjax').on('hidden.bs.modal', function (e) {
34 if (jQuery(this).hasClass('modal-feature-highlights')) {
35 var dismissForVersion = jQuery('#cbFeatureHighlightsDismissForVersion').is(':checked');
36 WHMCS.http.jqClient.post(
37 'whatsnew.php',
38 {
39 dismiss: "1",
40 until_next_update: dismissForVersion ? '1' : '0',
41 token: csrfToken
42 }
43 );
44 }
45
46 jQuery('#modalAjax').find('.modal-body').empty();
47 jQuery('#modalAjax').children('div.modal-dialog').removeClass('modal-lg');
48 jQuery('#modalAjax').removeClass().addClass('modal whmcs-modal fade');
49 jQuery('#modalAjax .modal-title').html('Title');
50 jQuery('#modalAjax .modal-submit').html('Submit')
51 .removeClass()
52 .addClass('btn btn-primary modal-submit')
53 .removeAttr('id')
54 .removeAttr('disabled');
55 jQuery('#modalAjax .loader').show();
56 });
57});
58
59function openModal(url, postData, modalTitle, modalSize, modalClass, submitLabel, submitId, submitColor, hideClose, successDataTable) {
60 //set the text of the modal title
61 jQuery('#modalAjax .modal-title').html(modalTitle);
62
63 // set the modal size via a class attribute
64 if (modalSize) {
65 jQuery('#modalAjax').children('div[class="modal-dialog"]').addClass(modalSize);
66 }
67 // set the modal class
68 if (modalClass) {
69 jQuery('#modalAjax').addClass(modalClass);
70 }
71
72 // set the text of the submit button
73 if(!submitLabel){
74 jQuery('#modalAjax .modal-submit').hide();
75 } else {
76 jQuery('#modalAjax .modal-submit').show().html(submitLabel);
77 // set the button id so we can target the click function of it.
78 if (submitId) {
79 jQuery('#modalAjax .modal-submit').attr('id', submitId);
80 }
81 }
82
83 if (hideClose) {
84 jQuery('#modalAjaxClose').hide();
85 }
86
87 if (submitColor) {
88 jQuery('#modalAjax .modal-submit').removeClass('btn-primary')
89 .addClass('btn-' + submitColor);
90 }
91
92 jQuery('#modalAjax .modal-body').html('');
93
94 jQuery('#modalSkip').hide();
95 disableSubmit();
96
97 // show modal
98 jQuery('#modalAjax').modal({
99 show: true,
100 keyboard: true,
101 backdrop: jQuery('#modalAjax').hasClass('static') ? 'static' : true
102 });
103
104 // fetch modal content
105 WHMCS.http.jqClient.post(url, postData, function(data) {
106 updateAjaxModal(data);
107 }, 'json').fail(function() {
108 jQuery('#modalAjax .modal-body').html('An error occurred while communicating with the server. Please try again.');
109 jQuery('#modalAjax .loader').fadeOut();
110 }).always(function () {
111 var modalForm = jQuery('#modalAjax').find('form');
112 // If a submitId is present, then we're working with a form and need to override the default event
113 if (submitId) {
114 modalForm.submit(function (event) {
115 submitIdAjaxModalClickEvent();
116 return false;
117 });
118 }
119 if (successDataTable) {
120 modalForm.data('successDataTable', successDataTable);
121 }
122
123 // Since the content is dynamically fetched, we have to check for the elements we want here too
124 var inputs = jQuery(modalForm).find('input:not(input[type=checkbox],input[type=radio],input[type=hidden])');
125
126 if (inputs.length > 0) {
127 jQuery(inputs).first().focus();
128 }
129 });
130
131 //define modal submit button click
132 if (submitId) {
133 /**
134 * Reloading ajax modal multiple times on the same page can add
135 * multiple "on" click events which submits the same form over
136 * and over.
137 * Remove the on click event with "off" to avoid multiple growl
138 * and save events being run.
139 *
140 * @see http://api.jquery.com/off/
141 */
142 var submitButton = jQuery('#' + submitId);
143 submitButton.off('click');
144 submitButton.on('click', submitIdAjaxModalClickEvent);
145 }
146}
147
148function submitIdAjaxModalClickEvent ()
149{
150 var canContinue = true,
151 loader = jQuery('#modalAjax .loader');
152 disableSubmit();
153 loader.show();
154 if (ajaxModalSubmitEvents.length) {
155 jQuery.each(ajaxModalSubmitEvents, function (index, value) {
156 var fn = window[value];
157 if (canContinue && typeof fn === 'function') {
158 canContinue = fn();
159 }
160 });
161 }
162 if (!canContinue) {
163 enableSubmit();
164 loader.hide();
165 return;
166 }
167 var modalForm = jQuery('#modalAjax').find('form');
168 var modalBody = jQuery('#modalAjax .modal-body');
169 var modalErrorContainer = jQuery(modalBody).find('.admin-modal-error');
170
171 jQuery(modalErrorContainer).slideUp();
172
173 var modalPost = WHMCS.http.jqClient.post(
174 modalForm.attr('action'),
175 modalForm.serialize(),
176 function(data) {
177 if (modalForm.data('successDataTable')) {
178 data.successDataTable = modalForm.data('successDataTable');
179 }
180 /**
181 * When actions should occur before the ajax modal is updated
182 * that do not fall into the standard actions.
183 * Calling code (ie the function defined in fn) should validate
184 * that the ajax modal being updated is the one that the code should
185 * run for, as there is potential for multiple ajax modals on the
186 * same page.
187 */
188 if (ajaxModalPostSubmitEvents.length) {
189 jQuery.each(ajaxModalPostSubmitEvents, function (index, value) {
190 var fn = window[value];
191 if (typeof fn === 'function') {
192 fn(data, modalForm);
193 }
194 });
195 }
196 updateAjaxModal(data);
197 },
198 'json'
199 ).fail(function(xhr) {
200 var data = xhr.responseJSON;
201 var genericErrorMsg = 'An error occurred while communicating with the server. Please try again.';
202 if (data && data.data) {
203 data = data.data;
204 if (data.errorMsg) {
205 if (modalErrorContainer.length > 0) {
206 jQuery(modalErrorContainer)
207 .html(data.errorMsg)
208 .slideDown();
209 } else {
210 jQuery.growl.warning({title: data.errorMsgTitle, message: data.errorMsg});
211 }
212 } else if (data.data.body) {
213 jQuery(modalBody).html(data.body);
214 } else {
215 jQuery(modalBody).html(genericErrorMsg);
216 }
217 } else {
218 jQuery(modalBody).html(genericErrorMsg);
219 }
220 jQuery('#modalAjax .loader').fadeOut();
221 enableSubmit();
222 });
223}
224
225function updateAjaxModal(data) {
226 if (data.reloadPage) {
227 if (typeof data.reloadPage === 'string') {
228 window.location = data.reloadPage;
229 } else {
230 window.location.reload();
231 }
232 return;
233 }
234 if (data.successDataTable) {
235 WHMCS.ui.dataTable.getTableById(data.successDataTable, undefined).ajax.reload();
236 }
237 if (data.redirect) {
238 window.location = data.redirect;
239 }
240 if (data.successWindow && typeof window[data.successWindow] === "function") {
241 window[data.successWindow]();
242 }
243 if (data.dismiss) {
244 dialogClose();
245 }
246 if (data.successMsg) {
247 jQuery.growl.notice({ title: data.successMsgTitle, message: data.successMsg });
248 }
249 if (data.errorMsg) {
250 var inModalErrorContainer = jQuery('#modalAjax .modal-body .admin-modal-error');
251
252 if (inModalErrorContainer.length > 0 && !data.dismiss) {
253 jQuery(inModalErrorContainer)
254 .html(data.errorMsg)
255 .slideDown();
256 } else {
257 jQuery.growl.warning({title: data.errorMsgTitle, message: data.errorMsg});
258 }
259 }
260 if (data.title) {
261 jQuery('#modalAjax .modal-title').html(data.title);
262 }
263 if (data.body) {
264 jQuery('#modalAjax .modal-body').html(data.body);
265 } else {
266 if (data.url) {
267 WHMCS.http.jqClient.post(data.url, '', function(data2) {
268 jQuery('#modalAjax').find('.modal-body').html(data2.body);
269 }, 'json').fail(function() {
270 jQuery('#modalAjax').find('.modal-body').html('An error occurred while communicating with the server. Please try again.');
271 jQuery('#modalAjax').find('.loader').fadeOut();
272 });
273 }
274 }
275 if (data.submitlabel) {
276 jQuery('#modalAjax .modal-submit').html(data.submitlabel).show();
277 if (data.submitId) {
278 jQuery('#modalAjax').find('.modal-submit').attr('id', data.submitId);
279 }
280 }
281
282 if (data.submitId) {
283 /**
284 * Reloading ajax modal multiple times on the same page can add
285 * multiple "on" click events which submits the same form over
286 * and over.
287 * Remove the on click event with "off" to avoid multiple growl
288 * and save events being run.
289 *
290 * @see http://api.jquery.com/off/
291 */
292 var submitButton = jQuery('#' + data.submitId);
293 submitButton.off('click');
294 submitButton.on('click', submitIdAjaxModalClickEvent);
295 }
296
297 if (data.disableSubmit) {
298 disableSubmit();
299 } else {
300 enableSubmit();
301 }
302
303 var dismissLoader = true;
304 if (typeof data.dismissLoader !== 'undefined') {
305 dismissLoader = data.dismissLoader;
306 }
307
308 dismissLoaderAfterRender(dismissLoader);
309
310 if (data.hideSubmit) {
311 ajaxModalHideSubmit();
312 }
313}
314
315// backwards compat for older dialog implementations
316
317function dialogSubmit() {
318 disableSubmit();
319 jQuery('#modalAjax .loader').show();
320 var postUrl = jQuery('#modalAjax').find('form').attr('action');
321 WHMCS.http.jqClient.post(postUrl, jQuery('#modalAjax').find('form').serialize(),
322 function(data) {
323 updateAjaxModal(data);
324 }, 'json').fail(function() {
325 jQuery('#modalAjax .modal-body').html('An error occurred while communicating with the server. Please try again.');
326 jQuery('#modalAjax .loader').fadeOut();
327 });
328}
329
330function dialogClose() {
331 jQuery('#modalAjax').modal('hide');
332}
333
334function addAjaxModalSubmitEvents(functionName) {
335 if (functionName) {
336 ajaxModalSubmitEvents.push(functionName);
337 }
338}
339
340function removeAjaxModalSubmitEvents(functionName) {
341 if (functionName) {
342 var index = ajaxModalSubmitEvents.indexOf(functionName);
343 if (index >= 0) {
344 ajaxModalSubmitEvents.splice(index, 1);
345 }
346 }
347}
348
349function addAjaxModalPostSubmitEvents(functionName) {
350 if (functionName) {
351 ajaxModalPostSubmitEvents.push(functionName);
352 }
353}
354
355function removeAjaxModalPostSubmitEvents(functionName) {
356 if (functionName) {
357 var index = ajaxModalPostSubmitEvents.indexOf(functionName);
358 if (index >= 0) {
359 ajaxModalPostSubmitEvents.splice(index, 1);
360 }
361 }
362}
363
364function disableSubmit()
365{
366 jQuery('#modalAjax .modal-submit').prop('disabled', true).addClass('disabled');
367}
368
369function enableSubmit()
370{
371 jQuery('#modalAjax .modal-submit').prop('disabled', false).removeClass('disabled');
372}
373
374function ajaxModalHideSubmit()
375{
376 jQuery('#modalAjax .modal-submit').hide();
377}
378
379function dismissLoaderAfterRender(showLoader)
380{
381 if (showLoader === false) {
382 jQuery('#modalAjax .loader').show();
383 } else {
384 jQuery('#modalAjax .loader').fadeOut();
385 }
386}
387