PageSourceSearch

https://theprivateclinic.me/wp-content/plugins/Mental_Health_Assessments/js/mht-save.js?ver=2.0

js theprivateclinic.me collected 2026-09-24 15:06:03 UTC 16,179 bytes, 314 lines download raw bytes

1/**
2 * Mental Health Assessments — Auto-Save Modal  v2.1
3 *
4 * CHANGES FROM v2.0:
5 *  - Completely independent modal (MhtModal) — no longer shares or depends on
6 *    window.MbtModalShared from the MBTI plugin. Eliminates the bug where
7 *    assessment "Get Results" buttons showed the MBTI personality popup.
8 *  - RTL / Arabic support built in from the start.
9 *  - Uses #mht-assessment-modal (separate from MBTI's #mbt-account-modal).
10 */
11(function ($) {
12    'use strict';
13
14    var WEBHOOK_ACCOUNT = 'https://theprivateclinic.me/wp-json/bit-integrations/v1/callback/4136e01d-4106-4c7a-b9d3-56858f6c2787';
15
16    var TEST_MAP = {
17        phq9:      { resultDiv: '#phq9Results',  field: 'input[name="phq9-result"]',  sevEl: '#phq9Severity' },
18        gad7:      { resultDiv: '#gad7Results',  field: 'input[name="gad7-result"]',  sevEl: '#gad7Severity' },
19        asrs:      { resultDiv: '#asrsResults',  field: 'input[name="asrs-result"]',  sevEl: null },
20        pcptsd:    { resultDiv: '#ptsdResults',  field: 'input[name="ptsd-result"]',  sevEl: null },
21        'pc-ptsd': { resultDiv: '#ptsdResults',  field: 'input[name="ptsd-result"]',  sevEl: null },
22        swed:      { resultDiv: '#swedResults',  field: 'input[name="swed-result"]',  sevEl: null },
23        epds:      { resultDiv: '#epdsResults',  field: 'input[name="epds-result"]',  sevEl: null },
24        bpd:       { resultDiv: '#bpdResults',   field: 'input[name="bpd-result"]',   sevEl: null },
25        spin:      { resultDiv: '#spinResults',  field: 'input[name="spin-result"]',  sevEl: '#spinSeverity' },
26    };
27
28    var EMAIL_SEL = [
29        'input[type="email"]',
30        'input.wpcf7-validates-as-email',
31        'input[name="your-email"]',
32        'input[name*="email"]',
33    ].join(', ');
34
35    /* ─────────────────────────────────────────────────────────────────
36     * LOCAL MODAL — completely independent of MBTI plugin.
37     * Uses #mht-assessment-modal so it never conflicts with
38     * MBTI's #mbt-account-modal even when both plugins are active.
39     * ───────────────────────────────────────────────────────────────── */
40    var MhtModal = (function () {
41        var _cb    = null;
42        var _built = false;
43
44        function _isRTL() {
45            return $('html').attr('dir') === 'rtl' || $('body').hasClass('rtl');
46        }
47
48        function _build() {
49            if (_built || $('#mht-assessment-modal').length) { _built = true; return; }
50            _built = true;
51
52            var rtl        = _isRTL();
53            var closePos   = rtl ? 'left:14px;right:auto;' : 'right:14px;left:auto;';
54            var dirStyle   = rtl ? 'direction:rtl;text-align:right;' : '';
55            var arrowChar  = rtl ? '\u2190' : '\u2192';  // ← or →
56
57            $('body').append(
58                '<div id="mht-assessment-modal" style="' +
59                    'display:none;position:fixed;top:0;left:0;right:0;bottom:0;z-index:999999;' +
60                    'background:rgba(15,23,42,.72);align-items:center;justify-content:center;' +
61                    'backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);' +
62                    'padding:16px;box-sizing:border-box;">' +
63
64                  '<div style="' +
65                      'background:#fff;border-radius:20px;max-width:420px;width:100%;' +
66                      'box-shadow:0 32px 80px rgba(0,0,0,.35);overflow:hidden;position:relative;' +
67                      'font-family:-apple-system,BlinkMacSystemFont,\'Segoe UI\',Roboto,sans-serif;' +
68                      dirStyle + '">' +
69
70                    /* gradient header */
71                    '<div style="background:linear-gradient(135deg,#3d7050 0%,#549263 100%);' +
72                        'padding:24px 24px 20px;text-align:center;position:relative;">' +
73                      '<button id="mht-modal-close" style="position:absolute;top:12px;' + closePos +
74                          'background:rgba(255,255,255,.18);border:none;border-radius:50%;' +
75                          'width:30px;height:30px;font-size:18px;
75cursor:pointer;color:#fff;' +
76                          'display:flex;align-items:center;justify-content:center;line-height:1;">' +
77                          '&#x2715;' +
78                      '</button>' +
79                      '<div style="font-size:38px;margin-bottom:10px;">&#x1F4CA;</div>' +
80                      '<h3 style="margin:0;font-size:18px;font-weight:700;color:#fff;">' +
81                          'Save Your Results' +
82                      '</h3>' +
83                    '</div>' +
84
85                    /* body */
86                    '<div style="padding:22px 24px 0;">' +
87                      '<p style="margin:0 0 16px;font-size:15px;font-weight:700;color:#111827;' +
88                          'text-align:center;line-height:1.4;">' +
89                          'Unlock your free personalised report' +
90                      '</p>' +
91
92                      '<ul style="list-style:none;margin:0 0 20px;padding:0;' +
93                          'display:flex;flex-direction:column;gap:10px;">' +
94                        _li('&#x1F4C8;', 'Track your mental health over time',
95                                         'See how you progress across multiple assessments') +
96                        _li('&#x1F9ED;', 'Get personalised therapy recommendations',
97                                         'Matched to your screening results') +
98                        _li('&#x1F512;', 'Private &amp; secure',
99                                         'Only you and your care team can see your results') +
100                      '</ul>' +
101
102                      '<div style="display:flex;flex-direction:column;gap:10px;">' +
103                        '<button id="mht-no-account" style="' +
104                            'padding:14px 22px;border-radius:12px;font-size:15px;font-weight:700;' +
105                            'background:linear-gradient(135deg,#549263,#3d7050);color:#fff;' +
106                            'border:none;cursor:pointer;width:100%;' +
107                            'box-shadow:0 4px 14px rgba(84,146,99,.45);">' +
108                            '&#x2728; Create my free account' +
109                        '</button>' +
110                        '<button id="mht-has-account" style="' +
111                            'padding:12px 22px;border-radius:12px;font-size:14px;font-weight:600;' +
112                            'background:#f8faf9;color:#374151;border:1.5px solid #d1d5db;' +
113                            'cursor:pointer;width:100%;">' +
114                            'I already have an account ' + arrowChar +
115                        '</button>' +
116                      '</div>' +
117
118                      '<p style="margin:14px 0 0;font-size:11px;color:#9ca3af;text-align:center;' +
119                          'padding-bottom:20px;">Free forever &middot; No credit card needed</p>' +
120
121                      '<p id="mht-modal-msg" style="display:none;margin:10px 0 16px;' +
122                          'font-size:13px;font-weight:600;text-align:center;"></p>' +
123                    '</div>' +
124                  '</div>' +
125                '</div>'
126            );
127
128            /* hover on primary CTA */
129            $(document).on('mouseenter', '#mht-no-account', function () {
130                $(this).css({ transform: 'translateY(-2px)', boxShadow: '0 8px 22px rgba(84,146,99,.5)' });
131            }).on('mouseleave', '#mht-no-account', function () {
132                $(this).css({ transform: '', boxShadow: '0 4px 14px rgba(84,146,99,.45)' });
133            });
134
135            $(document).on('click', '#mht-assessment-modal', function (e) {
136                if ($(e.target).is('#mht-assessment-modal')) _hide();
137            });
138            $(document).on('click', '#mht-modal-close', _hide);
139            $(document).on('click', '#mht-has-account', function () {
140                var s = _cb; _hide(); if (s) s('existing');
141            });
142            $(document).on('click', '#mht-no-account', function () {
143                if (_cb) _cb('new');
144            });
145        }
146
147        function _li(icon, title, sub) {
148            return '<li style="display:flex;align-items:flex-start;gap:12px;">' +
149                '<span style="font-size:18px;flex-shrink:0;margin-top:1px;">' + icon + '</span>' +
150                '<span>' +
151                  '<strong style="display:block;font-size:13px;font-weight:700;color:#111827;margin-bottom:1px;">' + title + '</strong>' +
152                  '<span style="font-size:12px;color:#6b7280;line-height:1.4;">' + sub + '</span>' +
153                '</span>' +
154              '</li>';
155        }
156
157        function _hide() {
158            $('#mht-assessment-modal').css('display', 'none');
159            _cb = null;
160            $('#mht-has-account, #mht-no-account').prop('disabled', false);
161        }
162
163        return {
164            show: function (cb) {
165                _build();
166                _cb = cb;
167                $('#mht-modal-msg').hide().text('');
168                $('#mht-has-account, #mht-no-account').prop('disabled', false);
169                $('#mht-assessment-modal').css('display', 'flex');
170                $('html,body').animate({ scrollTop: 0 }, 200);
171            },
172            hide: _hide,
173            setMsg: function (txt, col) {
174                $('#mht-modal-msg').text(txt).css('color', col || '#549263').show();
175            },
176            disableButtons: function () {
177                $('#mht-has-account, #mht-no-account').prop('disabled', true);
178            }
179        };
180    }());
181
182    /* ─────────────────────────────────────────────────────────────────
183     * HELPERS
184     * ───────────────────────────────────────────────────────────────── */
185    function findEmail($ctx) {
186        var email = '';
187        var $els = $ctx ? $ctx.find(EMAIL_SEL).add($(EMAIL_SEL)) : $(EMAIL_SEL);
188        $els.each(function () {
189            var v = $.trim($(this).val());
190            if (v && v.indexOf('@') > 0) { email = v; return false; }
191        });
192        return email;
193    }
194
195    function showStatus($div, text, type) {
196        var $s = $div.find('.mht-save-status');
197        if (!$s.length) {
198            $s = $('<div class="mht-save-status" style="margin-top:16px;padding:12px 16px;' +
199                'border-radius:8px;font-size:14px;font-weight:600;text-align:center;"></div>');
200            $div.append($s);
201        }
202        var bg, color, border;
203        if      (type === 'success') { bg='#f0fdf4'; color='#166534'; border='#bbf7d0'; }
204        else if (type === 'error')   { bg='#fef2f2'; color='#991b1b'; border='#fecaca'; }
205        else                         { bg='#eff6ff'; color='#1e40af'; border='#bfdbfe'; }
206        $s.text(text).css({ background: bg, color: color, border: '1px solid ' + border }).show();
207    }
208
209    function doSave(key, email, resultText, severity, $div) {
210        showStatus($div, 'Saving your results\u2026', 'info');
211        $.ajax({
212            url:  (typeof mht_save_data !== 'undefined') ? mht_save_data.ajax_url : '',
213            type: 'POST',
214            data: {
215                action:      'mht_save_to_account',
216                nonce:       (typeof mht_save_data !== 'undefined') ? mht_save_data.nonce : '',
217                email:        email,
218                test_key:     key,
219                result_text:  resultText,
220                severity:     severity,
221            },
222            success: function (res) {
223                if (res.success) {
224                    showStatus($div, '\u2713 Results saved to your account!', 'success');
225                } else {
226                    showStatus($div, '\u2717 ' + (res.data && res.data.message
227                        ? res.data.message : 'Could not save. Please try again.'), 'error');
228                }
229            },
230            error: function () { showStatus($div, '\u2717 Network error. Please try again.', 'error'); }
231        });
232    }
233
234    /* ─────────────────────────────────────────────────────────────────
235     * MAIN — intercept CF7 submit inside each [data-test] wrapper
236     * ───────────────────────────────────────────────────────────────── */
237    $(document).ready(function () {
238
239        $(document).on('submit', '[data-test] .wpcf7-form', function () {
240            var $form    = $(this);
241            var $wrapper = $form.closest('[data-test]');
242            var testKey  = $wrapper.data('test');
243            var cfg      = TEST_MAP[testKey];
244            if (!cfg) return;
245
246            /* Capture email NOW before any field clearing */
247            var email = findEmail($form);
248            if (!email) {
249                $(EMAIL_SEL).each(function () {
250                    var v = $.trim($(this).val());
251                    if (v && v.indexOf('@') > 0) { email = v; return false; }
252                });
253            }
254
255            /* Test JS runs synchronously — wait one tick for DOM updates */
256            setTimeout(function () {
257                var $resultDiv = $(cfg.resultDiv);
258                if (!$resultDiv.is(':visible')) return;
259                if ($resultDiv.data('mht-saved')) return;
260
261                var resultText = $form.find(cfg.field).val() || $(cfg.field).val() || '';
262                var severity   = cfg.sevEl ? $.trim($(cfg.sevEl).text()) : '';
263
264                if (!severity && resultText) {
265                    var lines = resultText.split('\n');
266                    for (var i = 0; i < lines.length; i++) {
267                        var l = $.trim(lines[i]);
268                        if (l && l.indexOf(':') > -1) {
269                            severity = $.trim(l.split(':').slice(1).join(':'));
270                            break;
271                        }
272                    }
273                }
274                if (!severity) severity = resultText.substring(0, 80);
275
276                if (!email) {
277                    showStatus($resultDiv,
278                        '\uD83D\uDCBE Want to save these results? Fill in your email in the form above then click Get Results again.',
279                        'info');
280                    return;
281                }
282
283                /* Show the assessment-specific modal (NOT the MBTI modal) */
284                MhtModal.show(function (choice) {
285                    if (choice === 'new') {
286                        MhtModal.disableButtons();
287                        MhtModal.setMsg('Creating your account\u2026', '#549263');
288                        $.ajax({
289                            url:  WEBHOOK_ACCOUNT,
290                            type: 'POST',
291                            contentType: 'application/json',
292                            data: JSON.stringify({ email: email, source: 'assessment_save', test: testKey }),
293                            complete: function () {
294                                MhtModal.setMsg('Account created! Saving results\u2026', '#549263');
295                                setTimeout(function () {
296                                    MhtModal.hide();
297                                    $resultDiv.data('mht-saved', true);
298                                    doSave(testKey, email, resultText, severity, $resultDiv);
299                                }, 900);
300                            }
301                        });
302                    } else {
303                        MhtModal.hide();
304                        $resultDiv.data('mht-saved', true);
305                        doSave(testKey, email, resultText, severity, $resultDiv);
306                    }
307                });
308
309            }, 80);
310        });
311
312    });
313
314})(jQuery);

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.