🇮🇷 Iran Proxy | https://www.wikipedia.org/wiki/User:Polygnotus/Scripts/Backlog.js
Jump to content

User:Polygnotus/Scripts/Backlog.js

From Wikipedia, the free encyclopedia
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() {
    const TASK_PAGE = 'User:Polygnotus/barfoo2';
    
    class WikiTaskManager {
        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();
        }

        async init() {
            // 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) {
                let portletId = 'p-namespaces';
                if (mw.config.get('skin') === 'vector-2022') {
                    portletId = 'p-associated-pages';
                }
                
                const tabText = this.isActive ? 'Tasks ✓' : 'Tasks';
                const tabTitle = this.isActive ? 'Task manager active (click to toggle sidebar)' : 'Start task manager';
                
                const taskLink = 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();
                    }
                });
            }
        }

        async activateTaskManager() {
            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();
            await this.loadTasks();
            
            // Reload to update the tab
            location.reload();
        }

            createUI() {
                console.log('Creating UI...');
                const sidebar = 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() {
                const style = 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() {
                let total = 0;
                let itemCount = 0;
                for (let key in localStorage) {
                    if (localStorage.hasOwnProperty(key)) {
                        total += localStorage[key].length + key.length;
                        itemCount++;
                    }
                }
                
                const taskData = localStorage.getItem('task_manager_data');
                const taskSize = taskData ? taskData.length : 0;
                
                const info = `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() {
                const handle = document.getElementById('task-resize-handle');
                const sidebar = document.getElementById('task-manager-sidebar');
                let isResizing = 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;
                    
                    const newWidth = 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() {
                const sidebar = 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');
            }

            async loadTasks() {
                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...';

                    const data = await this.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...');
                const storedData = localStorage.getItem('task_manager_data');
                console.log('Stored data:', storedData ? 'Found' : 'Not found');
                
                if (storedData) {
                    try {
                        const data = 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();
                            const refreshBtn = document.getElementById('task-btn-refresh');
                            if (refreshBtn) refreshBtn.disabled = false;
                        } else {
                            const instructionsText = 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);
                        const instructionsText = document.getElementById('task-instructions-text');
                        if (instructionsText) {
                            instructionsText.textContent = 'Error loading saved tasks. Click Refresh to reload.';
                        }
                    }
                } else {
                    const instructionsText = 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
                    const data = {
                        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 {
                            const minimalData = {
                                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);
                        }
                    }
                }
            }

            async fetchWikiPage(pageTitle) {
                const url = `/w/api.php?action=query&titles=${encodeURIComponent(pageTitle)}&prop=revisions&rvprop=content|timestamp&format=json&formatversion=2`;
                
                const username = mw.config.get('wgUserName') || 'Anonymous';
                const response = await fetch(url, {
                    headers: {
                        'Api-User-Agent': `WikiTaskManager/1.0 (User:${username})`
                    }
                });
                const data = await response.json();
                const page = data.query.pages[0];
                
                if (page.missing) {
                    throw new Error('Page not found');
                }
                
                return {
                    content: page.revisions[0].content,
                    timestamp: page.revisions[0].timestamp
                };
            }

            parseTasks(content) {
                const tasks = content.split('================================================================================');
                return tasks
                    .map(task => task.trim())
                    .filter(task => task.length > 0 && !task.startsWith('__NOTOC__'));
            }

            parseTask(taskLine) {
                const match = 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;
                }

                const taskLine = this.tasks[this.currentIndex];
                const task = 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
                const urlMatch = task.url.match(/\/wiki\/(.+)$/);
                const targetPage = urlMatch ? decodeURIComponent(urlMatch[1]) : '';
                const currentPage = 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;
                }
            }

            async markTaskDone() {
                document.getElementById('task-btn-done').disabled = true;
                document.getElementById('task-btn-next').disabled = true;
                
                try {
                    const taskToRemove = this.tasks[this.currentIndex];
                    
                    // Remove task from local array
                    this.tasks.splice(this.currentIndex, 1);
                    
                    // Rebuild page content
                    const newContent = this.tasks.join('\n================================================================================\n') + 
                                     (this.tasks.length > 0 ? '\n================================================================================\n' : '');
                    
                    // Save to Wikipedia
                    await this.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
                    await this.loadTasks();
                }
            }

            async saveWikiPage(pageTitle, content, summary) {
                const username = mw.config.get('wgUserName') || 'Anonymous';
                const userAgent = `WikiTaskManager/1.0 (User:${username})`;
                
                // Get edit token
                const tokenUrl = `/w/api.php?action=query&meta=tokens&format=json&formatversion=2`;
                const tokenResponse = await fetch(tokenUrl, {
                    headers: {
                        'Api-User-Agent': userAgent
                    }
                });
                const tokenData = await tokenResponse.json();
                const csrfToken = tokenData.query.tokens.csrftoken;
                
                // Edit page
                const editUrl = `/w/api.php`;
                const formData = new FormData();
                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);
                
                const editResponse = await fetch(editUrl, {
                    method: 'POST',
                    headers: {
                        'Api-User-Agent': userAgent
                    },
                    body: formData
                });
                
                const editData = await editResponse.json();
                
                if (editData.error) {
                    throw new Error(editData.error.info);
                }
                
                if (editData.edit && editData.edit.result !== 'Success') {
                    throw new Error('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
        new WikiTaskManager();
});