colgajo deltopectoral bilateral para el tratamiento de úlceras por radionecrosis en el cuello: caso clínico</h1> <div class="dropdown d-inline-block mt-1 mb-2"> <button class="btn btn-outline-secondary btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" style="font-size:0.8rem;"> <i class="fas fa-language me-1"></i> Translate Title </button> <ul class="dropdown-menu"> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="tr">Turkish</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="es">Spanish</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="pt">Portuguese</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="ar">Arabic</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="zh">Chinese</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="fr">French</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="de">German</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="id">Indonesian</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="ru">Russian</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="th">Thai</a></li> </ul> </div> </div> <div class="paper-metrics"> <div class="paper-metric"> <i class="fas fa-eye"></i> Clicks: 173 </div> <div class="paper-metric"> <i class="fas fa-hashtag"></i> ID: 172134 </div> <div class="paper-metric"> <i class="fas fa-calendar-alt"></i> 2006 </div> </div> <div class="paper-actions"> <div class="paper-actions"> <a href="http://scielo.isciii.es/scielo.php?script=sci_arttext&pid=S0376-78922006000300009" target="_blank" class="btn btn-success action-btn" rel="noopener noreferrer"> <i class="fas fa-file-pdf"></i> Free PDF </a> <!-- Combined AI Analysis + Performance Tier Button - Available to all authenticated users --> <!-- Get Abstract Button - For articles with DOI/URL but no abstract --> </div> </div> <div class="quality-metrics-section"> <div class="quality-header"> <h5 class="quality-title"> <i class="fas fa-chart-line me-2"></i> Article Quality & Performance Metrics </h5> </div> <div class="quality-body"> <div class="row g-3"> <div class="col-md-4"> <div class="metric-card combined-quality"> <div class="metric-header"> <span class="metric-label">Overall Quality</span> <span class="tier-badge improving" style="background-color: #4CAF50"> Improving Quality </span> </div> <div class="metric-value"> <span class="score-number">0.0</span> <span class="score-unit">/100</span> </div> <div class="metric-description"> Combines engagement data with AI-assessed academic quality </div> </div> </div> <div class="col-md-4"> <div class="metric-card engagement"> <div class="metric-header"> <span class="metric-label">Reader Engagement</span> <span class="tier-badge emerging"> Emerging Content </span> </div> <div class="metric-value"> <span class="score-number">5.1</span> <span class="score-unit">/100</span> </div> <div class="metric-breakdown"> <div class="breakdown-item"> <i class="fas fa-eye"></i> <span>17 views</span> </div> <div class="breakdown-item"> <i class="fas fa-users"></i> <span>17 readers</span> </div> <div class="breakdown-item trending"> <i class="fas fa-fire"></i> <span>Trending</span> </div> </div> </div> </div> <div class="col-md-4"> <div class="metric-card ai-quality"> <div class="metric-header"> <span class="metric-label">AI Quality Assessment</span> </div> <div class="metric-value"> <span class="score-pending">Not analyzed</span> </div> </div> </div> </div> </div> </div> <div class="paper-body"> <div class="abstract-section"> <h5 class="abstract-heading">Abstract</h5> <div class="translation-tabs-wrapper mt-3" data-type="bibliography" data-id="172134" data-field="abstract"> <ul class="nav nav-pills mb-2 translation-nav" style="font-size:0.8rem; gap:4px;"> <li class="nav-item"> <a class="nav-link active py-1 px-2" data-bs-toggle="pill" href="#abstract-lang-en">EN</a> </li> <li class="nav-item ms-auto"> <div class="dropdown d-inline-block"> <button class="btn btn-outline-secondary btn-sm py-0 px-2 dropdown-toggle" type="button" data-bs-toggle="dropdown" style="font-size:0.8rem;"> <i class="fas fa-plus"></i> </button> <ul class="dropdown-menu dropdown-menu-end"> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="tr">Turkish</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="es">Spanish</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="pt">Portuguese</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="ar">Arabic</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="zh">Chinese</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="fr">French</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="de">German</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="id">Indonesian</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="ru">Russian</a></li> <li><a class="dropdown-item translate-lang-btn" href="#" data-locale="th">Thai</a></li> </ul> </div> </li> </ul> <div class="tab-content"> <div class="tab-pane fade show active" id="abstract-lang-en"> <div class="abstract-content">Bakamjian describió el colgajo deltopectoral en 1965, recibiendo inicialmente una gran aceptación en cirugía de cabeza y cuello. Sin embargo, un porcentaje de fallos del 10%-25% descrito por varios autores motivó su progresivo abandono y su sustitución por otras alternativas. Los autores hacen una revisión de la bibliografía, analizando las modificaciones técnicas que han mejorado la viabilidad de este colgajo y describen la capacidad del colgajo deltopectoral elevado bilateralmente para aportar suficiente tejido en el caso de un paciente con defecto bilateral en el cuello secundario a radiodermitis severa. En la actualidad, el colgajo deltopectoral, teniendo en consideración las aportaciones técnicas descritas, puede seguir considerándose como una fuente de tejido con la calidad y fiabilidad idóneas para técnicas de reconstrucción superficial en cabeza y cuello.<br>Bakamjian introduced the deltopectoral skin flap en 1965, and there after it was used extensively for reconstructive surgery of the head and neck. Within the next years, flap failure rates of 20% to 25% were reported, and several alternative methods of reconstruction have supplanted the preeminent position of the deltopectoral flap. This work is a review of the reconstructive experience afforded by several authors and evaluates the technical modifications used to enhance the reliability of this flap. The authors describe the ability of the deltopectoral flap raised bilaterally to provide tissue enough for an extensive irradiated neck, allowing resurfacing a bilateral cutaneous defect after radiation therapy. The deltopectoral flap remains a useful and reliable tissue source for superficial neck reconstruction by implementing several technical modifications.</div> </div> </div> </div> <div class="abstract-quality-warning mt-3" style="position: relative; z-index: 10;"> <div class="alert alert-warning d-flex align-items-center justify-content-between" role="alert" style="opacity: 1 !important; display: flex !important; visibility: visible !important;"> <div> <i class="fas fa-exclamation-triangle me-2"></i> <strong>Abstract Quality Issue:</strong> This abstract appears to be incomplete or contains metadata (258 words). <span class="text-muted">Try re-searching for a better abstract.</span> </div> </div> </div> </div> <table class="info-table"> <tr> <th>Reference Key</th> <td> <div class="d-flex align-items-center flex-wrap"> <span class="badge bg-dark me-3">duque2006ciruga<title</span> <span class="text-muted">Use this key to autocite in the manuscript while using <a href="https://scimatic.org" class="info-link" target="_blank">SciMatic Manuscript Manager or Thesis Manager</a> </span> </div> </td> </tr> <tr> <th>Authors</th> <td>;P. Benito Duque;E. Elena Sorando;A. de Juan Huelves;M. Cano Rosas</td> </tr> <tr> <th>Journal</th> <td> <a href="https://journament.com/journal_stat/1203" class="info-link"> <i class="fas fa-book-open me-1"></i> Microbiology (Reading, England) </a> </td> </tr> <tr> <th>Year</th> <td> 2006 </td> </tr> <tr> <th>DOI</th> <td> <div id="doi-display-container"> <div class="d-flex align-items-center justify-content-between" id="doi-not-found-container"> <span class="badge bg-danger text-white">DOI not found</span> </div> </div> <div id="doi-search-status" class="mt-2" style="display: none;"> <div class="alert alert-info mb-0"> <i class="fas fa-spinner fa-spin me-2"></i> <span id="doi-search-message">Searching for DOI...</span> </div> </div> </td> </tr> <tr> <th>URL</th> <td> <div class="mb-2"> <i class="fas fa-external-link-alt text-primary me-2"></i> <a href="http://scielo.isciii.es/scielo.php?script=sci_arttext&pid=S0376-78922006000300009" target="_blank" class="info-link">http://scielo.isciii.es/scielo.php?script=sci_arttext&pid=S0376-78922006000300009</a> </div> </td> </tr> <tr> <th>Keywords</th> <td> <div class="keywords-list"> <a href="https://journament.com/keyword_connections/3285" class="keyword-tag"> <i class="fas fa-tag me-1"></i> radiotherapy </a> <a href="https://journament.com/keyword_connections/31721" class="keyword-tag"> <i class="fas fa-tag me-1"></i> neck </a> <a href="https://journament.com/keyword_connections/135590" class="keyword-tag"> <i class="fas fa-tag me-1"></i> deltopectoral flapmedicinesurgery </a> </div> </td> </tr> </table> </div> </div> <!-- Citations Chart --> <!-- Ad Unit --> <div class="my-4"> <script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4045225739665865" crossorigin="anonymous"></script> <ins class="adsbygoogle" style="display:block" data-ad-client="ca-pub-4045225739665865" data-ad-slot="2450678444" data-ad-format="auto" data-full-width-responsive="true"></ins> <script>(adsbygoogle = window.adsbygoogle || []).push({});</script> </div> <!-- Citations Section --> <div class="citations-section"> <div class="citations-header"> <h3 class="citations-title"> <i class="fas fa-quote-left"></i> Citations </h3> </div> <div class="citations-body"> <div class="empty-state"> <i class="fas fa-quote-right"></i> <p>No citations found. To add a citation, contact the admin at info@scimatic.org</p> </div> </div> </div> <!-- Comments Section --> <div class="comments-section"> <div class="comments-header"> <h3 class="comments-title"> <i class="fas fa-comments"></i> Comments </h3> <div> <a href="https://journament.com/login" class="btn btn-primary action-btn me-2"> <i class="fas fa-sign-in-alt"></i> Login to comment </a> <a href="https://journament.com/register" class="btn btn-secondary action-btn"> <i class="fas fa-user-plus"></i> Register </a> </div> </div> <div class="comments-body"> <div class="empty-state"> <i class="far fa-comment-dots"></i> <p>No comments yet. Be the first to comment on this article.</p> </div> </div> </div> </div> </div> </div> <!-- AI Analysis Confirmation Modal --> <div class="modal fade" id="ai_analysis_modal" tabindex="-1" aria-labelledby="aiAnalysisModalLabel" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content" style="border-radius: 15px; border: none; box-shadow: 0 10px 40px rgba(0,0,0,0.15);"> <div class="modal-header" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px 15px 0 0; border: none; padding: 25px 30px;"> <h5 class="modal-title text-white" id="aiAnalysisModalLabel" style="font-weight: 600; font-size: 1.3rem;"> <i class="fas fa-brain me-2"></i>Calculate Quality Scores </h5> <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body" style="padding: 30px;"> <div class="mb-4"> <h6 class="fw-bold mb-3" style="color: #333; font-size: 1.1rem;"> <i class="fas fa-chart-line text-primary me-2"></i>What will be calculated: </h6> <div class="row g-3"> <div class="col-md-6"> <div class="d-flex align-items-start"> <i class="fas fa-check-circle text-success mt-1 me-2"></i> <span>Readability & Clarity</span> </div> </div> <div class="col-md-6"> <div class="d-flex align-items-start"> <i class="fas fa-check-circle text-success mt-1 me-2"></i> <span>Academic Rigor</span> </div> </div> <div class="col-md-6"> <div class="d-flex align-items-start"> <i class="fas fa-check-circle text-success mt-1 me-2"></i> <span>Novelty</span> </div> </div> <div class="col-md-6"> <div class="d-flex align-items-start"> <i class="fas fa-check-circle text-success mt-1 me-2"></i> <span>Methodology Quality</span> </div> </div> <div class="col-md-6"> <div class="d-flex align-items-start"> <i class="fas fa-check-circle text-success mt-1 me-2"></i> <span>Practical Impact Potential</span> </div> </div> <div class="col-md-6"> <div class="d-flex align-items-start"> <i class="fas fa-check-circle text-success mt-1 me-2"></i> <span>Research Integrity</span> </div> </div> <div class="col-md-6"> <div class="d-flex align-items-start"> <i class="fas fa-check-circle text-success mt-1 me-2"></i> <span>Performance Tier</span> </div> </div> <div class="col-md-6"> <div class="d-flex align-items-start"> <i class="fas fa-check-circle text-success mt-1 me-2"></i> <span>Keyword Generation</span> </div> </div> </div> </div> <div class="alert alert-info mb-4" role="alert" style="border-left: 4px solid #2196F3; background-color: #E3F2FD; border-radius: 8px;"> <div class="d-flex align-items-center"> <i class="fas fa-info-circle text-info me-2" style="font-size: 1.2rem;"></i> <div> <strong>Analysis powered by Google Gemini AI</strong><br> <small>Following NISO RP-25-2016 and DORA assessment principles</small> </div> </div> </div> <div class="alert alert-warning mb-3" role="alert" style="border-left: 4px solid #FF9800; background-color: #FFF3E0; border-radius: 8px;"> <div class="d-flex align-items-center"> <i class="fas fa-clock text-warning me-2" style="font-size: 1.2rem;"></i> <div> <strong>Estimated time: 10-30 seconds</strong><br> <small>Please wait while AI analyzes the article</small> </div> </div> </div> <div class="pricing-box mb-0" style="background: linear-gradient(135deg, #e8f5e9 0%, #f1f8e9 100%); border-radius: 10px; padding: 15px; border: 1px solid #c8e6c9;"> <div class="d-flex justify-content-between align-items-center"> <div> <div style="font-weight: 600; color: #2e7d32; font-size: 0.95rem;"> <i class="fas fa-tag me-1"></i>Analysis Cost </div> <div style="font-size: 1.5rem; font-weight: 700; color: #1b5e20;"> 5.00 USDT </div> </div> <div class="text-end"> <div style="font-weight: 600; color: #666; font-size: 0.9rem;"> <i class="fas fa-wallet me-1"></i>Your Balance </div> <div style="font-size: 1.3rem; font-weight: 700; color: #c62828;"> 0.00 USDT </div> </div> </div> <div class="mt-2 text-center"> <span class="text-danger" style="font-size: 0.9rem;"> <i class="fas fa-exclamation-triangle me-1"></i> Insufficient balance. <a href="https://journament.com/dashboard/balance" class="fw-bold">Top up now</a> </span> </div> </div> </div> <div class="modal-footer" style="border-top: 1px solid rgba(0,0,0,0.05); padding: 20px 30px;"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" style="border-radius: 8px; padding: 10px 20px;"> <i class="fas fa-times me-1"></i>Cancel </button> <a href="https://journament.com/dashboard/balance" class="btn btn-success" style="border-radius: 8px; padding: 10px 25px; font-weight: 600;"> <i class="fas fa-plus-circle me-2"></i>Top Up Balance </a> </div> </div> </div> </div> <!-- Claim Authorship Modal --> <div class="modal fade" id="claim_authorship" tabindex="-1" aria-labelledby="claimAuthorshipLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="claimAuthorshipLabel"> Claim Authorship </h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <p> Do you claim this paper? If you are one of the authors of this paper, click the Claim button below. </p> <a class="btn btn-danger" href="https://journament.com/claim_biblio/172134"> <i class="fas fa-user-check me-2"></i>Claim </a> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> </div> </div> </div> </div> <!-- Modals --> <!-- Modal --> <div class="modal fade" id="add_keyword_biblio" tabindex="-1" aria-labelledby="addKeywordBiblioLabel" aria-hidden="true"> <div class="modal-dialog modal-sm"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="addKeywordBiblioLabel"> Add Keyword </h5> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body"> <form action="https://journament.com/journals" method="store"> <label for="keyword" class="">Enter Keyword e.g. Organic Chemistry</label> <input type="text" name="keyword" value="" class="form-control" placeholder="Suitable Keyword" > <input type="hidden" name="store_type" value="add_keyword"> <input type="hidden" name="bibliography_id" value="172134"> <button type="submit" class="btn btn-primary">Save</button> </form> </div> </div> </div> </div> </main> <!-- Footer --> <footer class="footer"> <div class="container"> <div class="row"> <div class="col-lg-4 footer-column"> <div class="d-flex align-items-center mb-3"> <img src="https://journament.com/storage/journament/logo.png" alt="Journament" class="footer-log" style="height: 160px; width: auto; margin-bottom: 0; margin-right: 12px;"> <span style="font-weight: 700; font-size: 1.5rem; color: #ffffff;">Journament</span> </div> <p class="mb-3">Journament is a unique journals indexing service that helps researchers find the perfect publication venue for their work.</p> <div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 8px; margin-top: 15px;"> <div style="font-size: 0.9rem; color: #ecf0f1; margin-bottom: 10px;"> <i class="fas fa-brain me-2"></i><strong>Powered by AI</strong> </div> <div style="font-size: 0.85rem; color: #bdc3c7; line-height: 1.5;"> Following NISO RP-25-2016, DORA 2025, and COPE international standards for transparent research assessment </div> </div> </div> <div class="col-lg-2 col-md-3 col-sm-6 footer-column"> <h5 class="footer-heading">Quick Links</h5> <ul class="footer-links"> <li><a href="https://journament.com/en">Home</a></li> <li><a href="https://journament.com/en/statistics">Statistics</a></li> <li><a href="https://journament.com/en/journals">Journals</a></li> <li><a href="https://journament.com/en/howitworks">How It Works</a></li> <li><a href="https://journament.com/en/pricing">Pricing</a></li> <li><a href="https://journament.com/login">Login</a></li> <li><a href="https://journament.com/register">Register</a></li> </ul> </div> <div class="col-lg-2 col-md-3 col-sm-6 footer-column"> <h5 class="footer-heading">Resources</h5> <ul class="footer-links"> <li><a href="https://journament.com/en/blog">Blog</a></li> <li><a href="https://journament.com/en/help">Help Center</a></li> <li><a href="https://journament.com/en/faqs">FAQs</a></li> <li><a href="#" data-bs-toggle="modal" data-bs-target="#contact_admin">Contact Us</a></li> </ul> </div> <div class="col-lg-2 col-md-3 col-sm-6 footer-column"> <h5 class="footer-heading">Partner Sites</h5> <ul class="footer-links"> <li><a href="https://scimatic.org" target="_blank">SciMatic</a></li> <li><a href="https://improofer.com" target="_blank">ImProofer</a></li> <li><a href="https://scimatic.net" target="_blank">SciMatic Network</a></li> <li><a href="https://decerta.scimatic.net" target="_blank">DeCerta</a></li> <li><a href="https://events.scimatic.net" target="_blank">SciMatic Events</a></li> </ul> </div> <div class="col-lg-2 col-md-3 col-sm-6 footer-column"> <h5 class="footer-heading">Company & Legal</h5> <ul class="footer-links"> <li><a href="https://journament.com/en/about">About Us</a></li> <li><a href="https://journament.com/en/terms">Terms of Service</a></li> <li><a href="https://journament.com/en/privacy">Privacy Policy</a></li> <li><a href="https://journament.com/en/delivery">Delivery & Returns</a></li> <li><a href="https://journament.com/en/distance-sales">Distance Sales Agreement</a></li> </ul> </div> </div> <!-- Payment Methods --> <div class="row mt-4"> <div class="col-12 text-center"> <div style="background: rgba(255,255,255,0.05); padding: 20px; border-radius: 8px;"> <p style="color: #bdc3c7; font-size: 14px; margin-bottom: 15px;">Secure Payment Methods</p> <div class="d-flex justify-content-center align-items-center gap-3 flex-wrap"> <img src="https://journament.com/images/payment/visa.svg" alt="Visa" style="height: 32px; background: white; padding: 4px 8px; border-radius: 4px;"> <img src="https://journament.com/images/payment/mastercard.svg" alt="MasterCard" style="height: 32px; background: white; padding: 4px 8px; border-radius: 4px;"> <img src="https://journament.com/images/payment/eppay-logo.svg" alt="Eppay" style="height: 32px;"> </div> </div> </div> </div> <div class="footer-bottom text-center"> <p class="footer-copyright mb-0"> © 2026 Journament. All rights reserved. | AI-Powered Quality Analysis </p> </div> </div> </footer> </div> <!-- Scripts are loaded via Vite --> <!-- Custom scripts per page --> <!-- jQuery CDN for immediate fix --> <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script> <!-- Web3.js for MetaMask integration --> <script src="https://cdn.jsdelivr.net/npm/web3@1.8.0/dist/web3.min.js"></script> <!-- Blockchain Minting Integration --> <script src="https://journament.com/js/blockchain-minting.js"></script> <script> // Translation strings for JavaScript window.__bib_trans = { confirmation_required: "Confirmation Required", cancel: "Cancel", confirm: "Confirm", ai_analysis_in_progress: "AI Analysis in Progress...", analyzing_with_gemini: "Analyzing article with Gemini AI...", evaluating_readability: "Evaluating readability and clarity...", assessing_rigor: "Assessing academic rigor...", measuring_novelty: "Measuring novelty and innovation...", analyzing_methodology: "Analyzing methodology quality...", calculating_relevance: "Calculating topic relevance...", evaluating_integrity: "Evaluating research integrity...", finalizing_scores: "Finalizing quality scores...", analysis_completed: "The page will now reload with fresh data.", analysis_failed_retry: "Failed to complete analysis", analysis_start_failed: "Failed to start analysis. Please try again.", analysis_longer_than_expected: "Analysis is taking longer than expected. Please refresh the page to check results.", analysis_status_failed: "Failed to check analysis status. Please refresh and try again.", ai_analysis_failed_retry: "AI analysis failed. Please try again.", calculating: "Calculating...", calculate_tier_confirm: "Calculate performance tier for this article?\u003Cbr\u003E\u003Cbr\u003EThis will determine if the article is a star\/popular\/steady\/emerging performer within its journal based on engagement metrics.\u003Cbr\u003E\u003Cstrong\u003ECost: 5 USDT\u003C\/strong\u003E", calculation_failed: "Calculation failed", tier_calculation_error: "An error occurred during tier calculation. Please try again.", extract_abstract_title: "Extract Abstract", extract_abstract_text: "This will attempt to automatically extract the abstract from:", not_available: "Not available", extract_time_warning: "This may take 10-30 seconds. Continue?", still_extracting: "Still extracting...", extract_timeout: "Abstract extraction timed out. The process may still be running in the background. Please refresh the page to check if the abstract was added.", extract_success: "Abstract extracted successfully! The page will now reload to show the results.", extract_failed: "Failed to extract abstract. Please try again.", extract_network_error: "Network error during abstract extraction. Please check your connection and try again.", research_abstract_title: "Re-search Abstract", research_abstract_text: "The current abstract appears to be incomplete or contains metadata.", research_abstract_sources: "This will attempt to find a better abstract from:", research_replace_warning: "This may take 10-30 seconds and will replace the current abstract. Continue?", still_researching: "Still re-searching...", research_timeout: "Abstract re-search timed out. Please try again or contact support.", research_success: "Abstract re-search completed! The page will now reload to show the improved abstract.", research_failed: "Failed to re-search abstract. Please try again.", research_kept: "The current abstract will be kept.", research_failed_prefix: "Re-search failed:", research_network_error: "Network error during re-search. Please check your connection and try again.", ai_powered_doi_search: "AI-Powered DOI Search", multi_strategy_doi: "Multi-Strategy DOI Discovery", doi_search_description: "Our AI will search for this article\u0027s DOI using multiple methods:", url_extraction: "URL Extraction", scan_url_metadata: "Scan article URL and metadata", google_scholar: "Google Scholar", search_academic_db: "Search academic database", google_gemini_ai: "Google Gemini AI", intelligent_matching: "Intelligent pattern matching", estimated_time: "Estimated time: 10-30 seconds", start_search: "Start Search", searching_doi: "Searching for DOI...", doi_not_found_msg: "Could not find DOI. The article may not have a DOI assigned or is not indexed in available sources.", doi_search_error: "An error occurred while searching for DOI", doi_found: "DOI Found!", doi_found_text: "The DOI has been found and saved to the article.", found_via: "Found via", ai_search: "AI Search", view_doi: "View DOI", reload_page: "Reload Page", insufficient_balance: "Insufficient balance.", calculate_quality: "Calculate Quality Scores", translate: "Translate", translated: "Translation completed successfully!", translating: "Translating...", translation_error: "Translation error", }; </script> <script> // Professional notification system (replaces amateur JavaScript alerts) function showNotification(message, type = 'info', requireConfirmation = false, onConfirm = null) { if (requireConfirmation) { // Show confirmation dialog const confirmModal = document.createElement('div'); confirmModal.className = 'modal fade'; confirmModal.innerHTML = ` <div class="modal-dialog modal-dialog-centered"> <div class="modal-content"> <div class="modal-header bg-${type === 'warning' ? 'warning' : 'primary'} text-white"> <h5 class="modal-title"> <i class="fas fa-${type === 'warning' ? 'exclamation-triangle' : 'question-circle'} me-2"></i> ${window.__bib_trans?.confirmation_required || 'Confirmation Required'} </h5> <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button> </div> <div class="modal-body"> <p>${message}</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">${window.__bib_trans?.cancel || 'Cancel'}</button> <button type="button" class="btn btn-primary" id="confirmBtn">${window.__bib_trans?.confirm || 'Confirm'}</button> </div> </div> </div> `; document.body.appendChild(confirmModal); // Use custom modal system with fallback to Bootstrap if (window.showCustomModal) { window.showCustomModal(confirmModal); } else if (typeof bootstrap !== 'undefined') { const modal = new bootstrap.Modal(confirmModal); modal.show(); } // Handle confirmation confirmModal.querySelector('#confirmBtn').addEventListener('click', function() { // Close modal using custom system if (window.hideCustomModal) { window.hideCustomModal(confirmModal); } else { confirmModal.classList.remove('show'); const backdrop = document.querySelector('.modal-backdrop'); if (backdrop) backdrop.remove(); } if (onConfirm) onConfirm(); }); // Handle cancel button const cancelBtn = confirmModal.querySelector('[data-bs-dismiss="modal"]'); if (cancelBtn) { cancelBtn.addEventListener('click', function() { if (window.hideCustomModal) { window.hideCustomModal(confirmModal); } }); } // Remove from DOM after hidden confirmModal.addEventListener('hidden.bs.modal', function() { confirmModal.remove(); }); } else { // Show notification toast const notification = document.createElement('div'); notification.className = `alert alert-${type} alert-dismissible fade show position-fixed`; notification.style.cssText = 'top: 80px; right: 20px; z-index: 10000; min-width: 350px; max-width: 500px; box-shadow: 0 8px 24px rgba(0,0,0,0.25); border: none; border-radius: 8px; font-size: 1rem; padding: 1rem 1.5rem;'; notification.innerHTML = ` <div style="display: flex; align-items: center; gap: 12px;"> <i class="fas fa-${type === 'success' ? 'check-circle' : type === 'danger' ? 'exclamation-circle' : type === 'warning' ? 'exclamation-triangle' : 'info-circle'}" style="font-size: 1.5rem;"></i> <div style="flex: 1; line-height: 1.5;">${message}</div> <button type="button" class="btn-close" data-bs-dismiss="alert" style="font-size: 1.2rem;"></button> </div> `; document.body.appendChild(notification); // Auto-dismiss after 6 seconds setTimeout(() => { notification.classList.remove('show'); setTimeout(() => notification.remove(), 150); }, 6000); } } // Calculate Article Performance Tier Function function calculateArticleTier(bibliographyId) { const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); showNotification( window.__bib_trans?.calculate_tier_confirm || 'Calculate performance tier for this article?', 'info', true, function() { const btn = event.target.closest('button'); if (!btn) return; const originalContent = btn.innerHTML; btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + (window.__bib_trans?.calculating || 'Calculating...'); btn.disabled = true; fetch(`/bibliographies/${bibliographyId}/calculate-tier`, { method: 'POST', headers: { 'X-CSRF-TOKEN': csrfToken, 'Content-Type': 'application/json', 'Accept': 'application/json' } }) .then(response => { if (response.status === 403) { return response.json().then(data => { showNotification(data.error, 'danger'); setTimeout(() => { window.location.href = '/dashboard/balance'; }, 3000); throw new Error('Insufficient balance'); }); } if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) .then(data => { if (data.success) { showNotification(data.message, 'success'); setTimeout(() => window.location.reload(), 2000); } else { showNotification(data.error || (window.__bib_trans?.calculation_failed || 'Calculation failed'), 'danger'); btn.innerHTML = originalContent; btn.disabled = false; } }) .catch(error => { console.error('Error:', error); if (error.message !== 'Insufficient balance') { showNotification(window.__bib_trans?.tier_calculation_error || 'An error occurred during tier calculation. Please try again.', 'danger'); } btn.innerHTML = originalContent; btn.disabled = false; }); } ); } // Start AI Analysis from modal function startAIAnalysis() { // Close the modal using Bootstrap 5 modal API const modalElement = document.getElementById('ai_analysis_modal'); if (modalElement) { // Remove focus from button before closing modal to prevent aria-hidden warning const focusedElement = document.activeElement; if (focusedElement && modalElement.contains(focusedElement)) { focusedElement.blur(); } // Use custom modal system (Bootstrap 5 compatible) if (window.hideCustomModal) { window.hideCustomModal(modalElement); } else { // Bootstrap 5 fallback using data-bs-dismiss trigger const modalInstance = bootstrap.Modal.getInstance(modalElement); if (modalInstance) { modalInstance.hide(); } else { // Create instance and hide (Bootstrap 5 method) const modal = bootstrap.Modal.getOrCreateInstance(modalElement); modal.hide(); } } } // Create persistent loading notification with spinner const loadingNotification = document.createElement('div'); loadingNotification.id = 'ai-analysis-loading'; loadingNotification.className = 'alert alert-info alert-dismissible fade show position-fixed'; loadingNotification.style.cssText = 'top: 80px; right: 20px; z-index: 10000; min-width: 400px; max-width: 500px; box-shadow: 0 8px 24px rgba(0,0,0,0.25); border: none; border-radius: 8px; font-size: 1rem; padding: 1.5rem;'; loadingNotification.innerHTML = ` <div style="display: flex; align-items: center; gap: 15px;"> <div> <i class="fas fa-brain fa-2x" style="color: #2196F3; animation: pulse 1.5s infinite;"></i> </div> <div style="flex: 1;"> <div style="font-weight: 600; margin-bottom: 5px;"> <i class="fas fa-spinner fa-spin me-2"></i>${window.__bib_trans?.ai_analysis_in_progress || 'AI Analysis in Progress...'} </div> <div style="font-size: 0.9rem; color: #555;"> <span id="analysis-status">${window.__bib_trans?.analyzing_with_gemini || 'Analyzing article with Gemini AI...'}</span> </div> <div style="margin-top: 8px;"> <div style="background-color: rgba(33, 150, 243, 0.2); border-radius: 10px; height: 6px; overflow: hidden;"> <div id="analysis-progress" style="background: linear-gradient(90deg, #2196F3, #64B5F6); height: 100%; width: 0%; transition: width 0.3s ease; animation: progress 20s linear forwards;"></div> </div> </div> </div> </div> <style> @keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.7; transform: scale(1.1); } } @keyframes progress { 0% { width: 0%; } 100% { width: 95%; } } </style> `; document.body.appendChild(loadingNotification); // Get CSRF token and bibliography ID const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content'); const bibliographyId = window.location.pathname.split('/').pop(); // Update status messages periodically const t = window.__bib_trans || {}; const statusMessages = [ t.analyzing_with_gemini || 'Analyzing article with Gemini AI...', t.evaluating_readability || 'Evaluating readability and clarity...', t.assessing_rigor || 'Assessing academic rigor...', t.measuring_novelty || 'Measuring novelty and innovation...', t.analyzing_methodology || 'Analyzing methodology quality...', t.calculating_relevance || 'Calculating topic relevance...', t.evaluating_integrity || 'Evaluating research integrity...', t.finalizing_scores || 'Finalizing quality scores...' ]; let messageIndex = 0; const statusInterval = setInterval(() => { const statusSpan = document.getElementById('analysis-status'); if (statusSpan && messageIndex < statusMessages.length) { messageIndex++; statusSpan.textContent = statusMessages[messageIndex % statusMessages.length]; } }, 3000); // Start analysis via AJAX fetch(`/bibliographies/${bibliographyId}/start-analysis`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': token, 'Accept': 'application/json' } }) .then(response => response.json()) .then(data => { clearInterval(statusInterval); // Remove loading notification const loadingEl = document.getElementById('ai-analysis-loading'); if (loadingEl) { loadingEl.remove(); } if (data.success) { console.log('Analysis completed successfully:', data.message); showNotification('✅ ' + data.message + ' ' + (window.__bib_trans?.analysis_completed || 'The page will now reload with fresh data.'), 'success'); // Force cache refresh by adding ?refresh=1 parameter const url = new URL(window.location.href); url.searchParams.set('refresh', '1'); setTimeout(() => window.location.href = url.toString(), 2000); } else { console.error('Failed to complete analysis:', data.error); showNotification('❌ ' + (window.__bib_trans?.analysis_failed_retry || 'Failed to complete analysis') + ': ' + (data.error || 'Unknown error'), 'danger'); } }) .catch(error => { clearInterval(statusInterval); // Remove loading notification const loadingEl = document.getElementById('ai-analysis-loading'); if (loadingEl) { loadingEl.remove(); } console.error('AJAX error starting analysis:', error); showNotification('❌ ' + (window.__bib_trans?.analysis_start_failed || 'Failed to start analysis. Please try again.'), 'danger'); }); } // Poll for analysis completion function startAnalysisPolling(button) { const bibliographyId = window.location.pathname.split('/').pop(); const statusUrl = `/bibliographies/${bibliographyId}/analysis-status`; let pollCount = 0; const maxPolls = 30; // Poll for up to 60 seconds (30 polls × 2 seconds) const pollInterval = setInterval(() => { pollCount++; fetch(statusUrl) .then(response => response.json()) .then(data => { console.log('Analysis status:', data); if (data.status === 'completed') { clearInterval(pollInterval); // Show success message and reload page with fresh data showNotification(data.message, 'success'); const url = new URL(window.location.href); url.searchParams.set('refresh', '1'); setTimeout(() => window.location.href = url.toString(), 1500); } else if (data.status === 'failed') { clearInterval(pollInterval); // Reset button and show error resetAnalysisButton(button); showNotification(window.__bib_trans?.ai_analysis_failed_retry || 'AI analysis failed. Please try again.', 'danger'); } else if (pollCount >= maxPolls) { clearInterval(pollInterval); resetAnalysisButton(button); showNotification(window.__bib_trans?.analysis_longer_than_expected || 'Analysis is taking longer than expected. Please refresh the page to check results.', 'warning'); } else { // Update loading text with current status const loadingSpan = button.querySelector('.btn-loading'); if (loadingSpan) { loadingSpan.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + (data.message ? data.message.replace('🤖 ', '') : 'Analyzing...'); } } }) .catch(error => { console.error('Error checking analysis status:', error); if (pollCount >= maxPolls) { clearInterval(pollInterval); resetAnalysisButton(button); showNotification(window.__bib_trans?.analysis_status_failed || 'Failed to check analysis status. Please refresh and try again.', 'danger'); } }); }, 2000); // Poll every 2 seconds } // Reset button to normal state function resetAnalysisButton(button) { const btnContent = button.querySelector('.btn-content'); const btnLoading = button.querySelector('.btn-loading'); if (btnContent) btnContent.style.display = 'inline'; if (btnLoading) btnLoading.style.display = 'none'; button.disabled = false; } // Abstract Extraction Button with Loading Spinner function confirmExtraction(button) { showNotification( '📄 <strong>' + (window.__bib_trans?.extract_abstract_title || 'Extract Abstract') + '</strong><br><br>' + (window.__bib_trans?.extract_abstract_text || 'This will attempt to automatically extract the abstract from:') + '<br>' + '• <strong>DOI:</strong> ' + (!!'' ? '' : (window.__bib_trans?.not_available || 'Not available')) + '<br>' + '• <strong>URL:</strong> http://scielo.isciii.es/scielo.php?script=sci_artt...' + (!!'http://scielo.isciii.es/scielo.php?script=sci_arttext&pid=S0376-78922006000300009' ? '' : (window.__bib_trans?.not_available || 'Not available')) + '<br><br>' + '<em>' + (window.__bib_trans?.extract_time_warning || 'This may take 10-30 seconds. Continue?') + '</em>', 'info', true, function() { // Show loading state const btnContent = button.querySelector('.btn-content'); const btnLoading = button.querySelector('.btn-loading'); btnContent.style.display = 'none'; btnLoading.style.display = 'inline'; button.disabled = true; // Add timeout warning after 15 seconds const timeoutWarning = setTimeout(() => { if (button.disabled) { // Still processing const loadingText = btnLoading.querySelector('i').nextSibling; if (loadingText) { loadingText.textContent = ' ' + (window.__bib_trans?.still_extracting || 'Still extracting...'); } } }, 15000); // Add safety timeout to reset button after 45 seconds const safetyTimeout = setTimeout(() => { if (button.disabled) { resetExtractionButton(button); showNotification(window.__bib_trans?.extract_timeout || 'Abstract extraction timed out. The process may still be running in the background. Please refresh the page to check if the abstract was added.', 'warning'); } }, 45000); // Handle form submission via AJAX to properly manage button state const form = button.closest('form'); if (form) { // Get CSRF token const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content'); const formData = new FormData(form); fetch(form.action, { method: 'POST', headers: { 'X-CSRF-TOKEN': token, 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }, body: formData }) .then(response => { clearTimeout(timeoutWarning); clearTimeout(safetyTimeout); if (response.ok) { // Success - reload page to show the new abstract showNotification(window.__bib_trans?.extract_success || 'Abstract extracted successfully! The page will now reload to show the results.', 'success'); setTimeout(() => window.location.reload(), 1500); } else { // Handle different error types response.json().then(data => { resetExtractionButton(button); if (data.warning) { // This is a warning (like "already has abstract") showNotification(data.warning + ' The page will now reload to show the current abstract.', 'info'); setTimeout(() => window.location.reload(), 2000); } else if (data.error) { // This is an actual error showNotification(data.error, 'danger'); console.error('Abstract extraction error:', data); } else { // Fallback error message showNotification(window.__bib_trans?.extract_failed || 'Failed to extract abstract. Please try again.', 'danger'); console.error('Abstract extraction error:', data); } }).catch(parseError => { // If JSON parsing fails, fall back to text response.text().then(errorText => { resetExtractionButton(button); showNotification(window.__bib_trans?.extract_failed || 'Failed to extract abstract. Please try again.', 'danger'); console.error('Abstract extraction error:', errorText); }); }); } }) .catch(error => { clearTimeout(timeoutWarning); clearTimeout(safetyTimeout); resetExtractionButton(button); showNotification(window.__bib_trans?.extract_network_error || 'Network error during abstract extraction. Please check your connection and try again.', 'danger'); console.error('Abstract extraction network error:', error); }); } } ); return false; } // Search Abstract Button (when no abstract exists) function confirmSearchAbstract(button) { const btnContent = button.querySelector('.btn-content'); const btnLoading = button.querySelector('.btn-loading'); btnContent.style.display = 'none'; btnLoading.style.display = 'inline'; button.disabled = true; const safetyTimeout = setTimeout(() => { if (button.disabled) { resetExtractionButton(button); showNotification('Abstract search timed out. The process may still be running. Please refresh the page.', 'warning'); } }, 45000); const form = button.closest('form'); if (form) { const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content'); const formData = new FormData(form); fetch(form.action, { method: 'POST', headers: { 'X-CSRF-TOKEN': token, 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }, body: formData }) .then(response => { clearTimeout(safetyTimeout); if (response.ok) { showNotification('Abstract found and saved! Reloading...', 'success'); setTimeout(() => window.location.reload(), 1500); } else { response.json().then(data => { resetExtractionButton(button); showNotification(data.error || 'Could not find abstract from available sources.', 'danger'); }).catch(() => { resetExtractionButton(button); showNotification('Could not find abstract. Please try again.', 'danger'); }); } }) .catch(() => { clearTimeout(safetyTimeout); resetExtractionButton(button); showNotification('Network error. Please check your connection and try again.', 'danger'); }); } return false; } // Reset extraction button to normal state function resetExtractionButton(button) { const btnContent = button.querySelector('.btn-content'); const btnLoading = button.querySelector('.btn-loading'); if (btnContent) btnContent.style.display = 'inline'; if (btnLoading) btnLoading.style.display = 'none'; button.disabled = false; } // Re-search Abstract Button (for poor quality abstracts) function confirmReExtraction(button) { showNotification( '🔄 <strong>' + (window.__bib_trans?.research_abstract_title || 'Re-search Abstract') + '</strong><br><br>' + (window.__bib_trans?.research_abstract_text || 'The current abstract appears to be incomplete or contains metadata.') + '<br><br>' + (window.__bib_trans?.research_abstract_sources || 'This will attempt to find a better abstract from:') + '<br>' + '• <strong>DOI:</strong> ' + (!!'' ? '' : (window.__bib_trans?.not_available || 'Not available')) + '<br>' + '• <strong>URL:</strong> http://scielo.isciii.es/scielo.php?script=sci_artt...' + (!!'http://scielo.isciii.es/scielo.php?script=sci_arttext&pid=S0376-78922006000300009' ? '' : (window.__bib_trans?.not_available || 'Not available')) + '<br><br>' + '<em>' + (window.__bib_trans?.research_replace_warning || 'This may take 10-30 seconds and will replace the current abstract. Continue?') + '</em>', 'info', true, function() { // Show loading state const btnContent = button.querySelector('.btn-content'); const btnLoading = button.querySelector('.btn-loading'); btnContent.style.display = 'none'; btnLoading.style.display = 'inline'; button.disabled = true; // Add timeout warning after 15 seconds const timeoutWarning = setTimeout(() => { if (button.disabled) { const loadingText = btnLoading.querySelector('i').nextSibling; if (loadingText) { loadingText.textContent = ' ' + (window.__bib_trans?.still_researching || 'Still re-searching...'); } } }, 15000); // Add safety timeout const safetyTimeout = setTimeout(() => { if (button.disabled) { resetExtractionButton(button); showNotification(window.__bib_trans?.research_timeout || 'Abstract re-search timed out. Please try again or contact support.', 'warning'); } }, 45000); // Handle form submission via AJAX const form = button.closest('form'); if (form) { const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content'); const formData = new FormData(form); fetch(form.action, { method: 'POST', headers: { 'X-CSRF-TOKEN': token, 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }, body: formData }) .then(response => { clearTimeout(timeoutWarning); clearTimeout(safetyTimeout); if (response.ok) { showNotification(window.__bib_trans?.research_success || 'Abstract re-search completed! The page will now reload to show the improved abstract.', 'success'); setTimeout(() => window.location.reload(), 1500); } else { response.json().then(data => { resetExtractionButton(button); if (data.warning) { showNotification(data.warning + ' ' + (window.__bib_trans?.research_kept || 'The current abstract will be kept.'), 'info'); } else if (data.error) { showNotification((window.__bib_trans?.research_failed_prefix || 'Re-search failed:') + ' ' + data.error, 'danger'); } else { showNotification(window.__bib_trans?.research_failed || 'Failed to re-search abstract. Please try again.', 'danger'); } }).catch(() => { resetExtractionButton(button); showNotification(window.__bib_trans?.research_failed || 'Failed to re-search abstract. Please try again.', 'danger'); }); } }) .catch(error => { clearTimeout(timeoutWarning); clearTimeout(safetyTimeout); resetExtractionButton(button); showNotification(window.__bib_trans?.research_network_error || 'Network error during re-search. Please check your connection and try again.', 'danger'); console.error('Abstract re-search error:', error); }); return false; } return true; } ); return false; } // Simplified animations for better performance document.addEventListener('DOMContentLoaded', function() { const elements = document.querySelectorAll('.paper-card, .chart-container, .citations-section, .comments-section'); elements.forEach((el, index) => { el.style.opacity = '0'; el.style.transform = 'translateY(10px)'; el.style.transition = 'all 0.3s ease'; setTimeout(() => { el.style.opacity = '1'; el.style.transform = 'translateY(0)'; }, index * 100); }); // Ensure abstract quality warning stays visible const abstractWarning = document.querySelector('.abstract-quality-warning'); if (abstractWarning) { abstractWarning.style.opacity = '1'; abstractWarning.style.visibility = 'visible'; abstractWarning.style.display = 'block'; } }); // Find DOI with AI - Professional Modal function findDOIWithAI(bibliographyId) { // Show professional confirmation modal showDOISearchModal(bibliographyId); } function showDOISearchModal(bibliographyId) { // Create modal HTML const modalHTML = ` <div class="modal fade" id="doiSearchModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content" style="border: none; border-radius: 15px; box-shadow: 0 10px 40px rgba(0,0,0,0.2);"> <div class="modal-header" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; border-radius: 15px 15px 0 0; padding: 1.5rem;"> <h5 class="modal-title fw-bold"> <i class="fas fa-robot me-2"></i>${window.__bib_trans?.ai_powered_doi_search || 'AI-Powered DOI Search'} </h5> <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body" style="padding: 2rem;"> <div class="text-center mb-3"> <div style="font-size: 3rem; color: #667eea; margin-bottom: 1rem;"> <i class="fas fa-search"></i> </div> <h6 class="fw-bold mb-3">${window.__bib_trans?.multi_strategy_doi || 'Multi-Strategy DOI Discovery'}</h6> <p class="text-muted mb-4">${window.__bib_trans?.doi_search_description || 'Our AI will search for this article\'s DOI using multiple methods:'}</p> </div> <div class="search-strategy-list"> <div class="strategy-item" style="display: flex; align-items: start; padding: 12px; background: #f8f9fa; border-radius: 8px; margin-bottom: 10px;"> <div style="width: 40px; height: 40px; background: linear-gradient(135deg, #4facfe, #00f2fe); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; margin-right: 15px; flex-shrink: 0;">1</div> <div> <strong style="color: #2c3e50;">${window.__bib_trans?.url_extraction || 'URL Extraction'}</strong> <p class="mb-0 text-muted" style="font-size: 0.9rem;">${window.__bib_trans?.scan_url_metadata || 'Scan article URL and metadata'}</p> </div> </div> <div class="strategy-item" style="display: flex; align-items: start; padding: 12px; background: #f8f9fa; border-radius: 8px; margin-bottom: 10px;"> <div style="width: 40px; height: 40px; background: linear-gradient(135deg, #11998e, #38ef7d); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; margin-right: 15px; flex-shrink: 0;">2</div> <div> <strong style="color: #2c3e50;">${window.__bib_trans?.google_scholar || 'Google Scholar'}</strong> <p class="mb-0 text-muted" style="font-size: 0.9rem;">${window.__bib_trans?.search_academic_db || 'Search academic database'}</p> </div> </div> <div class="strategy-item" style="display: flex; align-items: start; padding: 12px; background: #f8f9fa; border-radius: 8px;"> <div style="width: 40px; height: 40px; background: linear-gradient(135deg, #667eea, #764ba2); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; margin-right: 15px; flex-shrink: 0;">3</div> <div> <strong style="color: #2c3e50;">${window.__bib_trans?.google_gemini_ai || 'Google Gemini AI'}</strong> <p class="mb-0 text-muted" style="font-size: 0.9rem;">${window.__bib_trans?.intelligent_matching || 'Intelligent pattern matching'}</p> </div> </div> </div> <div class="alert alert-info mt-4 mb-0" style="background: rgba(79, 172, 254, 0.1); border: 1px solid rgba(79, 172, 254, 0.3); color: #0c5460;"> <i class="fas fa-info-circle me-2"></i> <strong>${window.__bib_trans?.estimated_time || 'Estimated time: 10-30 seconds'}</strong> </div> </div> <div class="modal-footer" style="border: none; padding: 1.5rem; background: #f8f9fa; border-radius: 0 0 15px 15px;"> <button type="button" class="btn btn-secondary px-4" data-bs-dismiss="modal" style="font-weight: 600;"> <i class="fas fa-times me-2"></i>${window.__bib_trans?.cancel || 'Cancel'} </button> <button type="button" class="btn px-4" onclick="startDOISearch(${bibliographyId})" style="background: linear-gradient(135deg, #667eea, #764ba2); color: white; border: none; font-weight: 600;"> <i class="fas fa-search me-2"></i>${window.__bib_trans?.start_search || 'Start Search'} </button> </div> </div> </div> </div> `; // Remove existing modal if any const existingModal = document.getElementById('doiSearchModal'); if (existingModal) { existingModal.remove(); } // Add modal to body document.body.insertAdjacentHTML('beforeend', modalHTML); // Show modal using custom modal system const modalElement = document.getElementById('doiSearchModal'); if (modalElement && window.showCustomModal) { window.showCustomModal(modalElement); } } function startDOISearch(bibliographyId) { // Close modal using custom modal system const modalElement = document.getElementById('doiSearchModal'); if (modalElement && window.hideCustomModal) { window.hideCustomModal(modalElement); } const statusDiv = document.getElementById('doi-search-status'); const messageSpan = document.getElementById('doi-search-message'); // Show loading status if (statusDiv) { statusDiv.style.display = 'block'; if (statusDiv.querySelector('.alert')) { statusDiv.querySelector('.alert').className = 'alert alert-info mb-0'; } } if (messageSpan) { messageSpan.textContent = '🔍 ' + (window.__bib_trans?.searching_doi || 'Searching for DOI...'); } // Make AJAX request fetch(`/bibliographies/${bibliographyId}/find-doi`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') }, body: JSON.stringify({}) }) .then(response => { // Check if response is ok before parsing JSON if (!response.ok) { // Try to parse error as JSON, otherwise use status text return response.json().catch(() => { throw new Error(`Server error: ${response.status} ${response.statusText}`); }).then(data => { throw new Error(data.message || `Server error: ${response.status}`); }); } return response.json(); }) .then(data => { if (data.success) { // Hide status div if (statusDiv) { statusDiv.style.display = 'none'; } // Update DOI on page without reloading const doiDisplayContainer = document.getElementById('doi-display-container'); if (doiDisplayContainer && data.doi) { // Replace the entire container content with just the DOI value doiDisplayContainer.innerHTML = '<span id="doi-value">' + data.doi + '</span>'; } // Show professional success modal showDOISuccessModal(data.doi, data.method || 'AI'); } else { // Hide status div if (statusDiv) { statusDiv.style.display = 'none'; } // Show professional error notification showNotification(data.message || ('❌ ' + (window.__bib_trans?.doi_not_found_msg || 'Could not find DOI. The article may not have a DOI assigned or is not indexed in available sources.')), 'danger'); } }) .catch(error => { console.error('Error:', error); // Hide status div if (statusDiv) { statusDiv.style.display = 'none'; } // Show professional error notification showNotification('❌ ' + (window.__bib_trans?.doi_search_error || 'An error occurred while searching for DOI') + ': ' + error.message, 'danger'); }); } function showDOISuccessModal(doi, method) { const t = window.__bib_trans || {}; const methodNames = { 'url_extraction': t.url_extraction || 'URL Extraction', 'google_scholar': t.google_scholar || 'Google Scholar', 'gemini_ai': t.google_gemini_ai || 'Google Gemini AI', 'AI': t.ai_search || 'AI Search' }; const methodIcons = { 'url_extraction': 'fa-link', 'google_scholar': 'fa-graduation-cap', 'gemini_ai': 'fa-robot', 'AI': 'fa-robot' }; const methodName = methodNames[method] || method; const methodIcon = methodIcons[method] || 'fa-check-circle'; const modalHTML = ` <div class="modal fade" id="doiSuccessModal" tabindex="-1" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content" style="border: none; border-radius: 15px; box-shadow: 0 10px 40px rgba(0,0,0,0.2);"> <div class="modal-header" style="background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); color: white; border: none; border-radius: 15px 15px 0 0; padding: 1.5rem;"> <h5 class="modal-title fw-bold"> <i class="fas fa-check-circle me-2"></i>${t.doi_found || 'DOI Found Successfully!'} </h5> <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button> </div> <div class="modal-body" style="padding: 2rem;"> <div class="text-center mb-4"> <div style="font-size: 4rem; color: #11998e; margin-bottom: 1rem; animation: successBounce 0.6s ease;"> <i class="fas fa-check-circle"></i> </div> <h6 class="fw-bold mb-2">${t.doi_found || 'DOI Successfully Retrieved'}</h6> <p class="text-muted">${t.doi_found_text || 'The DOI has been found and saved to this article.'}</p> </div> <div style="background: #f8f9fa; padding: 1.5rem; border-radius: 12px; border-left: 4px solid #11998e; margin-bottom: 1.5rem;"> <div class="d-flex align-items-center mb-2"> <i class="fas fa-fingerprint me-2" style="color: #11998e; font-size: 1.2rem;"></i> <strong style="color: #2c3e50;">${t.doi || 'Digital Object Identifier'}</strong> </div> <div style="font-family: monospace; font-size: 1.1rem; color: #495057; word-break: break-all;"> ${doi} </div> </div> <div class="alert alert-success mb-0" style="background: rgba(17, 153, 142, 0.1); border: 1px solid rgba(17, 153, 142, 0.3); color: #0a5f56;"> <i class="fas ${methodIcon} me-2"></i> <strong>${t.found_via || 'Found via'}:</strong> ${methodName} </div> </div> <div class="modal-footer" style="border: none; padding: 1.5rem; background: #f8f9fa; border-radius: 0 0 15px 15px;"> <button type="button" class="btn px-4" data-bs-dismiss="modal" style="background: linear-gradient(135deg, #11998e, #38ef7d); color: white; border: none; font-weight: 600;"> <i class="fas fa-check me-2"></i>${t.confirm || 'Got It'} </button> </div> </div> </div> </div> <style> @keyframes successBounce { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } } </style> `; // Remove existing modal if any const existingModal = document.getElementById('doiSuccessModal'); if (existingModal) { existingModal.remove(); } // Add modal to body document.body.insertAdjacentHTML('beforeend', modalHTML); // Show modal using custom modal system const modalElement = document.getElementById('doiSuccessModal'); if (modalElement && window.showCustomModal) { window.showCustomModal(modalElement); } // Also show a toast notification showNotification('✅ DOI found and saved successfully!', 'success'); } // Auto-translate to user's app language function autoTranslateToAppLanguage(appLocale) { // Find all translation wrappers (title and abstract) document.querySelectorAll('.translation-tabs-wrapper').forEach(wrapper => { const type = wrapper.getAttribute('data-type'); const id = wrapper.getAttribute('data-id'); const field = wrapper.getAttribute('data-field'); // Check if translation already exists for this locale const existingTab = wrapper.querySelector(`a[href="#${field}-lang-${appLocale}"]`); if (existingTab) { // Translation exists - activate the tab existingTab.click(); // Clear sessionStorage flag if it was set sessionStorage.removeItem('autoTranslated_' + field); } else { // Translation doesn't exist - check if we should translate const translateBtn = wrapper.querySelector(`.translate-lang-btn[data-locale="${appLocale}"]`); // Check if we just translated this (to prevent infinite reload loop) const justTranslated = sessionStorage.getItem('autoTranslated_' + field) === appLocale; if (translateBtn && !wrapper.dataset.autoTranslating && !justTranslated) { // Mark as translating to prevent duplicate calls wrapper.dataset.autoTranslating = 'true'; // Auto-translate in background (no loading state for user) fetch('/api/translate', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Accept': 'application/json' }, body: JSON.stringify({ type: type, id: id, field: field, locale: appLocale }) }) .then(response => response.json()) .then(data => { if (data.success) { // Translation successful - reload page to show new translation // Mark that we just translated so we don't get into a loop sessionStorage.setItem('autoTranslated_' + field, appLocale); window.location.reload(); } else { console.log('Auto-translation skipped:', data.message); } delete wrapper.dataset.autoTranslating; }) .catch(error => { console.error('Auto-translation error:', error); delete wrapper.dataset.autoTranslating; }); } } }); } // Manual translation trigger (for dropdown clicks) function translateContent(wrapper, locale) { const type = wrapper.getAttribute('data-type'); const id = wrapper.getAttribute('data-id'); const field = wrapper.getAttribute('data-field'); const btn = wrapper.querySelector(`.translate-lang-btn[data-locale="${locale}"]`); if (!btn) return; const originalText = btn.innerHTML; btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> ' + (window.__bib_trans?.translating || 'Translating...'); btn.classList.add('disabled'); fetch('/api/translate', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Accept': 'application/json' }, body: JSON.stringify({ type, id, field, locale }) }) .then(response => response.json()) .then(data => { if (data.success) { showNotification('✅ ' + (window.__bib_trans?.translated || 'Translation completed successfully!'), 'success'); setTimeout(() => window.location.reload(), 1000); } else { showNotification('❌ ' + (data.message || 'Translation failed. Please try again.'), 'danger'); btn.innerHTML = originalText; btn.classList.remove('disabled'); } }) .catch(error => { console.error('Translation error:', error); showNotification('❌ ' + (window.__bib_trans?.translation_error || 'Translation error') + ': ' + error.message, 'danger'); btn.innerHTML = originalText; btn.classList.remove('disabled'); }); } // Auto-translation for title and abstract when language is selected document.addEventListener('DOMContentLoaded', function() { const appLocale = "en"; // Auto-select and auto-translate to user's app language on page load if (appLocale !== 'en') { autoTranslateToAppLanguage(appLocale); } // Handle translation button clicks in dropdown (when no translation exists yet) document.querySelectorAll('.translate-lang-btn').forEach(btn => { btn.addEventListener('click', function(e) { e.preventDefault(); const locale = this.getAttribute('data-locale'); const wrapper = this.closest('.translation-tabs-wrapper'); translateContent(wrapper, locale); }); }); }); // Blockchain Minting Progress Modal function createMintingModal() { const modal = document.createElement('div'); modal.id = 'blockchain-minting-modal'; modal.innerHTML = ` <div class="minting-overlay"> <div class="minting-card"> <div class="minting-header"> <i class="fas fa-cube minting-icon"></i> <h3>Minting Article NFT</h3> <p class="minting-subtitle">Please wait while we process your transaction...</p> </div> <div class="minting-progress"> <div class="progress-step" data-step="1"> <div class="step-icon"><i class="fas fa-link"></i></div> <div class="step-content"> <h4>Connecting to MetaMask</h4> <p>Establishing secure connection...</p> </div> <div class="step-status"><i class="fas fa-spinner fa-spin"></i></div> </div> <div class="progress-step" data-step="2"> <div class="step-icon"><i class="fas fa-coins"></i></div> <div class="step-content"> <h4>Checking SUSD Allowance</h4> <p>Verifying token approval...</p> </div> <div class="step-status"><i class="fas fa-clock"></i></div> </div> <div class="progress-step" data-step="3"> <div class="step-icon"><i class="fas fa-check-circle"></i></div> <div class="step-content"> <h4>Approving SUSD</h4> <p>Authorizing payment token...</p> </div> <div class="step-status"><i class="fas fa-clock"></i></div> </div> <div class="progress-step" data-step="4"> <div class="step-icon"><i class="fas fa-gem"></i></div> <div class="step-content"> <h4>Minting NFT</h4> <p>Creating your blockchain asset...</p> </div> <div class="step-status"><i class="fas fa-clock"></i></div> </div> <div class="progress-step" data-step="5"> <div class="step-icon"><i class="fas fa-database"></i></div> <div class="step-content"> <h4>Recording Transaction</h4> <p>Saving to database...</p> </div> <div class="step-status"><i class="fas fa-clock"></i></div> </div> </div> <div class="minting-footer"> <div class="progress-bar"> <div class="progress-fill"></div> </div> <p class="progress-text">Step <span id="current-step">1</span> of 5</p> </div> </div> </div> `; document.body.appendChild(modal); // Add styles const style = document.createElement('style'); style.textContent = ` #blockchain-minting-modal { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 99999; animation: fadeIn 0.3s ease; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .minting-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.8); backdrop-filter: blur(10px); display: flex; align-items: center; justify-content: center; } .minting-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 20px; padding: 40px; max-width: 600px; width: 90%; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); animation: slideUp 0.4s ease; } @keyframes slideUp { from { transform: translateY(50px); opacity: 0; } to { transform: translateY(0); opacity: 1; } } .minting-header { text-align: center; color: white; margin-bottom: 30px; } .minting-icon { font-size: 48px; margin-bottom: 15px; animation: bounce 2s infinite; } @keyframes bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } } .minting-header h3 { font-size: 28px; font-weight: 700; margin: 0 0 10px 0; } .minting-subtitle { opacity: 0.9; font-size: 14px; margin: 0; } .minting-progress { background: rgba(255, 255, 255, 0.1); border-radius: 15px; padding: 20px; margin-bottom: 20px; } .progress-step { display: flex; align-items: center; padding: 15px; margin-bottom: 10px; border-radius: 10px; background: rgba(255, 255, 255, 0.05); transition: all 0.3s ease; } .progress-step.active { background: rgba(255, 255, 255, 0.15); transform: scale(1.02); } .progress-step.completed { background: rgba(76, 175, 80, 0.2); } .step-icon { width: 50px; height: 50px; border-radius: 50%; background: rgba(255, 255, 255, 0.2); display: flex; align-items: center; justify-content: center; margin-right: 15px; font-size: 20px; color: white; } .progress-step.active .step-icon { background: rgba(255, 255, 255, 0.3); animation: pulse 1.5s infinite; } @keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } } .progress-step.completed .step-icon { background: #4CAF50; } .step-content { flex: 1; color: white; } .step-content h4 { margin: 0 0 5px 0; font-size: 16px; font-weight: 600; } .step-content p { margin: 0; font-size: 13px; opacity: 0.8; } .step-status { font-size: 20px; color: white; } .progress-step.active .step-status i { animation: spin 1s linear infinite; } @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } .progress-step.completed .step-status { color: #4CAF50; } .minting-footer { color: white; text-align: center; } .progress-bar { height: 8px; background: rgba(255, 255, 255, 0.2); border-radius: 10px; overflow: hidden; margin-bottom: 10px; } .progress-fill { height: 100%; background: linear-gradient(90deg, #4CAF50, #8BC34A); border-radius: 10px; width: 0%; transition: width 0.5s ease; box-shadow: 0 0 10px rgba(76, 175, 80, 0.5); } .progress-text { margin: 0; font-size: 14px; opacity: 0.9; } .minting-success { text-align: center; animation: successPop 0.5s ease; } @keyframes successPop { 0% { transform: scale(0); } 50% { transform: scale(1.1); } 100% { transform: scale(1); } } .success-icon { font-size: 80px; color: #4CAF50; margin-bottom: 20px; animation: checkmark 0.8s ease; } @keyframes checkmark { 0% { transform: scale(0) rotate(0deg); } 50% { transform: scale(1.2) rotate(180deg); } 100% { transform: scale(1) rotate(360deg); } } `; document.head.appendChild(style); return modal; } function updateMintingProgress(step, status = 'active') { const modal = document.getElementById('blockchain-minting-modal'); if (!modal) return; const steps = modal.querySelectorAll('.progress-step'); const currentStepEl = modal.querySelector('#current-step'); const progressFill = modal.querySelector('.progress-fill'); // Update current step number currentStepEl.textContent = step; // Update progress bar const progress = (step / 5) * 100; progressFill.style.width = progress + '%'; // Update step states steps.forEach((stepEl, index) => { const stepNum = index + 1; stepEl.classList.remove('active', 'completed'); if (stepNum < step) { stepEl.classList.add('completed'); stepEl.querySelector('.step-status').innerHTML = '<i class="fas fa-check"></i>'; } else if (stepNum === step) { stepEl.classList.add('active'); if (status === 'active') { stepEl.querySelector('.step-status').innerHTML = '<i class="fas fa-spinner fa-spin"></i>'; } } }); } function showMintingSuccess(txHash) { const modal = document.getElementById('blockchain-minting-modal'); if (!modal) return; const card = modal.querySelector('.minting-card'); card.innerHTML = ` <div class="minting-success"> <div class="success-icon"> <i class="fas fa-check-circle"></i> </div> <h2 style="color: white; margin-bottom: 15px;">Minting Successful!</h2> <p style="color: rgba(255,255,255,0.9); margin-bottom: 20px;"> Your article has been successfully minted as an NFT on the blockchain. </p> <div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 10px; margin-bottom: 20px;"> <p style="color: rgba(255,255,255,0.7); font-size: 12px; margin: 0 0 5px 0;">Transaction Hash:</p> <p style="color: white; font-family: monospace; font-size: 12px; word-break: break-all; margin: 0;"> ${txHash} </p> </div> <p style="color: rgba(255,255,255,0.8); font-size: 14px;"> <i class="fas fa-sync-alt fa-spin"></i> Refreshing page in 2 seconds... </p> </div> `; } function closeMintingModal() { const modal = document.getElementById('blockchain-minting-modal'); if (modal) { modal.style.animation = 'fadeOut 0.3s ease'; setTimeout(() => modal.remove(), 300); } } // Add fadeOut animation const fadeOutStyle = document.createElement('style'); fadeOutStyle.textContent = ` @keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } } `; document.head.appendChild(fadeOutStyle); // Blockchain Minting for Article (Two-Contract Architecture) async function mintArticleOnBlockchain(articleId, journalId) { const btn = document.getElementById('mint-article-btn'); const originalHTML = btn.innerHTML; // Validate journal ID if (!journalId || journalId === 0) { showNotification('This article does not have a journal assigned. Please assign it to a journal before minting on blockchain.', 'warning'); return; } // Create and show minting modal const modal = createMintingModal(); btn.disabled = true; try { // Step 1: Initialize and connect wallet updateMintingProgress(1, 'active'); await window.blockchainMinting.init(); const account = await window.blockchainMinting.connectWallet(); if (!account) { throw new Error('Failed to connect MetaMask wallet'); } // Prepare metadata URI const metadataURI = `https://journament.com/api/nft/article/${articleId}`; // Step 2: Check SUSD allowance updateMintingProgress(2, 'active'); const currentAllowance = await window.blockchainMinting.checkAllowance(window.blockchainMinting.articleContractAddress); // Step 3: Approve SUSD if needed if (currentAllowance < 10) { updateMintingProgress(3, 'active'); await window.blockchainMinting.approveSUSD(10, window.blockchainMinting.articleContractAddress); await new Promise(resolve => setTimeout(resolve, 3000)); } else { updateMintingProgress(3, 'completed'); } // Step 4: Mint article NFT updateMintingProgress(4, 'active'); const txHash = await window.blockchainMinting.mintArticle( articleId, journalId, account ); // Step 5: Record transaction in database updateMintingProgress(5, 'active'); const response = await fetch(`/blockchain/article/${articleId}/record`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content') }, body: JSON.stringify({ transaction_hash: txHash, wallet_address: account, metadata_uri: metadataURI }) }); const data = await response.json(); if (data.success) { // Show success animation showMintingSuccess(txHash); showNotification('Article successfully minted on blockchain!', 'success'); // Reload page after showing success setTimeout(() => { closeMintingModal(); window.location.reload(); }, 2000); } else { throw new Error(data.message || 'Failed to record transaction'); } } catch (error) { console.error('Blockchain minting error:', error); closeMintingModal(); let errorMessage = 'Failed to mint article on blockchain.'; if (error.message.includes('User denied')) { errorMessage = 'Transaction cancelled by user.'; } else if (error.message.includes('insufficient funds')) { errorMessage = 'Insufficient funds in wallet.'; } else if (error.message.includes('MetaMask')) { errorMessage = 'Please install MetaMask extension to continue.'; } else if (error.message) { errorMessage = error.message; } showNotification(errorMessage, 'danger'); // Restore button btn.disabled = false; btn.innerHTML = originalHTML; } } // Translation tab system document.addEventListener('DOMContentLoaded', function() { document.querySelectorAll('.translation-tabs-wrapper').forEach(function(wrapper) { wrapper.addEventListener('click', function(e) { const langBtn = e.target.closest('.translate-lang-btn'); if (!langBtn) return; e.preventDefault(); const locale = langBtn.dataset.locale; const type = wrapper.dataset.type; const id = wrapper.dataset.id; const field = wrapper.dataset.field; langBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i> Translating...'; langBtn.classList.add('disabled'); fetch('https://journament.com/api/translate', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-TOKEN': 'QcqHJHZyHQMSsRVDaXcuoWR2ow1MmBYEBYkntsB8', 'Accept': 'application/json' }, body: JSON.stringify({ type, id: parseInt(id), field, locale }) }) .then(r => r.json()) .then(data => { if (data.success) { const tabId = field + '-lang-' + locale; const nav = wrapper.querySelector('.translation-nav') || wrapper.querySelector('.nav'); const tabContent = wrapper.querySelector('.tab-content'); const addBtn = wrapper.querySelector('.dropdown').closest('.nav-item'); // If no tabs exist yet, build the tab structure if (!nav) { const originalContent = wrapper.querySelector('.paper-title, .abstract-content'); const origHtml = originalContent ? originalContent.outerHTML : ''; wrapper.innerHTML = ` <ul class="nav nav-pills mb-2 translation-nav" style="font-size:0.8rem; gap:4px;"> <li class="nav-item"><a class="nav-link active py-1 px-2" data-bs-toggle="pill" href="#${field}-lang-en">EN</a></li> <li class="nav-item"><a class="nav-link py-1 px-2" data-bs-toggle="pill" href="#${tabId}">${locale.toUpperCase()}</a></li> <li class="nav-item ms-auto"> <div class="dropdown d-inline-block"> <button class="btn btn-outline-secondary btn-sm py-0 px-2 dropdown-toggle" type="button" data-bs-toggle="dropdown" style="font-size:0.8rem;"><i class="fas fa-plus"></i></button> <ul class="dropdown-menu dropdown-menu-end" id="${field}-lang-menu"></ul> </div> </li> </ul> <div class="tab-content"> <div class="tab-pane fade show active" id="${field}-lang-en">${origHtml}</div> <div class="tab-pane fade" id="${tabId}"><div class="${field === 'title' ? 'paper-title' : 'abstract-content'}" style="font-size:${field === 'title' ? '1.6rem' : 'inherit'}; font-weight:${field === 'title' ? '700' : 'inherit'};">${data.data.translated_text}</div></div> </div>`; rebuildLangMenu(wrapper, field, [locale]); // Activate new tab const newTab = wrapper.querySelector('a[href="#' + tabId + '"]'); if (newTab) new bootstrap.Tab(newTab).show(); return; } // Add new tab pill before the + button const newLi = document.createElement('li'); newLi.className = 'nav-item'; newLi.innerHTML = '<a class="nav-link py-1 px-2" data-bs-toggle="pill" href="#' + tabId + '">' + locale.toUpperCase() + '</a>'; addBtn.before(newLi); // Add tab pane const newPane = document.createElement('div'); newPane.className = 'tab-pane fade'; newPane.id = tabId; newPane.innerHTML = '<div class="' + (field === 'title' ? 'paper-title' : 'abstract-content') + '">' + data.data.translated_text + '</div>'; tabContent.appendChild(newPane); // Remove language from dropdown langBtn.closest('li').remove(); // Activate new tab new bootstrap.Tab(newLi.querySelector('a')).show(); showNotification('Translation completed successfully!: ' + locale.toUpperCase(), 'success'); } else { showNotification(data.message || 'Translation failed', 'danger'); langBtn.innerHTML = langBtn.dataset.locale; langBtn.classList.remove('disabled'); } }) .catch(() => { showNotification('Translation failed. Please try again.', 'danger'); langBtn.innerHTML = langBtn.dataset.locale; langBtn.classList.remove('disabled'); }); }); }); }); function rebuildLangMenu(wrapper, field, usedLocales) { const allLocales = {tr:'Turkish',es:'Spanish',pt:'Portuguese',ar:'Arabic',zh:'Chinese',fr:'French',de:'German',id:'Indonesian',ru:'Russian',th:'Thai'}; const menu = wrapper.querySelector('#' + field + '-lang-menu'); if (!menu) return; menu.innerHTML = ''; for (const [lc, ln] of Object.entries(allLocales)) { if (!usedLocales.includes(lc)) { menu.innerHTML += '<li><a class="dropdown-item translate-lang-btn" href="#" data-locale="' + lc + '">' + ln + '</a></li>'; } } } // Load Unpaywall data </script> <!-- Page-specific JavaScript --> <script> // Prevent FOUC - mark fonts as loaded immediately document.documentElement.classList.add('fonts-loaded'); // Bootstrap 5 Compatible JavaScript document.addEventListener('DOMContentLoaded', function() { // Auto-dismiss alerts after 6 seconds setTimeout(function() { const alerts = document.querySelectorAll('.alert:not(.alert-permanent)'); alerts.forEach(alert => { // Use Bootstrap 5 Alert instance if (typeof bootstrap !== 'undefined') { var alertInstance = bootstrap.Alert.getOrCreateInstance(alert); alertInstance.close(); } else { alert.style.opacity = '0'; setTimeout(() => { alert.style.display = 'none'; }, 300); } }); }, 6000); // Initialize Bootstrap 5 components if (typeof bootstrap !== 'undefined') { // Initialize popovers var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]')); var popoverList = popoverTriggerList.map(function (popoverTriggerEl) { return new bootstrap.Popover(popoverTriggerEl); }); // Initialize tooltips var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl); }); // Initialize dropdowns (explicit initialization for better reliability) var dropdownElementList = [].slice.call(document.querySelectorAll('.dropdown-toggle')); var dropdownList = dropdownElementList.map(function (dropdownToggleEl) { return new bootstrap.Dropdown(dropdownToggleEl); }); // Modal initialization handled by custom modal system in bootstrap.js // Removed duplicate initialization that was causing conflicts // Bootstrap 5 collapse is automatically initialized via data-bs-toggle attributes // Just verify Bootstrap is loaded console.log('Bootstrap loaded:', typeof bootstrap !== 'undefined'); console.log('Bootstrap Collapse available:', typeof bootstrap.Collapse !== 'undefined'); } }); // DISABLED - Modal handling is now done by custom modal system in bootstrap.js // This prevents conflicts with the custom modal implementation function openModal(modalId) { console.log('openModal() is deprecated - modals are handled by custom system'); } function closeModal(modalId) { console.log('closeModal() is deprecated - modals are handled by custom system'); } </script> </body> </html>