User:Polygnotus/Scripts/WebArchives.js
Appearance
From Wikipedia, the free encyclopedia
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump.
This code will be executed when previewing this page.
This code will be executed when previewing this page.
Documentation for this user script can be added at User:Polygnotus/Scripts/WebArchives.
// Wikipedia Archive Checker - Add to your common.js // Adds an archive checking form to Special:Preferences (function(){ 'use strict'; //don't run on mobile if(mw.config.get('skin')==='minerva'){ return; } // Only run on watchlist page if(mw.config.get('wgCanonicalSpecialPageName')!=='Watchlist'){ return; } // Flag to prevent double initialization letinitialized=false; // Create the archive checker section functioncreateArchiveChecker(){ constsection=document.createElement('div'); section.id='archive-checker-section'; section.style.cssText=` margin: 20px 0; padding: 20px; border: 1px solid #a2a9b1; border-radius: 4px; background-color: #f8f9fa; `; section.innerHTML=` <h3 style="margin-top: 0; color: #0645ad;">Archive Checker</h3> <p>Check if a URL exists in various web archives:</p> <div style="margin-bottom: 15px;"> <input type="text" id="url-input" placeholder="Enter URL (e.g., example.com)" style="width: 300px; padding: 8px; border: 1px solid #a2a9b1; border-radius: 2px;"> <button id="check-btn" style="margin-left: 10px; padding: 8px 15px; background: #0645ad; color: white; border: none; border-radius: 2px; cursor: pointer;">Check</button> </div> <div id="results-container" style="margin-top: 15px;"></div> `; returnsection; } // Normalize URL (add protocol if missing) functionnormalizeUrl(url){ url=url.trim(); if(!url)return''; // Remove protocol if present, we'll add it back consistently url=url.replace(/^https?:\/\//,''); url=url.replace(/^www\./,''); returnurl; } // Archive services configuration constarchiveServices=[ { name:'Archive.today', checkUrl:(url)=>`https://archive.ph/${encodeURIComponent(url)}`, searchUrl:(url)=>`https://archive.ph/${encodeURIComponent(url)}`, apiCheck:true, notFoundStrings:['No results'] }, { name:'Ghost Archive', checkUrl:(url)=>`https://ghostarchive.org/search?term=${encodeURIComponent(url)}`, searchUrl:(url)=>`https://ghostarchive.org/search?term=${encodeURIComponent(url)}`, apiCheck:false }, { name:'Wayback Machine', checkUrl:(url)=>`https://web.archive.org/web/20250000000000*/${encodeURIComponent(url)}`, searchUrl:(url)=>`https://web.archive.org/web/*/${encodeURIComponent(url)}`, apiUrl:(url)=>`https://archive.org/wayback/available?url=${encodeURIComponent(url)}`, apiCheck:true, notFoundStrings:['Wayback Machine has not archived that URL.'] } ]; // Check archives asyncfunctioncheckArchives(url){ constresultsContainer=document.getElementById('results-container'); resultsContainer.innerHTML='<p>Checking archives...</p>'; constnormalizedUrl=normalizeUrl(url); if(!normalizedUrl){ resultsContainer.innerHTML='<p style="color: red;">Please enter a valid URL.</p>'; return; } console.log(`[Archive Checker] Starting check for URL: ${normalizedUrl}`); constresults=[]; for(constserviceofarchiveServices){ console.log(`[Archive Checker] Checking ${service.name}...`); constresult={ name:service.name, url:service.searchUrl(normalizedUrl), available:null, error:null }; if(service.apiCheck){ try{ letcheckUrl; if(service.name==='Wayback Machine'&&service.apiUrl){ // Use the dedicated API for Wayback Machine checkUrl=service.apiUrl(normalizedUrl); console.log(`[Archive Checker] ${service.name} API URL: ${checkUrl}`); constresponse=awaitfetch(checkUrl); console.log(`[Archive Checker] ${service.name} API Response status: ${response.status}`); constdata=awaitresponse.json(); console.log(`[Archive Checker] ${service.name} API Response data:`,data); // Check if archived_snapshots has any properties (indicating archives exist) consthasArchives=data.archived_snapshots&& Object.keys(data.archived_snapshots).length>0&& data.archived_snapshots.closest; result.available=!!hasArchives;// Ensure it's a boolean if(hasArchives){ result.url=data.archived_snapshots.closest.url; console.log(`[Archive Checker] ${service.name} - Archive found: ${result.url}`); }else{ console.log(`[Archive Checker] ${service.name} - No archive found (empty archived_snapshots)`); } }else{ // For other services, fetch the page content and check for "not found" strings checkUrl=service.checkUrl(normalizedUrl); console.log(`[Archive Checker] ${service.name} Check URL: ${checkUrl}`); constresponse=awaitfetch(checkUrl,{ mode:'cors', credentials:'omit' }); console.log(`[Archive Checker] ${service.name} Response status: ${response.status}`); if(response.ok){ consttext=awaitresponse.text(); console.log(`[Archive Checker] ${service.name} Response length: ${text.length} characters`); // Check if any "not found" strings are present constnotFound=service.notFoundStrings&& service.notFoundStrings.some(str=>text.includes(str)); if(notFound){ result.available=false; console.log(`[Archive Checker] ${service.name} - Not found (detected: "${service.notFoundStrings.find(str=>text.includes(str))}")`); }else{ result.available=true; console.log(`[Archive Checker] ${service.name} - Archive appears to be available`); } }else{ result.available=null; result.error=`HTTP ${response.status}`; console.log(`[Archive Checker] ${service.name} - HTTP error: ${response.status}`); } } }catch(error){ console.error(`[Archive Checker] ${service.name} - Error:`,error); result.error=error.message; result.available=null; } }else{ console.log(`[Archive Checker] ${service.name} - API check disabled, will show manual link`); } results.push(result); } console.log(`[Archive Checker] Check completed. Results:`,results); displayResults(results,normalizedUrl); } // Display results functiondisplayResults(results,originalUrl){ constresultsContainer=document.getElementById('results-container'); console.log(`[Archive Checker] Displaying results for ${originalUrl}:`,results); lethtml=`<h4>Results for: ${originalUrl}</h4>`; html+='<div style="display: grid; gap: 10px;">'; results.forEach(result=>{ console.log(`[Archive Checker] Processing result for ${result.name}:`,{ available:result.available, availableType:typeofresult.available, error:result.error }); conststatusText=result.available===true?'✓ Available': result.available===false?'✗ Not found': result.error?`⚠ ${result.error}`:'? Check manually'; conststatusColor=result.available===true?'#006400': result.available===false?'#8b0000':'#666'; console.log(`[Archive Checker] ${result.name} - Status: ${statusText}, Color: ${statusColor}`); html+=` <div style="padding: 10px; border: 1px solid #ddd; border-radius: 4px; background: white;"> <div style="display: flex; justify-content: space-between; align-items: center;"> <span><strong>${result.name}</strong></span> <span style="color: ${statusColor};">${statusText}</span> </div> <div style="margin-top: 8px;"> <a href="${result.url}" target="_blank" rel="noopener" style="color: #0645ad; text-decoration: none;"> Visit ${result.name} → </a> </div> </div> `; }); html+='</div>'; html+=`<p style="margin-top: 15px; font-size: 0.9em; color: #666;"> <strong>Note:</strong> Some archives may require manual verification. Click the links above to check each archive service. </p>`; resultsContainer.innerHTML=html; } // Add the archive checker section and set up event listeners functionaddArchiveChecker(){ // Prevent multiple initialization if(initialized||document.getElementById('archive-checker-section')){ return; } constwatchlistContent=document.querySelector('.mw-changeslist, #mw-content-text'); if(!watchlistContent){ return; } initialized=true; // Add our section to the top of the watchlist page constarchiveChecker=createArchiveChecker(); watchlistContent.parentNode.insertBefore(archiveChecker,watchlistContent); // Add event listeners constcheckBtn=document.getElementById('check-btn'); consturlInput=document.getElementById('url-input'); checkBtn.addEventListener('click',()=>{ consturl=urlInput.value; checkArchives(url); }); urlInput.addEventListener('keypress',(e)=>{ if(e.key==='Enter'){ checkBtn.click(); } }); } // Initialize the checker functioninit(){ // Try to add immediately if page is already loaded addArchiveChecker(); // If not added yet, wait for the page to be fully loaded if(!initialized){ constobserver=newMutationObserver(function(mutations,obs){ if(!initialized){ addArchiveChecker(); if(initialized){ obs.disconnect(); } } }); observer.observe(document.body,{ childList:true, subtree:true }); // Cleanup observer after reasonable time setTimeout(()=>{ observer.disconnect(); },5000); } } // Start when DOM is ready if(document.readyState==='loading'){ document.addEventListener('DOMContentLoaded',init); }else{ init(); } })();