run:R W Run
46 By
2026-04-08 19:27:23
R W Run
1.54 MB
2026-04-08 19:27:24
R W Run
637.93 KB
2026-04-08 19:27:23
R W Run
49.73 KB
2026-04-08 19:27:23
R W Run
error_log
📄whmcs.js
1/*!
2 * WHMCS Twenty-One Theme
3 * Global Javascript
4 * Copyright (c) 2020 WHMCS Limited
5 * https://www.whmcs.com/license/
6 */
7
8jQuery(document).ready(function() {
9
10 // when the page loads
11 autoCollapse('#nav', 30);
12
13 if (jQuery('#lightbox').length === 0) {
14 lightbox.init();
15 }
16
17 // when the window is resized
18 jQuery(window).on('resize', function () {
19 if (jQuery('button[data-target="#mainNavbar"], button[data-toggle="collapse"]').is(':visible')) {
20 return;
21 }
22 autoCollapse('#nav', 30);
23 });
24
25 // Item selector
26 jQuery('.item-selector .item').click(function(e) {
27 e.preventDefault();
28 jQuery(this).closest('.item-selector').find('.item').removeClass('active').end()
29 .find('input').val(jQuery(this).data('value'));
30 jQuery(this).addClass('active');
31 });
32
33 // Password reveal
34 jQuery(document).on('click', '.btn-reveal-pw', function (e) {
35 $targetField = jQuery(this).closest('.input-group').find('.pw-input');
36 if ($targetField.attr('type') == 'password') {
37 $targetField.attr('type', 'text');
38 } else {
39 $targetField.attr('type', 'password');
40 }
41 });
42
43 // Account notifications popover
44 jQuery("#accountNotifications").popover({
45 container: 'body',
46 placement: 'bottom',
47 template: '<div class="popover popover-user-notifications" role="tooltip"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-header"></h3><div class="popover-body"><p></p></div></div></div>',
48 html: true,
49 content: function() {
50 return jQuery("#accountNotificationsContent").html();
51 },
52 });
53
54 jQuery('.card-sidebar .truncate').each(function () {
55 jQuery(this).attr('title', jQuery(this).text())
56 .attr('data-toggle', 'tooltip')
57 .attr('data-placement', 'bottom');
58 });
59
60 // Default catch for all other popovers
61 jQuery('[data-toggle="popover"]').popover({
62 html: true
63 });
64
65 // Enable tooltips
66 // Attach function to body so tooltips inserted by ajax will load
67 jQuery(function(jQuery){
68 jQuery('body').tooltip({
69 selector: '[data-toggle="tooltip"]'
70 });
71 });
72
73 // Logic to dismiss popovers on click outside
74 jQuery('body').on('click', function (e) {
75 jQuery('[data-toggle="popover"]').each(function () {
76 if (!jQuery(this).is(e.target) && jQuery(this).has(e.target).length === 0 && jQuery('.popover').has(e.target).length === 0) {
77 jQuery(this).popover('hide');
78 }
79 });
80 });
81
82 // Sidebar active class toggle
83 jQuery(".list-group-tab-nav a").click(function() {
84 if (jQuery(this).hasClass('disabled')) {
85 return false;
86 }
87 var urlFragment = this.href.split('#')[1];
88 if (urlFragment) {
89 // set the fragment in the URL bar for bookmarking and such.
90 window.location.hash = '#' + urlFragment;
91 }
92 });
93
94 // Sidebar minimise/maximise
95 jQuery('.card-minimise').click(function(e) {
96 e.preventDefault();
97 var collapsableBody = jQuery(this).closest('.card').find('.collapsable-card-body');
98 if (jQuery(this).hasClass('minimised')) {
99 collapsableBody.slideDown();
100 jQuery(this).removeClass('minimised');
101 } else {
102 collapsableBody.slideUp();
103 jQuery(this).addClass('minimised');
104 }
105 });
106
107 // Minimise sidebar panels by default on small devices
108 if (jQuery('.container').width() <= 720) {
109 jQuery('.card-sidebar').find('.collapsable-card-body').hide().end()
110 .find('.card-minimise').addClass('minimised');
111 }
112
113 // Internal page tab selection handling via location hash
114 var internalSelectionDisabled = false;
115 if (
116 typeof(disableInternalTabSelection) !== 'undefined'
117 &&
118 disableInternalTabSelection
119 ) {
120 internalSelectionDisabled = true;
121 }
122
123 if (!internalSelectionDisabled) {
124 if (jQuery(location).attr('hash').substr(1) !== "") {
125 var activeTab = jQuery(location).attr('hash');
126 jQuery(".primary-content > .tab-content > .tab-pane").removeClass('active');
127 jQuery(activeTab).removeClass('fade').addClass('active');
128 jQuery(".list-group-tab-nav a").removeClass('active');
129 jQuery('a[href="' + activeTab + '"]').addClass('active');
130 setTimeout(function() {
131 // Browsers automatically scroll on page load with a fragment.
132 // This scrolls back to the top right after page complete, but
133 // just before render (no perceptible scroll).
134 window.scrollTo(0, 0);
135 }, 1);
136 }
137 }
138
139 // Enable Switches for Checkboxes
140 if (jQuery.prototype.bootstrapSwitch) {
141 jQuery(".toggle-switch-success").bootstrapSwitch({
142 onColor: 'success'
143 });
144 }
145
146 // Collapsable Panels
147 jQuery(".panel-collapsable .card-header").click(function(e) {
148 var $this = jQuery(this);
149 if (!$this.closest('.card').hasClass('panel-collapsed')) {
150 $this.closest('.card').addClass('panel-collapsed').find('.card-body').slideUp();
151 $this.find('.collapse-icon i').removeClass('fa-minus').addClass('fa-plus');
152 } else {
153 $this.closest('.card').removeClass('panel-collapsed').find('.card-body').slideDown();
154 $this.find('.collapse-icon i').removeClass('fa-plus').addClass('fa-minus');
155 }
156 });
157
158 // Two-Factor Authentication Auto Focus Rules
159 if (("#frmLogin").length > 0) {
160 jQuery("#frmLogin input:text:visible:first").focus();
161 }
162 if (("#twofaactivation").length > 0) {
163 jQuery("#twofaactivation input:text:visible:first,#twofaactivation input:password:visible:first").focus();
164 }
165
166 // Sub-Account Activation Toggle
167 jQuery("#inputSubaccountActivate").click(function () {
168 if (jQuery("#inputSubaccountActivate:checked").val() != null) {
169 jQuery("#subacct-container").show();
170 } else {
171 jQuery("#subacct-container").hide();
172 }
173 });
174
175 // Mass Domain Management Bulk Action Handling
176 jQuery(".setBulkAction").click(function(event) {
177 event.preventDefault();
178 var id = jQuery(this).attr('id').replace('Link', ''),
179 domainForm = jQuery('#domainForm');
180
181 if (id === 'renewDomains') {
182 domainForm.attr('action', WHMCS.utils.getRouteUrl('/cart/domain/renew'));
183 } else {
184 if (jQuery('#' + id).length !== 0) {
185 var action = domainForm.attr('action');
186 domainForm.attr('action', action + '#' + id);
187 }
188 jQuery('#bulkaction').val(id);
189 }
190 domainForm.submit();
191 });
192
193 // Stop events on objects with this class from bubbling up the dom
194 jQuery('.stopEventBubble').click( function(event) {
195 event.stopPropagation();
196 });
197
198 // Tab Control Link handling for tab switching via regular links
199 jQuery('.tabControlLink').on(
200 'click',
201 function(event) {
202 event.preventDefault();
203 var id = jQuery(this).attr('href');
204 jQuery("a[href='/"+id+"']").click();
205 }
206 );
207
208 jQuery(document).on('click', '.delete-cc-email', function() {
209 var self = jQuery(this),
210 email = self.data('email'),
211 feedback = jQuery('#divCcEmailFeedback');
212
213 if (feedback.is(':visible')) {
214 feedback.slideUp('fast');
215 }
216
217 WHMCS.http.jqClient.jsonPost({
218 url: window.location.href,
219 data: {
220 action: 'delete',
221 email: email,
222 token: csrfToken
223 },
224 success: function (data) {
225 if (data.success) {
226 self.closest('.ticket-cc-email').parent('div').slideUp('fast').remove();
227 feedback.removeClass('alert-danger')
228 .addClass('alert-success')
229 .html(data.message)
230 .slideDown('fast');
231 }
232 },
233 error: function (error) {
234 if (error) {
235 feedback.removeClass('alert-success')
236 .addClass('alert-danger')
237 .html(error)
238 .slideDown('fast');
239 }
240 }
241 });
242 }).on('submit', '#frmAddCcEmail', function(e) {
243 e.preventDefault();
244 var frm = jQuery(this),
245 cloneRow = jQuery('#ccCloneRow').clone().removeAttr('id'),
246 email = jQuery('#inputAddCcEmail'),
247 feedback = jQuery('#divCcEmailFeedback');
248
249 if (feedback.is(':visible')) {
250 feedback.slideUp('fast');
251 }
252 WHMCS.http.jqClient.jsonPost({
253 url: frm.attr('action'),
254 data: frm.serialize(),
255 success: function (data) {
256 if (data.success) {
257 cloneRow.find('span.email')
258 .html(email.val())
259 .find('button')
260 .data('email', email.val())
261 .end();
262
263 cloneRow.show()
264 .appendTo(jQuery('#sidebarTicketCc').find('.list-group'));
265 email.val('');
266 feedback.slideUp('fast')
267 .removeClass('alert-danger hidden')
268 .addClass('alert-success')
269 .html(data.message)
270 .slideDown('fast');
271 }
272 },
273 error: function (error) {
274 if (error) {
275 feedback.slideUp('fast')
276 .removeClass('alert-success hidden')
277 .addClass('alert-danger')
278 .html(error)
279 .slideDown('fast');
280 }
281 }
282 });
283 });
284
285 // Ticket Rating Click Handler
286 jQuery('.ticket-reply .rating span.star').click( function(event) {
287 window.location = 'viewticket.php?tid='
288 + jQuery(this).parent('.rating').attr("ticketid")
289 + '&c=' + jQuery(this).parent('.rating').attr("ticketkey")
290 + '&rating=rate' + jQuery(this).parent('.rating').attr("ticketreplyid")
291 + '_' + jQuery(this).attr("rate");
292 });
293
294 // Prevent malicious window.opener activity from auto-linked URLs
295 jQuery('a.autoLinked').click(function (e) {
296 e.preventDefault();
297 if (jQuery(this).hasClass('disabled')) {
298 return false;
299 }
300
301 var child = window.open();
302 child.opener = null;
303 child.location = e.target.href;
304 });
305
306 // Handle Single Sign-On Toggle Setting
307 jQuery("#inputAllowSso").on('switchChange.bootstrapSwitch', function(event, isChecked) {
308 if (isChecked) {
309 jQuery("#ssoStatusTextEnabled").show();
310 jQuery("#ssoStatusTextDisabled").hide();
311 } else {
312 jQuery("#ssoStatusTextDisabled").show();
313 jQuery("#ssoStatusTextEnabled").hide();
314 }
315 WHMCS.http.jqClient.post("clientarea.php", jQuery("#frmSingleSignOn").serialize());
316 });
317
318 // Single Sign-On call for Product/Service
319 jQuery('.btn-service-sso').on('click', function(e) {
320 e.preventDefault();
321 var button = jQuery(this);
322
323 var form = button.closest('form');
324
325 if (form.length === 0) {
326 form = button.find('form');
327 }
328 if (form.hasClass('disabled') || button.hasClass('disabled')) {
329 return;
330 }
331 var url = form.data('href');
332 if (!url) {
333 url = window.location.href;
334 }
335
336 button.attr('disabled', 'disabled').addClass('disabled');
337 jQuery('.loading', button).show().end();
338 jQuery('.login-feedback', form).slideUp();
339 WHMCS.http.jqClient.post(
340 url,
341 form.serialize(),
342 function (data) {
343 jQuery('.loading', button).hide().end().removeAttr('disabled');
344 jQuery('.login-feedback', form).html('');
345 if (data.error) {
346 jQuery('.login-feedback', form).hide().html(data.error).slideDown();
347 }
348 if (data.redirect !== undefined && data.redirect.substr(0, 7) === 'window|') {
349 window.open(data.redirect.substr(7), '_blank');
350 }
351 },
352 'json'
353 ).always(function() {
354 button.removeAttr('disabled').removeClass('disabled');
355 button.find('.loading').hide().end();
356 });
357 });
358 jQuery('.btn-sidebar-form-submit').on('click', function(e) {
359 e.preventDefault();
360 jQuery(this).find('.loading').show().end()
361 .attr('disabled', 'disabled');
362
363 var form = jQuery(this).closest('form');
364
365 if (form.length === 0) {
366 form = jQuery(this).find('form');
367 }
368
369 if (form.length !== 0 && form.hasClass('disabled') === false) {
370 form.submit();
371 } else {
372 jQuery(this).find('.loading').hide().end().removeAttr('disabled');
373 }
374 });
375
376 // Back to top animated scroll
377 jQuery('.back-to-top').click(function(e) {
378 e.preventDefault();
379 jQuery('body,html').animate({scrollTop: 0}, 500);
380 });
381
382 // Prevent page scroll on language choose click
383 jQuery('.choose-language').click(function(e) {
384 e.preventDefault();
385 });
386
387 // Activate copy to clipboard functionality
388 jQuery('.copy-to-clipboard').click(WHMCS.ui.clipboard.copy);
389
390 // Handle Language Chooser modal
391 jQuery('#modalChooseLanguage button[type=submit]').click(function(e) {
392 e.preventDefault();
393 var form = jQuery(this).closest('form');
394 var currency = form.find('input[name="currency"]');
395 var language = form.find('input[name="language"]');
396 var fields = [];
397
398 if (language.data('current') != language.val()) {
399 fields.push('language=' + language.val());
400 }
401 if (currency.data('current') != currency.val() && currency.val() != "") {
402 fields.push('currency=' + currency.val());
403 }
404
405 window.location.replace(form.attr('action') + fields.join('&'));
406 });
407
408 // Password Generator
409 jQuery('.generate-password').click(function(e) {
410 jQuery('#frmGeneratePassword').submit();
411 jQuery('#modalGeneratePassword')
412 .data('targetfields', jQuery(this).data('targetfields'))
413 .modal('show');
414 });
415 jQuery('#frmGeneratePassword').submit(function(e) {
416 e.preventDefault();
417 var length = parseInt(jQuery('#inputGeneratePasswordLength').val(), 10);
418
419 // Check length
420 if (length < 8 || length > 64) {
421 jQuery('#generatePwLengthError').show();
422 return;
423 }
424
425 jQuery('#inputGeneratePasswordOutput').val(WHMCS.utils.generatePassword(length));
426 });
427 jQuery('#btnGeneratePasswordInsert')
428 .click(WHMCS.ui.clipboard.copy)
429 .click(function(e) {
430 jQuery(this).closest('.modal').modal('hide');
431 var targetFields = jQuery(this).closest('.modal').data('targetfields'),
432 generatedPassword = jQuery('#inputGeneratePasswordOutput');
433 targetFields = targetFields.split(',');
434 for(var i = 0; i < targetFields.length; i++) {
435 jQuery('#' + targetFields[i]).val(generatedPassword.val())
436 .trigger('keyup');
437 }
438 // Remove the generated password.
439 generatedPassword.val('');
440 });
441
442 /**
443 * If we are logged into the admin area and can edit a category and click edit,
444 * we need to stop the default and click the edit instead since its nested.
445 */
446 jQuery('a.card-body').click(function(e) {
447 if (e.target.id.includes('btnEditCategory')) {
448 e.preventDefault();
449 var editUrl = jQuery('#btnEditCategory-' + jQuery(this).data('id')).data('url');
450 window.location.href = editUrl;
451 }
452 });
453
454 jQuery('.kb-article-item').click(function(e) {
455 if (e.target.id.includes('btnEditArticle')) {
456 e.preventDefault();
457 var editUrl = jQuery('#btnEditArticle-' + jQuery(this).data('id')).data('url');
458 window.location.href = editUrl;
459 }
460 });
461 /**
462 * Code will loop through each element that has the class markdown-editor and
463 * enable the Markdown editor.
464 */
465 var count = 0,
466 editorName = 'clientMDE',
467 counter = 0;
468 jQuery(".markdown-editor").each(function( index ) {
469 count++;
470 var autoSaveName = jQuery(this).data('auto-save-name'),
471 footerId = jQuery(this).attr('id') + '-footer';
472 if (typeof autoSaveName == "undefined") {
473 autoSaveName = 'client_area';
474 }
475 window[editorName + count.toString()] = jQuery(this).markdown(
476 {
477 footer: '<div id="' + footerId + '" class="markdown-editor-status"></div>',
478 autofocus: false,
479 savable: false,
480 resize: 'vertical',
481 iconlibrary: 'fa-5',
482 language: locale,
483 onShow: function(e){
484 var content = '',
485 save_enabled = false;
486 if(typeof(Storage) !== "undefined") {
487 // Code for localStorage/sessionStorage.
488 content = localStorage.getItem(autoSaveName);
489 save_enabled = true;
490 if (content && typeof(content) !== "undefined") {
491 e.setContent(content);
492 }
493 }
494 jQuery("#" + footerId).html(parseMdeFooter(content, save_enabled, saved));
495 },
496 onChange: function(e){
497 var content = e.getContent(),
498 save_enabled = false;
499 if(typeof(Storage) !== "undefined") {
500 counter = 3;
501 save_enabled = true;
502 localStorage.setItem(autoSaveName, content);
503 doCountdown();
504 }
505 jQuery("#" + footerId).html(parseMdeFooter(content, save_enabled));
506 },
507 onPreview: function(e){
508 var originalContent = e.getContent(),
509 parsedContent;
510
511 jQuery.ajax({
512 url: WHMCS.utils.getRouteUrl('/clientarea/message/preview'),
513 async: false,
514 data: {token: csrfToken, content: originalContent},
515 dataType: 'json',
516 success: function (data) {
517 parsedContent = data;
518 }
519 });
520
521 return parsedContent.body ? parsedContent.body : '';
522 },
523 additionalButtons: [
524 [{
525 name: "groupCustom",
526 data: [{
527 name: "cmdHelp",
528 title: "Help",
529 hotkey: "Ctrl+F1",
530 btnClass: "btn open-modal",
531 icon: {
532 glyph: 'fas fa-question-circle',
533 fa: 'fas fa-question-circle',
534 'fa-3': 'icon-question-sign',
535 'fa-5': 'fas fa-question-circle',
536 },
537 callback: function(e) {
538 e.$editor.removeClass("md-fullscreen-mode");
539 }
540 }]
541 }]
542 ],
543 hiddenButtons: [
544 'cmdImage'
545 ]
546 });
547
548 jQuery('button[data-handler="bootstrap-markdown-cmdHelp"]')
549 .attr('data-modal-title', markdownGuide)
550 .attr('href', 'submitticket.php?action=markdown');
551
552 jQuery(this).closest("form").bind({
553 submit: function() {
554 if(typeof(Storage) !== "undefined") {
555 localStorage.removeItem(autoSaveName);
556 }
557 }
558 });
559 });
560
561 // Email verification
562 var btnResendEmail = jQuery('.btn-resend-verify-email');
563 jQuery(btnResendEmail).click(function() {
564 $(this).prop('disabled', true).find('.loader').show();
565 WHMCS.http.jqClient.post(
566 jQuery(this).data('uri'),
567 {
568 'token': csrfToken,
569 }).done(function(data) {
570 btnResendEmail.find('.loader').hide();
571 if (data.success) {
572 btnResendEmail.text(btnResendEmail.data('email-sent'));
573 } else {
574 btnResendEmail.text(btnResendEmail.data('error-msg'));
575 }
576 });
577 });
578 jQuery('#btnEmailVerificationClose').click(function(e) {
579 e.preventDefault();
580 WHMCS.http.jqClient.post(jQuery(this).data('uri'),
581 {
582 'token': csrfToken
583 });
584 jQuery('.verification-banner.email-verification').hide();
585 });
586 jQuery('#btnUserValidationClose').click(function(e) {
587 e.preventDefault();
588 WHMCS.http.jqClient.post(jQuery(this).data('uri'),
589 {
590 'token': csrfToken
591 });
592 jQuery('.verification-banner.user-validation').hide();
593 });
594
595 var ssoDropdown = jQuery('#servicesPanel').find('.list-group');
596 if (parseInt(ssoDropdown.css('height'), 10) < parseInt(ssoDropdown.css('max-height'), 10)) {
597 ssoDropdown.css('overflow', 'unset');
598 }
599
600
601 /**
602 * Parse the content to populate the markdown editor footer.
603 *
604 * @param {string} content
605 * @param {bool} auto_save
606 * @param {string} [saveText]
607 * @returns {string}
608 */
609 function parseMdeFooter(content, auto_save, saveText)
610 {
611 saveText = saveText || saving;
612 var pattern = /[^\s]+/g,
613 m = [],
614 word_count = 0,
615 line_count = 0;
616 if (content) {
617 m = content.match(pattern);
618 line_count = content.split(/\\r\\n|\\r|\\n/).length;
619 }
620 if (m) {
621 for (var i = 0; i < m.length; i++) {
622 if (m[i].charCodeAt(0) >= 0x4E00) {
623 word_count += m[i].length;
624 } else {
625 word_count += 1;
626 }
627 }
628 }
629 return '<div class="small-font">lines: ' + line_count
630 + '&nbsp;&nbsp;&nbsp;words: ' + word_count + ''
631 + (auto_save ? '&nbsp;&nbsp;&nbsp;<span class="markdown-save">' + saveText + '</span>' : '')
632 + '</div>';
633 }
634
635 /**
636 * Countdown the save timeout. When zero, the span will update to show saved.
637 */
638 function doCountdown()
639 {
640 if (counter >= 0) {
641 if (counter === 0) {
642 jQuery("span.markdown-save").html(saved);
643 }
644 counter--;
645 setTimeout(doCountdown, 1000);
646 }
647 }
648
649 // Two-Factor Activation Process Modal Handler.
650 var frmTwoFactorActivation = jQuery('input[name=2fasetup]').parent('form');
651 frmTwoFactorActivation.submit(function(e) {
652 e.preventDefault();
653 openModal(frmTwoFactorActivation.attr('action'), frmTwoFactorActivation.serialize(), 'Loading...');
654 });
655
656 $.fn.setInputError = function(error) {
657 this.closest('.form-group').addClass('has-error').find('.field-error-msg').text(error);
658 return this;
659 };
660
661 jQuery.fn.showInputError = function () {
662 this.closest('.form-group').addClass('has-error').find('.field-error-msg').show();
663 return this;
664 };
665
666 jQuery('#frmPayment').on('submit', function() {
667 var btn = jQuery('#btnSubmit');
668 btn.find('span').toggle();
669 btn.prop('disabled', true).addClass('disabled');
670 });
671
672 // SSL Manage Action Button.
673 jQuery('.btn-resend-approver-email').click(function () {
674 WHMCS.http.jqClient.post(
675 jQuery(this).data('url'),
676 {
677 addonId: jQuery(this).data('addonid'),
678 serviceId: jQuery(this).data('serviceid'),
679 },
680 function(data) {
681 if (data.success === true) {
682 jQuery('.alert-table-ssl-manage').addClass('alert-success').text('Approver Email Resent').show();
683 } else {
684 jQuery('.alert-table-ssl-manage').addClass('alert-danger').text('Error: ' + data.message).show();
685 }
686 }
687 );
688 });
689
690 // Domain Pricing Table Filters
691 jQuery(".tld-filters a").click(function(e) {
692 e.preventDefault();
693
694 var noTlds = jQuery('.tld-row.no-tlds');
695
696 if (jQuery(this).hasClass('badge-success')) {
697 jQuery(this).removeClass('badge-success');
698 } else {
699 jQuery(this).addClass('badge-success');
700 }
701 if (noTlds.is(':visible')) {
702 noTlds.hide();
703 }
704
705 jQuery('.tld-row').removeClass('filtered-row');
706 jQuery('.tld-filters a.badge-success').each(function(index) {
707 var filterValue = jQuery(this).data('category');
708 jQuery('.tld-row[data-category*="' + filterValue + '"]').addClass('filtered-row');
709 });
710 jQuery(".filtered-row:even").removeClass('highlighted');
711 jQuery(".filtered-row:odd").addClass('highlighted');
712
713 var rowsToHide = jQuery('.tld-row:not(".filtered-row")');
714 rowsToHide.fadeOut('fast');
715 rowsToHide.promise().done(function () {
716 if (jQuery('.filtered-row').length === 0) {
717 noTlds.show();
718 } else {
719 jQuery('.tld-row.filtered-row').show();
720 }
721 });
722 });
723 jQuery(".filtered-row:even").removeClass('highlighted');
724 jQuery(".filtered-row:odd").addClass('highlighted');
725
726 // DataTable data-driven auto object registration
727 WHMCS.ui.dataTable.register();
728
729 WHMCS.ui.jsonForm.initAll();
730
731 jQuery(document).on('click', '#btnTicketAttachmentsAdd', function() {
732 jQuery('#fileUploadsContainer').append(jQuery('.file-upload').html());
733 });
734 jQuery(document).on('change', '.custom-file-input', function() {
735 var fileName = jQuery(this).val().split('\\').pop();
736 jQuery(this).siblings('.custom-file-label').text(fileName);
737 });
738 jQuery('#frmReply').submit(function(e) {
739 jQuery('#frmReply').find('input[type="submit"]').addClass('disabled').prop('disabled', true);
740 });
741
742 jQuery('#frmDomainContactModification').on('submit', function(){
743 if (!allowSubmit) {
744 var changed = false;
745 jQuery('.irtp-field').each(function() {
746 var value = jQuery(this).val(),
747 originalValue = jQuery(this).data('original-value');
748 if (value !== originalValue) {
749 changed = true;
750 }
751 });
752 if (changed) {
753 jQuery('#modalIRTPConfirmation').modal('show');
754 return false;
755 }
756 }
757 return true;
758 });
759
760 jQuery('.ssl-state.ssl-sync').each(function () {
761 var self = jQuery(this),
762 type = getSslAttribute(self, 'type'),
763 domain = getSslAttribute(self, 'domain');
764 WHMCS.http.jqClient.post(
765 WHMCS.utils.getRouteUrl('/domain/ssl-check'),
766 {
767 'type': type,
768 'domain': domain,
769 'token': csrfToken
770 },
771 function (data) {
772 if (data.invalid) {
773 self.hide();
774 } else {
775 var width = '',
776 statusDisplayLabel = '';
777 if (self.attr('width')) {
778 width = ' width="' + self.attr('width') + '"';
779 }
780 if (self.data('showlabel')) {
781 statusDisplayLabel = ' ' + data.statusDisplayLabel;
782 }
783 self.replaceWith(
784 '<img src="' + data.image + '" data-toggle="tooltip" alt="' + data.tooltip + '" title="' + data.tooltip + '" class="' + data.class + '"' + width + '>'
785 );
786 if (data.ssl.status === 'active') {
787 jQuery('#ssl-startdate').text(data.ssl.startDate);
788 jQuery('#ssl-expirydate').text(data.ssl.expiryDate);
789 jQuery('#ssl-issuer').text(data.ssl.issuer);
790 } else {
791 jQuery('#ssl-startdate').parent('div').hide();
792 jQuery('#ssl-expirydate').parent('div').hide();
793 jQuery('#ssl-issuer').parent('div').hide();
794 }
795
796 jQuery('#statusDisplayLabel').text(statusDisplayLabel);
797 }
798 }
799 );
800 });
801
802 jQuery(document).on('click', '.ssl-state.ssl-inactive', function(e) {
803 e.preventDefault();
804 window.location.href = WHMCS.utils.getRouteUrl('/ssl-purchase');
805 });
806
807 WHMCS.recaptcha.register();
808
809 var dynamicRecaptchaContainer = jQuery('#divDynamicRecaptcha');
810 var homepageHasRecaptcha = jQuery(dynamicRecaptchaContainer).length > 0;
811 var homepageHasInvisibleRecaptcha = homepageHasRecaptcha && jQuery(dynamicRecaptchaContainer).data('size') === 'invisible';
812
813 var frmDomainHomepage = jQuery('#frmDomainHomepage');
814
815 jQuery(frmDomainHomepage).find('button[data-domain-action="transfer"]').click(function () {
816 jQuery(frmDomainHomepage).find('input[name="transfer"]').val('1');
817 });
818
819 if (homepageHasRecaptcha && !homepageHasInvisibleRecaptcha) {
820 jQuery('section#home-banner').addClass('with-recaptcha');
821 }
822
823 if (jQuery('.domainchecker-homepage-captcha').length && !homepageHasInvisibleRecaptcha) {
824 // invisible reCaptcha doesn't play well with onsubmit() handlers on all submissions following a prevented one
825
826 jQuery(frmDomainHomepage).submit(function (e) {
827 var inputDomain = jQuery(frmDomainHomepage).find('input[name="domain"]'),
828 reCaptchaContainer = jQuery('#divDynamicRecaptcha'),
829 reCaptcha = jQuery('#g-recaptcha-response'),
830 captcha = jQuery('#inputCaptcha');
831
832 if (reCaptcha.length && !reCaptcha.val()) {
833 reCaptchaContainer.tooltip('show');
834
835 e.preventDefault();
836 return;
837 }
838
839 if (captcha.length && !captcha.val()) {
840 captcha.tooltip('show');
841
842 e.preventDefault();
843 }
844 });
845 }
846
847 $('.icheck-button').iCheck({
848 inheritID: true,
849 checkboxClass: 'icheckbox_square-blue',
850 radioClass: 'iradio_square-blue',
851 increaseArea: '20%'
852 });
853
854 jQuery('#inputNoStore').on('switchChange.bootstrapSwitch', function(event, state) {
855 var descContainer = jQuery('#inputDescription');
856 if (!state) {
857 descContainer.prop('disabled', true).addClass('disabled');
858 }
859 if (state) {
860 descContainer.removeClass('disabled').prop('disabled', false);
861 }
862 });
863
864 jQuery(document).on('click', '#btnConfirmModalConfirmBtn', function () {
865 var confirmButton = jQuery(this),
866 confirmationModal = confirmButton.closest('div.modal'),
867 targetUrl = confirmButton.data('target-url'),
868 dataTable = confirmButton.closest('table.dataTable[data-on-draw-rebind-confirmation-modal="true"]');
869 WHMCS.http.jqClient.jsonPost(
870 {
871 url: targetUrl,
872 data: {
873 token: csrfToken
874 },
875 success: function(data) {
876 if (data.status === 'success' || data.status === 'okay') {
877 if (dataTable.length > 0) {
878 dataTable.DataTable().ajax.reload();
879 }
880 }
881 }
882 }
883 );
884 confirmationModal.modal('toggle');
885 });
886 hideOverlay();
887
888 jQuery('input[name="approval_method"]').on('ifChecked', function(event) {
889 var fileMethod = $('#containerApprovalMethodFile'),
890 emailMethod = $('#containerApprovalMethodEmail'),
891 dnsMethod = $('#containerApprovalMethodDns');
892 if (jQuery(this).attr('value') == 'file') {
893 fileMethod.show();
894 dnsMethod.hide();
895 emailMethod.hide();
896 } else if (jQuery(this).attr('value') == 'dns-txt-token') {
897 dnsMethod.show();
898 fileMethod.hide();
899 emailMethod.hide();
900 } else {
901 fileMethod.hide();
902 dnsMethod.hide();
903 emailMethod.show();
904 }
905 });
906
907 (function () {
908 jQuery('.div-service-status').css(
909 'width',
910 (jQuery('.div-service-status .label-placeholder').outerWidth() + 5)
911 );
912 jQuery('div[menuitemname="Active Products/Services"] .list-group-item:visible')
913 .last()
914 .css('border-bottom', '1px solid #ddd');
915 }());
916 jQuery('div[menuitemname="Active Products/Services"] .btn-view-more').on('click', function(event) {
917 var hiddenItems = jQuery('div[menuitemname="Active Products/Services"] .list-group-item:hidden');
918 var itemAmount = 8;
919 event.preventDefault();
920 hiddenItems.slice(0,itemAmount).css('display', 'block');
921 if ((hiddenItems.length - itemAmount) <= 0) {
922 jQuery(event.target).addClass('disabled').attr("aria-disabled", true);
923 }
924 jQuery('div[menuitemname="Active Products/Services"] .list-group-item:visible')
925 .css('border-bottom', '')
926 .last()
927 .css('border-bottom', '1px solid #ddd');
928 })
929 jQuery('div[menuitemname="Service Details Actions"] a[data-identifier][data-serviceid][data-active="1"]').on('click', function(event) {
930 return customActionAjaxCall(event, jQuery(event.target))
931 });
932 jQuery('.div-service-item').on('click', function (event) {
933 var element = jQuery(event.target);
934 if (element.is('.dropdown-toggle, .dropdown-menu')) {
935 return true;
936 }
937 if (element.hasClass('btn-custom-action')) {
938 return customActionAjaxCall(event, element);
939 }
940 window.location.href = element.closest('.div-service-item').data('href');
941 return false;
942 });
943});
944
945/**
946 * Control disabled/enabled state of elements by class name.
947 *
948 * @param {string} className Common element class name.
949 * @param {bool} disabledState Whether the elements should be disabled or not.
950 */
951function disableFields(className, disabledState) {
952 if (className[0] !== '.') {
953 className = '.' + className;
954 }
955 var elements = jQuery(className);
956 elements.prop('disabled', disabledState);
957 if (disabledState) {
958 elements.addClass('disabled');
959 } else {
960 elements.removeClass('disabled');
961 }
962}
963
964/**
965 * Check all checkboxes with a given class.
966 *
967 * @param {string} className Common class name.
968 * @param {Element} masterControl Parent checkbox to which the other checkboxes should mirror.
969 */
970function checkAll(className, masterControl) {
971 if (className[0] !== '.') {
972 className = '.' + className;
973 }
974 // In jQuery, if you set the checked attribute directly, the dom
975 // element is changed, but browsers don't show the check box as
976 // checked. Using the click event will properly display.
977 jQuery(className).removeAttr('checked');
978 if(jQuery(masterControl).is(":checked")) {
979 jQuery(className).click();
980 }
981}
982
983/**
984 * Redirect on click if an element is not a button or link.
985 *
986 * Where table rows are clickable, we only want to redirect if the row
987 * itself is clicked. If a button or link within the row is clicked,
988 * the event tied to that object should be executed. This function
989 * stops the standard JS event bubbling required to make that happen.
990 *
991 * @param {object} clickEvent jQuery click event
992 * @param {string} target Redirect location
993 * @param {bool} newWindow Open link in new window
994 */
995function clickableSafeRedirect(clickEvent, target, newWindow) {
996 var eventSource = clickEvent.target.tagName.toLowerCase();
997 var eventParent = clickEvent.target.parentNode.tagName.toLowerCase();
998 var eventTable = clickEvent.target.parentNode.parentNode.parentNode;
999 if (jQuery(eventTable).hasClass('collapsed')) {
1000 // This is a mobile device sized display, and datatables has triggered folding
1001 return false;
1002 }
1003 if (eventSource === 'i' && jQuery(clickEvent.target).hasClass('ssl-required')) {
1004 return false;
1005 }
1006 if(eventSource !== 'button' && eventSource !== 'a') {
1007 if(eventParent !== 'button' && eventParent !== 'a') {
1008 if (newWindow) {
1009 window.open(target);
1010 } else {
1011 window.location.href = target;
1012 }
1013 }
1014 }
1015}
1016
1017/**
1018 * Open a centered popup window.
1019 *
1020 * @param {string} addr The URL to navigate to
1021 * @param {string} popname The name to assign the window
1022 * @param {number} w The width
1023 * @param {number} h The height
1024 * @param {string} features Any additional settings to apply
1025 */
1026function popupWindow(addr, popname, w, h, features) {
1027 var winl = (screen.width-w) / 2,
1028 wint = (screen.height-h) / 2,
1029 win;
1030 if (winl < 0) {
1031 winl = 0;
1032 }
1033 if (wint < 0) {
1034 wint = 0;
1035 }
1036 var settings = 'height=' + h + ',';
1037 settings += 'width=' + w + ',';
1038 settings += 'top=' + wint + ',';
1039 settings += 'left=' + winl + ',';
1040 settings += features;
1041 win = window.open(addr, popname, settings);
1042 win.window.focus();
1043}
1044
1045/**
1046 * Navigate to a page on dropdown change.
1047 *
1048 * This is implemented onblur() for a dropdown. When the dropdown
1049 * changes state, the value is pulled and the browser navigated to
1050 * the selected page.
1051 *
1052 * @param {Element} select The dropdown triggering the event
1053 */
1054function selectChangeNavigate(select) {
1055 window.location.href = $(select).val();
1056}
1057
1058/**
1059 * Fetch load and uptime for a given server.
1060 *
1061 * @param {number} num Server Id
1062 */
1063function getStats(num) {
1064 WHMCS.http.jqClient.post('serverstatus.php', 'getstats=1&num=' + num, function(data) {
1065 jQuery("#load"+num).html(data.load);
1066 jQuery("#uptime"+num).html(data.uptime);
1067 },'json');
1068}
1069
1070/**
1071 * Determine status of a given port for a given server.
1072 *
1073 * @param {number} num Server Id
1074 * @param {number} port Port Number
1075 */
1076function checkPort(num, port) {
1077 WHMCS.http.jqClient.post('serverstatus.php', 'ping=1&num=' + num + '&port=' + port, function(data) {
1078 jQuery("#port" + port + "_" + num).html(data);
1079 });
1080}
1081
1082/**
1083 * Fetch automated knowledgebase suggestions for ticket content.
1084 */
1085var currentcheckcontent,
1086 lastcheckcontent;
1087function getticketsuggestions() {
1088 currentcheckcontent = jQuery("#message").val();
1089 if (currentcheckcontent !== lastcheckcontent && currentcheckcontent !== "") {
1090 WHMCS.http.jqClient.post("submitticket.php", { action: "getkbarticles", text: currentcheckcontent },
1091 function(data){
1092 if (data) {
1093 jQuery("#searchresults").html(data).slideDown();
1094 }
1095 });
1096 lastcheckcontent = currentcheckcontent;
1097 }
1098 setTimeout('getticketsuggestions();', 3000);
1099}
1100
1101/**
1102 * Update custom fields upon department change.
1103 *
1104 * @param {Element} input The department selector dropdown object
1105 */
1106function refreshCustomFields(input) {
1107 jQuery("#customFieldsContainer").load(
1108 "submitticket.php",
1109 { action: "getcustomfields", deptid: $(input).val() }
1110 );
1111}
1112
1113/**
1114 * Submit the first form that exists within a given container.
1115 *
1116 * @param {string} containerId The ID name of the container
1117 */
1118function autoSubmitFormByContainer(containerId) {
1119 if (typeof noAutoSubmit === "undefined" || noAutoSubmit === false) {
1120 jQuery("#" + containerId).find("form:first").submit();
1121 }
1122}
1123
1124/**
1125 * Submit default whois info and disable custom fields.
1126 *
1127 * @param {string} regType The contact registration type
1128 */
1129function useDefaultWhois(regType) {
1130 jQuery("." + regType.substr(0, regType.length - 1) + "customwhois").attr("disabled", true);
1131 jQuery("." + regType.substr(0, regType.length - 1) + "defaultwhois").attr("disabled", false);
1132 jQuery('#' + regType.substr(0, regType.length - 1) + '1').attr("checked", "checked");
1133}
1134
1135/**
1136 * Submit custom fields and disable default whois info.
1137 *
1138 * @param {string} regType The contact registration type
1139 */
1140function useCustomWhois(regType) {
1141 jQuery("." + regType.substr(0, regType.length - 1) + "customwhois").attr("disabled", false);
1142 jQuery("." + regType.substr(0, regType.length - 1) + "defaultwhois").attr("disabled", true);
1143 jQuery('#' + regType.substr(0, regType.length - 1) + '2').attr("checked", "checked");
1144}
1145
1146function showNewBillingAddressFields() {
1147 jQuery('#newBillingAddress').parent('div').slideDown();
1148}
1149
1150function hideNewBillingAddressFields() {
1151 jQuery('#newBillingAddress').parent('div').slideUp();
1152}
1153
1154/**
1155 * Show new credit card input fields.
1156 */
1157function showNewCardInputFields() {
1158 var ccDetails = jQuery('.cc-details'),
1159 ccNumber = jQuery('#inputCardNumber'),
1160 billAddress = jQuery('#billingAddressChoice'),
1161 container;
1162
1163 container = ccDetails.parent('div');
1164 if (container.not(':visible')) {
1165 container.show();
1166 }
1167 jQuery('.cc-details').slideDown();
1168 ccNumber.focus();
1169
1170 container = billAddress.parent('div');
1171 if (container.not(':visible')) {
1172 container.show();
1173 }
1174 billAddress.slideDown()
1175 .find('input[name="billingcontact"]')
1176 .first()
1177 .iCheck('check');
1178}
1179
1180/**
1181 * Show new bank account input fields.
1182 */
1183function showNewAccountInputFields() {
1184 var bankDetails = jQuery('.bank-details').parent('div');
1185 if (bankDetails.not(':visible')) {
1186 bankDetails.slideDown();
1187 }
1188
1189 jQuery("#billingAddressChoice")
1190 .parent('div')
1191 .slideDown()
1192 .find('input[name="billingcontact"]')
1193 .first()
1194 .iCheck('check');
1195}
1196
1197/**
1198 * Hide new credit card input fields.
1199 */
1200function hideNewCardInputFields() {
1201 hideNewBillingAddressFields();
1202 jQuery(".cc-details").slideUp();
1203 jQuery("#billingAddressChoice").slideUp();
1204 var contactId = jQuery('input[name="ccinfo"]:checked').data('billing-contact-id');
1205 if (contactId != undefined) {
1206 jQuery('#billingAddressChoice label.billing-contact-' + contactId)
1207 .iCheck('check');
1208 }
1209 jQuery('#inputCardCvv').focus();
1210}
1211
1212/**
1213 * Hide new bank account input fields.
1214 */
1215function hideNewAccountInputFields() {
1216 hideNewBillingAddressFields();
1217
1218 jQuery(".bank-details").parent('div').slideUp();
1219 jQuery("#billingAddressChoice").parent('div').slideUp();
1220
1221 var selectedAccount = jQuery('input[name="paymethod"]:checked'),
1222 selectedContactId = jQuery(selectedAccount).data('billing-contact-id'),
1223 selectedContactData = jQuery('.billing-contact-info[data-billing-contact-id="' + selectedContactId + '"]');
1224
1225 if (selectedContactData.length) {
1226 jQuery('.billing-contact-info').hide();
1227 jQuery(selectedContactData).show();
1228 }
1229}
1230
1231/**
1232 * Get automatic knowledgebase suggestions for support ticket message.
1233 */
1234var lastTicketMsg;
1235function getTicketSuggestions() {
1236 var userMsg = jQuery("#inputMessage").val();
1237 if (userMsg !== lastTicketMsg && userMsg !== '') {
1238 WHMCS.http.jqClient.post("submitticket.php", { action: "getkbarticles", text: userMsg },
1239 function (data) {
1240 var suggestions = jQuery("#autoAnswerSuggestions");
1241 if (data) {
1242 suggestions.html(data);
1243 if (suggestions.not(":visible")) {
1244 suggestions.slideDown();
1245 }
1246 }
1247 });
1248 lastTicketMsg = userMsg;
1249 }
1250 setTimeout('getTicketSuggestions()', 3000);
1251}
1252
1253/**
1254 * Smooth scroll to named element.
1255 */
1256function smoothScroll(element) {
1257 $('html, body').animate({
1258 scrollTop: $(element).offset().top
1259 }, 500);
1260}
1261var allowSubmit = false;
1262function irtpSubmit() {
1263 allowSubmit = true;
1264 var optOut = 0,
1265 optOutCheckbox = jQuery('#modalIrtpOptOut'),
1266 optOutReason = jQuery('#modalReason'),
1267 formOptOut = jQuery('#irtpOptOut'),
1268 formOptOutReason = jQuery('#irtpOptOutReason');
1269
1270 if (optOutCheckbox.is(':checked')) {
1271 optOut = 1;
1272 }
1273 formOptOut.val(optOut);
1274 formOptOutReason.val(optOutReason.val());
1275 jQuery('#frmDomainContactModification').submit();
1276}
1277
1278function showOverlay(msg) {
1279 jQuery('#fullpage-overlay .msg').html(msg);
1280 jQuery('#fullpage-overlay').show();
1281}
1282
1283function hideOverlay() {
1284 jQuery('#fullpage-overlay').hide();
1285}
1286
1287function getSslAttribute(element, attribute) {
1288 if (element.data(attribute)) {
1289 return element.data(attribute);
1290 }
1291 return element.parent('td').data(attribute);
1292}
1293
1294function removeRetweets() {
1295 jQuery('#twitter-widget-0')
1296 .contents()
1297 .find('.timeline-Tweet--isRetweet')
1298 .parent('li')
1299 .remove();
1300}
1301function addTwitterWidgetObserverWhenNodeAvailable() {
1302 if (elementsWaitTimeout) {
1303 clearTimeout(elementsWaitTimeout);
1304 }
1305
1306 var targetTwitterWidget = document.getElementById('twitter-widget-0');
1307 if (!targetTwitterWidget) {
1308 elementsWaitTimeout = window.setTimeout(addTwitterWidgetObserverWhenNodeAvailable, 500);
1309 return;
1310 }
1311
1312 var targetTimelineTweets = targetTwitterWidget
1313 .contentWindow
1314 .document
1315 .getElementsByClassName('timeline-TweetList')[0];
1316 if (!targetTimelineTweets) {
1317 elementsWaitTimeout = window.setTimeout(addTwitterWidgetObserverWhenNodeAvailable, 500);
1318 return;
1319 }
1320
1321 jQuery('#twitter-widget-0')
1322 .contents()
1323 .find('head')
1324 .append("<style>.timeline-Tweet-text { font-size: 18px !important; line-height: 25px !important; margin-bottom: 0px !important; }</style>");
1325 removeRetweets();
1326 observerTwitterWidget.observe(targetTimelineTweets, observerConfig);
1327}
1328
1329function openValidationSubmitModal(caller)
1330{
1331 var validationSubmitModal = jQuery('#validationSubmitModal');
1332 validationSubmitModal.find('.modal-body iframe').attr('src', caller.dataset.url);
1333 validationSubmitModal.modal('show');
1334}
1335
1336function completeValidationComClientWorkflow()
1337{
1338 var submitDocsRequestBanner = jQuery('.user-validation'),
1339 secondarySidebarStatus = jQuery('.validation-status-label'),
1340 submitDiv = jQuery('.validation-submit-div'),
1341 redirectUser = true;
1342
1343 $('#validationSubmitModal').modal('hide');
1344 if (submitDocsRequestBanner.length !== 0) {
1345 submitDocsRequestBanner.slideUp();
1346 redirectUser = false;
1347 }
1348 if (secondarySidebarStatus.length !== 0) {
1349 var submitString = submitDiv.find('a').data('submitted-string');
1350 secondarySidebarStatus.text(submitString).removeClass('label-default').addClass('label-warning');
1351 submitDiv.hide();
1352 redirectUser = false;
1353 }
1354
1355 if (redirectUser) {
1356 window.location.href = WHMCS.utils.autoDetermineBaseUrl();
1357 }
1358 return false;
1359}
1360
1361var autoCollapse = function (menu, maxHeight) {
1362
1363 var continueLoop = true,
1364 nav = jQuery(menu),
1365 navHeight = nav.innerHeight();
1366 if (navHeight >= maxHeight) {
1367
1368 jQuery(menu + ' .collapsable-dropdown').removeClass('d-none');
1369 jQuery(".navbar-nav").removeClass('w-auto').addClass("w-100");
1370
1371 while (navHeight > maxHeight && continueLoop) {
1372 // add child to dropdown
1373 var children = nav.children(menu + ' li:not(:last-child):not(".no-collapse")'),
1374 count = children.length;
1375 if (!count) {
1376 continueLoop = false;
1377 } else {
1378 children.data('original-classes', children.attr('class'));
1379 var child = jQuery(children[count - 1]);
1380 child.removeClass().addClass('dropdown-item');
1381 child.prependTo(menu + ' .collapsable-dropdown-menu');
1382 }
1383 navHeight = nav.innerHeight();
1384 }
1385 jQuery(".navbar-nav").addClass("w-auto").removeClass('w-100');
1386
1387 } else {
1388
1389 var collapsed = jQuery(menu + ' .collapsable-dropdown-menu').children(menu + ' li');
1390
1391 if (collapsed.length === 0) {
1392 jQuery(menu + ' .collapsable-dropdown').addClass('d-none');
1393 }
1394
1395 while (navHeight < maxHeight && (nav.children(menu + ' li').length > 0) && collapsed.length > 0) {
1396 // remove child from dropdown
1397 collapsed = jQuery(menu + ' .collapsable-dropdown-menu').children('li');
1398 var child = jQuery(collapsed[0]);
1399 child.removeClass().addClass(child.data('original-classes'));
1400 child.insertBefore(nav.children(menu + ' li:last-child'));
1401 navHeight = nav.innerHeight();
1402 }
1403
1404 if (navHeight > maxHeight) {
1405 autoCollapse(menu, maxHeight);
1406 }
1407 }
1408}
1409
1410/**
1411 * Perform the AjaxCall for a CustomAction.
1412 *
1413 * @param event
1414 * @param element
1415 * @returns {boolean}
1416 */
1417function customActionAjaxCall(event, element) {
1418 event.stopPropagation();
1419 if (!element.data('active')) {
1420 return false;
1421 }
1422 element.attr('disabled', 'disabled').addClass('disabled');
1423 jQuery('.loading', element).show();
1424 WHMCS.http.jqClient.jsonPost({
1425 url: WHMCS.utils.getRouteUrl(
1426 '/clientarea/service/' + element.data('serviceid') + '/custom-action/' + element.data('identifier')
1427 ),
1428 data: {
1429 'token': csrfToken
1430 },
1431 success: function(data) {
1432 if (data.success) {
1433 window.open(data.redirectTo);
1434 } else {
1435 window.open('clientarea.php?action=productdetails&id=' + element.data('serviceid') + '&customaction_error=1');
1436 }
1437 },
1438 fail: function () {
1439 window.open('clientarea.php?action=productdetails&id=' + element.data('serviceid') + '&customaction_ajax_error=1');
1440 },
1441 always: function() {
1442 jQuery('.loading', element).hide();
1443 element.removeAttr('disabled').removeClass('disabled');
1444 if (element.hasClass('dropdown-item')) {
1445 element.closest('.dropdown-menu').removeClass('show');
1446 }
1447 },
1448 });
1449 return true;
1450}
1451