diff --git a/static/src/js/sign_image_upload.js b/static/src/js/sign_image_upload.js index 559384c..cd0aac2 100755 --- a/static/src/js/sign_image_upload.js +++ b/static/src/js/sign_image_upload.js @@ -4,7 +4,40 @@ import { patch } from "@web/core/utils/patch"; import { PDFIframe } from "@sign/components/sign_request/PDF_iframe"; import { SignablePDFIframe } from "@sign/components/sign_request/signable_PDF_iframe"; -console.log("Sign Image Field: Initializing universal stability patches..."); +console.log("Sign Image Field: Initializing bulletproof stability patches..."); + +/** + * Bulletproof Bridge Restoration + * Re-connects Odoo parent to PDF viewer iframe on multiple levels to ensure viewer.js + * can always find the refreshSignItemsForPage method. + */ +const restoreBridge = (instance) => { + if (!instance || !instance.root || !instance.root.defaultView) return; + + const iwin = instance.root.defaultView; + const iframe = iwin.frameElement; + + // 1. Parent-side element (Standard Odoo mechanism) + if (iframe && !iframe.odoo_iframe_instance) { + console.log("Sign Image Field: Restoring bridge on iframe element"); + iframe.odoo_iframe_instance = instance; + } + + // 2. Iframe-side global (In-frame customization) + if (!iwin.odoo_iframe_instance) { + iwin.odoo_iframe_instance = instance; + } + + // 3. Document-side global (In-frame customization fallback) + if (iwin.document && !iwin.document.odoo_iframe_instance) { + iwin.document.odoo_iframe_instance = instance; + } + + // 4. Attach directly to PDFViewerApplication if ready + if (iwin.PDFViewerApplication && !iwin.PDFViewerApplication.odoo_iframe_instance) { + iwin.PDFViewerApplication.odoo_iframe_instance = instance; + } +}; /** * Shared utility for lazy loading image fields. @@ -23,20 +56,14 @@ const triggerLazyLoad = (el) => { lazyImg.style.opacity = 1; }; - // If already loaded or assigned if (lazyImg.src && !lazyImg.src.startsWith('data:image/gif') && lazyImg.src !== '') { finalize(); return; } - // Assign BEFORE src to avoid race with cache lazyImg.onload = finalize; lazyImg.src = dataSrc; - - // Immediate check if already complete (from cache) - if (lazyImg.complete) { - finalize(); - } + if (lazyImg.complete) finalize(); }; /** @@ -45,8 +72,7 @@ const triggerLazyLoad = (el) => { const setupLazyObserver = (el) => { if (!el) return; - // Safety fallback: Load after 1.5s regardless of intersection - // This is vital for remote connections where observer triggers might be erratic + // Safety fallback: Force load after 1.5s setTimeout(() => { if (el.classList.contains('o_sign_image_loading_container')) { triggerLazyLoad(el); @@ -68,35 +94,26 @@ const setupLazyObserver = (el) => { } }; -/** - * Restore the Odoo-Iframe bridge if it's missing (Odoo 19 systemic issue) - * This fixes the "refreshSignItemsForPage is not a function" error in viewer.js - */ -const restoreBridge = (instance) => { - if (instance.root && instance.root.defaultView && instance.root.defaultView.frameElement) { - const iframe = instance.root.defaultView.frameElement; - if (!iframe.odoo_iframe_instance) { - console.log("Sign Image Field: Restoring odoo_iframe_instance bridge on iframe"); - iframe.odoo_iframe_instance = instance; - } - } -}; - // 1. Patch PDFIframe (ROOT) patch(PDFIframe.prototype, { enableCustom(signItem) { - // Restore communication bridge immediately to prevent viewer.js crashes restoreBridge(this); - // Defensive super call to preserve original Odoo behavior and other patches + // Call original implementation FIRST if (typeof super.enableCustom === 'function') { super.enableCustom(...arguments); } - // OUR LOGIC: Setup lazy loading if it's an image + // Setup lazy loading for images if (signItem && signItem.data && signItem.data.type === 'image' && signItem.el) { setupLazyObserver(signItem.el); } + }, + + refreshSignItems(page = false) { + // Ensure bridge is active during page rendering events + restoreBridge(this); + return super.refreshSignItems(...arguments); } }); @@ -105,34 +122,30 @@ patch(SignablePDFIframe.prototype, { enableCustom(signItem) { restoreBridge(this); - // Setup observer early to ensure it runs for all documents/roles + // Setup image observers early for ALL users if (signItem && signItem.data && signItem.data.type === 'image' && signItem.el) { setupLazyObserver(signItem.el); } - // Call original implementation (which handles text/signature etc) + // Call original implementation to populate Nama, NIK, etc. super.enableCustom(...arguments); + // Add upload logic for active signer if (signItem && signItem.data && signItem.data.type === 'image' && signItem.el) { const el = signItem.el; const data = signItem.data; - // Strictly check responsibility for UPLOAD logic if (this.readonly || (data.responsible > 0 && data.responsible !== this.currentRole)) { return; } el.setAttribute('data-type', 'image'); const input = el.querySelector('.o_sign_image_upload_input'); - if (input) { - // Ensure we don't double-attach if enableCustom is called multiple times - if (!el._hasImgListener) { - el.addEventListener('click', (e) => { - if (e.target !== input) input.click(); - }); - el._hasImgListener = true; - } - + if (input && !el._hasImgListener) { + el.addEventListener('click', (e) => { + if (e.target !== input) input.click(); + }); + input.addEventListener('change', (e) => { const file = e.target.files[0]; if (!file) return; @@ -187,6 +200,7 @@ patch(SignablePDFIframe.prototype, { }; reader.readAsDataURL(file); }); + el._hasImgListener = true; } } },