MediaWiki:Common.js: различия между версиями

Страница интерфейса MediaWiki
Нет описания правки
Нет описания правки
Строка 2: Строка 2:
     'use strict';
     'use strict';
      
      
    // Глобальный менеджер для Tippy.js
     window.MediaWikiTooltips = {
     window.MediaWikiTooltips = {
         loaded: false,
         loaded: false,
Строка 41: Строка 40:
     };
     };
      
      
    // Модуль боковой панели
     var SidebarModule = {
     var SidebarModule = {
         init: function() {
         init: function() {
Строка 68: Строка 66:
     };
     };
      
      
    // Модуль подсказок допусков
     var AccessTooltipsModule = {
     var AccessTooltipsModule = {
         init: function() {
         init: function() {
Строка 109: Строка 106:
     };
     };
      
      
    // Модуль подсказок законов
     var LawTooltipsModule = {
     var LawTooltipsModule = {
         init: function() {
         init: function() {
Строка 255: Строка 251:
     };
     };
      
      
    // Модуль data-text подсказок
     var DataTooltipsModule = {
     var DataTooltipsModule = {
         init: function() {
         init: function() {
Строка 280: Строка 275:
             });
             });
              
              
            // Наблюдатель за новыми элементами
             var observer = new MutationObserver(function(mutations) {
             var observer = new MutationObserver(function(mutations) {
                 var shouldReinit = false;
                 var shouldReinit = false;
Строка 309: Строка 303:
         }
         }
     };
     };
    // Модуль кнопки копирования текста
var CopyButtonModule = {
    init: function() {
        var containers = document.querySelectorAll('.copy-container');
        if (containers.length === 0) return;
        containers.forEach(function(container) {
            // Проверяем, чтобы кнопка не добавлялась повторно
            if (container.querySelector('.copy-btn')) return;
            // Создаём кнопку
            var btn = document.createElement('button');
            btn.textContent = 'Скопировать';
            btn.className = 'copy-btn';
            // Стили кнопки: справа сверху
            btn.style.position = 'absolute';
            btn.style.top = '5px';
            btn.style.right = '5px';
            btn.style.padding = '2px 6px';
            btn.style.fontSize = '0.9em';
            btn.style.cursor = 'pointer';
            btn.style.zIndex = '10';
            // Обеспечиваем, чтобы контейнер был position: relative
            if (getComputedStyle(container).position === 'static') {
                container.style.position = 'relative';
            }
            // Копирование содержимого без кнопки
            btn.addEventListener('click', function() {
                var clone = container.cloneNode(true);
                var btnInside = clone.querySelector('.copy-btn');
                if (btnInside) btnInside.remove(); // удаляем кнопку из копируемого клона
                var text = clone.textContent.trim();
                navigator.clipboard.writeText(text).then(function() {
                    var originalText = btn.textContent;
                    btn.textContent = 'Скопировано!';
                    setTimeout(function() { btn.textContent = originalText; }, 1500);
                }).catch(function() {
                    btn.textContent = 'Ошибка';
                    setTimeout(function() { btn.textContent = 'Скопировать'; }, 1500);
                });
            });
            // Вставляем кнопку в контейнер
            container.appendChild(btn);
        });
    }
};
      
      
     // Инициализация всех модулей
     var CopyTextModule = {
        init: function() {
            var elements = document.querySelectorAll('.copyable-text');
            if (elements.length === 0) return;
           
            elements.forEach(function(element) {
                element.style.cursor = 'pointer';
                element.addEventListener('click', CopyTextModule.handleClick);
            });
           
            var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
            if (isIOS) {
                elements.forEach(function(element) {
                    element.style.webkitUserSelect = 'text';
                    element.style.userSelect = 'text';
                });
            }
        },
       
        handleClick: function(e) {
            e.preventDefault();
           
            var element = this;
            var textToCopy = element.textContent.trim();
           
            if (navigator.clipboard && window.isSecureContext) {
                navigator.clipboard.writeText(textToCopy).then(function() {
                    CopyTextModule.showNotification(element);
                }).catch(function(err) {
                    console.error('Ошибка при копировании: ', err);
                    CopyTextModule.fallbackCopy(textToCopy, element);
                });
            } else {
                CopyTextModule.fallbackCopy(textToCopy, element);
            }
           
            element.classList.add('copying');
            setTimeout(function() {
                element.classList.remove('copying');
            }, 200);
        },
       
        fallbackCopy: function(text, element) {
            var textArea = document.createElement('textarea');
            textArea.value = text;
            textArea.style.position = 'fixed';
            textArea.style.top = '-9999px';
            textArea.style.left = '-9999px';
            textArea.style.width = '2em';
            textArea.style.height = '2em';
            textArea.style.padding = '0';
            textArea.style.border = 'none';
            textArea.style.outline = 'none';
            textArea.style.boxShadow = 'none';
            textArea.style.background = 'transparent';
           
            document.body.appendChild(textArea);
            textArea.focus();
            textArea.select();
           
            try {
                var successful = document.execCommand('copy');
                if (successful) {
                    CopyTextModule.showNotification(element);
                } else {
                    console.error('Не удалось скопировать текст');
                }
            } catch (err) {
                console.error('Ошибка при копировании: ', err);
            }
           
            document.body.removeChild(textArea);
        },
       
        showNotification: function(element) {
            var existingNotifications = document.querySelectorAll('.copy-notification');
            existingNotifications.forEach(function(notification) {
                notification.remove();
            });
           
            var notification = document.createElement('div');
            notification.className = 'copy-notification';
            notification.textContent = 'Текст скопирован!';
            document.body.appendChild(notification);
           
            var rect = element.getBoundingClientRect();
            var isMobile = window.innerWidth <= 768;
           
            if (isMobile) {
                notification.style.position = 'fixed';
                notification.style.top = '20px';
                notification.style.left = '50%';
                notification.style.transform = 'translateX(-50%) translateY(-10px)';
                notification.style.zIndex = '10000';
            } else {
                notification.style.position = 'absolute';
                notification.style.top = (rect.top + window.scrollY - 40) + 'px';
                notification.style.left = (rect.left + window.scrollX) + 'px';
                notification.style.zIndex = '9999';
            }
           
            setTimeout(function() {
                notification.classList.add('show');
            }, 10);
           
            setTimeout(function() {
                notification.classList.remove('show');
                setTimeout(function() {
                    if (notification.parentNode) {
                        notification.remove();
                    }
                }, 300);
            }, 2000);
        }
    };
   
     function initAllModules() {
     function initAllModules() {
         SidebarModule.init();
         SidebarModule.init();
Строка 368: Строка 425:
         LawTooltipsModule.init();
         LawTooltipsModule.init();
         DataTooltipsModule.init();
         DataTooltipsModule.init();
         CopyButtonModule.init();  
         CopyTextModule.init();
     }
     }
      
      
    // Запуск
     if (document.readyState === 'loading') {
     if (document.readyState === 'loading') {
         document.addEventListener('DOMContentLoaded', initAllModules);
         document.addEventListener('DOMContentLoaded', initAllModules);

Версия от 17:31, 28 сентября 2025

(function() {
    'use strict';
    
    window.MediaWikiTooltips = {
        loaded: false,
        callbacks: [],
        
        load: function(callback) {
            if (this.loaded && window.tippy) {
                callback();
                return;
            }
            
            this.callbacks.push(callback);
            
            if (this.loading) return;
            this.loading = true;
            
            var self = this;
            var popperScript = document.createElement('script');
            popperScript.src = 'https://unpkg.com/@popperjs/core@2/dist/umd/popper.min.js';
            popperScript.onload = function() {
                var tippyScript = document.createElement('script');
                tippyScript.src = 'https://unpkg.com/tippy.js@6/dist/tippy-bundle.umd.min.js';
                tippyScript.onload = function() {
                    self.loaded = true;
                    self.loading = false;
                    self.callbacks.forEach(function(cb) { cb(); });
                    self.callbacks = [];
                };
                document.head.appendChild(tippyScript);
            };
            document.head.appendChild(popperScript);
            
            var tippyCSS = document.createElement('link');
            tippyCSS.rel = 'stylesheet';
            tippyCSS.href = 'https://unpkg.com/tippy.js@6/dist/tippy.css';
            document.head.appendChild(tippyCSS);
        }
    };
    
    var SidebarModule = {
        init: function() {
            var buttons = document.querySelectorAll('.боковая-панель-кнопка');
            var sections = document.querySelectorAll('.боковая-панель-раздел');
            
            if (buttons.length === 0) return;
            
            buttons.forEach(function(button) {
                button.addEventListener('click', function() {
                    var targetId = this.dataset.target;
                    
                    buttons.forEach(function(btn) { btn.classList.remove('active'); });
                    this.classList.add('active');
                    
                    sections.forEach(function(section) { section.classList.remove('default'); });
                    var targetSection = document.getElementById(targetId);
                    if (targetSection) {
                        targetSection.classList.add('default');
                    }
                });
            });
            
            buttons[0].click();
        }
    };
    
    var AccessTooltipsModule = {
        init: function() {
            var containers = document.querySelectorAll('.допуск-контейнер');
            if (containers.length === 0) return;
            
            MediaWikiTooltips.load(function() {
                containers.forEach(function(container) {
                    var contentElement = container.querySelector('.допуск-подсказка');
                    if (!contentElement) return;
                    
                    var content = contentElement.innerHTML;
                    tippy(container, {
                        content: content,
                        allowHTML: true,
                        interactive: true,
                        placement: 'auto',
                        maxWidth: 500,
                        theme: 'dark',
                        arrow: true,
                        duration: [200, 200],
                        popperOptions: {
                            modifiers: [
                                {
                                    name: 'preventOverflow',
                                    options: { padding: 8 }
                                },
                                {
                                    name: 'flip',
                                    options: {
                                        fallbackPlacements: ['top', 'bottom', 'right', 'left']
                                    }
                                }
                            ]
                        }
                    });
                });
            });
        }
    };
    
    var LawTooltipsModule = {
        init: function() {
            var tableCells = document.querySelectorAll('.law-tooltips td, .law-tooltips th');
            if (tableCells.length === 0) return;
            
            MediaWikiTooltips.load(function() {
                var lawData = LawTooltipsModule.extractLawData();
                LawTooltipsModule.initCellTooltips(tableCells, lawData);
            });
        },
        
        extractLawData: function() {
            var lawData = {};
            var detailTables = document.querySelectorAll('.mw-collapsible-content table');
            
            detailTables.forEach(function(table) {
                var rows = table.querySelectorAll('tr');
                
                rows.forEach(function(row) {
                    var cells = row.querySelectorAll('td');
                    if (cells.length < 3) return;
                    
                    var crimeCell = cells[0];
                    var descriptionCell = cells[1];
                    var exampleCell = cells[2];
                    
                    var anchor = crimeCell.querySelector('[id]');
                    if (!anchor) return;
                    
                    var lawCode = anchor.id;
                    var titleElement = crimeCell.querySelector('b');
                    
                    if (titleElement && lawCode.match(/^\d{3}$/)) {
                        var titleText = titleElement.textContent.trim();
                        var lawTitle = titleText.replace(/^\d{3}\s*/, '').replace(/^\d{3}/, '').trim();
                        var description = descriptionCell.textContent.trim();
                        
                        var examples = [];
                        var exampleItems = exampleCell.querySelectorAll('li');
                        exampleItems.forEach(function(item) {
                            var text = item.textContent.trim();
                            if (text) examples.push(text);
                        });
                        
                        lawData[lawCode] = {
                            title: lawTitle,
                            description: description,
                            examples: examples
                        };
                    }
                });
            });
            
            return lawData;
        },
        
        createTooltipContent: function(lawCode, lawData) {
            var data = lawData[lawCode];
            if (!data) return null;
            
            var html = '<div class="law-tooltip">';
            html += '<h4 class="law-tooltip-title">' + lawCode + ' - ' + data.title + '</h4>';
            html += '<p class="law-tooltip-description">' + data.description + '</p>';
            
            if (data.examples && data.examples.length > 0) {
                html += '<div class="law-tooltip-examples">';
                html += '<strong>Примеры:</strong><ul>';
                data.examples.slice(0, 5).forEach(function(example) {
                    html += '<li>' + example + '</li>';
                });
                html += '</ul></div>';
            }
            html += '</div>';
            
            return html;
        },
        
        initCellTooltips: function(tableCells, lawData) {
            tableCells.forEach(function(cell) {
                var link = cell.querySelector('a[href*="#"]');
                if (!link) return;
                
                var href = link.getAttribute('href');
                var match = href.match(/#(\d{3})/);
                if (!match) return;
                
                var lawCode = match[1];
                var tooltipContent = LawTooltipsModule.createTooltipContent(lawCode, lawData);
                if (!tooltipContent) return;
                
                cell.classList.add('law-cell-with-tooltip');
                
                cell.addEventListener('click', function(e) {
                    e.preventDefault();
                    window.location.hash = lawCode;
                });
                
                tippy(cell, {
                    content: tooltipContent,
                    allowHTML: true,
                    interactive: true,
                    placement: 'top',
                    maxWidth: 400,
                    theme: 'law-theme',
                    arrow: true,
                    duration: [200, 150],
                    delay: [0, 50],
                    appendTo: document.body,
                    boundary: 'viewport',
                    popperOptions: {
                        strategy: 'fixed',
                        modifiers: [
                            {
                                name: 'preventOverflow',
                                options: {
                                    boundary: 'viewport',
                                    padding: 20,
                                    altAxis: true,
                                    altBoundary: true
                                }
                            },
                            {
                                name: 'flip',
                                options: {
                                    fallbackPlacements: ['bottom', 'left', 'right']
                                }
                            },
                            {
                                name: 'computeStyles',
                                options: { adaptive: false }
                            }
                        ]
                    },
                    onShow: function(instance) {
                        document.querySelectorAll('[data-tippy-root]').forEach(function(tooltip) {
                            if (tooltip._tippy && tooltip._tippy !== instance) {
                                tooltip._tippy.hide();
                            }
                        });
                    }
                });
            });
        }
    };
    
    var DataTooltipsModule = {
        init: function() {
            var elements = document.querySelectorAll('.data-tooltip[data-text]');
            if (elements.length === 0) return;
            
            MediaWikiTooltips.load(function() {
                elements.forEach(function(element) {
                    var tooltipText = element.getAttribute('data-text');
                    if (!tooltipText || !tooltipText.trim()) return;
                    
                    tippy(element, {
                        content: tooltipText,
                        allowHTML: true,
                        interactive: false,
                        placement: 'top',
                        maxWidth: 300,
                        theme: 'data-tooltip-theme',
                        arrow: true,
                        duration: [200, 150],
                        delay: [0, 50]
                    });
                });
            });
            
            var observer = new MutationObserver(function(mutations) {
                var shouldReinit = false;
                mutations.forEach(function(mutation) {
                    mutation.addedNodes.forEach(function(node) {
                        if (node.nodeType === 1) {
                            if (node.classList && node.classList.contains('data-tooltip') && node.hasAttribute('data-text')) {
                                shouldReinit = true;
                            } else if (node.querySelectorAll) {
                                var newElements = node.querySelectorAll('.data-tooltip[data-text]');
                                if (newElements.length > 0) {
                                    shouldReinit = true;
                                }
                            }
                        }
                    });
                });
                
                if (shouldReinit) {
                    setTimeout(function() { DataTooltipsModule.init(); }, 100);
                }
            });
            
            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
        }
    };
    
    var CopyTextModule = {
        init: function() {
            var elements = document.querySelectorAll('.copyable-text');
            if (elements.length === 0) return;
            
            elements.forEach(function(element) {
                element.style.cursor = 'pointer';
                element.addEventListener('click', CopyTextModule.handleClick);
            });
            
            var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
            if (isIOS) {
                elements.forEach(function(element) {
                    element.style.webkitUserSelect = 'text';
                    element.style.userSelect = 'text';
                });
            }
        },
        
        handleClick: function(e) {
            e.preventDefault();
            
            var element = this;
            var textToCopy = element.textContent.trim();
            
            if (navigator.clipboard && window.isSecureContext) {
                navigator.clipboard.writeText(textToCopy).then(function() {
                    CopyTextModule.showNotification(element);
                }).catch(function(err) {
                    console.error('Ошибка при копировании: ', err);
                    CopyTextModule.fallbackCopy(textToCopy, element);
                });
            } else {
                CopyTextModule.fallbackCopy(textToCopy, element);
            }
            
            element.classList.add('copying');
            setTimeout(function() {
                element.classList.remove('copying');
            }, 200);
        },
        
        fallbackCopy: function(text, element) {
            var textArea = document.createElement('textarea');
            textArea.value = text;
            textArea.style.position = 'fixed';
            textArea.style.top = '-9999px';
            textArea.style.left = '-9999px';
            textArea.style.width = '2em';
            textArea.style.height = '2em';
            textArea.style.padding = '0';
            textArea.style.border = 'none';
            textArea.style.outline = 'none';
            textArea.style.boxShadow = 'none';
            textArea.style.background = 'transparent';
            
            document.body.appendChild(textArea);
            textArea.focus();
            textArea.select();
            
            try {
                var successful = document.execCommand('copy');
                if (successful) {
                    CopyTextModule.showNotification(element);
                } else {
                    console.error('Не удалось скопировать текст');
                }
            } catch (err) {
                console.error('Ошибка при копировании: ', err);
            }
            
            document.body.removeChild(textArea);
        },
        
        showNotification: function(element) {
            var existingNotifications = document.querySelectorAll('.copy-notification');
            existingNotifications.forEach(function(notification) {
                notification.remove();
            });
            
            var notification = document.createElement('div');
            notification.className = 'copy-notification';
            notification.textContent = 'Текст скопирован!';
            document.body.appendChild(notification);
            
            var rect = element.getBoundingClientRect();
            var isMobile = window.innerWidth <= 768;
            
            if (isMobile) {
                notification.style.position = 'fixed';
                notification.style.top = '20px';
                notification.style.left = '50%';
                notification.style.transform = 'translateX(-50%) translateY(-10px)';
                notification.style.zIndex = '10000';
            } else {
                notification.style.position = 'absolute';
                notification.style.top = (rect.top + window.scrollY - 40) + 'px';
                notification.style.left = (rect.left + window.scrollX) + 'px';
                notification.style.zIndex = '9999';
            }
            
            setTimeout(function() {
                notification.classList.add('show');
            }, 10);
            
            setTimeout(function() {
                notification.classList.remove('show');
                setTimeout(function() {
                    if (notification.parentNode) {
                        notification.remove();
                    }
                }, 300);
            }, 2000);
        }
    };
    
    function initAllModules() {
        SidebarModule.init();
        AccessTooltipsModule.init();
        LawTooltipsModule.init();
        DataTooltipsModule.init();
        CopyTextModule.init();
    }
    
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initAllModules);
    } else {
        initAllModules();
    }
    
})();