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.