User:Polygnotus/Scripts/Backlog.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/Backlog.
/** * Wikipedia Task Manager for common.js - Sidebar Version * Add this to your User:YourUsername/common.js page * A "Tasks" tab appears next to Article/Talk tabs - click to activate */ $(document).ready(function(){ constTASK_PAGE='User:Polygnotus/barfoo2'; classWikiTaskManager{ constructor(){ this.tasks=[]; this.currentIndex=0; this.pageContent=''; this.baseTimestamp=''; this.sidebarWidth=localStorage.getItem('task_sidebar_width')||'450px'; this.isVisible=localStorage.getItem('task_sidebar_visible')!=='false'; this.isActive=localStorage.getItem('task_manager_active')==='true'; this.init(); } asyncinit(){ // Create tab on article pages this.createTaskTab(); // Check if task manager is active if(this.isActive){ console.log('Task manager active - creating UI and loading from storage'); this.createUI(); this.loadTasksFromStorage(); } } createTaskTab(){ // Only add tab on article pages (namespace 0) or if already active if(mw.config.get('wgNamespaceNumber')===0||this.isActive){ letportletId='p-namespaces'; if(mw.config.get('skin')==='vector-2022'){ portletId='p-associated-pages'; } consttabText=this.isActive?'Tasks ✓':'Tasks'; consttabTitle=this.isActive?'Task manager active (click to toggle sidebar)':'Start task manager'; consttaskLink=mw.util.addPortletLink( portletId, '#', tabText, 'ca-tasks', tabTitle, 't' ); taskLink.addEventListener('click',(e)=>{ e.preventDefault(); if(this.isActive){ // Toggle sidebar visibility this.toggleSidebar(); }else{ // Activate task manager this.activateTaskManager(); } }); } } asyncactivateTaskManager(){ localStorage.setItem('task_manager_active','true'); localStorage.setItem('task_sidebar_visible','true'); this.isActive=true; this.isVisible=true; // Create UI and load tasks this.createUI(); awaitthis.loadTasks(); // Reload to update the tab location.reload(); } createUI(){ console.log('Creating UI...'); constsidebar=document.createElement('div'); sidebar.id='task-manager-sidebar'; sidebar.innerHTML=` <div id="task-sidebar-header"> <h3>Task Manager</h3> <div id="task-sidebar-controls"> <button id="task-close-btn" title="Hide sidebar">−</button> </div> </div> <div id="task-sidebar-content"> <div id="task-info-section"> <div id="task-counter">Loading...</div> <div id="task-article-info"> <strong id="task-article-title">...</strong> <div id="task-article-url-container"> <a id="task-article-url" href="#" target="_blank">Open article in new tab →</a> </div> </div> </div> <div id="task-instructions-section"> <h4>Instructions</h4> <pre id="task-instructions-text">Loading tasks...</pre> </div> <div id="task-controls-section"> <button class="task-btn task-btn-done" id="task-btn-done" disabled>✓ Done</button> <button class="task-btn task-btn-next" id="task-btn-next" disabled>Next →</button> <button class="task-btn task-btn-refresh" id="task-btn-refresh">↻ Refresh</button> <button class="task-btn task-btn-storage" id="task-btn-storage">📊 Storage Info</button> <button class="task-btn task-btn-deactivate" id="task-btn-deactivate">✕ Close Task Manager</button> </div> </div> <div id="task-resize-handle"></div> `; this.createStyles(); console.log('Appending sidebar to body...'); document.body.appendChild(sidebar); console.log('Sidebar appended. Element exists:',!!document.getElementById('task-manager-sidebar')); this.attachEventListeners(); this.makeResizable(); if(!this.isVisible){ console.log('Hiding sidebar (isVisible is false)'); this.hideSidebar(); }else{ console.log('Sidebar should be visible'); } } createStyles(){ conststyle=document.createElement('style'); style.textContent=` #task-manager-sidebar { position: fixed; top: 0; right: 0; width: ${this.sidebarWidth}; height: 100vh; background: #fff; border-left: 2px solid #0645ad; box-shadow: -2px 0 8px rgba(0,0,0,0.1); z-index: 10000; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-size: 14px; display: flex; flex-direction: column; transition: transform 0.3s ease; } #task-manager-sidebar.hidden { transform: translateX(100%); } #task-sidebar-header { background: #0645ad; color: white; padding: 12px 15px; display: flex; justify-content: space-between; align-items: center; flex-shrink: 0; } #task-sidebar-header h3 { margin: 0; font-size: 16px; font-weight: 600; } #task-close-btn { background: none; border: none; color: white; font-size: 24px; line-height: 20px; cursor: pointer; padding: 0; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-weight: bold; } #task-close-btn:hover { opacity: 0.8; } #task-sidebar-content { padding: 20px; flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 20px; } #task-info-section { background: #f8f9fa; padding: 15px; border-radius: 6px; border: 1px solid #ddd; } #task-counter { font-size: 13px; color: #666; margin-bottom: 10px; font-weight: 500; } #task-article-info { margin-top: 10px; } #task-article-title { font-size: 16px; color: #333; display: block; margin-bottom: 8px; } #task-article-url-container { margin-top: 8px; } #task-article-url { color: #0645ad; text-decoration: none; font-size: 13px; display: inline-block; } #task-article-url:hover { text-decoration: underline; } #task-instructions-section { flex: 1; min-height: 200px; display: flex; flex-direction: column; } #task-instructions-section h4 { margin: 0 0 10px 0; font-size: 14px; font-weight: 600; color: #333; } #task-instructions-text { white-space: pre-wrap; word-wrap: break-word; font-size: 13px; line-height: 1.6; font-family: 'Courier New', monospace; background: #f8f9fa; padding: 15px; border-radius: 6px; border: 1px solid #ddd; margin: 0; flex: 1; overflow-y: auto; } #task-controls-section { display: flex; flex-direction: column; gap: 8px; padding-top: 10px; border-top: 1px solid #ddd; } .task-btn { padding: 12px 20px; font-size: 14px; font-weight: 500; border: none; border-radius: 6px; cursor: pointer; transition: background-color 0.2s, opacity 0.2s; width: 100%; } .task-btn:disabled { opacity: 0.5; cursor: not-allowed; } .task-btn-done { background: #28a745; color: white; } .task-btn-done:hover:not(:disabled) { background: #218838; } .task-btn-next { background: #007bff; color: white; } .task-btn-next:hover:not(:disabled) { background: #0056b3; } .task-btn-refresh { background: #6c757d; color: white; } .task-btn-refresh:hover:not(:disabled) { background: #5a6268; } .task-btn-storage { background: #17a2b8; color: white; font-size: 13px; } .task-btn-storage:hover:not(:disabled) { background: #138496; } .task-btn-deactivate { background: #dc3545; color: white; margin-top: 10px; font-size: 13px; } .task-btn-deactivate:hover:not(:disabled) { background: #c82333; } #task-resize-handle { position: absolute; left: 0; top: 0; width: 5px; height: 100%; cursor: ew-resize; background: transparent; } #task-resize-handle:hover { background: rgba(6, 69, 173, 0.2); } .task-error { background: #f8d7da; color: #721c24; padding: 15px; border-radius: 6px; border: 1px solid #f5c6cb; } .task-success { background: #d4edda; color: #155724; padding: 15px; border-radius: 6px; border: 1px solid #c3e6cb; } body { margin-right: ${this.sidebarWidth}; transition: margin-right 0.3s ease; } body.task-sidebar-hidden { margin-right: 0; } `; document.head.appendChild(style); this.styleElement=style; } attachEventListeners(){ document.getElementById('task-btn-done').addEventListener('click',()=>this.markTaskDone()); document.getElementById('task-btn-next').addEventListener('click',()=>this.nextTask()); document.getElementById('task-btn-refresh').addEventListener('click',()=>this.loadTasks()); document.getElementById('task-close-btn').addEventListener('click',()=>this.toggleSidebar()); document.getElementById('task-btn-storage').addEventListener('click',()=>this.showStorageInfo()); document.getElementById('task-btn-deactivate').addEventListener('click',()=>this.deactivateTaskManager()); } showStorageInfo(){ lettotal=0; letitemCount=0; for(letkeyinlocalStorage){ if(localStorage.hasOwnProperty(key)){ total+=localStorage[key].length+key.length; itemCount++; } } consttaskData=localStorage.getItem('task_manager_data'); consttaskSize=taskData?taskData.length:0; constinfo=`Storage Usage: ━━━━━━━━━━━━━━━━━━━━━━ Total localStorage: ${(total/1024).toFixed(2)} KB Task Manager data: ${(taskSize/1024).toFixed(2)} KB Total items: ${itemCount} Limit: ~5-10 MB (browser dependent) Used: ${((total/(5*1024*1024))*100).toFixed(1)}%`; alert(info); } deactivateTaskManager(){ if(confirm('Close task manager? You can restart it by clicking "Start Tasks" tab')){ localStorage.setItem('task_manager_active','false'); localStorage.removeItem('task_manager_data'); localStorage.setItem('task_sidebar_visible','false'); location.reload(); } } makeResizable(){ consthandle=document.getElementById('task-resize-handle'); constsidebar=document.getElementById('task-manager-sidebar'); letisResizing=false; handle.addEventListener('mousedown',(e)=>{ isResizing=true; document.body.style.cursor='ew-resize'; document.body.style.userSelect='none'; }); document.addEventListener('mousemove',(e)=>{ if(!isResizing)return; constnewWidth=window.innerWidth-e.clientX; if(newWidth>=300&&newWidth<=800){ this.sidebarWidth=newWidth+'px'; sidebar.style.width=this.sidebarWidth; document.body.style.marginRight=this.sidebarWidth; this.styleElement.textContent=this.styleElement.textContent.replace( /width: \d+px;/g, `width: ${this.sidebarWidth};` ).replace( /margin-right: \d+px;/g, `margin-right: ${this.sidebarWidth};` ); localStorage.setItem('task_sidebar_width',this.sidebarWidth); } }); document.addEventListener('mouseup',()=>{ if(isResizing){ isResizing=false; document.body.style.cursor=''; document.body.style.userSelect=''; } }); } toggleSidebar(){ constsidebar=document.getElementById('task-manager-sidebar'); this.isVisible=!this.isVisible; if(this.isVisible){ sidebar.classList.remove('hidden'); document.body.classList.remove('task-sidebar-hidden'); }else{ sidebar.classList.add('hidden'); document.body.classList.add('task-sidebar-hidden'); } localStorage.setItem('task_sidebar_visible',this.isVisible); } hideSidebar(){ document.getElementById('task-manager-sidebar').classList.add('hidden'); document.body.classList.add('task-sidebar-hidden'); } asyncloadTasks(){ try{ document.getElementById('task-btn-done').disabled=true; document.getElementById('task-btn-next').disabled=true; document.getElementById('task-btn-refresh').disabled=true; document.getElementById('task-instructions-text').textContent='Loading tasks from Wikipedia...'; constdata=awaitthis.fetchWikiPage(TASK_PAGE); this.pageContent=data.content; this.baseTimestamp=data.timestamp; this.tasks=this.parseTasks(this.pageContent); if(this.tasks.length===0){ this.showError('No tasks found on the page'); return; } this.currentIndex=0; this.saveTasksToStorage(); this.displayCurrentTask(); document.getElementById('task-btn-refresh').disabled=false; }catch(error){ this.showError('Error loading tasks: '+error.message); document.getElementById('task-btn-refresh').disabled=false; } } loadTasksFromStorage(){ console.log('Loading tasks from storage...'); conststoredData=localStorage.getItem('task_manager_data'); console.log('Stored data:',storedData?'Found':'Not found'); if(storedData){ try{ constdata=JSON.parse(storedData); this.tasks=data.tasks||[]; this.currentIndex=data.currentIndex||0; this.baseTimestamp=data.baseTimestamp||''; console.log('Loaded tasks:',this.tasks.length,'Current index:',this.currentIndex); if(this.tasks.length>0){ this.displayCurrentTask(); constrefreshBtn=document.getElementById('task-btn-refresh'); if(refreshBtn)refreshBtn.disabled=false; }else{ constinstructionsText=document.getElementById('task-instructions-text'); if(instructionsText){ instructionsText.textContent='No tasks loaded. Click Refresh to load tasks.'; } } }catch(e){ console.error('Error parsing stored tasks:',e); constinstructionsText=document.getElementById('task-instructions-text'); if(instructionsText){ instructionsText.textContent='Error loading saved tasks. Click Refresh to reload.'; } } }else{ constinstructionsText=document.getElementById('task-instructions-text'); if(instructionsText){ instructionsText.textContent='No tasks loaded. Click Refresh to load tasks.'; } } } saveTasksToStorage(){ try{ // Don't store pageContent - it's too large and not needed constdata={ tasks:this.tasks, currentIndex:this.currentIndex, baseTimestamp:this.baseTimestamp }; localStorage.setItem('task_manager_data',JSON.stringify(data)); }catch(e){ // If quota exceeded, try to store minimal data if(e.name==='QuotaExceededError'){ console.warn('LocalStorage quota exceeded, storing minimal data'); try{ constminimalData={ tasks:this.tasks.slice(0,3),// Only store first 3 tasks currentIndex:this.currentIndex, baseTimestamp:this.baseTimestamp }; localStorage.setItem('task_manager_data',JSON.stringify(minimalData)); }catch(e2){ console.error('Failed to save even minimal task data:',e2); } } } } asyncfetchWikiPage(pageTitle){ consturl=`/w/api.php?action=query&titles=${encodeURIComponent(pageTitle)}&prop=revisions&rvprop=content|timestamp&format=json&formatversion=2`; constusername=mw.config.get('wgUserName')||'Anonymous'; constresponse=awaitfetch(url,{ headers:{ 'Api-User-Agent':`WikiTaskManager/1.0 (User:${username})` } }); constdata=awaitresponse.json(); constpage=data.query.pages[0]; if(page.missing){ thrownewError('Page not found'); } return{ content:page.revisions[0].content, timestamp:page.revisions[0].timestamp }; } parseTasks(content){ consttasks=content.split('================================================================================'); returntasks .map(task=>task.trim()) .filter(task=>task.length>0&&!task.startsWith('__NOTOC__')); } parseTask(taskLine){ constmatch=taskLine.match(/^Article: (.+?)\nURL: (.+?)\n\nCLAUDE'S ANALYSIS:\n([\s\S]+)$/); if(!match){ return{ title:'Unknown', url:'', instructions:taskLine }; } return{ title:match[1].trim(), url:match[2].trim(), instructions:match[3].trim() }; } displayCurrentTask(){ if(this.currentIndex>=this.tasks.length){ this.showCompletion(); return; } consttaskLine=this.tasks[this.currentIndex]; consttask=this.parseTask(taskLine); console.log('Displaying task:',{ index:this.currentIndex, title:task.title, url:task.url }); document.getElementById('task-counter').textContent=`Task ${this.currentIndex+1} of ${this.tasks.length}`; document.getElementById('task-instructions-text').textContent=task.instructions; document.getElementById('task-article-title').textContent=task.title; document.getElementById('task-article-url').textContent=task.url; document.getElementById('task-article-url').href=task.url; document.getElementById('task-btn-done').disabled=false; document.getElementById('task-btn-next').disabled=this.currentIndex>=this.tasks.length-1; this.saveTasksToStorage(); // Check if we need to navigate to the article // Extract the page title from the task URL consturlMatch=task.url.match(/\/wiki\/(.+)$/); consttargetPage=urlMatch?decodeURIComponent(urlMatch[1]):''; constcurrentPage=mw.config.get('wgPageName'); console.log('Navigation check:',{ targetPage:targetPage, currentPage:currentPage, needsNavigation:targetPage&¤tPage!==targetPage }); // Only navigate if we're not already on the target page if(targetPage&¤tPage!==targetPage){ console.log('Navigating to:',task.url); window.location.href=task.url; } } asyncmarkTaskDone(){ document.getElementById('task-btn-done').disabled=true; document.getElementById('task-btn-next').disabled=true; try{ consttaskToRemove=this.tasks[this.currentIndex]; // Remove task from local array this.tasks.splice(this.currentIndex,1); // Rebuild page content constnewContent=this.tasks.join('\n================================================================================\n')+ (this.tasks.length>0?'\n================================================================================\n':''); // Save to Wikipedia awaitthis.saveWikiPage(TASK_PAGE,newContent,'Task completed and removed'); // Save state this.saveTasksToStorage(); // Display next task if(this.tasks.length===0){ this.showCompletion(); }else{ if(this.currentIndex>=this.tasks.length){ this.currentIndex=this.tasks.length-1; } this.displayCurrentTask(); } }catch(error){ this.showError('Error marking task as done: '+error.message); // Reload tasks to get current state awaitthis.loadTasks(); } } asyncsaveWikiPage(pageTitle,content,summary){ constusername=mw.config.get('wgUserName')||'Anonymous'; constuserAgent=`WikiTaskManager/1.0 (User:${username})`; // Get edit token consttokenUrl=`/w/api.php?action=query&meta=tokens&format=json&formatversion=2`; consttokenResponse=awaitfetch(tokenUrl,{ headers:{ 'Api-User-Agent':userAgent } }); consttokenData=awaittokenResponse.json(); constcsrfToken=tokenData.query.tokens.csrftoken; // Edit page consteditUrl=`/w/api.php`; constformData=newFormData(); formData.append('action','edit'); formData.append('title',pageTitle); formData.append('text',content); formData.append('summary',summary); formData.append('token',csrfToken); formData.append('format','json'); formData.append('basetimestamp',this.baseTimestamp); consteditResponse=awaitfetch(editUrl,{ method:'POST', headers:{ 'Api-User-Agent':userAgent }, body:formData }); consteditData=awaiteditResponse.json(); if(editData.error){ thrownewError(editData.error.info); } if(editData.edit&&editData.edit.result!=='Success'){ thrownewError('Edit failed: '+JSON.stringify(editData.edit)); } // Update base timestamp for next edit this.baseTimestamp=editData.edit.newtimestamp; this.saveTasksToStorage(); } nextTask(){ if(this.currentIndex<this.tasks.length-1){ this.currentIndex++; this.saveTasksToStorage(); this.displayCurrentTask(); } } showCompletion(){ document.getElementById('task-instructions-text').innerHTML='<div class="task-success">All tasks completed!</div>'; document.getElementById('task-counter').textContent='No tasks remaining'; document.getElementById('task-article-title').textContent='All done'; document.getElementById('task-article-url').textContent=''; document.getElementById('task-article-url').href='#'; document.getElementById('task-btn-done').disabled=true; document.getElementById('task-btn-next').disabled=true; } showError(message){ document.getElementById('task-instructions-text').innerHTML=`<div class="task-error">${message}</div>`; document.getElementById('task-btn-done').disabled=true; document.getElementById('task-btn-next').disabled=true; } } // Initialize the task manager newWikiTaskManager(); });