PageSourceSearch

https://xtoscorm.com/assets/js/custom.js?v=9.34

js xtoscorm.com collected 2026-09-24 10:18:20 UTC 17,583 bytes, 491 lines download raw bytes

1document.addEventListener('DOMContentLoaded', function () {
2    document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(function (el) {
3        new bootstrap.Tooltip(el);
4    });
5    /* ================= Completion Slider ================= */
6
7    function bindSliderDisplay(sliderId, displayId) {
8        const slider = document.getElementById(sliderId);
9        const display = document.getElementById(displayId);
10        if (!slider || !display) return;
11
12        const updateValue = () => {
13            display.textContent = slider.value + '%';
14        };
15
16        slider.addEventListener('input', updateValue);
17        slider.addEventListener('change', updateValue);
18    }
19
20    bindSliderDisplay('completion', 'completionValue');
21    bindSliderDisplay('YTcompletion', 'YTcompletionValue');
22    bindSliderDisplay('HLScompletion', 'HLScompletionValue');
23
24    /* ================= COMMON HELPERS ================= */
25
26    function startLoading(btnSelector) {
27        const loading = document.getElementById('loading');
28        if (loading) loading.classList.remove('d-none');
29
30        if (btnSelector) {
31            const btn = document.querySelector(btnSelector);
32            if (btn) btn.disabled = true;
33        }
34    }
35
36    function stopLoading(btnSelector) {
37        const loading = document.getElementById('loading');
38        if (loading) loading.classList.add('d-none');
39
40        if (btnSelector) {
41            const btn = document.querySelector(btnSelector);
42            if (btn) btn.disabled = false;
43        }
44    }
45
46    function showFormMessage(form, message, type = 'error') {
47        const typeMap = {
48            error: 'danger',
49            success: 'success',
50            warning: 'warning',
51            info: 'info'
52        };
53
54        const alertClass = typeMap[type] || 'danger';
55
56        const html = `
57        <div class="alert alert-${alertClass} alert-dismissible fade show" role="alert">
58            ${message}
59            <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
60        </div>
61    `;
62
63        const messagesContainer = form.querySelector('.form-messages');
64        if (messagesContainer) messagesContainer.innerHTML = html;
65    }
66
67    function handleAjaxError(xhr, form) {
68        let msg = 'Server error. Please try again.';
69
70        if (xhr.responseJSON) {
71            msg = xhr.responseJSON.message || xhr.responseJSON.detail || msg;
72        } else if (xhr.responseText) {
73            try {
74                const json = JSON.parse(xhr.responseText);
75                msg = json.message || json.detail || msg;
76            } catch (e) { }
77        }
78
79        showFormMessage(form, msg, 'error');
80    }
81
82    function handleSuccess(res, downloadUrl, form) {
83        // Hidden FastAPI error inside data
84        if (res.data && res.data.detail) {
85            showFormMessage(form, res.data.detail, 'error');
86            return;
87        }
88
89        // Proper error response
90        if (res.status !== 'success') {
91            showFormMessage(form, res.message || 'Something went wrong', 'error');
92            return;
93        }
94
95        // Success
96        const data = res.data;
97
98        if (!data || !data.file_name) {
99            showFormMessage(form, 'Invalid server response', 'error');
100            return;
101        }
102        showFormMessage(form, 'Conversion successful!', 'success');
103
104        // Build query params
105        const params = new URLSearchParams({
106            file: data.file_name,
107            size: data.file_size,
108            time: data.conversion_time,
109            og_file_ext: data.og_file_ext,
110            og_file_name: data.og_file_name,
111            converter_type: data.converter_type,
112            source: data.source,
113        });
114
115        // Redirect to download page WITHOUT session
116        window.location.href = downloadUrl + '?' + params.toString();
117    }
118
119    /**
120     * Performs an XHR request that mimics the shape of the jQuery.ajax
121     * responses/errors used throughout this file (xhr.responseJSON /
122     * xhr.responseText) so the existing handleAjaxError/handleSuccess
123     * helpers can be reused unchanged.
124     */
125    function sendFormRequest(url, formData, { headers = {}, onSuccess, onError, onComplete }) {
126        const xhr = new XMLHttpRequest();
127        xhr.open('POST', url, true);
128        xhr.responseType = 'text';
129
130        Object.keys(headers).forEach(key => {
131            if (headers[key] !== undefined && headers[key] !== null) {
132                xhr.setRequestHeader(key, headers[key]);
133            }
134        });
135
136        xhr.onload = function () {
137            const status = xhr.status;
138            let json = null;
139            try {
140                json = JSON.parse(xhr.responseText);
141            } catch (e) {
142                json = null;
143            }
144
145            if (status >= 200 && status < 300 && json !== null) {
146                onSuccess(json);
147            } else {
148                // Build a jQuery-like xhr object for handleAjaxError
149                const fakeXhr = {
150                    responseText: xhr.responseText,
151                    responseJSON: json
152                };
153                onError(fakeXhr);
154            }
155
156            if (onComplete) onComplete();
157        };
158
159        xhr.onerror = function () {
160            const fakeXhr = {
161                responseText: xhr.responseText,
162                responseJSON: null
163            };
164            onError(fakeXhr);
165            if (onComplete) onComplete();
166        };
167
168        xhr.send(formData);
169    }
170
171    /* ================= VIDEO ================= */
172
173    const videoConvertForm = document.getElementById('videoConvertForm');
174    if (videoConvertForm) {
175        videoConvertForm.addEventListener('submit', function (e) {
176            e.preventDefault();
177
178            const selectedThemeInput = document.querySelector('input[name="player_theme"]:checked');
179            const selectedTheme = selectedThemeInput ? selectedThemeInput.value : null;
180
181            if (selectedTheme === 'modern') {
182                // Ignore Classic controls
183                document
184                    .querySelectorAll('#classicPlayerControls input[type="checkbox"]')
185                    .forEach(cb => cb.disabled = true);
186
187                // Enable Modern controls
188                document
189                    .querySelectorAll('#modernPlayerControls input[type="checkbox"]')
190                    .forEach(cb => cb.disabled = false);
191            } else {
192                // Ignore Modern controls
193                document
194                    .querySelectorAll('#modernPlayerControls input[type="checkbox"]')
195                    .forEach(cb => cb.disabled = true);
196
197                // Enable Classic controls
198                document
199                    .querySelectorAll('#classicPlayerControls input[type="checkbox"]')
200                    .forEach(cb => cb.disabled = false);
201            }
202
203            const form = this;
204            const formData = new FormData(form);
205            const fileInput = document.getElementById('videoFile');
206            const file = fileInput && fileInput.files[0];
207
208            if (!file) {
209                showFormMessage(form, 'Please select a video file');
210                return;
211            }
212
213            const convertUrl = form.dataset.convertUrl;
214            const downloadUrl = form.dataset.downloadUrl;
215
216            startLoading('#convertBtn');
217
218            sendFormRequest(convertUrl, formData, {
219                onSuccess: function (res) {
220                    stopLoading('#convertBtn');
221                    handleSuccess(res, downloadUrl, form);
222                },
223                onError: function (xhr) {
224                    stopLoading('#convertBtn');
225                    handleAjaxError(xhr, form);
226                }
227            });
228        });
229    }
230
231    /* ================= PPT ================= */
232
233    const pptConvertForm = document.getElementById('pptConvertForm');
234    if (pptConvertForm) {
235        pptConvertForm.addEventListener('submit', function (e) {
236            e.preventDefault();
237
238            const form = this;
239            const formData = new FormData(form);
240            const fileInput = document.getElementById('pptFile');
241            const file = fileInput && fileInput.files[0];
242
243            if (!file) {
244                showFormMessage(form, 'Please select a PPT file');
245                return;
246            }
247
248            // if (file.size > 10 * 1024 * 1024) {
249            //     showFormMessage(form, 'File size must not exceed 10 MB.');
250            //     return;
251            // }
252
253            const convertUrl = form.dataset.convertUrl;
254            const downloadUrl = form.dataset.downloadUrl;
255
256            startLoading('#convertBtn');
257
258            sendFormRequest(convertUrl, formData, {
259                onSuccess: function (res) {
260                    stopLoading('#convertBtn');
261                    handleSuccess(res, downloadUrl, form);
262                },
263                onError: function (xhr) {
264                    stopLoading('#convertBtn');
265                    handleAjaxError(xhr, form);
266                }
267            });
268        });
269    }
270
271    /* ================= PDF ================= */
272
273    const pdfConvertForm = document.getElementById('pdfConvertForm');
274    if (pdfConvertForm) {
275        pdfConvertForm.addEventListener('submit', function (e) {
276            e.preventDefault();
277
278            const pageCountEl = document.getElementById('pdf_page_count');
279            const pageCompletionEl = document.getElementById('page_completion');
280
281            const totalPages = parseInt(pageCountEl ? pageCountEl.value : '', 10);
282            const completedPages = parseInt(pageCompletionEl ? pageCompletionEl.value : '', 10);
283
284            const form = this;
285            const fileInput = document.getElementById('pdfFile');
286            const file = fileInput && fileInput.files[0];
287
288            if (!file) {
289                showFormMessage(form, 'Please select a PDF file');
290                return;
291            }
292
293            if (completedPages > totalPages) {
294                showFormMessage(form, `Pages cannot exceed ${totalPages}`, 'warning');
295                return;
296            }
297
298            if (file.type !== 'application/pdf') {
299                showFormMessage(form, 'Only PDF files allowed');
300                return;
301            }
302
303            // if (file.size > 10 * 1024 * 1024) {
304            //     showFormMessage(form, 'File size must not exceed 10 MB.');
305            //     return;
306            // }
307
308            const formData = new FormData(form);
309
310            const csrfNameMeta = document.querySelector('meta[name="csrf-token-name"]');
311            const csrfHashMeta = document.querySelector('meta[name="csrf-token-hash"]');
312
313            const csrfName = csrfNameMeta ? csrfNameMeta.getAttribute('content') : null;
314            const csrfHash = csrfHashMeta ? csrfHashMeta.getAttribute('content') : null;
315
316            if (csrfName && csrfHash) {
317                formData.append(csrfName, csrfHash);
318            }
319
320            const convertUrl = form.dataset.convertUrl;
321            const downloadUrl = form.dataset.downloadUrl;
322
323            startLoading('#convertBtn');
324
325            sendFormRequest(convertUrl, formData, {
326                headers: csrfHash ? { 'X-CSRF-TOKEN': csrfHash } : {},
327                onSuccess: function (res) {
328                    // update csrf if returned
329                    if (res.csrf_token && csrfHashMeta) {
330                        csrfHashMeta.setAttribute('content', res.csrf_token);
331                    }
332
333                    stopLoading('#convertBtn');
334                    handleSuccess(res, downloadUrl, form);
335                },
336                onError: function (xhr) {
337                    stopLoading('#convertBtn');
338                    handleAjaxError(xhr, form);
339                }
340            });
341        });
342    }
343
344    /* ================= YT VIDEO ================= */
345
346    const youtubeConvertForm = document.getElementById('youtubeConvertForm');
347    if (youtubeConvertForm) {
348        youtubeConvertForm.addEventListener('submit', function (e) {
349            e.preventDefault();
350
351            const form = this;
352            const formData = new FormData(form);
353            const convertUrl = form.dataset.convertUrl;
354            const downloadUrl = form.dataset.downloadUrl;
355
356            const ytLoading = document.getElementById('YTloading');
357            const ytBtn = document.getElementById('YTconvertBtn');
358
359            if (ytLoading) ytLoading.classList.remove('d-none');
360            if (ytBtn) ytBtn.disabled = true;
361
362            sendFormRequest(convertUrl, formData, {
363                onSuccess: function (res) {
364                    if (ytLoading) ytLoading.classList.add('d-none');
365                    if (ytBtn) ytBtn.disabled = false;
366                    handleSuccess(res, downloadUrl, form);
367                },
368                onError: function (xhr) {
369                    if (ytLoading) ytLoading.classList.add('d-none');
370                    if (ytBtn) ytBtn.disabled = false;
371                    handleAjaxError(xhr, form);
372                }
373            });
374        });
375    }
376
377    /* ================= HLS VIDEO ================= */
378
379    const hlsConvertForm = document.getElementById('hlsConvertForm');
380    if (hlsConvertForm) {
381        hlsConvertForm.addEventListener('submit', function (e) {
382            e.preventDefault();
383
384            const form = this;
385            const formData = new FormData(form);
386            const convertUrl = form.dataset.convertUrl;
387            const downloadUrl = form.dataset.downloadUrl;
388
389            const hlsLoading = document.getElementById('HLSloading');
390            const hlsBtn = document.getElementById('HLSconvertBtn');
391
392            if (hlsLoading) hlsLoading.classList.remove('d-none');
393            if (hlsBtn) hlsBtn.disabled = true;
394
395            sendFormRequest(convertUrl, formData, {
396                onSuccess: function (res) {
397                    if (hlsLoading) hlsLoading.classList.add('d-none');
398                    if (hlsBtn) hlsBtn.disabled = false;
399                    handleSuccess(res, downloadUrl, form);
400                },
401                onError: function (xhr) {
402                    if (hlsLoading) hlsLoading.classList.add('d-none');
403                    if (hlsBtn) hlsBtn.disabled = false;
404                    handleAjaxError(xhr, form);
405                }
406            });
407        });
408    }
409
410    /* ================= CONTACT FORMS ================= */
411
412    function handleContactForm(formSelector, buttonSelector, successSelector, errorSelector) {
413        const form = document.querySelector(formSelector);
414        if (!form) return;
415
416        form.addEventListener('submit', function (e) {
417            e.preventDefault();
418
419            const successEl = document.querySelector(successSelector);
420            const errorEl = document.querySelector(errorSelector);
421            const btn = document.querySelector(buttonSelector);
422            const btnText = btn ? btn.querySelector('.btn-text') : null;
423            const btnLoader = btn ? btn.querySelector('.btn-loader') : null;
424
425            [successEl, errorEl].forEach(el => {
426                if (el) {
427                    el.classList.add('d-none');
428                    el.textContent = '';
429                }
430            });
431
432            if (btn) btn.disabled = true;
433            if (btnText) btnText.classList.add('d-none');
434            if (btnLoader) btnLoader.classList.remove('d-none');
435
436            const url = form.dataset.email_url || form.dataset.emailUrl;
437
438            const params = new URLSearchParams(new FormData(form)).toString();
439
440            const xhr = new XMLHttpRequest();
441            xhr.open('POST', url, true);
442            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
443            xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
444
445            xhr.onload = function () {
446                let res = null;
447                let parseOk = true;
448                try {
449                    res = JSON.parse(xhr.responseText);
450                } catch (e) {
451                    parseOk = false;
452                }
453
454                if (parseOk && res && res.status === 'success') {
455                    if (successEl) {
456                        successEl.classList.remove('d-none');
457                        successEl.textContent = '✅ Message sent successfully';
458                    }
459                    form.reset();
460                } else {
461                    if (errorEl) {
462                        errorEl.classList.remove('d-none');
463                        errorEl.textContent = (res && res.message) || '❌ Failed to send message';
464                    }
465                }
466
467                if (btn) btn.disabled = false;
468                if (btnText) btnText.classList.remove('d-none');
469                if (btnLoader) btnLoader.classList.add('d-none');
470            };
471
472            xhr.onerror = function () {
473                if (errorEl) {
474                    errorEl.classList.remove('d-none');
475                    errorEl.textContent = '❌ Something went wrong';
476                }
477
478                if (btn) btn.disabled = false;
479                if (btnText) btnText.classList.remove('d-none');
480                if (btnLoader) btnLoader.classList.add('d-none');
481            };
482
483            xhr.send(params);
484        });
485    }
486
487    // Initialize both forms
488    handleContactForm('#contactus_form', '#submitBtn', '#contactFormSuccessMsg', '#contactFormErrorMsg');
489    handleContactForm('#contactForm', '#contactForm_submitBtn', '#contactSuccessMsg', '#contactErrorMsg');
490
491});

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.