PageSourceSearch

https://client.brixly.uk/assets/js/whmcs-twofa-backup-modal.js?v=1

js brixly.uk collected 2026-09-29 05:15:09 UTC 6,934 bytes, 186 lines download raw bytes

1/**
2 * 2FA backup code: delegated copy/download + inject toolbar when core HTML has no Smarty partial.
3 * Required because jQuery .html() does not run <script> tags, and bundled theme JS does not include this.
4 */
5(function () {
6    'use strict';
7
8    function whmcsTwofaBackupEnsureDelegation() {
9        if (window.whmcsTwofaBackupDelegationBound) {
10            return;
11        }
12        window.whmcsTwofaBackupDelegationBound = true;
13        document.addEventListener('click', function (ev) {
14            var copyBtn = ev.target.closest('.twofa-backup-copy');
15            if (copyBtn) {
16                ev.preventDefault();
17                var wrap = copyBtn.closest('.twofa-backup-code-wrap');
18                if (!wrap) {
19                    return;
20                }
21                var valEl = wrap.querySelector('.twofa-backup-code-value');
22                if (!valEl) {
23                    return;
24                }
25                var text = (valEl.textContent || '').replace(/\s+/g, ' ').trim();
26                if (!text) {
27                    return;
28                }
29                function copyText(t) {
30                    if (navigator.clipboard && window.isSecureContext) {
31                        return navigator.clipboard.writeText(t);
32                    }
33                    var x = document.createElement('textarea');
34                    x.value = t;
35                    x.style.position = 'fixed';
36                    x.style.left = '-9999px';
37                    document.body.appendChild(x);
38                    x.select();
39                    try {
40                        return document.execCommand('copy') ? Promise.resolve() : Promise.reject();
41                    } finally {
42                        document.body.removeChild(x);
43                    }
44                }
45                copyText(text)
46                    .then(function () {
47                        if (typeof jQuery !== 'undefined' && jQuery.growl) {
48                            jQuery.growl.notice({ message: 'Copied to clipboard.' });
49                        }
50                    })
51                    .catch(function () {
52                        window.alert('Could not copy. Please select and copy the code manually.');
53                    });
54                return;
55            }
56            var dlBtn = ev.target.closest('.twofa-backup-download');
57            if (dlBtn) {
58                ev.preventDefault();
59                var wrap = dlBtn.closest('.twofa-backup-code-wrap');
60                if (!wrap) {
61                    return;
62                }
63                var valEl = wrap.querySelector('.twofa-backup-code-value');
64                if (!valEl) {
65                    return;
66                }
67                var text = (valEl.textContent || '').replace(/\s+/g, ' ').trim();
68                if (!text) {
69                    return;
70                }
71                var blob = new Blob(['WHMCS two-factor backup code\r\n\r\n' + text + '\r\n'], {
72                    type: 'text/plain;charset=utf-8',
73                });
74                var u = URL.createObjectURL(blob);
75                var a = document.createElement('a');
76                a.href = u;
77                a.download = 'whmcs-2fa-backup-code.txt';
78                document.body.appendChild(a);
79                a.click();
80                document.body.removeChild(a);
81                URL.revokeObjectURL(u);
82            }
83        });
84    }
85
86    function enhanceWhmcsModalTwoFactorBackupCode() {
87        var body = document.querySelector('#modalAjax .modal-body');
88        if (!body || body.querySelector('.twofa-backup-code-wrap')) {
89            return;
90        }
91        var re = /^[a-f0-9]{4}(?:\s+[a-f0-9]{4}){3}$/i;
92        var candidates = body.querySelectorAll('p, div, span, strong, td, code, kbd, h1, h2, h3, h4, h5, h6');
93        var el = null;
94        var i;
95        for (i = 0; i < candidates.length; i++) {
96            var c = candidates[i];
97            if (c.closest('.twofa-backup-code-wrap')) {
98                continue;
99            }
100            var t = (c.textContent || '').trim().replace(/\s+/g, ' ');
101            if (re.test(t) && t.length < 48) {
102                el = c;
103                break;
104            }
105        }
106        if (!el) {
107            return;
108        }
109        var codeText = el.textContent.trim().replace(/\s+/g, ' ');
110        el.style.display = 'none';
111        el.classList.add('twofa-backup-code-enhanced-source');
112
113        var toolbar = document.createElement('div');
114        toolbar.className = 'alert alert-success text-center twofa-backup-code-wrap';
115        toolbar.setAttribute('role', 'region');
116
117        var inner = document.createElement('div');
118        inner.className = 'd-flex align-items-center justify-content-center flex-wrap';
119        inner.style.gap = '10px';
120
121        var val = document.createElement('span');
122        val.className = 'twofa-backup-code-value';
123        val.style.cssText = 'font-size:1.25em;font-weight:600;letter-spacing:0.05em;';
124        val.textContent = codeText;
125
126        var group = document.createElement('span');
127        group.className = 'btn-group';
128
129        var b1 = document.createElement('button');
130        b1.type = 'button';
131        b1.className = 'btn btn-default btn-sm twofa-backup-copy';
132        b1.title = 'Copy';
133        b1.setAttribute('aria-label', 'Copy');
134        b1.innerHTML = '<i class="ls ls-copy" aria-hidden="true"></i>';
135
136        var b2 = document.createElement('button');
137        b2.type = 'button';
138        b2.className = 'btn btn-default btn-sm twofa-backup-download';
139        b2.title = 'Download';
140        b2.setAttribute('aria-label', 'Download');
141        b2.innerHTML = '<i class="ls ls-download" aria-hidden="true"></i>';
142
143        group.appendChild(b1);
144        group.appendChild(b2);
145        inner.appendChild(val);
146        inner.appendChild(group);
147        toolbar.appendChild(inner);
148
149        if (el.nextSibling) {
150            el.parentNode.insertBefore(toolbar, el.nextSibling);
151        } else {
152            el.parentNode.appendChild(toolbar);
153        }
154    }
155
156    function patchUpdateAjaxModal() {
157        if (window.whmcsTwofaBackupUpdateAjaxModalPatched) {
158            return;
159        }
160        if (typeof window.updateAjaxModal !== 'function') {
161            return;
162        }
163        window.whmcsTwofaBackupUpdateAjaxModalPatched = true;
164        var orig = window.updateAjaxModal;
165        window.updateAjaxModal = function (data) {
166            orig(data);
167            enhanceWhmcsModalTwoFactorBackupCode();
168        };
169    }
170
171    function init() {
172        whmcsTwofaBackupEnsureDelegation();
173        patchUpdateAjaxModal();
174        enhanceWhmcsModalTwoFactorBackupCode();
175    }
176
177    if (typeof jQuery !== 'undefined') {
178        jQuery(init);
179    } else {
180        if (document.readyState === 'loading') {
181            document.addEventListener('DOMContentLoaded', init);
182        } else {
183            init();
184        }
185    }
186})();

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.