erythema elevatum diutinum</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: 126 </div> <div class="paper-metric"> <i class="fas fa-hashtag"></i> ID: 147698 </div> <div class="paper-metric"> <i class="fas fa-calendar-alt"></i> 2007 </div> </div> <div class="paper-actions"> <div class="paper-actions"> <a href="http://www.scielo.org.ar/scielo.php?script=sci_arttext&pid=S1851-300X2007000200001" 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="147698" 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">El eritema elevatum diutinum (EED) es una dermatosis crónica, poco frecuente, de causa desconocida, que al comienzo presenta histopatológicamente una vasculitis leucocitoclásticade la piel y luego evoluciona hacia una fibrosis. Presentamos una paciente con lesiones dermatológicas características y generalizadas de esta rara entidad, en la que se obtuvo una buena respuesta con la sulfonoterapia.<br>Erythema Elevatum Diutinum (EED) is a rare chronic cutaneous condition of unknow origin, that initially presents histological as leucocytoclastic vasculitis of the skin and later resolves with fibrosis. We present a patient of this uncommon condition with typical cutaneous lesions and widespread involvement. The disease was initially misinterpreted on clinical examination. Dapsone therapy was followed by a remarkable recovery.</div> </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">lorenz2007revista<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>;A M Lorenz;M I Garlatti;M S Romano;L Bollea;J Anzorena</td> </tr> <tr> <th>Journal</th> <td> <a href="https://journament.com/journal_stat/2818" class="info-link"> <i class="fas fa-book-open me-1"></i> journal of the saudi heart association </a> </td> </tr> <tr> <th>Year</th> <td> 2007 </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://www.scielo.org.ar/scielo.php?script=sci_arttext&pid=S1851-300X2007000200001" target="_blank" class="info-link">http://www.scielo.org.ar/scielo.php?script=sci_arttext&pid=S1851-300X2007000200001</a> </div> </td> </tr> <tr> <th>Keywords</th> <td> <div class="keywords-list"> <a href="https://journament.com/keyword_connections/138666" class="keyword-tag"> <i class="fas fa-tag me-1"></i> erythema elevatum diutinummedicinedermatology </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/147698"> <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="147698"> <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://www.scielo.org.ar/scielo.php?script=sci_art...' + (!!'http://www.scielo.org.ar/scielo.php?script=sci_arttext&pid=S1851-300X2007000200001' ? '' : (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://www.scielo.org.ar/scielo.php?script=sci_art...' + (!!'http://www.scielo.org.ar/scielo.php?script=sci_arttext&pid=S1851-300X2007000200001' ? '' : (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': 'i08cmtdtghOmqxy9dGa8vR7V6pfhJab6qSwaatjs', '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>