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 UserOptions API Explorer with Tools Menu Integration
$(document).ready(function() {
    // Add link to Tools menu on all pages
    mw.hook('wikipage.content').add(function() {
        mw.loader.using(['mediawiki.util'], function() {
            // Only add the link if it doesn't already exist
            if (!document.getElementById('t-useroptions-api')) {
                mw.util.addPortletLink(
                    'p-tb',                                     // Target portlet ID (Tools menu)
                    mw.config.get('wgServer') + '/wiki/Special:BlankPage/UserOptions',  // Link URL
                    'User Options API',                        // Link text
                    't-useroptions-api',                       // Link ID
                    'Explore user options via MediaWiki API'  // Tooltip
                );
            }
        });
    });

    // Only run the main interface if we're on the specific page
    if (mw.config.get('wgCanonicalSpecialPageName') === 'Blankpage' && 
        mw.config.get('wgPageName') === 'Special:BlankPage/UserOptions') {
        
        $('#firstHeading').text('User Options API Explorer');
        
        var html = '<div id="useroptions-container">' +
            '<div class="api-info" style="background: #e8f4f8; border: 1px solid #bee5eb; border-radius: 5px; padding: 15px; margin-bottom: 20px;">' +
            '<strong>API Endpoint:</strong> ' + mw.config.get('wgServer') + '/w/api.php<br>' +
            '<strong>Parameters:</strong> action=query, meta=userinfo, uiprop=options, format=json' +
            '</div>' +
            '<div class="controls" style="margin-bottom: 20px;">' +
            '<button id="fetchBtn" class="btn btn-primary">Fetch User Options</button>' +
            '<button id="expandBtn" class="btn btn-success">Expand All</button>' +
            '<button id="collapseBtn" class="btn btn-danger">Collapse All</button>' +
            '</div>' +
            '<div id="status"></div>' +
            '<div id="jsonResult"></div>' +
            '</div>';
        
        $('#mw-content-text').html(html);
        
        // Wikipedia UserOptions API Module
        var UserOptionsAPI = {
            fetchUserOptions: function() {
                var params = {
                    action: 'query',
                    meta: 'userinfo',
                    uiprop: 'options',
                    format: 'json'
                };
                
                return new mw.Api().get(params);
            },

            createJSONTree: function(containerId, data) {
                var container = document.getElementById(containerId);
                if (!container) return;

                container.innerHTML = '';
                
                var tree = document.createElement('div');
                tree.className = 'json-tree';
                tree.appendChild(this.createNode('response', data, 0));
                container.appendChild(tree);
                
                this.addStyles();
            },

            createNode: function(key, value, depth) {
                var node = document.createElement('div');
                node.className = 'json-node';
                var self = this;
                
                if (value === null) {
                    node.innerHTML = '<span class="json-key">' + key + ':</span> <span class="json-null">null</span>';
                } else if (typeof value === 'string') {
                    node.innerHTML = '<span class="json-key">' + key + ':</span> <span class="json-string">"' + this.escapeHtml(value) + '"</span>';
                } else if (typeof value === 'number') {
                    node.innerHTML = '<span class="json-key">' + key + ':</span> <span class="json-number">' + value + '</span>';
                } else if (typeof value === 'boolean') {
                    node.innerHTML = '<span class="json-key">' + key + ':</span> <span class="json-boolean">' + value + '</span>';
                } else if (Array.isArray(value)) {
                    var toggleId = 'toggle-' + Date.now() + '-' + Math.random().toString().replace('.', '');
                    
                    var toggle = document.createElement('span');
                    toggle.className = 'json-toggle';
                    toggle.textContent = '▼';
                    toggle.id = toggleId;
                    
                    var keySpan = document.createElement('span');
                    keySpan.className = 'json-key';
                    keySpan.textContent = key + ':';
                    
                    var summary = document.createElement('span');
                    summary.className = 'json-collapsed';
                    summary.textContent = ' [' + value.length + ' items]';
                    
                    node.appendChild(toggle);
                    node.appendChild(keySpan);
                    node.appendChild(summary);
                    
                    var children = document.createElement('div');
                    children.className = 'json-children';
                    
                    for (var i = 0; i < value.length; i++) {
                        children.appendChild(this.createNode('[' + i + ']', value[i], depth + 1));
                    }
                    
                    node.appendChild(children);
                    
                    // Create closure to preserve toggleId
                    (function(id) {
                        toggle.onclick = function() {
                            self.toggleNode(id);
                        };
                    })(toggleId);
                    
                } else if (typeof value === 'object') {
                    var keys = Object.keys(value);
                    var toggleId = 'toggle-' + Date.now() + '-' + Math.random();
                    
                    var toggle = document.createElement('span');
                    toggle.className = 'json-toggle';
                    toggle.textContent = '▼';
                    toggle.id = toggleId;
                    
                    var keySpan = document.createElement('span');
                    keySpan.className = 'json-key';
                    keySpan.textSize = key + ':';
                    
                    var summary = document.createElement('span');
                    summary.className = 'json-collapsed';
                    summary.textContent = ' {' + keys.length + ' properties}';
                    
                    node.appendChild(toggle);
                    node.appendChild(keySpan);
                    node.appendChild(summary);
                    
                    var children = document.createElement('div');
                    children.className = 'json-children';
                    
                    for (var j = 0; j < keys.length; j++) {
                        var objKey = keys[j];
                        children.appendChild(this.createNode(objKey, value[objKey], depth + 1));
                    }
                    
                    node.appendChild(children);
                    
                    toggle.onclick = function() {
                        self.toggleNode(toggleId);
                    };
                }
                
                return node;
            },

            toggleNode: function(toggleId) {
                var toggle = document.getElementById(toggleId);
                if (!toggle) {
                    console.error('Toggle element not found:', toggleId);
                    return;
                }
                
                var children = toggle.parentNode.querySelector('.json-children');
                if (!children) {
                    console.error('Children element not found for toggle:', toggleId);
                    return;
                }
                
                if (children.classList.contains('hidden')) {
                    children.classList.remove('hidden');
                    toggle.textContent = '▼';
                } else {
                    children.classList.add('hidden');
                    toggle.textContent = '▶';
                }
            },

            expandAll: function(containerId) {
                var container = document.getElementById(containerId);
                var toggles = container.querySelectorAll('.json-toggle');
                for (var i = 0; i < toggles.length; i++) {
                    var toggle = toggles[i];
                    var children = toggle.parentNode.querySelector('.json-children');
                    if (children) {
                        children.classList.remove('hidden');
                        toggle.textContent = '▼';
                    }
                }
            },

            collapseAll: function(containerId) {
                var container = document.getElementById(containerId);
                var toggles = container.querySelectorAll('.json-toggle');
                for (var i = 0; i < toggles.length; i++) {
                    var toggle = toggles[i];
                    var children = toggle.parentNode.querySelector('.json-children');
                    if (children) {
                        children.classList.add('hidden');
                        toggle.textContent = '▶';
                    }
                }
            },

            escapeHtml: function(text) {
                var div = document.createElement('div');
                div.textContent = text;
                return div.innerHTML;
            },

            addStyles: function() {
                if (document.getElementById('json-tree-styles')) return;
                
                var style = document.createElement('style');
                style.id = 'json-tree-styles';
                style.textContent = 
                    '.json-tree {' +
                        'font-family: "Courier New", monospace;' +
                        'background: #f8f9fa;' +
                        'border: 1px solid #e1e5e9;' +
                        'border-radius: 5px;' +
                        'padding: 15px;' +
                        'margin: 10px 0;' +
                        'overflow-x: auto;' +
                        'max-height: 600px;' +
                        'overflow-y: auto;' +
                    '}' +
                    '.json-node {' +
                        'margin: 2px 0;' +
                    '}' +
                    '.json-key {' +
                        'color: #0645ad;' +
                        'font-weight: bold;' +
                    '}' +
                    '.json-string {' +
                        'color: #d73a49;' +
                    '}' +
                    '.json-number {' +
                        'color: #005cc5;' +
                    '}' +
                    '.json-boolean {' +
                        'color: #6f42c1;' +
                    '}' +
                    '.json-null {' +
                        'color: #6a737d;' +
                    '}' +
                    '.json-toggle {' +
                        'cursor: pointer;' +
                        'user-select: none;' +
                        'display: inline-block;' +
                        'width: 20px;' +
                        'color: #666;' +
                        'font-weight: bold;' +
                    '}' +
                    '.json-toggle:hover {' +
                        'color: #0645ad;' +
                    '}' +
                    '.json-collapsed {' +
                        'color: #666;' +
                    '}' +
                    '.json-children {' +
                        'margin-left: 20px;' +
                        'border-left: 1px solid #e1e5e9;' +
                        'padding-left: 10px;' +
                    '}' +
                    '.json-children.hidden {' +
                        'display: none;' +
                    '}' +
                    '.btn {' +
                        'display: inline-block;' +
                        'padding: 6px 12px;' +
                        'margin: 0 5px 5px 0;' +
                        'font-size: 14px;' +
                        'font-weight: normal;' +
                        'line-height: 1.42857143;' +
                        'text-align: center;' +
                        'white-space: nowrap;' +
                        'vertical-align: middle;' +
                        'cursor: pointer;' +
                        'border: 1px solid transparent;' +
                        'border-radius: 4px;' +
                        'text-decoration: none;' +
                    '}' +
                    '.btn-primary {' +
                        'color: #fff;' +
                        'background-color: #0645ad;' +
                        'border-color: #0645ad;' +
                    '}' +
                    '.btn-success {' +
                        'color: #fff;' +
                        'background-color: #28a745;' +
                        'border-color: #28a745;' +
                    '}' +
                    '.btn-danger {' +
                        'color: #fff;' +
                        'background-color: #dc3545;' +
                        'border-color: #dc3545;' +
                    '}' +
                    '.btn:hover {' +
                        'opacity: 0.8;' +
                    '}';
                document.head.appendChild(style);
            },

            displayUserOptions: function(containerId) {
                var statusDiv = document.getElementById('status');
                var fetchBtn = document.getElementById('fetchBtn');
                var self = this;
                
                fetchBtn.disabled = true;
                statusDiv.innerHTML = '<div style="color: #666; font-style: italic;">Fetching user options...</div>';
                
                this.fetchUserOptions().done(function(data) {
                    statusDiv.innerHTML = '<div style="color: #28a745;">✓ User options loaded successfully</div>';
                    self.createJSONTree(containerId, data);
                    fetchBtn.disabled = false;
                }).fail(function(error) {
                    statusDiv.innerHTML = '<div style="color: #d33; background: #ffeaea; padding: 10px; border-radius: 5px;">Error: ' + error + '</div>';
                    console.error('Fetch error:', error);
                    fetchBtn.disabled = false;
                });
            }
        };
        
        // Event listeners (only when on the correct page)
        $('#fetchBtn').on('click', function() {
            UserOptionsAPI.displayUserOptions('jsonResult');
        });
        $('#expandBtn').on('click', function() {
            UserOptionsAPI.expandAll('jsonResult');
        });
        $('#collapseBtn').on('click', function() {
            UserOptionsAPI.collapseAll('jsonResult');
        });
        
        // Auto-load on page load
        UserOptionsAPI.displayUserOptions('jsonResult');
    }
});