PageSourceSearch

https://tuguoba.com/static/js/form-submit.js

js tuguoba.com collected 2026-09-25 14:49:13 UTC 25,684 bytes, 625 lines download raw bytes

1/**
2 * FormSubmit - Reusable form submission utility for image processing pages
3 * Provides consistent behavior across sketch, enhance, cartoon, scanner, etc.
4 */
5
6(function(global) {
7    'use strict';
8
9    /**
10     * FormSubmit class - handles form submission with loading state and result display
11     */
12    var FormSubmit = function(options) {
13        this.options = Object.assign({
14            formId: '',              // Form element ID (e.g., 'sketch_form')
15            submitId: '',            // Submit button ID (e.g., 'sketch_submit')
16            endpoint: '',            // URL endpoint (e.g., 'sketch_apply')
17            spinnerId: 'mergebackendstatus', // Loading spinner ID
18            resultContainerId: 'catch_result', // Result container ID
19            pathId: 'path_corresponding', // Hidden path element ID
20            downloadDivId: 'download_link_div', // Download links container
21            onData: null,            // Function to collect additional form data
22            onSuccess: null,         // Custom success callback
23            timeout: 60000           // Request timeout in ms
24        }, options);
25
26        this.init();
27    };
28
29    /**
30     * Get user-friendly error message based on HTTP status code
31     */
32    function getStatusMessage(status) {
33        var msgs = window.formSubmitMessages || {};
34        var messages = {
35            429: msgs.server_busy,
36            502: msgs.server_error,
37            503: msgs.server_busy,
38            504: msgs.request_timeout
39        };
40        return messages[status] || null;
41    }
42
43    FormSubmit.prototype.init = function() {
44        var self = this;
45        var form = document.getElementById(this.options.formId);
46        var submitBtn = document.getElementById(this.options.submitId);
47
48        if (!form || !submitBtn) {
49            console.warn('FormSubmit: Form or submit button not found', this.options);
50            return;
51        }
52
53        // Check if jQuery ajaxSubmit is available
54        if (typeof jQuery !== 'undefined' && jQuery.fn.ajaxSubmit) {
55            this.useJqueryAjaxSubmit(submitBtn, form);
56        } else {
57            this.useVanillaXHR(submitBtn, form);
58        }
59    };
60
61    /**
62     * Submit using jQuery ajaxSubmit (legacy but widely used)
63     */
64    FormSubmit.prototype.useJqueryAjaxSubmit = function(submitBtn, form) {
65        var self = this;
66
67        jQuery(submitBtn).on('click', function() {
68            var transPath = document.getElementById(self.options.pathId);
69            if (!transPath || !transPath.value) {
70                if (typeof showError === 'function') {
71                    showError(window.formSubmitMessages.please_upload_first);
72                } else {
73                    alert(window.formSubmitMessages.please_upload_first);
74                }
75                return;
76            }
77
78            // Collect additional data from onData callback
79            var additionalData = {};
80            if (typeof self.options.onData === 'function') {
81                additionalData = self.options.onData();
82            }
83
84            // Get radio button values
85            var radioData = self.getRadioButtonData(form);
86
87            // Merge all data
88            var submitData = Object.assign({}, radioData, additionalData, {
89                trans_path: transPath.value
90            });
91
92            // Show loading state
93            jQuery('#' + self.options.spinnerId).show();
94            jQuery(submitBtn).prop('disabled', true);
95            var downloadDiv = document.getElementById(self.options.downloadDivId);
96            if (downloadDiv) {
97                jQuery(downloadDiv).hide();
98            }
99
100            // Submit form
101            jQuery(form).ajaxSubmit({
102                url: self.options.endpoint,
103                type: 'post',
104                dataType: 'json',
105                data: submitData,
106                beforeSubmit: function() {
107                    // Already handled above
108                },
109                success: function(data) {
110                    self.handleSuccess(data);
111                },
112                timeout: self.options.timeout,
113                error: function(xhr, status, error) {
114                    self.handleError(xhr, status, error, true);
115                }
116            });
117        });
118    };
119
120    /**
121     * Submit using vanilla XMLHttpRequest (modern, no jQuery depen
121dency)
122     */
123    FormSubmit.prototype.useVanillaXHR = function(submitBtn, form) {
124        var self = this;
125
126        submitBtn.addEventListener('click', function() {
127            var transPath = document.getElementById(self.options.pathId);
128            if (!transPath || !transPath.value) {
129                if (typeof showError === 'function') {
130                    showError(window.formSubmitMessages.please_upload_first);
131                } else {
132                    alert(window.formSubmitMessages.please_upload_first);
133                }
134                return;
135            }
136
137            // Collect form data
138            var formData = new FormData(form);
139            formData.set('trans_path', transPath.value);
140
141            // Collect additional data from onData callback
142            if (typeof self.options.onData === 'function') {
143                var additionalData = self.options.onData();
144                Object.keys(additionalData).forEach(function(key) {
145                    formData.set(key, additionalData[key]);
146                });
147            }
148
149            // Collect radio button data
150            var radioData = self.getRadioButtonData(form);
151            Object.keys(radioData).forEach(function(key) {
152                formData.set(key, radioData[key]);
153            });
154
155            // Show loading state
156            var spinner = document.getElementById(self.options.spinnerId);
157            if (spinner) spinner.style.display = 'inline-block';
158            submitBtn.disabled = true;
159            var originalText = submitBtn.textContent;
160            submitBtn.textContent = window.formSubmitMessages.processing + '...';
161
162            var downloadDiv = document.getElementById(self.options.downloadDivId);
163            if (downloadDiv) downloadDiv.style.display = 'none';
164
165            // Send request
166            var xhr = new XMLHttpRequest();
167            xhr.open('POST', self.options.endpoint, true);
168            xhr.timeout = self.options.timeout;
169
170            xhr.onload = function() {
171                submitBtn.textContent = originalText || window.formSubmitMessages.submit;
172                submitBtn.disabled = false;
173                if (spinner) spinner.style.display = 'none';
174
175                if (xhr.status >= 200 && xhr.status < 300) {
176                    try {
177                        var data = JSON.parse(xhr.responseText);
178                        self.handleSuccess(data);
179                    } catch (e) {
180                        alert(window.formSubmitMessages.error_parsing_response);
181                    }
182                } else {
183                    var specificMsg = getStatusMessage(xhr.status);
184                    if (typeof showError === 'function') {
185                        showError(specificMsg || window.formSubmitMessages.request_failed + ' (' + xhr.status + ')');
186                    } else {
187                        alert(specificMsg || window.formSubmitMessages.request_failed + ': ' + xhr.status);
188                    }
189                }
190            };
191
192            xhr.onerror = function() {
193                submitBtn.textContent = originalText || window.formSubmitMessages.submit;
194                submitBtn.disabled = false;
195                if (spinner) spinner.style.display = 'none';
196                if (typeof showError === 'function') {
197                    showError(window.formSubmitMessages.network_error);
198                } else {
199                    alert(window.formSubmitMessages.network_error);
200                }
201            };
202
203            xhr.ontimeout = function() {
204                submitBtn.textContent = originalText || window.formSubmitMessages.submit;
205                submitBtn.disabled = false;
206                if (spinner) spinner.style.display = 'none';
207                if (typeof showError === 'function') {
208                    showError(window.formSubmitMessages.server_busy);
209                } else {
210                    alert(window.formSubmitMessages.server_busy);
211                }
212            };
213
214            xhr.send(formData);
215        });
216    };
217
218    /**
219     * Get values from radio buttons in the form
220     */
221    FormSubmit.prototype.getRadioButtonData = function(form) {
222        var data = {};
223        var radios = form.querySelectorAll('input[type="radio"]');
224        var checkedNames = {};
225
226        // Only take first checked value for each radio group name
227        radios.forEach(function(radio) {
228            var name = radio.name;
229            if (name && !checkedNames[name] && radio.checked) {
230                checkedNames[name] = true;
231                data[name] = radio.value;
232            }
233        });
234
235        return data;
236    };
237
238    /**
239     * Handle successful response
240     */
241    FormSubmit.prototype.handleSuccess = function(data) {
242        var spinner = document.getElementById(this.options.spinnerId);
243        var submitBtn = document.getElementById(this.options.submitId);
244
245        if (spinner) spinner.style.display = 'none';
246        if (submitBtn) {
247            if (typeof jQuery !== 'undefined') {
248                jQuery(submitBtn).prop('disabled', false);
249            } else {
250                submitBtn.disabled = false;
251            }
252        }
253
254        if (data.ret === 'pass') {
255            // Add cache busting
256            var seed = Math.random();
257
258            // Update image sources
259            this.updateImageSrc('single_modal_image', data.showfaceimage_jpg, seed);
260            this.updateImageSrc('single_image', data.previewimage, Math.random());
261            this.updateImageSrc('result_modal_img', data.showfaceimage_jpg, seed);
262            this.updateDownloadLink('download_link_jpg', data.showfaceimage_jpg, seed);
263            this.updateDownloadLink('download_link_png', data.showfaceimage_png, seed);
264            this.updateDownloadLink('download_link_pdf', data.showfaceimage_pdf, seed);
265
266            // Update edit link
267            var editLink = document.getElementById('edit_link');
268            if (editLink && data.showfaceimage_jpg) {
269                editLink.href = 'editor?utm_source=' + encodeURIComponent(data.showfaceimage_jpg);
270            }
271
272            // Show result container and download div
273            var resultContainer = document.getElementById(this.options.resultContainerId);
274            if (resultContainer) {
275                resultContainer.style.display = 'block';
276                resultContainer.classList.remove('hidden');
277            }
278
279            var downloadDiv = document.getElementById(this.options.downloadDivId);
280            if (downloadDiv) {
281                if (typeof jQuery !== 'undefined') {
282                    jQuery(downloadDiv).show();
283                } else {
284                    downloadDiv.style.display = 'block';
285                }
286                downloadDiv.classList.remove('hidden');
287            }
288
289            // Call custom success callback if provided
290            if (typeof this.options.onSuccess === 'function') {
291                this.options.onSuccess(data);
292            }
293        } else {
294            var errorMsg = data.errormsg || window.formSubmitMessages.processing_failed;
295            if (typeof showError === 'function') {
296                showError(errorMsg);
297            } else {
298                alert(errorMsg);
299            }
300        }
301    };
302
303    /**
304     * Handle error response
305     */
306    FormSubmit.prototype.handleError = function(xhr, status, error, isJquery) {
307        var spinner = document.getElementById(this.options.spinnerId);
308        var submitBtn = document.getElementById(this.options.submitId);
309
310        if (spinner) spinner.style.display = 'none';
311        if (submitBtn) {
312            if (typeof jQuery !== 'undefined') {
313                jQuery(submitBtn).prop('disabled', false);
314            } else {
315                submitBtn.disabled = false;
316            }
317        }
318
319        if (typeof showError !== 'function') {
320            if (status === 'timeout') {
321                alert(window.formSubmitMessages.server_busy);
322            } else {
323                alert(window.formSubmitMessages.request_failed + ': ' + (error || status));
324            }
325            return;
326        }
327
328        if (status === 'timeout') {
329            showError(window.formSubmitMessages.server_busy);
330            return;
331        }
332
333        // jQuery ajaxSubmit provides status string, not xhr object
334        // For non-jQuery, check HTTP status code
335        if (!isJquery && xhr && xhr.status) {
336            var specificMsg = getStatusMessage(xhr.status);
337            if (specificMsg) {
338                showError(specificMsg);
339                return;
340            }
341        }
342
343        showError(window.formSubmitMessages.request_failed + ' (' + (error || status) + ')');
344    };
345
346    /**
347     * Update image source with cache busting
348     */
349    FormSubmit.prototype.updateImageSrc = function(elementId, src, seed) {
350        var img = document.getElementById(elementId);
351        if (img && src) {
352            img.src = src.indexOf('?') > -1 ? src + '&t=' + seed : src + '?t=' + seed;
353        }
354    };
355
356    /**
357     * Update download link
358     */
359    FormSubmit.prototype.updateDownloadLink = function(elementId, href, seed) {
360        var link = document.getElementById(elementId);
361        if (link && href) {
362            var cacheBusted = href.indexOf('?') > -1 ? href + '&t=' + seed : href + '?t=' + seed;
363            link.href = cacheBusted;
364        }
365    };
366
367    /**
368     * Factory function for creating a form submit handler
369     */
370    global.FormSubmit = {
371        create: function(options) {
372            return new FormSubmit(options);
373        },
374
375         /**
376          * Quick setup for scanner page
377          */
378        scanner: function() {
379            return new FormSubmit({
380                formId: 'scanner_form',
381                submitId: 'scanner_submit',
382                endpoint: 'scanner_apply',
383                resultContainerId: 'catch_result',
384                onData: function() {
385                    // Collect scanner-specific data
386                    var filterType = '1';
387                    var radios = document.getElementsByName('radio_scanner');
388                    for (var i = 0; i < radios.length; i++) {
389                        if (radios[i].checked) {
390                            filterType = radios[i].value;
391                            break;
392                        }
393                    }
394
395                    // Get slider values
396                    var getSliderValue = function(id, defaultVal) {
397                        var el = document.getElementById(id);
398                        if (el) {
399                            var val = el.getAttribute('data-value');
400                            return val !== null ? parseFloat(val) : defaultVal;
401                        }
402                        return defaultVal;
403                    };
404
405                    function getSaveInkValue() {
406                        var radios = document.getElementsByName('radio_scanner');
407                        var val;
408                        for(var i = 0; i < radios.length; i++) {
409                            if(radios[i].checked) { val = radios[i].value; break; }
410                        }
411                        if(val == '5'){
412                            var toggle = document.getElementById('save_ink_toggle');
413                            if(toggle && toggle.checked){
414                                return document.getElementById("saveink_controller").getAttribute('data-value') || 100;
415                            }
416                        }
417                        return 100;
418                    }
419
420                    // Get filter-specific value based on selected filter type
421                    var getFilterValue = function() {
422                        var value;
423                        switch(filterType) {
424                            case '3': // sharpen
425                                value = getSliderValue('sharpen_controller', 0.1);
426                                return value;
427                            case '4': // denoise
428                                value = getSliderValue('denoise_controller', 100);
429                                return value;
430                            case '5': // B/W
431                                value = getSliderValue('blackwhite_controller', 128);
432                                return value;
433                            case '6': // Detail Enhance
434                                value = getSliderValue('detail_controller', 10);
435                                return value;
436                            case '7': // Image Enhance
437                                return 0; // No slider needed
438                            case '8': // Shadow Removal
439                                return 0; // No slider needed
440                            case '9': // Auto-Levels
441                                return 0; // No slider needed
442                            case '10': // Illumination Correction
443                                value = getSliderValue('illumination_controller', 50);
444                                return value;
445                            default:
446                                console.log('[FormSubmit] No filter value needed for type:', filterType);
447                                return 0;
448                        }
449                    };
450
451                    // Get polygon points
452                    var lefttop = Array.isArray(window.verticelefttop) && window.verticelefttop.length === 2 
453                        ? window.verticelefttop : [0, 0];
454                    var righttop = Array.isArray(window.verticerighttop) && window.verticerighttop.length === 2 
455                        ? window.verticerighttop : [1, 0];
456                    var rightbottom = Array.isArray(window.verticerightbottom) && window.verticerightbottom.length === 2 
457                        ? window.verticerightbottom : [1, 1];
458                    var leftbottom = Array.isArray(window.verticeleftbottom) && window.verticeleftbottom.length === 2 
459                        ? window.verticeleftbottom : [0, 1];
460
461                    var arrayPoints = {
462                        data: [lefttop, righttop, rightbottom, leftbottom]
463                    };
464
465                    var data = {
466                        transform_type: document.getElementById('transfrom_option').value,
467                        filter_type: filterType,
468                        filter_value: getFilterValue(),
469                        save_ink: getSaveInkValue(),
470                        brightness: getSliderValue('brightness_controller', 100),
471                        contrast: getSliderValue('contrast_controller', 100),
472                        array_points: JSON.stringify(arrayPoints)
473                    };
474                    // console.log('[FormSubmit] Submitting data:', data);
475                    return data;
476                },
477                onSuccess: function(data) {
478                    var seed = Math.random();
479                    
480                    // Update preview image (single_image)
481                    var previewImg = document.getElementById('single_image');
482                    if (previewImg && data.previewimage) {
483                        var previewSrc = data.previewimage.indexOf('?') > -1 
484                            ? data.previewimage + '&t=' + seed 
485                            : data.previewimage + '?t=' + seed;
486                        previewImg.src = previewSrc;
487                    }
488                    
489                    // Update modal image
490                    var modalImg = document.getElementById('single_modal_image');
491                    if (modalImg && data.showfaceimage_jpg) {
492                        var modalSrc = data.showfaceimage_jpg.indexOf('?') > -1 
493                            ? data.showfaceimage_jpg + '&t=' + seed 
494                            : data.showfaceimage_jpg + '?t=' + seed;
495                        modalImg.src = modalSrc;
496                    }
497                    
498                    // Update download links
499                    var jpgLink = document.getElementById('download_link_jpg');
500                    var pngLink = document.getElementById('download_link_png');
501                    var pdfLink = document.getElementById('download_link_pdf');
502                    
503                    if (jpgLink && data.showfaceimage_jpg) {
504                        jpgLink.href = data.showfaceimage_jpg.indexOf('?') > -1 
505                            ? data.showfaceimage_jpg + '&t=' + seed 
506                            : data.showfaceimage_jpg + '?t=' + seed;
507                    }
508                    if (pngLink && data.showfaceimage_png) {
509                        pngLink.href = data.showfaceimage_png.indexOf('?') > -1 
510                            ? data.showfaceimage_png + '&t=' + seed 
511                            : data.showfaceimage_png + '?t=' + seed;
512                    }
513                    if (pdfLink && data.showfaceimage_pdf) {
514                        pdfLink.href = data.showfaceimage_pdf.indexOf('?') > -1 
515                            ? data.showfaceimage_pdf + '&t=' + seed 
516                            : data.showfaceimage_pdf + '?t=' + seed;
517                    }
518                    
519                    // Update edit link
520                    var editLink = document.getElementById('edit_link');
521                    if (editLink && data.showfaceimage_jpg) {
522                        editLink.href = 'editor?utm_source=' + encodeURIComponent(data.showfaceimage_jpg);
523                    }
524                    
525                    // Show result container (remove inline display:none)
526                    var catchResult = document.getElementById('catch_result');
527                    if (catchResult) {
528                        catchResult.style.display = '';
529                        catchResult.classList.remove('hidden');
530                    }
531                    
532                    // Show download link div
533                    var downloadDiv = document.getElementById('download_link_div');
534                    if (downloadDiv) {
535                        downloadDiv.style.display = '';
536                        downloadDiv.classList.remove('hidden');
537                    }
538                    
539                    // Show OCR section
540                    var toggleOcr = document.getElementById('toggle_ocr');
541                    if (toggleOcr) {
542                        toggleOcr.removeAttribute('hidden');
543                        toggleOcr.classList.remove('uk-hidden');
544                    }
545                    
546                    // Enable OCR button
547                    var ocrSubmit = document.getElementById('ocr_submit');
548                    if (ocrSubmit) {
549                        ocrSubmit.disabled = false;
550                    }
551                    
552                    // Show print link if exists
553                    var printLink = document.getElementById('print_link');
554                    if (printLink) {
555                        printLink.style.display = '';
556                    }
557                    
558                    // Show mail link if exists
559                    var mailLink = document.getElementById('mail_link');
560                    if (mailLink) {
561                        mailLink.style.display = '';
562                    }
563                    
564                    // Dispatch event to enable action buttons (OCR, Print, Mail)
565                    var event = new Event('scanner-success', { bubbles: true });
566                    document.dispatchEvent(event);
567                }
568            });
569        },
570
571        /**
572         * Quick setup for sketch page
573         */
574        sketch: function() {
575            return new FormSubmit({
576                formId: 'sketch_form',
577                submitId: 'sketch_submit',
578                endpoint: 'sketch_apply',
579                onData: function() {
580                    return {
581                        sigma_s: jQuery('#slider_sigma_s').slider('value'),
582                        sigma_r: jQuery('#slider_sigma_r').slider('value'),
583                        shade_factor: jQuery('#slider_shade_factor').slider('value'),
584                        sketch_type: jQuery('input[name="radio_sketch"]:checked').val()
585                    };
586                }
587            });
588        },
589
590        /**
591         * Quick setup for enhance page
592         */
593        enhance: function() {
594            return new FormSubmit({
595                formId: 'enhance_form',
596                submitId: 'enhance_submit',
597                endpoint: 'enhance_apply',
598                onData: function() {
599                    return {
600                        sigma_s: jQuery('#slider_sigma_s').slider('value'),
601                        sigma_r: jQuery('#slider_sigma_r').slider('value')
602                    };
603                }
604            });
605        },
606
607        /**
608         * Quick setup for cartoon page
609         */
610        cartoon: function() {
611            return new FormSubmit({
612                formId: 'cartoon_form',
613                submitId: 'cartoon_submit',
614                endpoint: 'cartoon_apply',
615                onData: function() {
616                    return {
617                        sigma_c: jQuery('#slider_sigma_c').slider('value'),
618                        blur_factor: jQuery('#slider_blur_factor').slider('value')
619                    };
620                }
621            });
622        }
623    };
624
625})(typeof window !== 'undefined' ? window : this);

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.