Jump to content
Wikipedia The Free Encyclopedia

User:Polygnotus/Scripts/Backlog.js

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.
Documentation for this user script can be added at User:Polygnotus/Scripts/Backlog.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
 /**
  * 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&&currentPage!==targetPage
 });

 // Only navigate if we're not already on the target page
 if(targetPage&&currentPage!==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();
 });

AltStyle によって変換されたページ (->オリジナル) /