1/** 2 * WHMCS core JS library reference 3 * 4 * @copyright Copyright (c) WHMCS Limited 2005-2017 5 * @license http://www.whmcs.com/license/ WHMCS Eula 6 */ 7 8 9(function (window, factory) { 10 if (typeof window.WHMCS !== 'object') { 11 window.WHMCS = factory; 12 } 13}( 14 window, 15 { 16 hasModule: function (name) { 17 return (typeof WHMCS[name] !== 'undefined' 18 && Object.getOwnPropertyNames(WHMCS[name]).length > 0); 19 }, 20 loadModule: function (name, module) { 21 if (this.hasModule(name)) { 22 return; 23 } 24 25 WHMCS[name] = {}; 26 if (typeof module === 'function') { 27 (module).apply(WHMCS[name]); 28 } else { 29 for (var key in module) { 30 if (module.hasOwnProperty(key)) { 31 WHMCS[name][key] = {}; 32 (module[key]).apply(WHMCS[name][key]); 33 } 34 } 35 } 36 } 37 } 38)); 39jQuery(document).ready(function() { 40 jQuery(document).on('click', '.disable-on-click', function () { 41 jQuery(this).addClass('disabled'); 42 43 if (jQuery(this).hasClass('spinner-on-click')) { 44 var icon = $(this).find('i.fas,i.far,i.fal,i.fab'); 45 46 jQuery(icon) 47 .removeAttr('class') 48 .addClass('fas fa-spinner fa-spin'); 49 } 50 }); 51}); 52/** 53 * WHMCS authentication module 54 * 55 * @copyright Copyright (c) WHMCS Limited 2005-2017 56 * @license http://www.whmcs.com/license/ WHMCS Eula 57 */ 58 59(function(module) { 60 if (!WHMCS.hasModule('authn')) { 61 WHMCS.loadModule('authn', module); 62 } 63})({ 64provider: function () { 65 var callbackFired = false; 66 67 /** 68 * @return {jQuery} 69 */ 70 this.feedbackContainer = function () { 71 return jQuery(".providerLinkingFeedback"); 72 }; 73 74 /** 75 * @returns {jQuery} 76 */ 77 this.btnContainer = function () { 78 return jQuery(".providerPreLinking"); 79 }; 80 81 this.feedbackMessage = function (context) { 82 if (typeof context === 'undefined') { 83 context = 'complete_sign_in'; 84 } 85 var msgContainer = jQuery('p.providerLinkingMsg-preLink-' + context); 86 if (msgContainer.length) { 87 return msgContainer.first().html(); 88 } 89 90 return ''; 91 }; 92 93 this.showProgressMessage = function(callback) { 94 this.feedbackContainer().fadeIn('fast', function () { 95 if (typeof callback === 'function' && !callbackFired) { 96 callbackFired = true; 97 callback(); 98 } 99 }); 100 }; 101 102 this.preLinkInit = function (callback) { 103 var icon = '<i class="fas fa-fw fa-spinner fa-spin"></i> '; 104 105 this.feedbackContainer() 106 .removeClass('alert-danger alert-success') 107 .addClass('alert alert-info') 108 .html(icon + this.feedbackMessage()) 109 .hide(); 110 111 var btnContainer = this.btnContainer(); 112 if (btnContainer.length) { 113 if (btnContainer.data('hideOnPrelink')) { 114 var self = this; 115 btnContainer.fadeOut('false', function () 116 { 117 self.showProgressMessage(callback) 118 }); 119 } else if (btnContainer.data('disableOnPrelink')) { 120 btnContainer.find('.btn').addClass('disabled'); 121 this.showProgressMessage(callback); 122 } else { 123 this.showProgressMessage(callback); 124 } 125 } else { 126 this.showProgressMessage(callback); 127 } 128 }; 129 130 this.displayError = function (provider, errorCondition, providerErrorText){ 131 jQuery('#providerLinkingMessages .provider-name').html(provider); 132 133 var feedbackMsg = this.feedbackMessage('connect_error'); 134 if (errorCondition) { 135 var errorMsg = this.feedbackMessage(errorCondition); 136 if (errorMsg) { 137 feedbackMsg = errorMsg 138 } 139 } 140 141 if (providerErrorText && jQuery('.btn-logged-in-admin').length > 0) { 142 feedbackMsg += ' Error: ' + providerErrorText; 143 } 144 145 this.feedbackContainer().removeClass('alert-info alert-success') 146 .addClass('alert alert-danger') 147 .html(feedbackMsg).slideDown(); 148 }; 149 150 this.displaySuccess = function (data, context, provider) { 151 var icon = provider.icon; 152 var htmlTarget = context.htmlTarget;
153 var targetLogin = context.targetLogin; 154 var targetRegister = context.targetRegister; 155 var displayName = provider.name; 156 var feedbackMsg = ''; 157 158 switch (data.result) { 159 case "logged_in": 160 case "2fa_needed": 161 feedbackMsg = this.feedbackMessage('2fa_needed'); 162 this.feedbackContainer().removeClass('alert-danger alert-warning alert-success') 163 .addClass('alert alert-info') 164 .html(feedbackMsg); 165 window.location = data.redirect_url 166 ? decodeURIComponent(data.redirect_url) 167 : decodeURIComponent(context.redirectUrl); 168 169 break; 170 171 case "linking_complete": 172 var accountInfo = ''; 173 if (data.remote_account.email) { 174 accountInfo = data.remote_account.email; 175 } else { 176 accountInfo = data.remote_account.firstname + " " + data.remote_account.lastname; 177 } 178 179 accountInfo = accountInfo.trim(); 180 181 feedbackMsg = this.feedbackMessage('linking_complete').trim().replace(':displayName', displayName); 182 if (accountInfo) { 183 feedbackMsg = feedbackMsg.replace(/\.$/, ' (' + accountInfo + ').'); 184 } 185 186 this.feedbackContainer().removeClass('alert-danger alert-warning alert-info') 187 .addClass('alert alert-success') 188 .html(icon + feedbackMsg); 189 break; 190 191 case "login_to_link": 192 if (htmlTarget === targetLogin) { 193 feedbackMsg = this.feedbackMessage('login_to_link-signin-required'); 194 this.feedbackContainer().removeClass('alert-danger alert-success alert-info') 195 .addClass('alert alert-warning') 196 .html(icon + feedbackMsg); 197 } else { 198 var emailField = jQuery("input[name=email]"); 199 var firstNameField = jQuery("input[name=firstname]"); 200 var lastNameField = jQuery("input[name=lastname]"); 201 202 if (emailField.val() === "") { 203 emailField.val(data.remote_account.email); 204 } 205 206 if (firstNameField.val() === "") { 207 firstNameField.val(data.remote_account.firstname); 208 } 209 210 if (lastNameField.val() === "") { 211 lastNameField.val(data.remote_account.lastname); 212 } 213 214 if (htmlTarget === targetRegister) { 215 if (typeof WHMCS.client.registration === 'object') { 216 WHMCS.client.registration.prefillPassword(); 217 } 218 feedbackMsg = this.feedbackMessage('login_to_link-registration-required'); 219 this.feedbackContainer().fadeOut('slow', function () { 220 jQuery(this).removeClass('alert-danger alert-success alert-info') 221 .addClass('alert alert-warning') 222 .html(icon + feedbackMsg).fadeIn('fast'); 223 }); 224 225 } else { 226 // this is checkout 227 if (typeof WHMCS.client.registration === 'object') { 228 WHMCS.client.registration.prefillPassword(); 229 } 230 231 var self = this; 232 this.feedbackContainer().each(function (i, el) { 233 var container = jQuery(el); 234 var linkContext = container.siblings('div .providerPreLinking').data('linkContext'); 235 236 container.fadeOut('slow', function () { 237 if (linkContext === 'checkout-new') { 238 feedbackMsg = self.feedbackMessage('checkout-new'); 239 } else { 240 feedbackMsg = self.feedbackMessage('login_to_link-signin-required'); 241 } 242 container.removeClass('alert-danger alert-success alert-info') 243 .addClass('alert alert-warning') 244 .html(icon + feedbackMsg).fadeIn('fast'); 245 }); 246 }); 247 } 248 } 249 250 break; 251 252 case "other_user_exists": 253 feedbackMsg = this.feedbackMessage('other_user_exists'); 254 this.feedbackContainer().removeClass('alert-info alert-success') 255 .addClass('alert alert-danger') 256 .html(icon + feedbackMsg).slideDown(); 257 break; 258 259 case "already_linked": 260 feedbackMsg = this.feedbackMessage('already_linked'); 261 this.feedbackContainer().removeClass('alert-info alert-success') 262 .addClass('alert alert-danger') 263 .html(icon + feedbackMsg).slideDown(); 264 break; 265 266 default: 267 feedbackMsg = this.feedbackMessage('default'); 268 this.feedbackContainer().removeClass('alert-info alert-success') 269 .addClass('alert alert-danger') 270 .html(icon + feedbackMsg).slideDown(); 271 break; 272 } 273 }; 274 275 this.signIn = function (config, context, provider, providerDone, providerError) { 276 jQuery.ajax(config).done(function(data) { 277 providerDone(); 278 WHMCS.authn.provider.displaySuccess(data, context, provider); 279 var table = jQuery('#tableLinkedAccounts'); 280 if (table.length) { 281 WHMCS.ui.dataTable.getTableById('tableLinkedAccounts').ajax.reload(); 282 } 283 }).error(function() { 284 providerError(); 285 WHMCS.authn.provider.displayError(); 286 }); 287 }; 288 289 return this; 290}}); 291 292/** 293 * WHMCS client module 294 * 295 * @copyright Copyright (c) WHMCS Limited 2005-2017 296 * @license http://www.whmcs.com/license/ WHMCS Eula 297 */ 298(function(module) { 299 if (!WHMCS.hasModule('client')) { 300 WHMCS.loadModule('client', module); 301 } 302})({ 303registration: function () { 304 this.prefillPassword = function (params) { 305 params = params || {}; 306 if (typeof params.hideContainer === 'undefined') { 307 var id = (jQuery('#inputSecurityQId').attr('id')) ? '#containerPassword' : '#containerNewUserSecurity'; 308 params.hideContainer = jQuery(id); 309 params.hideInputs = true; 310 } else if (typeof params.hideContainer === 'string' && params.hideContainer.length) { 311 params.hideContainer = jQuery(params.hideContainer); 312 } 313 314 if (typeof params.form === 'undefined') { 315 params.form = { 316 password: [ 317 {id: 'inputNewPassword1'}, 318 {id: 'inputNewPassword2'} 319 ] 320 }; 321 } 322 323 var prefillFunc = function () { 324 var $randomPasswd = WHMCS.utils.simpleRNG(); 325 for (var i = 0, len = params.form.password.length; i < len; i++) { 326 jQuery('#' + params.form.password[i].id) 327 .val($randomPasswd).trigger('keyup'); 328 } 329 }; 330 331 if (params.hideInputs) { 332 params.hideContainer.slideUp('fast', prefillFunc); 333 } else { 334 prefillFunc(); 335 } 336 }; 337 338 return this; 339}}); 340 341/** 342 * WHMCS HTTP module 343 * 344 * @copyright Copyright (c) WHMCS Limited 2005-2018 345 * @license http://www.whmcs.com/license/ WHMCS Eula 346 */ 347(function(module) { 348 if (!WHMCS.hasModule('http')) { 349 WHMCS.loadModule('http', module); 350 } 351})({ 352jqClient: function () { 353 _getSettings = function (url, data, success, dataType) 354 { 355 if (typeof url === 'object') { 356 /* 357 Settings may be the only argument 358 */ 359 return url; 360 } 361 362 if (typeof data === 'function') { 363 /* 364 If 'data' is omitted, 'success' will come in its place 365 */ 366 success = data; 367 data = null; 368 } 369 370 return { 371 url: url, 372 data: data, 373 success: success, 374 dataType: dataType 375 }; 376 }; 377 378 /** 379 * @param url 380 * @param data 381 * @param success 382 * @param dataType 383 * @returns {*} 384 */ 385 this.get = function (url, data, success, dataType) 386 { 387 return WHMCS.http.client.request( 388 jQuery.extend( 389 _getSettings(url, data, success, dataType), 390 { 391 type: 'GET' 392 } 393 ) 394 ); 395 }; 396 397 /** 398 * @param url 399 * @param data 400 * @param success 401 * @param dataType 402 * @returns {*} 403 */ 404 this.post = function (url, data, success, dataType) 405 { 406 return WHMCS.http.client.request( 407 jQuery.extend( 408 _getSettings(url, data, success, dataType), 409 { 410 type: 'POST' 411 } 412 ) 413 ); 414 }; 415 416 /** 417 * @param options 418 * @returns {*} 419 */ 420 this.jsonPost = function (options) { 421 options = options || {}; 422 this.post(options.url, options.data, function(response) { 423 if (response.warning) { 424 console.log('[WHMCS] Warning: ' + response.warning); 425 if (typeof options.warning === 'function') { 426 options.warning(response.warning); 427 } 428 } else if (response.error) { 429 console.log('[WHMCS] Error: ' + response.error); 430 if (typeof options.error === 'function') { 431 options.error(response.error); 432 } 433 } else { 434 if (typeof options.success === 'function') { 435 options.success(response); 436 } 437 } 438 }, 'json').error(function(xhr, errorMsg){ 439 console.log('[WHMCS] Error: ' + errorMsg); 440 if (typeof options.fail === 'function') { 441 options.fail(errorMsg); 442 } 443 }).always(function() { 444 if (typeof options.always === 'function') { 445 options.always(); 446 } 447 }); 448 }; 449 450 return this; 451}, 452 453client: function () { 454 var methods = ['get', 'post', 'put', 'delete']; 455 var client = this; 456 457 _beforeRequest = function (settings) 458 { 459 /* 460 Enforcing dataType was found to break many invocations expecting HTML back. 461 If/when those are refactored, this may be uncommented to enforce a safer 462 data transit. 463 */ 464 /*if (typeof settings.dataType === 'undefined') { 465 settings.dataType = 'json'; 466 }*/ 467 468 if (typeof settings.type === 'undefined') { 469 // default request type is GET 470 settings.type = 'GET'; 471 } 472 473 /* 474 Add other preprocessing here if required 475 */ 476 477 return settings; 478 }; 479 480 this.request = function (settings) 481 { 482 settings = _beforeRequest(settings || {}); 483 return jQuery.ajax(settings); 484 }; 485 486 /* 487 Create shortcut methods for methods[] array above 488 */ 489 jQuery.each(methods, function(index, method) { 490 client[method] = (function(method, client) { 491 return function (settings) 492 { 493 settings = settings || {}; 494 495 settings.type = method.toUpperCase(); 496 497 return client.request(settings); 498 } 499 })(method, client); 500 });
501 502 return this; 503} 504 505}); 506 507/** 508 * WHMCS UI module 509 * 510 * @copyright Copyright (c) WHMCS Limited 2005-2017 511 * @license http://www.whmcs.com/license/ WHMCS Eula 512 */ 513(function(module) { 514 if (!WHMCS.hasModule('ui')) { 515 WHMCS.loadModule('ui', module); 516 } 517})({ 518/** 519 * Confirmation PopUp 520 */ 521confirmation: function () { 522 523 /** 524 * @type {Array} Registered confirmation root selectors 525 */ 526 var toggles = []; 527 528 /** 529 * Register/Re-Register all confirmation elements with jQuery 530 * By default all elements of data toggle "confirmation" will be registered 531 * 532 * @param {(string|undefined)} rootSelector 533 * @return {Array} array of registered toggles 534 */ 535 this.register = function (rootSelector) { 536 if (typeof rootSelector === 'undefined') { 537 rootSelector = '[data-toggle=confirmation]'; 538 } 539 if (toggles.indexOf(rootSelector) < 0) { 540 toggles.push(rootSelector); 541 } 542 543 jQuery(rootSelector).confirmation({ 544 rootSelector: rootSelector 545 }); 546 547 return toggles; 548 }; 549 550 return this; 551}, 552 553/** 554 * Data Driven Table 555 */ 556dataTable: function () { 557 558 /** 559 * @type {{}} 560 */ 561 this.tables = {}; 562 563 /** 564 * Register all tables on page with the class "data-driven" 565 */ 566 this.register = function () { 567 var self = this; 568 jQuery('table.data-driven').each(function (i, table) { 569 self.getTableById(table.id, undefined); 570 }); 571 }; 572 573 /** 574 * Get a table by id; create table object on fly as necessary 575 * 576 * @param {string} id 577 * @param {({}|undefined)} options 578 * @returns {DataTable} 579 */ 580 this.getTableById = function (id, options) { 581 var self = this; 582 var el = jQuery('#' + id); 583 if (typeof self.tables[id] === 'undefined') { 584 if (typeof options === 'undefined') { 585 options = { 586 dom: '<"listtable"ift>pl', 587 paging: false, 588 lengthChange: false, 589 searching: false, 590 ordering: true, 591 info: false, 592 autoWidth: true, 593 language: { 594 emptyTable: (el.data('lang-empty-table')) ? el.data('lang-empty-table') : "No records found" 595 } 596 }; 597 } 598 var ajaxUrl = el.data('ajax-url'); 599 if (typeof ajaxUrl !== 'undefined') { 600 options.ajax = { 601 url: ajaxUrl 602 }; 603 } 604 var dom = el.data('dom'); 605 if (typeof dom !== 'undefined') { 606 options.dom = dom; 607 } 608 var searching = el.data('searching'); 609 if (typeof searching !== 'undefined') { 610 options.searching = searching; 611 } 612 var responsive = el.data('responsive'); 613 if (typeof responsive !== 'undefined') { 614 options.responsive = responsive; 615 } 616 var ordering = el.data('ordering'); 617 if (typeof ordering !== 'undefined') { 618 options["ordering"] = ordering; 619 } 620 var order = el.data('order'); 621 if (typeof order !== 'undefined' && order) { 622 options["order"] = order; 623 } 624 var colCss = el.data('columns'); 625 if (typeof colCss !== 'undefined' && colCss) { 626 options["columns"] = colCss; 627 } 628 var autoWidth = el.data('auto-width'); 629 if (typeof autoWidth !== 'undefined') { 630 options["autoWidth"] = autoWidth; 631 } 632 var paging = el.data('paging'); 633 if (typeof paging !== 'undefined') { 634 options["paging"] = paging; 635 } 636 var lengthChange = el.data('length-change'); 637 if (typeof lengthChange !== 'undefined') { 638 options["lengthChange"] = lengthChange; 639 } 640 var pageLength = el.data('page-length'); 641 if (typeof pageLength !== 'undefined') { 642 options["pageLength"] = pageLength; 643 } 644 645 self.tables[id] = self.initTable(el, options); 646 } else if (typeof options !== 'undefined') { 647 var oldTable = self.tables[id];
648 var initOpts = oldTable.init(); 649 var newOpts = jQuery.extend( initOpts, options); 650 oldTable.destroy(); 651 self.tables[id] = self.initTable(el, newOpts); 652 } 653 654 return self.tables[id]; 655 }; 656 657 this.initTable = function (el, options) { 658 var table = el.DataTable(options); 659 var self = this; 660 if (el.data('on-draw')) { 661 table.on('draw.dt', function (e, settings) { 662 var namedCallback = el.data('on-draw'); 663 if (typeof window[namedCallback] === 'function') { 664 window[namedCallback](e, settings); 665 } 666 }); 667 } else if (el.data('on-draw-rebind-confirmation')) { 668 table.on('draw.dt', function (e) { 669 self.rebindConfirmation(e); 670 }); 671 } 672 673 return table; 674 }; 675 676 this.rebindConfirmation = function (e) { 677 var self = this; 678 var tableId = e.target.id; 679 var toggles = WHMCS.ui.confirmation.register(); 680 for(var i = 0, len = toggles.length; i < len; i++ ) { 681 jQuery(toggles[i]).on( 682 'confirmed.bs.confirmation', 683 function (e) 684 { 685 e.preventDefault(); 686 WHMCS.http.jqClient.post( 687 jQuery(e.target).data('target-url'), 688 { 689 'token': csrfToken 690 } 691 ).done(function (data) 692 { 693 if (data.status === 'success' || data.status === 'okay') { 694 self.getTableById(tableId, undefined).ajax.reload(); 695 } 696 }); 697 698 } 699 ); 700 } 701 }; 702 703 return this; 704}, 705 706clipboard: function() { 707 this.copy = function(e) { 708 e.preventDefault(); 709 710 var trigger = $(e.currentTarget); 711 var contentElement = $(trigger).data('clipboard-target'); 712 var container = $(contentElement).parent(); 713 714 try { 715 var tempElement = $('<textarea>') 716 .css('position', 'fixed') 717 .css('opacity', '0') 718 .css('width', '1px') 719 .css('height', '1px') 720 .val($(contentElement).val()); 721 722 container.append(tempElement); 723 tempElement.focus().select(); 724 document.execCommand('copy'); 725 } finally { 726 tempElement.remove(); 727 } 728 729 trigger.tooltip({ 730 trigger: 'click', 731 placement: 'bottom' 732 }); 733 WHMCS.ui.toolTip.setTip(trigger, 'Copied!'); 734 WHMCS.ui.toolTip.hideTip(trigger); 735 }; 736 737 return this; 738}, 739 740/** 741 * ToolTip and Clipboard behaviors 742 */ 743toolTip: function () { 744 this.setTip = function (btn, message) { 745 var tip = btn.data('bs.tooltip'); 746 if (tip.hoverState !== 'in') { 747 tip.hoverState = 'in'; 748 } 749 btn.attr('data-original-title', message); 750 tip.show(); 751 752 return tip; 753 }; 754 755 this.hideTip = function (btn) { 756 return setTimeout(function() { 757 btn.data('bs.tooltip').hide() 758 }, 2000); 759 } 760}, 761 762jsonForm: function() { 763 this.managedElements = 'input,textarea,select'; 764 765 this.initFields = function (form) { 766 var self = this; 767 $(form).find(self.managedElements).each(function () { 768 var field = this; 769 770 $(field).on('keypress change', function () { 771 if (self.fieldHasError(field)) { 772 self.clearFieldError(field); 773 } 774 }); 775 }); 776 }; 777 778 this.init = function (form) { 779 var self = this; 780 781 self.initFields(form); 782 783 $(form).on('submit', function(e) { 784 e.preventDefault(); 785 e.stopPropagation(); 786 787 self.clearErrors(form); 788 789 var formModal = $(form).parents('.modal[role="dialog"]').first(); 790 791 if ($(formModal).length) { 792 $(formModal).on('show.bs.modal hidden.bs.modal', function() { 793 self.clearErrors(form); 794 }); 795 796 /* 797 * Make this optional if the form is used for editing 798 */ 799 $(formModal).on('show.bs.modal', function() { 800 $(form)[0].reset(); 801 }); 802 } 803 804 WHMCS.http.client.post({ 805 url: $(form).attr('action'), 806 data: $(form).serializeArray(), 807 }) 808 .done(function (response) { 809 self.onSuccess(form, response); 810 }) 811 .fail(function (jqXHR) { 812 self.onError(form, jqXHR); 813 }) 814 .always(function (data) { 815 self.onRequestComplete(form, data); 816 }); 817 }); 818 }; 819 820 this.initAll = function () { 821 var self = this; 822 823 $('form[data-role="json-form"]').each(function() { 824 var formElement = this; 825 self.init(formElement); 826 }); 827 }; 828 829 this.markFieldErrors = function (form, fields) 830 { 831 var self = this; 832 var errorMessage = null; 833 var field, fieldLookup; 834 835 for (var fieldName in fields) { 836 if (fields.hasOwnProperty(fieldName)) { 837 errorMessage = fields[fieldName]; 838 } 839 840 fieldLookup = self.managedElements.split(',').map(function(element) { 841 return element + '[name="' + fieldName + '"]'; 842 }).join(','); 843 844 field = $(form).find(fieldLookup); 845 846 if (errorMessage) { 847 $(field).parents('.form-group').addClass('has-error'); 848 $(field).attr('title', errorMessage); 849 $(field).tooltip(); 850 } 851 } 852 853 $(form).find('.form-group.has-error input[title]').first().tooltip('show'); 854 }; 855 856 this.fieldHasError = function (field) { 857 return $(field).parents('.form-group').hasClass('has-error'); 858 }; 859 860 this.clearFieldError = function (field) { 861 $(field).tooltip('destroy'); 862 $(field).parents('.form-group').removeClass('has-error'); 863 }; 864 865 this.onSuccess = function (form, response) { 866 var formOnSuccess = $(form).data('on-success'); 867 868 if (typeof formOnSuccess === 'function') { 869 formOnSuccess(response.data); 870 } 871 }; 872 873 this.onError = function (form, jqXHR) { 874 if (jqXHR.responseJSON && jqXHR.responseJSON.fields && typeof jqXHR.responseJSON.fields === 'object') { 875 this.markFieldErrors(form, jqXHR.responseJSON.fields); 876 } else { 877 // TODO: replace with client-accessible generic error messaging 878 console.log('Unknown error - please try again later.'); 879 } 880 881 var formOnError = $(form).data('on-error'); 882 883 if (typeof formOnError === 'function') { 884 formOnError(jqXHR); 885 } 886 }; 887 888 this.clearErrors = function (form) { 889 var self = this; 890 891 $(form).find(self.managedElements).each(function () { 892 self.clearFieldError(this); 893 }) 894 }; 895 896 this.onRequestComplete = function (form, data) { 897 // implement as needed 898 }; 899 900 return this; 901}, 902 903effects: function () { 904 this.errorShake = function (element) { 905 /** 906 * Shake effect without jQuery UI inspired by Hiren Patel | ninty9notout: 907 * @see https://github.com/ninty9notout/jquery-shake/blob/51f3dcf625970c78505bcac831fd9e28fc85d374/jquery.ui.shake.js 908 */ 909 options = options || {}; 910 var options = $.extend({ 911 direction: "left", 912 distance: 8, 913 times: 3, 914 speed: 90 915 }, options); 916 917 return element.each(function () { 918 var el = $(this), props = { 919 position: el.css("position"), 920 top: el.css("top"), 921 bottom: el.css("bottom"), 922 left: el.css("left"), 923 right: el.css("right") 924 }; 925 926 el.css("position", "relative"); 927 928 var ref = (options.direction === "up" || options.direction === "down") ? "top" : "left"; 929 var motion = (options.direction === "up" || options.direction === "left") ? "pos" : "neg"; 930 931 var animation = {}, animation1 = {}, animation2 = {}; 932 animation[ref] = (motion === "pos" ? "-=" : "+=") + options.distance; 933 animation1[ref] = (motion === "pos" ? "+=" : "-=") + options.distance * 2; 934 animation2[ref] = (motion === "pos" ? "-=" : "+=") + options.distance * 2; 935 936 el.animate(animation, options.speed); 937 for (var i = 1; i < options.times; i++) { 938 el.animate(animation1, options.speed).animate(animation2, options.speed); 939 } 940 941 el.animate(animation1, options.speed).animate(animation, options.speed / 2, function () { 942 el.css(props); 943 }); 944 }); 945 }; 946 947} 948}); 949 950/** 951* Form module 952* 953* @copyright Copyright (c) WHMCS Limited 2005-2017 954* @license http://www.whmcs.com/license/ WHMCS Eula 955*/ 956(function(module) { 957 if (!WHMCS.hasModule('form')) { 958 WHMCS.loadModule('form', module); 959 } 960})( 961 function () { 962 this.checkAllBound = false; 963 964 this.register = function () { 965 if (!this.checkAllBound) { 966 this.bindCheckAll(); 967 this.checkAllBound = true; 968 } 969 }; 970 971 this.bindCheckAll = function () 972 { 973 var huntSelector = '.btn-check-all'; 974 jQuery(huntSelector).click(function (e) { 975 var btn = jQuery(e.target); 976 var targetInputs = jQuery( 977 '#' + btn.data('checkbox-container') + ' input[type="checkbox"]' 978 ); 979 if (btn.data('btn-check-toggle')) { 980 // one control that changes 981 var textDeselect = 'Deselect All'; 982 var textSelect = 'Select All'; 983 if (btn.data('label-text-deselect')) { 984 textDeselect = btn.data('label-text-deselect'); 985 } 986 if (btn.data('label-text-select')) { 987 textSelect = btn.data('label-text-select'); 988 } 989 990 if (btn.hasClass('toggle-active')) {
991 targetInputs.prop('checked',false); 992 btn.text(textSelect); 993 btn.removeClass('toggle-active'); 994 } else { 995 targetInputs.prop('checked',true); 996 btn.text(textDeselect); 997 btn.addClass('toggle-active'); 998 } 999 } else { 1000 // two controls that are static 1001 if (btn.data('btn-toggle-on')) { 1002 targetInputs.prop('checked',true); 1003 } else { 1004 targetInputs.prop('checked',false); 1005 } 1006 } 1007 }); 1008 }; 1009 1010 return this; 1011}); 1012 1013/** 1014 * reCaptcha module 1015 * 1016 * @copyright Copyright (c) WHMCS Limited 2005-2018 1017 * @license http://www.whmcs.com/license/ WHMCS Eula 1018 */ 1019var recaptchaLoadComplete = false; 1020 1021(function(module) { 1022 if (!WHMCS.hasModule('recaptcha')) { 1023 WHMCS.loadModule('recaptcha', module); 1024 } 1025})( 1026 function () { 1027 1028 this.register = function () { 1029 if (recaptchaLoadComplete) { 1030 return; 1031 } 1032 var postLoad = []; 1033 var recaptchaForms = jQuery(".btn-recaptcha").parents('form'); 1034 recaptchaForms.each(function (i, el){ 1035 if (typeof recaptchaSiteKey === 'undefined') { 1036 console.log('Recaptcha site key not defined'); 1037 return; 1038 } 1039 var frm = jQuery(el); 1040 var btnRecaptcha = frm.find(".btn-recaptcha"); 1041 var isInvisible = btnRecaptcha.hasClass('btn-recaptcha-invisible'), 1042 required = (typeof requiredText !== 'undefined') ? requiredText : 'Required'; 1043 1044 // if no recaptcha element, make one 1045 var recaptchaContent = frm.find("#divDynamicRecaptcha .g-recaptcha"), 1046 recaptchaElement = frm.find('.recaptcha-container'), 1047 appendElement = frm; 1048 1049 if (recaptchaElement.length) { 1050 appendElement = recaptchaElement; 1051 } 1052 if (!recaptchaContent.length) { 1053 appendElement.append('<div id="divDynamicRecaptcha" class="g-recaptcha" data-toggle="tooltip" data-placement="bottom" data-trigger="manual" title="' + required + '"></div>'); 1054 recaptchaContent = appendElement.find("#divDynamicRecaptcha"); 1055 } 1056 // propagate invisible recaptcha if necessary 1057 if (isInvisible) { 1058 if (recaptchaContent.data('size') !== 'invisible') { 1059 recaptchaContent.attr('data-size', 'invisible'); 1060 } 1061 } else { 1062 recaptchaContent.hide() 1063 } 1064 1065 // ensure site key is available to grecaptcha 1066 recaptchaContent.attr('data-sitekey', recaptchaSiteKey); 1067 1068 1069 // alter form to work around JS behavior on .submit() when there 1070 // there is an input with the name 'submit' 1071 var btnSubmit = frm.find("input[name='submit']"); 1072 if (btnSubmit.length) { 1073 var action = frm.prop('action'); 1074 frm.prop('action', action + '&submit=1'); 1075 btnSubmit.remove(); 1076 } 1077 1078 // make callback for grecaptcha to invoke after 1079 // injecting token & make it known via data-callback 1080 var funcName = 'recaptchaCallback' + i; 1081 window[funcName] = function () { 1082 if (isInvisible) { 1083 frm.submit(); 1084 } 1085 }; 1086 recaptchaContent.attr('data-callback', funcName); 1087 1088 // alter submit button to integrate invisible recaptcha 1089 // otherwise setup a callback to twiddle UI after grecaptcha 1090 // has inject DOM 1091 if (isInvisible) { 1092 btnRecaptcha.on('click', function (event) { 1093 if (!grecaptcha.getResponse().trim()) { 1094 event.preventDefault(); 1095 grecaptcha.execute(); 1096 } 1097 }); 1098 } else { 1099 postLoad.push(function () { 1100 recaptchaContent.slideDown('fast', function() { 1101 // just in case there's a delay in DOM; rare 1102 recaptchaContent.find(':first').addClass('center-block'); 1103 }); 1104 }); 1105 postLoad.push(function() { 1106 recaptchaContent.find(':first').addClass('center-block'); 1107 }); 1108 } 1109 }); 1110 1111 // fetch/invoke the grecaptcha lib 1112 if (recaptchaForms.length) { 1113 var gUrl = "https://www.google.com/recaptcha/api.js"; 1114 jQuery.getScript(gUrl, function () { 1115 for(var i = postLoad.length -1; i >= 0 ; i--){ 1116 postLoad[i](); 1117 } 1118 }); 1119 } 1120 recaptchaLoadComplete = true; 1121 }; 1122 1123 return this; 1124 });
vendor: 16,403 bytes, lines 1125-1713
1125 1126 /** 1127 * microplugin.js 1128 * Copyright (c) 2013 Brian Reavis & contributors 1129 * 1130 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 1131 * file except in compliance with the License. You may obtain a copy of the License at: 1132 * http://www.apache.org/licenses/LICENSE-2.0 1133 * 1134 * Unless required by applicable law or agreed to in writing, software distributed under 1135 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 1136 * ANY KIND, either express or implied. See the License for the specific language 1137 * governing permissions and limitations under the License. 1138 * 1139 * @author Brian Reavis <[email protected]> 1140 */ 1141 1142(function(root, factory) { 1143 if (typeof define === 'function' && define.amd) { 1144 define(factory); 1145 } else if (typeof exports === 'object') { 1146 module.exports = factory(); 1147 } else { 1148 root.MicroPlugin = factory(); 1149 } 1150}(this, function() { 1151 var MicroPlugin = {}; 1152 1153 MicroPlugin.mixin = function(Interface) { 1154 Interface.plugins = {}; 1155 1156 /** 1157 * Initializes the listed plugins (with options). 1158 * Acceptable formats: 1159 * 1160 * List (without options): 1161 * ['a', 'b', 'c'] 1162 * 1163 * List (with options): 1164 * [{'name': 'a', options: {}}, {'name': 'b', options: {}}] 1165 * 1166 * Hash (with options): 1167 * {'a': { ... }, 'b': { ... }, 'c': { ... }} 1168 * 1169 * @param {mixed} plugins 1170 */ 1171 Interface.prototype.initializePlugins = function(plugins) { 1172 var i, n, key; 1173 var self = this; 1174 var queue = []; 1175 1176 self.plugins = { 1177 names : [], 1178 settings : {}, 1179 requested : {}, 1180 loaded : {} 1181 }; 1182 1183 if (utils.isArray(plugins)) { 1184 for (i = 0, n = plugins.length; i < n; i++) { 1185 if (typeof plugins[i] === 'string') { 1186 queue.push(plugins[i]); 1187 } else { 1188 self.plugins.settings[plugins[i].name] = plugins[i].options; 1189 queue.push(plugins[i].name); 1190 } 1191 } 1192 } else if (plugins) { 1193 for (key in plugins) { 1194 if (plugins.hasOwnProperty(key)) { 1195 self.plugins.settings[key] = plugins[key]; 1196 queue.push(key); 1197 } 1198 } 1199 } 1200 1201 while (queue.length) { 1202 self.require(queue.shift()); 1203 } 1204 }; 1205 1206 Interface.prototype.loadPlugin = function(name) { 1207 var self = this; 1208 var plugins = self.plugins; 1209 var plugin = Interface.plugins[name]; 1210 1211 if (!Interface.plugins.hasOwnProperty(name)) { 1212 throw new Error('Unable to find "' + name + '" plugin'); 1213 } 1214 1215 plugins.requested[name] = true; 1216 plugins.loaded[name] = plugin.fn.apply(self, [self.plugins.settings[name] || {}]); 1217 plugins.names.push(name); 1218 }; 1219 1220 /** 1221 * Initializes a plugin. 1222 * 1223 * @param {string} name 1224 */ 1225 Interface.prototype.require = function(name) { 1226 var self = this; 1227 var plugins = self.plugins; 1228 1229 if (!self.plugins.loaded.hasOwnProperty(name)) { 1230 if (plugins.requested[name]) { 1231 throw new Error('Plugin has circular dependency ("' + name + '")'); 1232 } 1233 self.loadPlugin(name); 1234 } 1235 1236 return plugins.loaded[name]; 1237 }; 1238 1239 /** 1240 * Registers a plugin. 1241 * 1242 * @param {string} name 1243 * @param {function} fn 1244 */ 1245 Interface.define = function(name, fn) { 1246 Interface.plugins[name] = { 1247 'name' : name, 1248 'fn' : fn 1249 }; 1250 }; 1251 }; 1252 1253 var utils = { 1254 isArray: Array.isArray || function(vArg) { 1255 return Object.prototype.toString.call(vArg) === '[object Array]'; 1256 } 1257 }; 1258 1259 return MicroPlugin; 1260})); 1261 1262/** 1263 * sifter.js 1264 * Copyright (c) 2013 Brian Reavis & contributors 1265 * 1266 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 1267 * file except in compliance with the License. You may obtain a copy of the License at: 1268 * http://www.apache.org/licenses/LICENSE-2.0 1269 * 1270 * Unless required by applicable law or agreed to in writing, software distributed under 1271 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF 1272 * ANY KIND, either express or implied. See the License for the specific language 1273 * governing permissions and limitations under the License. 1274 * 1275 * @author Brian Reavis <[email protected]> 1276 */ 1277 1278(function(root, factory) { 1279 if (typeof define === 'function' && define.amd) { 1280 define(factory); 1281 } else if (typeof exports === 'object') { 1282 module.exports = factory(); 1283 } else { 1284 root.Sifter = factory(); 1285 } 1286}(this, function() { 1287 1288 /** 1289 * Textually searches arrays and hashes of objects 1290 * by property (or multiple properties). Designed 1291 * specifically for autocomplete. 1292 * 1293 * @constructor 1294 * @param {array|object} items 1295 * @param {object} items 1296 */ 1297 var Sifter = function(items, settings) { 1298 this.items = items; 1299 this.settings = settings || {diacritics: true}; 1300 }; 1301 1302 /** 1303 * Splits a search string into an array of individual 1304 * regexps to be used to match results. 1305 * 1306 * @param {string} query 1307 * @returns {array} 1308 */ 1309 Sifter.prototype.tokenize = function(query) { 1310 query = trim(String(query || '').toLowerCase()); 1311 if (!query || !query.length) return []; 1312 1313 var i, n, regex, letter; 1314 var tokens = []; 1315 var words = query.split(/ +/); 1316 1317 for (i = 0, n = words.length; i < n; i++) { 1318 regex = escape_regex(words[i]); 1319 if (this.settings.diacritics) { 1320 for (letter in DIACRITICS) { 1321 if (DIACRITICS.hasOwnProperty(letter)) { 1322 regex = regex.replace(new RegExp(letter, 'g'), DIACRITICS[letter]); 1323 } 1324 } 1325 } 1326 tokens.push({ 1327 string : words[i], 1328 regex : new RegExp(regex, 'i') 1329 }); 1330 } 1331 1332 return tokens; 1333 }; 1334 1335 /** 1336 * Iterates over arrays and hashes. 1337 * 1338 * ``` 1339 * this.iterator(this.items, function(item, id) { 1340 * // invoked for each item 1341 * }); 1342 * ``` 1343 * 1344 * @param {array|object} object 1345 */ 1346 Sifter.prototype.iterator = function(object, callback) { 1347 var iterator; 1348 if (is_array(object)) { 1349 iterator = Array.prototype.forEach || function(callback) { 1350 for (var i = 0, n = this.length; i < n; i++) { 1351 callback(this[i], i, this); 1352 } 1353 }; 1354 } else { 1355 iterator = function(callback) { 1356 for (var key in this) { 1357 if (this.hasOwnProperty(key)) { 1358 callback(this[key], key, this); 1359 } 1360 } 1361 }; 1362 } 1363 1364 iterator.apply(object, [callback]); 1365 }; 1366 1367 /** 1368 * Returns a function to be used to score individual results. 1369 * 1370 * Good matches will have a higher score than poor matches. 1371 * If an item is not a match, 0 will be returned by the function. 1372 * 1373 * @param {object|string} search 1374 * @param {object} options (optional) 1375 * @returns {function} 1376 */ 1377 Sifter.prototype.getScoreFunction = function(search, options) { 1378 var self, fields, tokens, token_count, nesting; 1379 1380 self = this; 1381 search = self.prepareSearch(search, options); 1382 tokens = search.tokens; 1383 fields = search.options.fields; 1384 token_count = tokens.length; 1385 nesting = search.options.nesting; 1386 1387 /** 1388 * Calculates how close of a match the 1389 * given value is against a search token. 1390 * 1391 * @param {mixed} value 1392 * @param {object} token 1393 * @return {number} 1394 */ 1395 var scoreValue = function(value, token) { 1396 var score, pos; 1397 1398 if (!value) return 0; 1399 value = String(value || ''); 1400 pos = value.search(token.regex); 1401 if (pos === -1) return 0; 1402 score = token.string.length / value.length; 1403 if (pos === 0) score += 0.5; 1404 return score; 1405 }; 1406 1407 /** 1408 * Calculates the score of an object 1409 * against the search query. 1410 * 1411 * @param {object} token 1412 * @param {object} data 1413 * @return {number} 1414 */ 1415 var scoreObject = (function() { 1416 var field_count = fields.length; 1417 if (!field_count) { 1418 return function() { return 0; }; 1419 } 1420 if (field_count === 1) { 1421 return function(token, data) { 1422 return scoreValue(getattr(data, fields[0], nesting), token); 1423 }; 1424 } 1425 return function(token, data) { 1426 for (var i = 0, sum = 0; i < field_count; i++) { 1427 sum += scoreValue(getattr(data, fields[i], nesting), token); 1428 } 1429 return sum / field_count; 1430 }; 1431 })(); 1432 1433 if (!token_count) { 1434 return function() { return 0; }; 1435 } 1436 if (token_count === 1) { 1437 return function(data) { 1438 return scoreObject(tokens[0], data); 1439 }; 1440 } 1441 1442 if (search.options.conjunction === 'and') { 1443 return function(data) { 1444 var score; 1445 for (var i = 0, sum = 0; i < token_count; i++) { 1446 score = scoreObject(tokens[i], data); 1447 if (score <= 0) return 0; 1448 sum += score; 1449 } 1450 return sum / token_count; 1451 }; 1452 } else { 1453 return function(data) { 1454 for (var i = 0, sum = 0; i < token_count; i++) { 1455 sum += scoreObject(tokens[i], data); 1456 } 1457 return sum / token_count; 1458 }; 1459 } 1460 }; 1461 1462 /** 1463 * Returns a function that can be used to compare two 1464 * results, for sorting purposes. If no sorting should 1465 * be performed, `null` will be returned. 1466 * 1467 * @param {string|object} search 1468 * @param {object} options 1469 * @return function(a,b) 1470 */ 1471 Sifter.prototype.getSortFunction = function(search, options) { 1472 var i, n, self, field, fields, fields_count, multiplier, multipliers, get_field, implicit_score, sort; 1473 1474 self = this; 1475 search = self.prepareSearch(search, options); 1476 sort = (!search.query && options.sort_empty) || options.sort; 1477 1478 /** 1479 * Fetches the specified sort field value 1480 * from a search result item. 1481 * 1482 * @param {string} name 1483 * @param {object} result 1484 * @return {mixed} 1485 */ 1486 get_field = function(name, result) { 1487 if (name === '$score') return result.score; 1488 return getattr(self.items[result.id], name, options.nesting); 1489 }; 1490 1491 // parse options 1492 fields = []; 1493 if (sort) { 1494 for (i = 0, n = sort.length; i < n; i++) { 1495 if (search.query || sort[i].field !== '$score') { 1496 fields.push(sort[i]); 1497 } 1498 } 1499 } 1500 1501 // the "$score" field is implied to be the primary 1502 // sort field, unless it's manually specified 1503 if (search.query) { 1504 implicit_score = true; 1505 for (i = 0, n = fields.length; i < n; i++) { 1506 if (fields[i].field === '$score') { 1507 implicit_score = false; 1508 break; 1509 } 1510 } 1511 if (implicit_score) { 1512 fields.unshift({field: '$score', direction: 'desc'}); 1513 } 1514 } else { 1515 for (i = 0, n = fields.length; i < n; i++) { 1516 if (fields[i].field === '$score') { 1517 fields.splice(i, 1); 1518 break; 1519 } 1520 } 1521 } 1522 1523 multipliers = []; 1524 for (i = 0, n = fields.length; i < n; i++) { 1525 multipliers.push(fields[i].direction === 'desc' ? -1 : 1); 1526 } 1527 1528 // build function 1529 fields_count = fields.length; 1530 if (!fields_count) { 1531 return null; 1532 } else if (fields_count === 1) { 1533 field = fields[0].field; 1534 multiplier = multipliers[0]; 1535 return function(a, b) { 1536 return multiplier * cmp( 1537 get_field(field, a), 1538 get_field(field, b) 1539 ); 1540 }; 1541 } else { 1542 return function(a, b) { 1543 var i, result, a_value, b_value, field; 1544 for (i = 0; i < fields_count; i++) { 1545 field = fields[i].field; 1546 result = multipliers[i] * cmp( 1547 get_field(field, a), 1548 get_field(field, b) 1549 ); 1550 if (result) return result; 1551 } 1552 return 0; 1553 }; 1554 } 1555 }; 1556 1557 /** 1558 * Parses a search query and returns an object 1559 * with tokens and fields ready to be populated 1560 * with results. 1561 * 1562 * @param {string} query 1563 * @param {object} options 1564 * @returns {object} 1565 */ 1566 Sifter.prototype.prepareSearch = function(query, options) { 1567 if (typeof query === 'object') return query; 1568 1569 options = extend({}, options); 1570 1571 var option_fields = options.fields; 1572 var option_sort = options.sort; 1573 var option_sort_empty = options.sort_empty; 1574 1575 if (option_fields && !is_array(option_fields)) options.fields = [option_fields]; 1576 if (option_sort && !is_array(option_sort)) options.sort = [option_sort]; 1577 if (option_sort_empty && !is_array(option_sort_empty)) options.sort_empty = [option_sort_empty]; 1578 1579 return { 1580 options : options, 1581 query : String(query || '').toLowerCase(), 1582 tokens : this.tokenize(query), 1583 total : 0, 1584 items : [] 1585 }; 1586 }; 1587 1588 /** 1589 * Searches through all items and returns a sorted array of matches. 1590 * 1591 * The `options` parameter can contain: 1592 * 1593 * - fields {string|array} 1594 * - sort {array} 1595 * - score {function} 1596 * - filter {bool} 1597 * - limit {integer} 1598 * 1599 * Returns an object containing: 1600 * 1601 * - options {object} 1602 * - query {string} 1603 * - tokens {array} 1604 * - total {int} 1605 * - items {array} 1606 * 1607 * @param {string} query 1608 * @param {object} options 1609 * @returns {object} 1610 */ 1611 Sifter.prototype.search = function(query, options) { 1612 var self = this, value, score, search, calculateScore; 1613 var fn_sort; 1614 var fn_score; 1615 1616 search = this.prepareSearch(query, options); 1617 options = search.options; 1618 query = search.query; 1619 1620 // generate result scoring function 1621 fn_score = options.score || self.getScoreFunction(search); 1622 1623 // perform search and sort 1624 if (query.length) { 1625 self.iterator(self.items, function(item, id) { 1626 score = fn_score(item); 1627 if (options.filter === false || score > 0) { 1628 search.items.push({'score': score, 'id': id}); 1629 } 1630 }); 1631 } else { 1632 self.iterator(self.items, function(item, id) { 1633 search.items.push({'score': 1, 'id': id}); 1634 }); 1635 } 1636 1637 fn_sort = self.getSortFunction(search, options); 1638 if (fn_sort) search.items.sort(fn_sort); 1639 1640 // apply limits 1641 search.total = search.items.length; 1642 if (typeof options.limit === 'number') { 1643 search.items = search.items.slice(0, options.limit); 1644 } 1645 1646 return search; 1647 }; 1648 1649 // utilities 1650 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1651 1652 var cmp = function(a, b) { 1653 if (typeof a === 'number' && typeof b === 'number') { 1654 return a > b ? 1 : (a < b ? -1 : 0); 1655 } 1656 a = asciifold(String(a || '')); 1657 b = asciifold(String(b || '')); 1658 if (a > b) return 1; 1659 if (b > a) return -1; 1660 return 0; 1661 }; 1662 1663 var extend = function(a, b) { 1664 var i, n, k, object; 1665 for (i = 1, n = arguments.length; i < n; i++) { 1666 object = arguments[i]; 1667 if (!object) continue; 1668 for (k in object) { 1669 if (object.hasOwnProperty(k)) { 1670 a[k] = object[k]; 1671 } 1672 } 1673 } 1674 return a; 1675 }; 1676 1677 /** 1678 * A property getter resolving dot-notation 1679 * @param {Object} obj The root object to fetch property on 1680 * @param {String} name The optionally dotted property name to fetch 1681 * @param {Boolean} nesting Handle nesting or not 1682 * @return {Object} The resolved property value 1683 */ 1684 var getattr = function(obj, name, nesting) { 1685 if (!obj || !name) return; 1686 if (!nesting) return obj[name]; 1687 var names = name.split("."); 1688 while(names.length && (obj = obj[names.shift()])); 1689 return obj; 1690 }; 1691 1692 var trim = function(str) { 1693 return (str + '').replace(/^\s+|\s+$|/g, ''); 1694 }; 1695 1696 var escape_regex = function(str) { 1697 return (str + '').replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1'); 1698 }; 1699 1700 var is_array = Array.isArray || (typeof $ !== 'undefined' && $.isArray) || function(object) { 1701 return Object.prototype.toString.call(object) === '[object Array]'; 1702 }; 1703 1704 var DIACRITICS = { 1705 'a': '[aá¸â¬Ã¡Â¸ÂÃâÃÆÃââÃÂÎúⱥæçẠạÃâäÃâ¬Ã ÃÂáÃâ¬ÃÂ̮̣Ãâ¦ÃÂ¥Ãâ¦ÃâÃÆÃâ¦Ãâ]', 1706 'b': '[bâ¢òÃâBà ¸¿ðÂÅÂáâºâ]', 1707 'c': '[cÃâ Ãâ¡ÃËÃâ°ÃÅÃÂÃÅ Ãâ¹CÃâcÃâÃâ¡Ã§á¸Ëá¸â°Ã»Ã¼Ãâ¡ÃËÃâ¢Ã¡Â´âï¼£ï½Æ]', 1708 'd': '[dÃŽÃÂḊá¸â¹Ã¡Â¸Âá¸âá¸Åá¸Âá¸âá¸âḎá¸ÂÃÂÃâDædæÃâ°ÃâÃÅ ÃâÃâ¹ÃÅáµÂá¶Âá¶âáá´â¦Ã¯Â¼Â¤Ã¯Â½âð]', 1709 'e': '[eÃâ°Ã©ÃËèÊêá¸Ëá¸â¢ÃÅ¡ÃâºÃâÃâ¢Ã¡ÂºÂ¼Ã¡ÂºÂ½Ã¡Â¸Å¡Ã¡Â¸âºÃ¡ÂºÂºÃ¡ÂºÂ»ÃâÃâÃâ¹Ã«ÃâÃâèéÃËÃâ¢Ã¡Â¶âÃâ Ãâ¡ÃâÃâ¦Ã¡ÂºÂ¾Ã¡ÂºÂ¿Ã¡Â»â¬Ã¡Â»Âá»âá»â¦Ã¡Â»âá»ÆÃ¡Â¸Åá¸Âá¸âá¸âá¸âá¸â¢Ãâ Ãâ¡Ã¡ÂºÂ¸Ã¡ÂºÂ¹Ã¡Â»â á»â¡Ã¢Â±Â¸Ã¡Â´â¡Ã¯Â¼Â¥Ã¯Â½â¦ÃËÃÂÃÂÃÂõ]', 1710 'f': '[fÃâÃâḞḟ]', 1711 'g': '[gâââ²Ã¤ÃÂ¥ÃÅÃÂÞßâãÃâààá]', 1712 'h': '[häÃ¥æçḨḩáºâáºâḤḥḢḣæðöÃâ¢]', 1713 'i': '[iÃÂÃÂÃÅììÃÂÎîÃÂÃÂÃÂïḮÃ
1713¡Â¸Â¯Ã¨Ã©Ã®Ã¯ÃªÃ«á»Ëá»â°ÃËÃâ°ÃÅ Ãâ¹Ã¡Â»Å á»â¹Ã¡Â¸Â¬Ã¡Â¸ÂÃâèèÃâ áµ»á¶âðiIñêIï½â°]', 1714 'j': '[j÷ôõÃËÃâ°ÃÂßò]', 1715 'k': '[kÃËÃâ¢ÃªÂâ¬ÃªÂÂḰḱèéḲḳḴḵúðââÂ]', 1716 'l': '[là Âà âýþûüùúḶḷḸḹḼḽḺḻÿà â¬Ã½Ãšâ± â±¡â±¢Ã«Ã¬á¶â¦ÃÂôßLï½Å]', 1717 'n': '[nà ÆÃ âøùà â¡Ã ËÃâñá¹âá¹â¦Ã â¦Ã â á¹â á¹â¡Ã¡Â¹Å á¹â¹Ã¡Â¹Ëá¹â°NÃËnÃËÃÂòàÞᵰá¶â¡Ã³ÃµÃ´ï¼®ï½Žà Šà â¹]', 1718 'o': '[oÃËøÃâöÃâóÃâòÃâôÃâÃâà Âà âà Žà Âîïá»Åá»ÂßõàáỎá»Âà Åà ÂÃâ¢ÃµÃªÃ«ÃÅÃÂÃâ¢Ãâ¦]', 1719 'p': '[pá¹âá¹â¢Ã¡Â¹âá¹âⱣᵽäåᵱ]', 1720 'q': '[qêÂâêÂâàÃÅ Ãâ¹ÃªÂËêÂâ¢qÃÆ]', 1721 'r': '[rà âà â¢ÃÅÃÂà Ëà â¢Ã âà âá¹Ëá¹â¢ÃÂÃâÃâÃâṚá¹âºÃ¢Â±Â¤Ã½]', 1722 's': '[sà šà âºÃ¡Â¹Â ṡṢṣꞨꞩà Åà Âà  à ¡à žà ŸÃËÃâ¢SÃËsÃË]', 1723 't': '[tà ¤à ¥á¹ªá¹«à ¢à £á¹¬á¹ÂîÃËÃÅ¡ÃâºÃ¡Â¹Â°Ã¡Â¹Â±Ã¡Â¹Â®Ã¡Â¹Â¯Ã¬ÃÂ]', 1724 'u': '[uà ¬à ÂÃâÃâ°Ã¡Â»Â¤Ã¡Â»Â¥ÃÅüÚúÃâ¢Ã¹ÃâºÃ»ÃâÃâà °à ±à ¬à ÂÃ¯Ã°á»¦á»§à ªà «à ¨à ©à ²à ³ÃâÃâ¢Ã¢Ëª]', 1725 'v': '[vṼṽṾṿòÃâ¹ÃªÂžêŸⱱÃâ¹]', 1726 'w': '[wáºâáºÆÃ¡Âºâ¬Ã¡ÂºÂà ´à µáºâáºâ¦Ã¡Âºâ áºâ¡Ã¡ÂºËáºâ°]', 1727 'x': '[xáºÅáºÂẊáºâ¹Ãâ¡]', 1728 'y': '[yÃÂÃ½á»²á»³à ¶à ·à ¸Ã¿á»¸á»¹áºŽáºÂỴỵÃŽÃÂóô]', 1729 'z': '[zà ¹à ºáºÂáºâà ½à ¾à »à ¼áºâáºâáºâáºâ¢ÃµÃ¶]' 1730 }; 1731 1732 var asciifold = (function() { 1733 var i, n, k, chunk; 1734 var foreignletters = ''; 1735 var lookup = {}; 1736 for (k in DIACRITICS) { 1737 if (DIACRITICS.hasOwnProperty(k)) { 1738 chunk = DIACRITICS[k].substring(2, DIACRITICS[k].length - 1); 1739 foreignletters += chunk; 1740 for (i = 0, n = chunk.length; i < n; i++) { 1741 lookup[chunk.charAt(i)] = k; 1742 } 1743 } 1744 } 1745 var regexp = new RegExp('[' + foreignletters + ']', 'g'); 1746 return function(str) { 1747 return str.replace(regexp, function(foreignletter) { 1748 return lookup[foreignletter]; 1749 }).toLowerCase(); 1750 }; 1751 })(); 1752 1753 1754 // export 1755 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1756 1757 return Sifter; 1758})); 1759 1760/** 1761 * General utilities module 1762 * 1763 * @copyright Copyright (c) WHMCS Limited 2005-2017 1764 * @license http://www.whmcs.com/license/ WHMCS Eula 1765 */ 1766(function(module) { 1767 if (!WHMCS.hasModule('utils')) { 1768 WHMCS.loadModule('utils', module); 1769 } 1770})( 1771function () { 1772 /** 1773 * Not crypto strong; server-side must discard for 1774 * something with more entropy; the value is sufficient 1775 * for strong client-side validation check 1776 */ 1777 this.simpleRNG = function () { 1778 var chars = './$_-#!,^*()|'; 1779 var r = 0; 1780 for (var i = 0; r < 3; i++) { 1781 r += Math.floor((Math.random() * 10) / 2); 1782 } 1783 r = Math.floor(r); 1784 var s = ''; 1785 for (var x = 0; x < r; x++) { 1786 v = (Math.random() + 1).toString(24).split('.')[1]; 1787 if ((Math.random()) > 0.5) { 1788 s += btoa(v).substr(0,4) 1789 } else { 1790 s += v 1791 } 1792 1793 if ((Math.random()) > 0.5) { 1794 s += chars.substr( 1795 Math.floor(Math.random() * 13), 1796 1 1797 ); 1798 } 1799 } 1800 1801 return s; 1802 }; 1803 1804 this.getRouteUrl = function (path) { 1805 return whmcsBaseUrl + "/index.php?rp=" + path; 1806 }; 1807 1808 this.validateBaseUrl = function() { 1809 if (typeof window.whmcsBaseUrl === 'undefined') { 1810 console.log('Warning: The WHMCS Base URL definition is missing ' 1811 + 'from your active template. Please refer to ' 1812 + 'https://docs.whmcs.com/WHMCS_Base_URL_Template_Variable ' 1813 + 'for more information and details of how to resolve this ' 1814 + 'warning.'); 1815 window.whmcsBaseUrl = this.autoDetermineBaseUrl(); 1816 window.whmcsBaseUrlAutoSet = true; 1817 } else if (window.whmcsBaseUrl === '' 1818 && typeof window.whmcsBaseUrlAutoSet !== 'undefined' 1819 && window.whmcsBaseUrlAutoSet === true 1820 ) { 1821 window.whmcsBaseUrl = this.autoDetermineBaseUrl(); 1822 } 1823 }; 1824 1825 this.autoDetermineBaseUrl = function() { 1826 var windowLocation = window.location.href; 1827 var phpExtensionLocation = -1; 1828 1829 if (typeof windowLocation !== 'undefined') { 1830 phpExtensionLocation = windowLocation.indexOf('.php'); 1831 } 1832 1833 if (phpExtensionLocation === -1) { 1834 windowLocation = jQuery('#Primary_Navbar-Home a').attr('href'); 1835 if (typeof windowLocation !== 'undefined') { 1836 phpExtensionLocation = windowLocation.indexOf('.php'); 1837 } 1838 } 1839 1840 if (phpExtensionLocation !== -1) { 1841 windowLocation = windowLocation.substring(0, phpExtensionLocation); 1842 var lastTrailingSlash = windowLocation.lastIndexOf('/'); 1843 if (lastTrailingSlash !== false) { 1844 return windowLocation.substring(0, lastTrailingSlash); 1845 } 1846 } 1847 1848 return ''; 1849 }; 1850 1851 this.normaliseStringValue = function(status) { 1852 return status ? status.toLowerCase().replace(/\s/g, '-') : ''; 1853 }; 1854 1855 this.generatePassword = function(len) { 1856 var charset = this.getPasswordCharacterSet(); 1857 var result = ""; 1858 for (var i = 0; len > i; i++) 1859 result += charset[this.randomInt(charset.length)];
1860 return result; 1861 }; 1862 this.getPasswordCharacterSet = function() { 1863 var rawCharset = '0123456789' 1864 + 'abcdefghijklmnopqrstuvwxyz' 1865 + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 1866 + '!#$%()*+,-.:;=@_|{ldelim}{rdelim}~'; 1867 1868 // Parse UTF-16, remove duplicates, convert to array of strings 1869 var charset = []; 1870 for (var i = 0; rawCharset.length > i; i++) { 1871 var c = rawCharset.charCodeAt(i); 1872 if (0xD800 > c || c >= 0xE000) { // Regular UTF-16 character 1873 var s = rawCharset.charAt(i); 1874 if (charset.indexOf(s) == -1) 1875 charset.push(s); 1876 continue; 1877 } 1878 if (0xDC00 > c ? rawCharset.length > i + 1 : false) { // High surrogate 1879 var d = rawCharset.charCodeAt(i + 1); 1880 if (d >= 0xDC00 ? 0xE000 > d : false) { // Low surrogate 1881 var s = rawCharset.substring(i, i + 2); 1882 i++; 1883 if (charset.indexOf(s) == -1) 1884 charset.push(s); 1885 continue; 1886 } 1887 } 1888 throw "Invalid UTF-16"; 1889 } 1890 return charset; 1891 }; 1892 this.randomInt = function(n) { 1893 var x = this.randomIntMathRandom(n); 1894 x = (x + this.randomIntBrowserCrypto(n)) % n; 1895 return x; 1896 }; 1897 this.randomIntMathRandom = function(n) { 1898 var x = Math.floor(Math.random() * n); 1899 if (0 > x || x >= n) 1900 throw "Arithmetic exception"; 1901 return x; 1902 }; 1903 this.randomIntBrowserCrypto = function(n) { 1904 var cryptoObject = null; 1905 1906 if ("crypto" in window) 1907 cryptoObject = crypto; 1908 else if ("msCrypto" in window) 1909 cryptoObject = msCrypto; 1910 else 1911 return 0; 1912 1913 if (!("getRandomValues" in cryptoObject) || !("Uint32Array" in window) || typeof Uint32Array != "function") 1914 cryptoObject = null; 1915 1916 if (cryptoObject == null) 1917 return 0; 1918 1919 // Generate an unbiased sample 1920 var x = new Uint32Array(1); 1921 do cryptoObject.getRandomValues(x); 1922 while (x[0] - x[0] % n > 4294967296 - n); 1923 return x[0] % n; 1924 }; 1925 1926 return this; 1927}); 1928 1929WHMCS.utils.validateBaseUrl(); 1930 1931// Default catch for all other popovers 1932jQuery('[data-toggle="popover"]').popover({ 1933 html: true 1934}); 1935 1936 // Enable tooltips 1937 // Attach function to body so tooltips inserted by ajax will load 1938 jQuery(function(jQuery){ 1939 jQuery('body').tooltip({ 1940 selector: '[data-toggle="tooltip"]' 1941 }); 1942 }); 1943 1944// Logic to dismiss popovers on click outside 1945jQuery('body').on('click', function (e) { 1946 jQuery('[data-toggle="popover"]').each(function () { 1947 if (!jQuery(this).is(e.target) && jQuery(this).has(e.target).length === 0 && jQuery('.popover').has(e.target).length === 0) { 1948 jQuery(this).popover('hide'); 1949 } 1950 }); 1951}); 1952 1953(function(j) { 1954 j.fn.extend({ 1955 accordion: function() { 1956 return this.each(function() { 1957 function b(c, b) { 1958 jQuery(c).parent(d).siblings().removeClass(e).children(f).slideUp(g); 1959 jQuery(c).siblings(f)[b || h](b == "show" ? g : !1, function() { 1960 jQuery(c).siblings(f).is(":visible") ? jQuery(c).parents(d).not(a.parents()).addClass(e) : jQuery(c).parent(d).removeClass(e); 1961 b == "show" && jQuery(c).parents(d).not(a.parents()).addClass(e); 1962 jQuery(c).parents().show() 1963 }) 1964 } 1965 var a = jQuery(this), e = "active", h = "slideToggle", f = "ul, div", g = "fast", d = "li"; 1966 if (a.data("accordiated")) 1967 return !1; 1968 jQuery.each(a.find("ul, li>div"), 1969 function() { 1970 jQuery(this).data("accordiated", !0); 1971 jQuery(this).hide() 1972 }); 1973 jQuery.each(a.find("a"), function() { 1974 jQuery(this).click(function() { 1975 b(this, h) 1976 }); 1977 jQuery(this).bind("activate-node", function() { 1978 a.find(f).not(jQuery(this).parents()).not(jQuery(this).siblings()).slideUp(g); 1979 b(this, "slideDown") 1980 }) 1981 }); 1982 var i = location.hash ? a.find("a[href='" + location.hash + "']")[0]: a.find("li.current a")[0]; 1983 i && b(i, !1) 1984 }) 1985} 1986}) 1987})(jQuery); 1988 1989jQuery(function(){ 1990 var url = document.location.toString(); 1991 if (url.match('#')) { 1992 jQuery('.nav a[href="#' + url.split('#')[1] + '"]').tab('show'); 1993 } 1994}); 1995 1996// Checkboxes Toggle 1997// =============================== 1998function toggleCheckboxes(classname) { 1999 jQuery("."+classname).attr('checked',!jQuery("."+classname+":first").is(':checked')); 2000} 2001 2002// Disable Field Class 2003// =============================== 2004function disableFields(radiobutton, classname,disable) { 2005 2006 $('.domain-ns-options').each(function() { 2007 $( this ).removeClass( "active" ); 2008 });
2009 $(radiobutton).closest('.domain-ns-options').addClass("active") 2010 2011 // $(radiobutton).closest('.domain-ns-options').addClass("active"); 2012 // 2013 2014 2015 if (disable){ 2016 jQuery("."+classname).attr("disabled","disabled"); 2017 }else{ 2018 jQuery("."+classname).removeAttr("disabled"); 2019 } 2020} 2021 2022function navigateToTab(link, tabname){ 2023 2024 $('.domain-tab').each(function() { 2025 $( this ).removeClass( "active" ); 2026 }); 2027 2028 $('.domain-link').each(function() { 2029 $( this ).removeClass( "active" ); 2030 }); 2031 2032 $(link).closest('.domain-link').addClass("active") 2033 $("#"+tabname).addClass("active"); 2034} 2035 2036function navigateToContact(elem = null, contact){ 2037 $('.contact-container').each(function() { 2038 $( this ).removeClass( "active" ); 2039 }); 2040 $('.domain-contacts').each(function() { 2041 $( this ).removeClass( "active" ); 2042 }); 2043 $("#"+contact).addClass("active"); 2044 $(elem).closest('.domain-contacts').addClass("active"); 2045} 2046 2047function showNewBillingAddressFields() { 2048 jQuery('#newBillingAddress').slideDown(); 2049} 2050 2051function hideNewBillingAddressFields() { 2052 jQuery('#newBillingAddress').slideUp(); 2053} 2054 2055/** 2056 * Show new credit card input fields. 2057 */ 2058function showNewCardInputFields() { 2059 if (jQuery(".cc-details").hasClass("hidden")) { 2060 jQuery(".cc-details").hide().removeClass("hidden"); 2061 } 2062 jQuery(".cc-details").slideDown(); 2063 2064 jQuery("#billingAddressChoice") 2065 .slideDown() 2066 .find('input[name="billingcontact"]') 2067 .first() 2068 .iCheck('check'); 2069} 2070/** 2071 * Hide new credit card input fields. 2072 */ 2073function hideNewCardInputFields() { 2074 hideNewBillingAddressFields(); 2075 2076 jQuery(".cc-details").slideUp(); 2077 jQuery("#billingAddressChoice").slideUp(); 2078 2079 var selectedCcInfo = jQuery('input[name="ccinfo"]:checked'); 2080 2081 var selectedCcBillingContactId = jQuery(selectedCcInfo).data('billing-contact-id'); 2082 2083 var selectedBillingContactData = jQuery('.billing-contact-info[data-billing-contact-id="' + selectedCcBillingContactId + '"]'); 2084 2085 if (selectedBillingContactData.length) { 2086 jQuery('.billing-contact-info').hide(); 2087 jQuery(selectedBillingContactData).show(); 2088 } 2089} 2090 2091function showRemoteInputForm() { 2092 var remoteInputForm = jQuery('#remoteInput'), 2093 paymentGateway = remoteInputForm.data('payment-module'), 2094 errorDiv = jQuery('#remoteInputError'), 2095 btnSubmitContainer = jQuery('#btnSubmitContainer'); 2096 if (btnSubmitContainer.is(':visible')) { 2097 btnSubmitContainer.slideUp('fast'); 2098 } 2099 if (remoteInputForm.not(':visible')) { 2100 remoteInputForm.html( 2101 jQuery('#remoteInputLoading').html() 2102 ) 2103 .hide() 2104 .removeClass('hidden') 2105 .slideDown('fast', function() { 2106 var invoiceId = jQuery('input[name="invoiceid"]').val(); 2107 WHMCS.http.jqClient.jsonPost({ 2108 url: WHMCS.utils.getRouteUrl('/account/paymentmethods/remote-input'), 2109 data: 'gateway=' + paymentGateway + '&invoice_id=' + invoiceId + '&token=' + csrfToken, 2110 success: function(response) { 2111 remoteInputForm.slideUp('fast').html(response.output).slideDown('fast'); 2112 }, 2113 warning: function(error) { 2114 errorDiv.find('div').html(error); 2115 remoteInputForm.slideUp('fast').html(errorDiv.html()).slideDown('fast'); 2116 }, 2117 fail: function(error) { 2118 errorDiv.find('div').html(error); 2119 remoteInputForm.slideUp('fast').html(errorDiv.html()).slideDown('fast'); 2120 } 2121 }); 2122 }); 2123 } 2124} 2125 2126function hideRemoteInputForm() { 2127 var remoteInputForm = jQuery('#remoteInput'), 2128 btnSubmitContainer = jQuery('#btnSubmitContainer'); 2129 if (remoteInputForm.is(':visible')) { 2130 remoteInputForm.slideUp('fast').html('').addClass('hidden'); 2131 } 2132 if (btnSubmitContainer.not(':visible')) { 2133 btnSubmitContainer.slideDown('fast'); 2134 } 2135} 2136/** 2137 * Redirect on click if an element is not a button or link. 2138 * 2139 * Where table rows are clickable, we only want to redirect if the row 2140 * itself is clicked. If a button or link within the row is clicked, 2141 * the event tied to that object should be executed. This function 2142 * stops the standard JS event bubbling required to make that happen. 2143 * 2144 * @param {object} clickEvent jQuery click event 2145 * @param {string} target Redirect location 2146 * @param {bool} newWindow Open link in new window 2147 */ 2148function clickableSafeRedirect(clickEvent, target, newWindow) { 2149 var eventSource = clickEvent.target.tagName.toLowerCase(); 2150 var eventParent = clickEvent.target.parentNode.tagName.toLowerCase(); 2151 var eventTable = clickEvent.target.parentNode.parentNode.parentNode; 2152 if (jQuery(eventTable).hasClass('collapsed')) { 2153 // This is a mobile device sized display, and datatables has triggered folding 2154 return false; 2155 } 2156 if (eventSource === 'i' && jQuery(clickEvent.target).hasClass('ssl-required')) { 2157 return false; 2158 } 2159 if(eventSource != 'button' && eventSource != 'a') { 2160 if(eventParent != 'button' && eventParent != 'a') { 2161 if (newWindow) { 2162 window.open(target); 2163 } else { 2164 window.location.href = target; 2165 } 2166 } 2167 } 2168} 2169 2170// Open Centered Popup 2171// =============================== 2172function popupWindow(addr,popname,w,h,features) { 2173 var winl = (screen.width-w)/2; 2174 var wint = (screen.height-h)/2; 2175 if (winl < 0) winl = 0; 2176 if (wint < 0) wint = 0; 2177 var settings = 'height=' + h + ','; 2178 settings += 'width=' + w + ','; 2179 settings += 'top=' + wint + ','; 2180 settings += 'left=' + winl + ','; 2181 settings += features; 2182 win = window.open(addr,popname,settings); 2183 win.window.focus(); 2184} 2185// Support Tickets 2186// =============================== 2187function extraTicketAttachment() { 2188 jQuery("#fileuploads").append('<p><div class="input-group"><span class="input-group-btn"><span class="btn btn-default btn-sm btn-file"><span class="glyphicon glyphicon-folder-open"></span> <input type="file" name="attachments[]" multiple=""></span></span><input type="text" class="form-control input-sm" readonly=""></div></p>'); 2189 jQuery(document).ready( function() { 2190 jQuery('.btn-file :file').on('fileselect', function(event, numFiles, label) { 2191 2192 var input = jQuery(this).parents('.input-group').find(':text'), 2193 log = numFiles > 1 ? numFiles + ' files selected' : label; 2194 2195 if( input.length ) { 2196 input.val(log); 2197 } else { 2198 if( log ) alert(log); 2199 } 2200 2201 });
2202 }); 2203} 2204function rating_hover(id) { 2205 var selrating=id.split('_'); 2206 for(var i=1; i<=5; i++){ 2207 if(i<=selrating[1]) document.getElementById(selrating[0]+'_'+i).childNodes[0].className="glyphicon glyphicon-star"; 2208 if(i>selrating[1]) document.getElementById(selrating[0]+'_'+i).childNodes[0].className="glyphicon glyphicon-star-empty"; 2209 } 2210} 2211function rating_leave(id){ 2212 for(var i=1; i<=5; i++){ 2213 document.getElementById(id+'_'+i).childNodes[0].className="glyphicon glyphicon-star-empty"; 2214 } 2215} 2216function rating_select(tid,c,id){ 2217 window.location='viewticket.php?tid='+tid+'&c='+c+'&rating='+id; 2218} 2219// Sidebar 2220// =============================== 2221jQuery('ul.nav-list').accordion(); 2222jQuery('.site-holder.container .nav > li > ul > li.active').parent().css('display','block'); 2223jQuery('.site-holder.container.mini-sidebar .nav > li > ul > li.active').parent().css('display','none'); 2224jQuery('.btn-nav-toggle-responsive').click(function(){ 2225 jQuery('.left-sidebar').toggleClass('show-fullsidebar'); 2226}); 2227jQuery('li.nav-toggle > button').click(function(e){ 2228 //Set cookie 2229 if(jQuery.cookie('minibar')==null||jQuery.cookie('minibar')==0) { 2230 jQuery.cookie('minibar',1); 2231 jQuery('li.submenu').removeClass('active'); 2232 jQuery('li.submenu > ul').hide(); 2233 jQuery('.fa.arrow.rotate').removeClass('down'); 2234 } 2235 else { 2236 jQuery.cookie('minibar',0); 2237 } 2238 e.preventDefault(); 2239 changeSidebarState(); 2240 }); 2241 //Load sidebar state 2242 jQuery(function(){ 2243 if(jQuery.cookie('minibar')==1) { 2244 changeSidebarStateNoAnimate(); 2245 } 2246 }); 2247 function changeSidebarState(){ 2248 jQuery('.hidden-minibar').toggleClass("hide"); 2249 jQuery('.site-holder').toggleClass("mini-sidebar"); 2250 2251 if(jQuery('.toggle-right').hasClass('fa-angle-double-right')){ jQuery('.toggle-right').removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); } 2252 else { jQuery('.toggle-right').removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); } 2253 2254 if(jQuery('.toggle-left').hasClass('fa-angle-double-left')){ jQuery('.toggle-left').removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); } 2255 else { jQuery('.toggle-left').removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); } 2256 2257 if(jQuery('.site-holder').hasClass('mini-sidebar')) 2258 { 2259 jQuery('.sidebar-holder').tooltip({ 2260 selector: "a", 2261 container: "body", 2262 placement: "right" 2263 }); 2264 jQuery('li.submenu ul').tooltip('destroy'); 2265 } 2266 else 2267 { 2268 jQuery('.sidebar-holder').tooltip('destroy'); 2269 } 2270 } 2271 function changeSidebarStateNoAnimate(){ 2272 jQuery('.toggle-left').removeClass('fa-angle-double-left').addClass('fa-angle-double-right'); 2273 jQuery('.toggle-right').removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); 2274 if(jQuery('.site-holder').hasClass('mini-sidebar')) 2275 { 2276 jQuery('.sidebar-holder').tooltip({ 2277 selector: "a", 2278 container: "body", 2279 placement: "right" 2280 }); 2281 jQuery('li.submenu ul').tooltip('destroy'); 2282 } 2283 else 2284 { 2285 jQuery('.sidebar-holder').tooltip('destroy'); 2286 } 2287 } 2288 // 2289 if(jQuery('.site-holder').hasClass('mini-sidebar')) 2290 { 2291 jQuery('.sidebar-holder').tooltip({ 2292 selector: "a", 2293 container: "body", 2294 placement: "right" 2295 }); 2296 jQuery('li.submenu').tooltip('destroy'); 2297 } 2298 else 2299 { 2300 jQuery('.sidebar-holder').tooltip('destroy'); 2301 } 2302 jQuery('.show-info').click(function(){ 2303 jQuery(this).toggleClass("down"); 2304 jQuery('.page-information').toggleClass('hidden'); 2305 }); 2306 2307 jQuery('.show-task-info').click(function(){ 2308 jQuery(this).toggleClass("down"); 2309 jQuery(this).parents('.todo-task').find('.task-info').toggleClass('hidden'); 2310 }); 2311 2312 jQuery('.site-holder.mini-sidebar .content').click(function () { 2313 jQuery('.site-holder.mini-sidebar li.submenu ul').hide(); 2314 jQuery('.site-holder.mini-sidebar li.submenu').removeClass('active'); 2315 2316 }); 2317 2318// Rotate icon 2319// =============================== 2320jQuery(".sidebar-holder .nav a.dropdown").click(function(){ 2321jQuery('.rotate', this).toggleClass("down") ; 2322}) 2323 2324 2325// Language Dropdown 2326// =============================== 2327 2328 2329jQuery('.settings-toggle').click(function(e){
2330 e.preventDefault(); 2331 jQuery('.right-sidebar').toggleClass('right-sidebar-hidden'); 2332}); 2333jQuery('.theme-panel-close').click(function(){ 2334 jQuery('.right-sidebar').toggleClass('right-sidebar-hidden'); 2335}); 2336 2337jQuery(function(){ 2338 jQuery('.right-sidebar-holder').perfectScrollbar(); 2339}); 2340 2341// Scrollbar 2342// =============================== 2343 2344 2345jQuery(function(){ 2346 jQuery('.ps-messages').perfectScrollbar(); 2347}); 2348 2349jQuery(function(){ 2350 jQuery('.ps-languages').perfectScrollbar(); 2351}); 2352 2353jQuery(function(){ 2354 jQuery('.server-list').perfectScrollbar(); 2355}); 2356 2357// Server List 2358// =============================== 2359 2360 2361jQuery(function(){ 2362 jQuery('.server-list').tooltip({ 2363 selector: "a", 2364 container: "body", 2365 placement: "top" 2366 }); 2367}); 2368 2369 // Enable Switches for Checkboxes 2370 if (jQuery.prototype.bootstrapSwitch) { 2371 jQuery(".toggle-switch-success").bootstrapSwitch({ 2372 onColor: 'success' 2373 }); 2374 } 2375 2376// Handle Single Sign-On Toggle Setting 2377 jQuery("#inputAllowSso").on('switchChange.bootstrapSwitch', function(event, isChecked) { 2378 if (isChecked) { 2379 jQuery("#ssoStatusTextEnabled").removeClass('hidden').show(); 2380 jQuery("#ssoStatusTextDisabled").hide(); 2381 } else { 2382 jQuery("#ssoStatusTextDisabled").removeClass('hidden').show(); 2383 jQuery("#ssoStatusTextEnabled").hide(); 2384 } 2385 WHMCS.http.jqClient.post("clientarea.php", jQuery("#frmSingleSignOn").serialize()); 2386 }); 2387 2388 2389 // Single Sign-On call for Product/Service 2390 jQuery('.btn-service-sso').on('click', function(e) { 2391 e.preventDefault(); 2392 var button = jQuery(this); 2393 2394 var form = button.parents('form'); 2395 2396 if (form.length == 0) { 2397 form = button.find('form'); 2398 } 2399 if (form.hasClass('disabled')) { 2400 return; 2401 } 2402 2403 button.find('.loading').removeClass('hidden').show().end() 2404 .attr('disabled', 'disabled'); 2405 WHMCS.http.jqClient.post( 2406 window.location.href, 2407 form.serialize(), 2408 function (data) { 2409 button.find('.loading').hide().end().removeAttr('disabled'); 2410 form.find('.login-feedback').html(''); 2411 if (data.error) { 2412 form.find('.login-feedback').html(data.error); 2413 } 2414 if (data.redirect !== undefined && data.redirect.substr(0, 7) === 'window|') { 2415 window.open(data.redirect.substr(7), '_blank'); 2416 } 2417 }, 2418 'json' 2419 ); 2420 }); 2421 jQuery('.btn-sidebar-form-submit').on('click', function(e) { 2422 e.preventDefault(); 2423 jQuery(this).find('.loading').removeClass('hidden').show().end() 2424 .attr('disabled', 'disabled'); 2425 2426 var form = jQuery(this).parents('form'); 2427 2428 if (form.length == 0) { 2429 form = jQuery(this).find('form'); 2430 } 2431 2432 if (form.length !== 0 && form.hasClass('disabled') === false) { 2433 form.submit(); 2434 } else { 2435 jQuery(this).find('.loading').hide().end().removeAttr('disabled'); 2436 } 2437 }); 2438 2439 // Email verification close 2440 jQuery('.email-verification .btn.close').click(function(e) { 2441 e.preventDefault(); 2442 WHMCS.http.jqClient.post('clientarea.php', 'action=dismiss-email-banner&token=' + csrfToken); 2443 jQuery('.email-verification').hide(); 2444 }); 2445 2446 // Back to top animated scroll 2447 jQuery('.back-to-top').click(function(e) { 2448 e.preventDefault(); 2449 jQuery('body,html').animate({scrollTop: 0}, 500); 2450 }); 2451 2452 // Prevent page scroll on language choose click 2453 jQuery('.choose-language').click(function(e) { 2454 e.preventDefault(); 2455 }); 2456 2457 // Activate copy to clipboard functionality 2458 jQuery('.copy-to-clipboard').click(WHMCS.ui.clipboard.copy); 2459 2460 // Password Generator 2461 jQuery('.generate-password').click(function(e) { 2462 jQuery('#frmGeneratePassword').submit(); 2463 jQuery('#modalGeneratePassword') 2464 .data('targetfields', jQuery(this).data('targetfields')) 2465 .modal('show'); 2466 });
2467 jQuery('#frmGeneratePassword').submit(function(e) { 2468 e.preventDefault(); 2469 var length = parseInt(jQuery('#inputGeneratePasswordLength').val(), 10); 2470 2471 // Check length 2472 if (length < 8 || length > 64) { 2473 jQuery('#generatePasswordLengthError').removeClass('hidden').show(); 2474 return; 2475 } 2476 2477 jQuery('#inputGeneratePasswordOutput').val(WHMCS.utils.generatePassword(length)); 2478 }); 2479 jQuery('#btnGeneratePasswordInsert') 2480 .click(WHMCS.ui.clipboard.copy) 2481 .click(function(e) { 2482 jQuery(this).closest('.modal').modal('hide'); 2483 var targetFields = jQuery(this).closest('.modal').data('targetfields'); 2484 targetFields = targetFields.split(','); 2485 for(var i = 0; i < targetFields.length; i++) { 2486 jQuery('#' + targetFields[i]).val(jQuery('#inputGeneratePasswordOutput').val()) 2487 .trigger('keyup'); 2488 } 2489 }); 2490 2491// Sub-Account Activation Toggle 2492 jQuery("#inputSubaccountActivate").click(function () { 2493 if (jQuery("#inputSubaccountActivate:checked").val() != null) { 2494 jQuery("#subacct-container").removeClass('hidden'); 2495 } else { 2496 jQuery("#subacct-container").addClass('hidden'); 2497 } 2498 }); 2499 2500/** 2501 * Add domain renewal to shopping cart. 2502 * 2503 * @param {number} renewalID The domain ID to be added 2504 * @param {domElement} selfThis The object triggering the add 2505 */ 2506function addRenewalToCart(renewalID, selfThis) { 2507 jQuery("#domainRow" + renewalID).attr('disabled', 'disabled'); 2508 jQuery("#domainRow" + renewalID).find("select,button").attr("disabled", "disabled"); 2509 jQuery(selfThis).html('<span class="glyphicon glyphicon-shopping-cart"></span> Adding...'); 2510 var renewalPeriod = jQuery("#renewalPeriod" + renewalID).val(); 2511 WHMCS.http.jqClient.post( 2512 "clientarea.php", 2513 "addRenewalToCart=1&token=" + csrfToken + "&renewID="+ renewalID + "&period=" + renewalPeriod, 2514 function( data ) { 2515 jQuery("#cartItemCount").html(((jQuery("#cartItemCount").html() * 1) + 1)); 2516 jQuery(selfThis).html('<span class="glyphicon glyphicon-shopping-cart"></span> Added'); 2517 jQuery("#btnCheckout").fadeIn(); 2518 } 2519 ); 2520} 2521 2522 // Mass Domain Management Bulk Action Handling 2523 jQuery(".setBulkAction").click(function(event) { 2524 event.preventDefault(); 2525 var id = jQuery(this).attr('id').replace('Link', ''), 2526 domainForm = jQuery('#domainForm'); 2527 2528 if (id === 'renewDomains') { 2529 domainForm.attr('action', WHMCS.utils.getRouteUrl('/cart/domain/renew')); 2530 } else { 2531 if (jQuery('#' + id).length !== 0) { 2532 var action = domainForm.attr('action'); 2533 domainForm.attr('action', action + '#' + id); 2534 } 2535 jQuery('#bulkaction').val(id); 2536 } 2537 domainForm.submit(); 2538 }); 2539 2540/** 2541 * Submit default whois info and disable custom fields. 2542 * 2543 * @param {string} regType The contact registration type 2544 */ 2545function useDefaultWhois(regType) { 2546 jQuery("." + regType.substr(0, regType.length - 1) + "customwhois").attr("disabled", true); 2547 jQuery("." + regType.substr(0, regType.length - 1) + "defaultwhois").attr("disabled", false); 2548 jQuery('#' + regType.substr(0, regType.length - 1) + '1').attr("checked", "checked"); 2549} 2550 2551/** 2552 * Submit the first form that exists within a given container. 2553 * 2554 * @param {string} containerId The ID name of the container 2555 */ 2556 function autoSubmitFormByContainer(containerId) { 2557 if (typeof noAutoSubmit === "undefined" || noAutoSubmit === false) { 2558 jQuery("#" + containerId).find("form:first").submit(); 2559 } 2560 } 2561 2562/** 2563 * Submit custom fields and disable default whois info. 2564 * 2565 * @param {string} regType The contact registration type 2566 */ 2567function useCustomWhois(regType) { 2568 jQuery("." + regType.substr(0, regType.length - 1) + "customwhois").attr("disabled", false); 2569 jQuery("." + regType.substr(0, regType.length - 1) + "defaultwhois").attr("disabled", true); 2570 jQuery('#' + regType.substr(0, regType.length - 1) + '2').attr("checked", "checked"); 2571} 2572 2573 2574if (typeof Waves !== 'undefined') { 2575 Waves.init(); 2576 Waves.attach(".sidebar-holder .nav a", ["waves-light", "waves-round"]);
2577} 2578 2579/** 2580 * Smooth scroll to named element. 2581 */ 2582function smoothScroll(element) { 2583 $('html, body').animate({ 2584 scrollTop: $(element).offset().top 2585 }, 500); 2586} 2587 2588function irtpSubmit() 2589{ 2590 allowSubmit = true; 2591 var optOut = 0, 2592 optOutCheckbox = jQuery('#modalIrtpOptOut'), 2593 optOutReason = jQuery('#modalReason'), 2594 formOptOut = jQuery('#irtpOptOut'), 2595 formOptOutReason = jQuery('#irtpOptOutReason'); 2596 2597 if (optOutCheckbox.is(':checked')) { 2598 optOut = 1; 2599 } 2600 formOptOut.val(optOut); 2601 formOptOutReason.val(optOutReason.val()); 2602 jQuery('#frmDomainContactModification').submit(); 2603} 2604 2605/*! 2606 * WHMCS Ajax Driven Modal Framework 2607 * 2608 * @copyright Copyright (c) WHMCS Limited 2005-2019 2609 * @license http://www.whmcs.com/license/ WHMCS Eula 2610 */ 2611var ajaxModalSubmitEvents = []; 2612jQuery(document).ready(function(){ 2613 jQuery(document).on('click', '.open-modal', function(e) { 2614 e.preventDefault(); 2615 var url = jQuery(this).attr('href'), 2616 modalSize = jQuery(this).data('modal-size'), 2617 modalClass = jQuery(this).data('modal-class'), 2618 modalTitle = jQuery(this).data('modal-title'), 2619 submitId = jQuery(this).data('btn-submit-id'), 2620 submitLabel = jQuery(this).data('btn-submit-label'), 2621 hideClose = jQuery(this).data('btn-close-hide'), 2622 disabled = jQuery(this).attr('disabled'), 2623 successDataTable = jQuery(this).data('datatable-reload-success'); 2624 2625 if (!disabled) { 2626 openModal(url, '', modalTitle, modalSize, modalClass, submitLabel, submitId, hideClose, successDataTable); 2627 } 2628 }); 2629 2630 // define modal close reset action 2631 jQuery('#modalAjax').on('hidden.bs.modal', function (e) { 2632 if (jQuery(this).hasClass('modal-feature-highlights')) { 2633 var dismissForVersion = jQuery('#cbFeatureHighlightsDismissForVersion').is(':checked'); 2634 WHMCS.http.jqClient.post( 2635 'whatsnew.php', 2636 { 2637 dismiss: "1", 2638 until_next_update: dismissForVersion ? '1' : '0', 2639 token: csrfToken 2640 } 2641 ); 2642 } 2643 2644 jQuery('#modalAjax').find('.modal-body').empty(); 2645 jQuery('#modalAjax').children('div.modal-dialog').removeClass('modal-lg'); 2646 jQuery('#modalAjax').removeClass().addClass('modal whmcs-modal fade'); 2647 jQuery('#modalAjax .modal-title').html('Title'); 2648 jQuery('#modalAjax .modal-submit').html('Submit') 2649 .removeClass() 2650 .addClass('btn btn-primary modal-submit') 2651 .removeAttr('id') 2652 .removeAttr('disabled'); 2653 jQuery('#modalAjax .loader').show(); 2654 }); 2655}); 2656 2657function openModal(url, postData, modalTitle, modalSize, modalClass, submitLabel, submitId, hideClose, successDataTable) { 2658 //set the text of the modal title 2659 jQuery('#modalAjax .modal-title').html(modalTitle); 2660 2661 // set the modal size via a class attribute 2662 if (modalSize) { 2663 jQuery('#modalAjax').children('div[class="modal-dialog"]').addClass(modalSize); 2664 } 2665 // set the modal class 2666 if (modalClass) { 2667 jQuery('#modalAjax').addClass(modalClass); 2668 } 2669 2670 // set the modal class 2671 if (modalClass) { 2672 jQuery('#modalAjax').addClass(modalClass); 2673 } 2674 2675 // set the text of the submit button 2676 if(!submitLabel){ 2677 jQuery('#modalAjax .modal-submit').hide(); 2678 } else { 2679 jQuery('#modalAjax .modal-submit').show().html(submitLabel); 2680 // set the button id so we can target the click function of it. 2681 if (submitId) { 2682 jQuery('#modalAjax .modal-submit').attr('id', submitId); 2683 } 2684 } 2685 2686 if (hideClose) { 2687 jQuery('#modalAjaxClose').hide(); 2688 } 2689 2690 jQuery('#modalAjax .modal-body').html(''); 2691 2692 jQuery('#modalSkip').hide(); 2693 jQuery('#modalAjax .modal-submit').prop('disabled', true); 2694 2695 // show modal 2696 jQuery('#modalAjax').modal('show'); 2697 2698 // fetch modal content 2699 WHMCS.http.jqClient.post(url, postData, function(data) { 2700 updateAjaxModal(data); 2701 }, 'json').fail(function() { 2702 jQuery('#modalAjax .modal-body').html('An error occurred while communicating with the server. Please try again.'); 2703 jQuery('#modalAjax .loader').fadeOut(); 2704 }).always(function () { 2705 if (successDataTable) { 2706 var modalForm = jQuery('#modalAjax').find('form'); 2707 modalForm.data('successDataTable', successDataTable); 2708 } 2709 });
2710 2711 //define modal submit button click 2712 if (submitId) { 2713 /** 2714 * Reloading ajax modal multiple times on the same page can add 2715 * multiple "on" click events which submits the same form over 2716 * and over. 2717 * Remove the on click event with "off" to avoid multiple growl 2718 * and save events being run. 2719 * 2720 * @see http://api.jquery.com/off/ 2721 */ 2722 var submitButton = jQuery('#' + submitId); 2723 submitButton.off('click'); 2724 submitButton.on('click', submitIdAjaxModalClickEvent); 2725 } 2726} 2727 2728function submitIdAjaxModalClickEvent () 2729{ 2730 if (jQuery(this).hasClass('disabled')) { 2731 return; 2732 } 2733 var canContinue = true; 2734 if (ajaxModalSubmitEvents.length) { 2735 jQuery.each(ajaxModalSubmitEvents, function (index, value) { 2736 var fn = window[value]; 2737 if (canContinue && typeof fn === 'function') { 2738 canContinue = fn(); 2739 } 2740 }); 2741 } 2742 if (!canContinue) { 2743 return; 2744 } 2745 var modalForm = jQuery('#modalAjax').find('form'); 2746 var modalBody = jQuery('#modalAjax .modal-body'); 2747 var modalErrorContainer = jQuery(modalBody).find('.admin-modal-error'); 2748 2749 jQuery('#modalAjax .loader').show(); 2750 jQuery(modalErrorContainer).slideUp(); 2751 2752 var modalPost = WHMCS.http.jqClient.post( 2753 modalForm.attr('action'), 2754 modalForm.serialize(), 2755 function(data) { 2756 if (modalForm.data('successDataTable')) { 2757 data.successDataTable = modalForm.data('successDataTable'); 2758 } 2759 updateAjaxModal(data); 2760 }, 2761 'json' 2762 ).fail(function(xhr) { 2763 var data = xhr.responseJSON; 2764 var genericErrorMsg = 'An error occurred while communicating with the server. Please try again.'; 2765 if (data && data.data) { 2766 data = data.data; 2767 if (data.errorMsg) { 2768 if (modalErrorContainer.length > 0) { 2769 jQuery(modalErrorContainer) 2770 .html(data.errorMsg) 2771 .slideDown(); 2772 } else { 2773 jQuery.growl.warning({title: data.errorMsgTitle, message: data.errorMsg}); 2774 } 2775 } else if (data.data.body) { 2776 jQuery(modalBody).html(data.body); 2777 } else { 2778 jQuery(modalBody).html(genericErrorMsg); 2779 } 2780 } else { 2781 jQuery(modalBody).html(genericErrorMsg); 2782 } 2783 jQuery('#modalAjax .loader').fadeOut(); 2784 }); 2785} 2786 2787function updateAjaxModal(data) { 2788 if (data.reloadPage) { 2789 if (typeof data.reloadPage === 'string') { 2790 window.location = data.reloadPage; 2791 } else { 2792 window.location.reload(); 2793 } 2794 return; 2795 } 2796 if (data.successDataTable) { 2797 WHMCS.ui.dataTable.getTableById(data.successDataTable, undefined).ajax.reload(); 2798 } 2799 if (data.redirect) { 2800 window.location = data.redirect; 2801 dialogClose(); 2802 } 2803 if (data.successWindow && typeof window[data.successWindow] === "function") { 2804 window[data.successWindow](); 2805 } 2806 if (data.dismiss) { 2807 dialogClose(); 2808 } 2809 if (data.successMsg) { 2810 jQuery.growl.notice({ title: data.successMsgTitle, message: data.successMsg }); 2811 } 2812 if (data.errorMsg) { 2813 var inModalErrorContainer = jQuery('#modalAjax .modal-body .admin-modal-error'); 2814 2815 if (inModalErrorContainer.length > 0 && !data.dismiss) { 2816 jQuery(inModalErrorContainer) 2817 .html(data.errorMsg) 2818 .slideDown(); 2819 } else { 2820 jQuery.growl.warning({title: data.errorMsgTitle, message: data.errorMsg}); 2821 } 2822 } 2823 if (data.title) { 2824 jQuery('#modalAjax .modal-title').html(data.title); 2825 } 2826 if (data.body) { 2827 jQuery('#modalAjax .modal-body').html(data.body); 2828 } else { 2829 if (data.url) { 2830 WHMCS.http.jqClient.post(data.url, '', function(data2) {
2831 jQuery('#modalAjax').find('.modal-body').html(data2.body); 2832 }, 'json').fail(function() { 2833 jQuery('#modalAjax').find('.modal-body').html('An error occurred while communicating with the server. Please try again.'); 2834 jQuery('#modalAjax').find('.loader').fadeOut(); 2835 }); 2836 } 2837 } 2838 if (data.submitlabel) { 2839 jQuery('#modalAjax .modal-submit').html(data.submitlabel).show(); 2840 if (data.submitId) { 2841 jQuery('#modalAjax').find('.modal-submit').attr('id', data.submitId); 2842 } 2843 } 2844 2845 if (data.submitId) { 2846 /** 2847 * Reloading ajax modal multiple times on the same page can add 2848 * multiple "on" click events which submits the same form over 2849 * and over. 2850 * Remove the on click event with "off" to avoid multiple growl 2851 * and save events being run. 2852 * 2853 * @see http://api.jquery.com/off/ 2854 */ 2855 var submitButton = jQuery('#' + data.submitId); 2856 submitButton.off('click'); 2857 submitButton.on('click', function() { 2858 var modalForm = jQuery('#modalAjax').find('form'); 2859 jQuery('#modalAjax .loader').show(); 2860 var modalPost = WHMCS.http.jqClient.post(modalForm.attr('action'), modalForm.serialize(), 2861 function(data) { 2862 updateAjaxModal(data); 2863 }, 'json').fail(function() { 2864 jQuery('#modalAjax .modal-body').html('An error occurred while communicating with the server. Please try again.'); 2865 jQuery('#modalAjax .loader').fadeOut(); 2866 } 2867 ); 2868 }) 2869 } 2870 2871 jQuery('#modalAjax .loader').fadeOut(); 2872 jQuery('#modalAjax .modal-submit').removeProp('disabled'); 2873} 2874 2875// backwards compat for older dialog implementations 2876 2877function dialogSubmit() { 2878 jQuery('#modalAjax .modal-submit').prop("disabled", true); 2879 jQuery('#modalAjax .loader').show(); 2880 var postUrl = jQuery('#modalAjax').find('form').attr('action'); 2881 WHMCS.http.jqClient.post(postUrl, jQuery('#modalAjax').find('form').serialize(), 2882 function(data) { 2883 updateAjaxModal(data); 2884 }, 'json').fail(function() { 2885 jQuery('#modalAjax .modal-body').html('An error occurred while communicating with the server. Please try again.'); 2886 jQuery('#modalAjax .loader').fadeOut(); 2887 }); 2888} 2889 2890function dialogClose() { 2891 jQuery('#modalAjax').modal('hide'); 2892} 2893 2894function addAjaxModalSubmitEvents(functionName) { 2895 if (functionName) { 2896 ajaxModalSubmitEvents.push(functionName); 2897 } 2898} 2899 2900function removeAjaxModalSubmitEvents(functionName) { 2901 if (functionName) { 2902 var index = ajaxModalSubmitEvents.indexOf(functionName); 2903 if (index >= 0) { 2904 ajaxModalSubmitEvents.splice(index, 1); 2905 } 2906 } 2907} 2908 2909 2910jQuery(document).ready(function() { 2911 2912 /** 2913 * Code will loop through each element that has the class markdown-editor and 2914 * enable the Markdown editor. 2915 */ 2916 var count = 0, 2917 editorName = 'clientMDE', 2918 counter = 0; 2919 jQuery(".markdown-editor").each(function( index ) { 2920 count++; 2921 var autoSaveName = jQuery(this).data('auto-save-name'), 2922 footerId = jQuery(this).attr('id') + '-footer'; 2923 if (typeof autoSaveName == "undefined") { 2924 autoSaveName = 'client_area'; 2925 } 2926 window[editorName + count.toString()] = jQuery(this).markdown( 2927 { 2928 footer: '<div id="' + footerId + '" class="markdown-editor-status"></div>', 2929 autofocus: false, 2930 savable: false, 2931 resize: 'vertical', 2932 iconlibrary: 'glyph', 2933 language: locale, 2934 onShow: function(e){ 2935 var content = '', 2936 save_enabled = false; 2937 if(typeof(Storage) !== "undefined") { 2938 // Code for localStorage/sessionStorage. 2939 content = localStorage.getItem(autoSaveName); 2940 save_enabled = true; 2941 if (content && typeof(content) !== "undefined") { 2942 e.setContent(content); 2943 } 2944 } 2945 jQuery("#" + footerId).html(parseMdeFooter(content, save_enabled, saved)); 2946 }, 2947 onChange: function(e){
2948 var content = e.getContent(), 2949 save_enabled = false; 2950 if(typeof(Storage) !== "undefined") { 2951 counter = 3; 2952 save_enabled = true; 2953 localStorage.setItem(autoSaveName, content); 2954 doCountdown(); 2955 } 2956 jQuery("#" + footerId).html(parseMdeFooter(content, save_enabled)); 2957 }, 2958 onPreview: function(e){ 2959 var originalContent = e.getContent(), 2960 parsedContent; 2961 2962 jQuery.ajax({ 2963 url: 'clientarea.php', 2964 async: false, 2965 data: {token: csrfToken, action: 'parseMarkdown', content: originalContent}, 2966 dataType: 'json', 2967 success: function (data) { 2968 parsedContent = data; 2969 } 2970 }); 2971 2972 return parsedContent.body ? parsedContent.body : ''; 2973 }, 2974 additionalButtons: [ 2975 [{ 2976 name: "groupCustom", 2977 data: [{ 2978 name: "cmdHelp", 2979 title: "Help", 2980 hotkey: "Ctrl+F1", 2981 btnClass: "btn open-modal", 2982 icon: { 2983 glyph: 'fas fa-question-circle', 2984 fa: 'fa fa-question-circle', 2985 'fa-3': 'icon-question-sign' 2986 }, 2987 callback: function(e) { 2988 e.$editor.removeClass("md-fullscreen-mode"); 2989 } 2990 }] 2991 }] 2992 ], 2993 hiddenButtons: [ 2994 'cmdImage' 2995 ] 2996 }); 2997 jQuery('button[data-handler="bootstrap-markdown-cmdHelp"]') 2998 .attr('data-modal-title', markdownGuide) 2999 .attr('href', 'submitticket.php?action=markdown'); 3000 3001 jQuery(this).closest("form").bind({ 3002 submit: function() { 3003 if(typeof(Storage) !== "undefined") { 3004 localStorage.removeItem(autoSaveName); 3005 } 3006 } 3007 }); 3008 }); 3009 3010 // Email verification 3011 jQuery('#btnResendVerificationEmail').click(function() { 3012 WHMCS.http.jqClient.post('clientarea.php', 3013 { 3014 'token': csrfToken, 3015 'action': 'resendVerificationEmail' 3016 }).done(function(data) { 3017 jQuery('#btnResendVerificationEmail').html('Email Sent').prop('disabled', true); 3018 }); 3019 }); 3020 3021 /** 3022 * Parse the content to populate the markdown editor footer. 3023 * 3024 * @param {string} content 3025 * @param {bool} auto_save 3026 * @param {string} [saveText] 3027 * @returns {string} 3028 */ 3029 function parseMdeFooter(content, auto_save, saveText) 3030 { 3031 saveText = saveText || saving; 3032 var pattern = /[a-zA-Z0-9_\u0392-\u03c9]+|[\u4E00-\u9FFF\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af]+/g, 3033 m = [], 3034 word_count = 0, 3035 line_count = 0; 3036 if (content) { 3037 m = content.match(pattern); 3038 line_count = content.split(/\\r\\n|\\r|\\n/).length; 3039 } 3040 for(var i = 0; i < m.length; i++) { 3041 if(m[i].charCodeAt(0) >= 0x4E00) { 3042 word_count += m[i].length; 3043 } else { 3044 word_count += 1; 3045 } 3046 } 3047 return '<div class="small-font">lines: ' + line_count 3048 + ' words: ' + word_count + '' 3049 + (auto_save ? ' <span class="markdown-save">' + saveText + '</span>' : '') 3050 + '</div>'; 3051 } 3052 3053 /** 3054 * Countdown the save timeout. When zero, the span will update to show saved. 3055 */ 3056 function doCountdown() 3057 { 3058 if (counter >= 0) { 3059 if (counter == 0) { 3060 jQuery("span.markdown-save").html(saved); 3061 } 3062 counter--; 3063 setTimeout(doCountdown, 1000); 3064 } 3065 } 3066 3067 // Two-Factor Activation Process Modal Handler. 3068 var frmTwoFactorActivation = jQuery('input[name=2fasetup]').parent('form'); 3069 frmTwoFactorActivation.submit(function(e) { 3070 e.preventDefault(); 3071 openModal(frmTwoFactorActivation.attr('action'), frmTwoFactorActivation.seri
3071alize(), 'Loading...'); 3072 }); 3073 3074 jQuery.fn.showInputError = function () { 3075 this.parents('.form-group').addClass('has-error').find('.field-error-msg').show(); 3076 return this; 3077 }; 3078 3079 jQuery('#frmPayment').on('submit', function() { 3080 var btn = jQuery('#btnSubmit'); 3081 btn.find('span').toggleClass('hidden'); 3082 btn.prop('disabled', true).addClass('disabled'); 3083 }); 3084 3085 // SSL Manage Action Button. 3086jQuery('.btn-resend-approver-email').click(function () { 3087 WHMCS.http.jqClient.post( 3088 jQuery(this).data('url'), 3089 { 3090 addonId: jQuery(this).data('addonid'), 3091 serviceId: jQuery(this).data('serviceid'), 3092 }, 3093 function(data) { 3094 if (data.success == true) { 3095 jQuery('.alert-table-ssl-manage').addClass('alert-success').text('Approver Email Resent').removeClass('hidden'); 3096 } else { 3097 jQuery('.alert-table-ssl-manage').addClass('alert-danger').text('Error: ' + data.message).removeClass('hidden'); 3098 } 3099 } 3100 ); 3101}); 3102 3103 // Domain Pricing Table Filters 3104 jQuery(".tld-filters a").click(function(e) { 3105 e.preventDefault(); 3106 3107 if (jQuery(this).hasClass('label-success')) { 3108 jQuery(this).removeClass('label-success'); 3109 } else { 3110 jQuery(this).addClass('label-success'); 3111 } 3112 3113 jQuery('.tld-row').removeClass('filtered-row'); 3114 jQuery('.tld-filters a.label-success').each(function(index) { 3115 var filterValue = jQuery(this).data('category'); 3116 jQuery('.tld-row[data-category*="' + filterValue + '"]').addClass('filtered-row'); 3117 }); 3118 jQuery(".filtered-row:even").removeClass('highlighted'); 3119 jQuery(".filtered-row:odd").addClass('highlighted'); 3120 jQuery('.tld-row:not(".filtered-row")').fadeOut('', function() { 3121 if (jQuery('.filtered-row').size() === 0) { 3122 jQuery('.tld-row.no-tlds').show(); 3123 } else { 3124 jQuery('.tld-row.no-tlds').hide(); 3125 } 3126 }); 3127 jQuery('.tld-row.filtered-row').fadeIn(); 3128 }); 3129 jQuery(".filtered-row:even").removeClass('highlighted'); 3130 jQuery(".filtered-row:odd").addClass('highlighted'); 3131 3132// DataTable data-driven auto object registration 3133WHMCS.ui.dataTable.register(); 3134 3135 // Bootstrap Confirmation popup auto object registration 3136 WHMCS.ui.confirmation.register(); 3137 3138 WHMCS.ui.jsonForm.initAll(); 3139 3140jQuery('#frmReply').submit(function(e) { 3141 jQuery('#frmReply').find('input[type="submit"]').addClass('disabled').prop('disabled', true); 3142 }); 3143 3144jQuery('#frmDomainContactModification').on('submit', function(){ 3145 if (!allowSubmit) { 3146 var changed = false; 3147 jQuery('.irtp-field').each(function() { 3148 var value = jQuery(this).val(), 3149 originalValue = jQuery(this).data('original-value'); 3150 if (value !== originalValue) { 3151 changed = true; 3152 } 3153 }); 3154 if (changed) { 3155 jQuery('#modalIRTPConfirmation').modal('show'); 3156 return false; 3157 } 3158 } 3159 return true; 3160}); 3161 3162jQuery('.ssl-state.ssl-sync').each(function () { 3163 var self = jQuery(this); 3164 WHMCS.http.jqClient.post( 3165 WHMCS.utils.getRouteUrl('/domain/ssl-check'), 3166 { 3167 'type': self.parent('td').data('type'), 3168 'domain': self.parent('td').data('domain'), 3169 'token': csrfToken 3170 }, 3171 function (data) { 3172 if (data.invalid) { 3173 self.hide(); 3174 } else { 3175 self.replaceWith( 3176 '<img src="' + data.image + '" data-toggle="tooltip" title="' + data.tooltip + '" class="' + data.class + '">' 3177 ); 3178 } 3179 } 3180 ); 3181}); 3182jQuery(document).on('click', '.ssl-state.ssl-inactive', function(e) { 3183 e.preventDefault(); 3184 window.location.href = WHMCS.utils.getRouteUrl('/ssl-purchase'); 3185}); 3186 3187WHMCS.recaptcha.register(); 3188 3189var dynamicRecaptchaContainer = jQuery('#divDynamicRecaptcha'); 3190var homepageHasRecaptcha = jQuery(dynamicRecaptchaContainer).length > 0;
3191var homepageHasInvisibleRecaptcha = homepageHasRecaptcha && jQuery(dynamicRecaptchaContainer).data('size') === 'invisible'; 3192 3193if (homepageHasRecaptcha && !homepageHasInvisibleRecaptcha) { 3194 jQuery('section#home-banner').addClass('with-recaptcha'); 3195} 3196 3197if (jQuery('.domainchecker-homepage-captcha').length && !homepageHasInvisibleRecaptcha) { 3198 // invisible reCaptcha doesn't play well with onsubmit() handlers on all submissions following a prevented one 3199 3200 jQuery('#frmDomainHomepage').submit(function (e) { 3201 var frmDomain = jQuery('#frmDomainHomepage'), 3202 inputDomain = jQuery(frmDomain).find('input[name="domain"]'), 3203 reCaptchaContainer = jQuery('#divDynamicRecaptcha'), 3204 reCaptcha = jQuery('#g-recaptcha-response'), 3205 captcha = jQuery('#inputCaptcha'); 3206 3207 if (reCaptcha.length && !reCaptcha.val()) { 3208 reCaptchaContainer.tooltip('show'); 3209 3210 e.preventDefault(); 3211 return; 3212 } 3213 3214 if (captcha.length && !captcha.val()) { 3215 captcha.tooltip('show'); 3216 3217 e.preventDefault(); 3218 return; 3219 } 3220 }); 3221} 3222 $('.icheck-button').iCheck({ 3223 inheritID: true, 3224 checkboxClass: 'icheckbox_square-blue', 3225 radioClass: 'iradio_square-blue', 3226 increaseArea: '20%' 3227 }); 3228 3229 jQuery('#inputNoStore').on('switchChange.bootstrapSwitch', function(event, state) { 3230 var descContainer = jQuery('#inputDescription'); 3231 if (!state) { 3232 descContainer.prop('disabled', true).addClass('disabled'); 3233 } 3234 if (state) { 3235 descContainer.removeClass('disabled').prop('disabled', false); 3236 } 3237 }); 3238}); 3239 3240/**
3241 * WHMCS Telephone Country Code Dropdown 3242 * 3243 * Using https://github.com/jackocnr/intl-tel-input 3244 * 3245 * @copyright Copyright (c) WHMCS Limited 2005-2017 3246 * @license http://www.whmcs.com/license/ WHMCS Eula 3247 */ 3248// 3249// jQuery(document).ready(function() { 3250// if (jQuery('body').data('phone-cc-input')) { 3251// var phoneInput = jQuery('input[name^="phone"], input[name$="phone"]').not('input[type="hidden"]'); 3252// if (phoneInput.length) { 3253// var countryInput = jQuery('[name^="country"], [name$="country"]'), 3254// initialCountry = 'us'; 3255// if (countryInput.length) { 3256// initialCountry = countryInput.val().toLowerCase(); 3257// if (initialCountry === 'um') { 3258// initialCountry = 'us'; 3259// } 3260// } 3261// 3262// phoneInput.each(function(){ 3263// var thisInput = jQuery(this), 3264// inputName = thisInput.attr('name'); 3265// jQuery(this).before( 3266// '<input id="populatedCountryCode' + inputName + '" type="hidden" name="country-calling-code-' + inputName + '" value="" />' 3267// ); 3268// thisInput.intlTelInput({ 3269// preferredCountries: [initialCountry, "us", "gb"].filter(function(value, index, self) { 3270// return self.indexOf(value) === index; 3271// }), 3272// initialCountry: initialCountry, 3273// autoPlaceholder: 'polite', //always show the helper placeholder 3274// separateDialCode: true 3275// }); 3276// 3277// thisInput.on('countrychange', function (e, countryData) { 3278// jQuery('#populatedCountryCode' + inputName).val(countryData.dialCode); 3279// if (jQuery(this).val() === '+' + countryData.dialCode) { 3280// jQuery(this).val(''); 3281// } 3282// }); 3283// thisInput.on('blur keydown', function (e) { 3284// if (e.type === 'blur' || (e.type === 'keydown' && e.keyCode === 13)) { 3285// var number = jQuery(this).intlTelInput("getNumber"), 3286// countryData = jQuery(this).intlTelInput("getSelectedCountryData"), 3287// countryPrefix = '+' + countryData.dialCode; 3288// 3289// if (number.indexOf(countryPrefix) === 0 && (number.match(/\+/g) || []).length > 1) { 3290// number = number.substr(countryPrefix.length); 3291// } 3292// jQuery(this).intlTelInput("setNumber", number); 3293// } 3294// }); 3295// jQuery('#populatedCountryCode' + inputName).val(thisInput.intlTelInput('getSelectedCountryData').dialCode); 3296// 3297// countryInput.on('change', function() { 3298// if (thisInput.val() === '') { 3299// var country = jQuery(this).val().toLowerCase(); 3300// if (country === 'um') { 3301// country = 'us'; 3302// } 3303// phoneInput.intlTelInput('setCountry', country); 3304// } 3305// }); 3306// // this must be .attr (not .data) in order for it to be found by [data-initial-value] selector 3307// thisInput.attr('data-initial-value', $(thisInput).val()); 3308// 3309// thisInput.parents('form').find('input[type=reset]').each(function() { 3310// var resetButton = this; 3311// var form = $(resetButton).parents('form'); 3312// 3313// if (!$(resetButton).data('phone-handler')) { 3314// $(resetButton).data('phone-handler', true); 3315// 3316// $(resetButton).click(function(e) { 3317// e.stopPropagation();
3318// 3319// $(form).trigger('reset'); 3320// 3321// $(form).find('input[data-initial-value]').each(function() { 3322// var inputToReset = this; 3323// 3324// $(inputToReset).val( 3325// $(inputToReset).attr('data-initial-value') 3326// ); 3327// }); 3328// 3329// return false; 3330// }); 3331// } 3332// }); 3333// }); 3334// 3335// /** 3336// * In places where a form icon is present, hide it. 3337// * Where the input has a class of field, remove that and add form-control in place. 3338// */ 3339// phoneInput.parents('div.form-group').find('.field-icon').addClass('hidden').end(); 3340// phoneInput.removeClass('field').addClass('form-control'); 3341// } 3342// 3343// var registrarPhoneInput = jQuery('input[name$="][Phone Number]"], input[name$="][Phone]"]').not('input[type="hidden"]'); 3344// if (registrarPhoneInput.length) { 3345// jQuery.each(registrarPhoneInput, function(index, input) { 3346// var thisInput = jQuery(this), 3347// inputName = thisInput.attr('name'); 3348// inputName = inputName.replace('contactdetails[', '').replace('][Phone Number]', '').replace('][Phone]', ''); 3349// 3350// var countryInput = jQuery('[name$="' + inputName + '][Country]"]'), 3351// initialCountry = countryInput.val().toLowerCase(); 3352// if (initialCountry === 'um') { 3353// initialCountry = 'us'; 3354// } 3355// 3356// thisInput.before('<input id="populated' + inputName + 'CountryCode" type="hidden" name="contactdetails[' + inputName + '][Phone Country Code]" value="" />'); 3357// thisInput.intlTelInput({ 3358// preferredCountries: [initialCountry, "us", "gb"].filter(function(value, index, self) { 3359// return self.indexOf(value) === index; 3360// }), 3361// initialCountry: initialCountry, 3362// autoPlaceholder: 'polite', //always show the helper placeholder 3363// separateDialCode: true 3364// }); 3365// 3366// thisInput.on('countrychange', function (e, countryData) { 3367// jQuery('#populated' + inputName + 'CountryCode').val(countryData.dialCode); 3368// if (jQuery(this).val() === '+' + countryData.dialCode) { 3369// jQuery(this).val(''); 3370// } 3371// }); 3372// thisInput.on('blur keydown', function (e) { 3373// if (e.type === 'blur' || (e.type === 'keydown' && e.keyCode === 13)) { 3374// var number = jQuery(this).intlTelInput("getNumber"), 3375// countryData = jQuery(this).intlTelInput("getSelectedCountryData"), 3376// countryPrefix = '+' + countryData.dialCode; 3377// 3378// if (number.indexOf(countryPrefix) === 0 && (number.match(/\+/g) || []).length > 1) { 3379// number = number.substr(countryPrefix.length); 3380// } 3381// jQuery(this).intlTelInput("setNumber", number); 3382// } 3383// }); 3384// jQuery('#populated' + inputName + 'CountryCode').val(thisInput.intlTelInput('getSelectedCountryData').dialCode); 3385// 3386// countryInput.on('blur', function() { 3387// if (thisInput.val() === '') { 3388// var country = jQuery(this).val().toLowerCase(); 3389// if (country === 'um') { 3390// country = 'us'; 3391// } 3392// thisInput.intlTelInput('setCountry', country);
3393// } 3394// }); 3395// 3396// }); 3397// } 3398// } 3399// });
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.