پرش به محتوا

مدیاویکی:Gadget-QuranTab.js

از ویکی شیعه

نکته: پس از انتشار ممکن است برای دیدن تغییرات نیاز باشد که حافظهٔ نهانی مرورگر خود را پاک کنید.

  • فایرفاکس / سافاری: کلید Shift را نگه دارید و روی دکمهٔ Reload کلیک کنید، یا کلید‌های Ctrl-F5 یا Ctrl-R را با هم فشار دهید (در رایانه‌های اپل مکینتاش کلید‌های ⌘-R)
  • گوگل کروم: کلیدهای Ctrl+Shift+R را با هم فشار دهید (در رایانه‌های اپل مکینتاش کلید‌های ⌘-Shift-R)
  • Edge: کلید Ctrl را نگه‌دارید و روی دکمهٔ Refresh کلیک کنید، یا کلید‌های Ctrl-F5 را با هم فشار دهید
(function (mw, $) {

    'use strict';

    // =================================================================================
    // == بخش اول: کد مربوط به نمایش تب‌ها و مدیریت ترجمه (اسکریپت شماره ۱)
    // =================================================================================

    var script1Initialized = false; // برای جلوگیری از اجرای چندباره منطق اصلی

    /**
     * متغیرها و توابع مربوط به اسکریپت اول
     */
    var translatorVersions = null;
    var CACHE_PREFIX = 'quran-translation-';
    var MAX_CACHE_ITEMS = 1000; // محدودیت تعداد آیتم‌های کش شده

    function cleanupCache() {
        try {
            var keys = Object.keys(localStorage);
            var cacheItems = [];
            for (var i = 0; i < keys.length; i++) {
                if (keys[i].startsWith(CACHE_PREFIX)) {
                    var item = JSON.parse(localStorage.getItem(keys[i]));
                    if (item && item.timestamp) {
                        cacheItems.push({
                            key: keys[i],
                            timestamp: item.timestamp
                        });
                    } else {
                        localStorage.removeItem(keys[i]);
                    }
                }
            }
            if (cacheItems.length > MAX_CACHE_ITEMS) {
                cacheItems.sort(function (a, b) {
                    return a.timestamp - b.timestamp;
                });
                var itemsToDelete = cacheItems.length - MAX_CACHE_ITEMS;
                for (var j = 0; j < itemsToDelete; j++) {
                    localStorage.removeItem(cacheItems[j].key);
                }
            }
        } catch (e) {
            console.error('QuranGadget: Error during cache cleanup.', e);
        }
    }

    function getTranslation(sura, ayah, translator, callback) {
        if (!translatorVersions) {
            console.error('Translator version data is not available. Falling back to API.');
            fetchTranslationFromApi(sura, ayah, translator, callback);
            return;
        }
        var currentVersion = translatorVersions[translator];
        var storageKey = CACHE_PREFIX + sura + '-' + ayah + '-' + translator;
        try {
            var cachedItem = localStorage.getItem(storageKey);
            if (cachedItem) {
                var cachedData = JSON.parse(cachedItem);
                if (cachedData && cachedData.version === currentVersion) {
                    cachedData.timestamp = Date.now();
                    localStorage.setItem(storageKey, JSON.stringify(cachedData));
                    callback(cachedData.html);
                    return;
                }
            }
        } catch (e) {
            console.warn('Could not read from localStorage:', e);
        }

        fetchTranslationFromApi(sura, ayah, translator, function (htmlResult) {
            if (htmlResult && !htmlResult.includes('class="error"')) {
                cleanupCache();
                try {
                    var dataToStore = {
                        version: currentVersion,
                        html: htmlResult,
                        timestamp: Date.now()
                    };
                    localStorage.setItem(storageKey, JSON.stringify(dataToStore));
                } catch (e) {
                    console.warn('Could not save translation to localStorage.', e);
                }
            }
            callback(htmlResult);
        });
    }

    function fetchTranslationFromApi(sura, ayah, translator, callback) {
        var wikitextToParse = '{{مترجم|translator=' + translator + '|سوره=' + sura.trim() + '|آیه=' + ayah.trim() + '}}';
        var blockSelector = '.translation-block[data-sura="' + sura + '"][data-ayah="' + ayah + '"]';
        var block = document.querySelector(blockSelector);
        if (block) {
            block.innerHTML = '<span class="loading-translation" style="font-style:italic; color:#777;">در حال بارگذاری...</span>';
        }
        new mw.Api().parse(wikitextToParse, {
                uselang: mw.config.get('wgUserLanguage')
            })
            .done(function (htmlResultString) {
                callback(htmlResultString);
            })
            .fail(function (errorCode, errorInfo) {
                console.error('API error:', errorCode, errorInfo);
                callback('<span class="error-translation" style="color:red;">خطا در بارگذاری ترجمه.</span>');
            });
    }

    function initializeQuranTabView(viewElement) {
        const instanceId = viewElement.getAttribute('data-instance-id-quran');
        if (!instanceId || viewElement.dataset.quranTabInitialized === 'true') {
            return;
        }
        viewElement.dataset.quranTabInitialized = 'true';

        const tabHeaders = Array.from(viewElement.querySelectorAll('.quran-tab-header .quran-tab'));
        const contentPanesWrapper = viewElement.querySelector('.quran-tab-wrapper');
        const increaseButton = viewElement.querySelector(`#quran-increase-font-${instanceId}`);
        const decreaseButton = viewElement.querySelector(`#quran-decrease-font-${instanceId}`);
        const resetButton = viewElement.querySelector(`#quran-reset-font-${instanceId}`);

        const MIN_FONT_SIZE = 10,
            DEFAULT_FONT_SIZE = 16,
            MAX_FONT_SIZE = 32;
        const RESIZABLE_TEXT_CLASS = 'quran-resizable-text-content';

        function setActiveTab(clickedTabHeader) {
            if (!clickedTabHeader) return;
            tabHeaders.forEach(th => th.classList.remove('active'));
            if (contentPanesWrapper) {
                Array.from(contentPanesWrapper.querySelectorAll('.quran-tab-content')).forEach(cp => {
                    cp.classList.remove('active');
                    cp.classList.add('quran-tab-hidden');
                });
            }
            clickedTabHeader.classList.add('active');
            const targetContentId = clickedTabHeader.getAttribute('data-tab-target-id');
            if (targetContentId) {
                const targetContentPane = viewElement.querySelector(`#${targetContentId}`);
                if (targetContentPane) {
                    targetContentPane.classList.add('active');
                    targetContentPane.classList.remove('quran-tab-hidden');
                    initializeFontSizeForActiveTab();
                }
            }
        }

        tabHeaders.forEach(tabHeader => {
            const tabEventHandler = function (event) {
                if (event.type === 'click' || (event.type === 'keydown' && (event.key === 'Enter' || event.key === ' '))) {
                    if (event.type === 'keydown') event.preventDefault();
                    setActiveTab(this);
                    const newTabUrlKey = this.getAttribute('data-tab-url-key');
                    if (newTabUrlKey && window.history && window.history.pushState) {
                        try {
                            const currentUrl = new URL(window.location.href);
                            currentUrl.searchParams.set('ttq_tab', newTabUrlKey);
                            let newUrlString = currentUrl.toString().replace(/%7C$|\|$/, '');
                            window.history.pushState({
                                tabId: newTabUrlKey,
                                instanceId
                            }, '', newUrlString);
                        } catch (e) {
                            console.error('Error updating URL for Quran tabs:', e);
                        }
                    }
                }
            };
            tabHeader.addEventListener('click', tabEventHandler);
            tabHeader.addEventListener('keydown', tabEventHandler);
        });

        const getActiveResizableElements = () => {
            const activeContentPane = viewElement.querySelector('.quran-tab-content.active');
            return activeContentPane ? activeContentPane.querySelectorAll(`.${RESIZABLE_TEXT_CLASS}, .quran-resizable-text`) : [];
        };
        const applyFontSizeToElements = (elements, size) => {
            elements.forEach(el => {
                el.style.fontSize = `${size}px`;
            });
        };
        const setDefaultFontSize = () => {
            applyFontSizeToElements(getActiveResizableElements(), DEFAULT_FONT_SIZE);
            try {
                localStorage.setItem(`font-size-${instanceId}`, `${DEFAULT_FONT_SIZE}px`);
            } catch (e) { /* ignore */ }
        };
        const adjustFontSize = (delta) => {
            const elements = getActiveResizableElements();
            if (elements.length === 0) return;
            let currentSize = NaN;
            try {
                currentSize = parseFloat(localStorage.getItem(`font-size-${instanceId}`));
            } catch (e) { /* ignore */ }
            if (isNaN(currentSize)) {
                let initialSizeStr = '';
                if (elements[0] && elements[0].style.fontSize) {
                    initialSizeStr = elements[0].style.fontSize;
                } else if (elements[0] && elements[0].offsetParent !== null) {
                    initialSizeStr = window.getComputedStyle(elements[0]).fontSize;
                }
                currentSize = parseFloat(initialSizeStr) || DEFAULT_FONT_SIZE;
            }
            let newSize = currentSize + delta;
            newSize = Math.max(MIN_FONT_SIZE, Math.min(MAX_FONT_SIZE, newSize));
            applyFontSizeToElements(elements, newSize);
            try {
                localStorage.setItem(`font-size-${instanceId}`, `${newSize}px`);
            } catch (e) { /* ignore */ }
        };
        const initializeFontSizeForActiveTab = () => {
            const elements = getActiveResizableElements();
            if (elements.length === 0) return;
            let storedSizePx;
            try {
                storedSizePx = localStorage.getItem(`font-size-${instanceId}`);
            } catch (e) { /* ignore */ }
            const sizeToApply = storedSizePx ? parseFloat(storedSizePx) : DEFAULT_FONT_SIZE;
            applyFontSizeToElements(elements, sizeToApply);
            if (!storedSizePx) {
                try {
                    localStorage.setItem(`font-size-${instanceId}`, `${DEFAULT_FONT_SIZE}px`);
                } catch (e) { /* ignore */ }
            }
        };

        if (increaseButton) increaseButton.addEventListener('click', () => adjustFontSize(1));
        if (decreaseButton) decreaseButton.addEventListener('click', () => adjustFontSize(-1));
        if (resetButton) resetButton.addEventListener('click', setDefaultFontSize);
        if (viewElement.querySelector('.quran-tab-content.active')) {
            initializeFontSizeForActiveTab();
        }
        viewElement.quranTabView = {
            initializeFontSizeForActiveTab: initializeFontSizeForActiveTab
        };
    }

    function handleTranslatorSelection(event) {
        const clickedSpan = event.target.closest('span.translator-button');
        if (!clickedSpan) return;
        const linkElement = clickedSpan.closest('a');
        if (!linkElement || !linkElement.href) return;
        event.preventDefault();
        let targetUrl;
        try {
            targetUrl = new URL(linkElement.href);
        } catch (e) {
            return;
        }
        const newTranslator = targetUrl.searchParams.get('1');
        if (!newTranslator) return;

        if (window.history && window.history.pushState) {
            let newUrlString = targetUrl.toString().replace(/%7C$|\|$/, '');
            window.history.pushState({
                translator: newTranslator
            }, '', newUrlString);
        } else {
            window.location.href = targetUrl.toString().replace(/%7C$|\|$/, '');
            return;
        }
        document.querySelectorAll('div.translator-list span.translator-button.active').forEach(el => el.classList.remove('active'));
        clickedSpan.classList.add('active');

        const activeTripleTabView = clickedSpan.closest('.quran-TripleTabView') || document;
        activeTripleTabView.querySelectorAll('.quran-tab-content.active .translation-block').forEach(block => {
            const sura = block.dataset.sura;
            const ayah = block.dataset.ayah;
            if (sura && ayah) {
                getTranslation(sura, ayah, newTranslator, function (html) {
                    if (block.dataset.sura === sura && block.dataset.ayah === ayah) {
                        block.innerHTML = html;
                        const viewElement = block.closest('.quran-TripleTabView');
                        if (viewElement && viewElement.quranTabView) {
                            viewElement.quranTabView.initializeFontSizeForActiveTab();
                        }
                    }
                });
            }
        });
    }

    /**
     * تابع اصلی برای مقداردهی اولیه اسکریپت اول
     */
    function initQuranTabViewFeatures() {
        if (mw.config.get('wgAction') !== 'view') return;
        var allowedCategoryNames = ['صفحات قرآنی'];

        // تابع کمکی برای چک فعال بودن گجت بر اساس دسته‌ها
        function checkAndActivate(pageCategories) {
            var isGadgetActivated = pageCategories.some(function (category) {
                return allowedCategoryNames.indexOf(category) > -1;
            });
            if (!isGadgetActivated) return;

            if (script1Initialized) return; // اگر قبلا اجرا شده، خارج شو
            script1Initialized = true;

            var translatorListElement = document.querySelector('.translator-list[data-translator-versions]');
            if (translatorListElement && translatorListElement.dataset.translatorVersions) {
                try {
                    translatorVersions = JSON.parse(translatorListElement.dataset.translatorVersions);
                } catch (e) {
                    console.error('QuranGadget: Could not parse translator versions.', e);
                }
            }
            document.querySelectorAll('.quran-TripleTabView').forEach(initializeQuranTabView);
            if (!document.body.dataset.translatorClickHandlerAttachedQuranTab) {
                $(document.body).on('click', 'span.translator-button', handleTranslatorSelection);
                document.body.dataset.translatorClickHandlerAttachedQuranTab = 'true';
            }
        }

        // چک پوسته فعلی
        var skin = mw.config.get('skin');
        if (skin === 'minerva') {
            // در مینروا، از API برای گرفتن دسته‌ها استفاده کن
            var pageName = mw.config.get('wgPageName');
            new mw.Api().get({
                action: 'query',
                prop: 'categories',
                titles: pageName,
                cllimit: 'max' // همه دسته‌ها را بگیر
            }).done(function (data) {
                var pageCategories = [];
                var pages = data.query.pages;
                for (var pageId in pages) {
                    if (pages[pageId].categories) {
                        pages[pageId].categories.forEach(function (cat) {
                            pageCategories.push(cat.title.replace(/^رده:/, '')); // حذف پیشوند "رده:"
                        });
                    }
                }
                checkAndActivate(pageCategories);
            }).fail(function (error) {
                console.error('QuranGadget: Error fetching categories via API in Minerva.', error);
            });
        } else {
            // در پوسته‌های دیگر (مثل وکتور)، از wgCategories استفاده کن
            var pageCategories = mw.config.get('wgCategories') || [];
            checkAndActivate(pageCategories);
        }
    }

    // =================================================================================
    // == بخش دوم: کد مربوط به کنترل‌های متن قرآن (اسکریپت شماره ۲)
    // =================================================================================

    const QuranTextTranslate = {
        setupQuranEventListeners(container) {
            if (container.dataset.quranTextTranslateInitialized === 'true') {
                return;
            }
            container.dataset.quranTextTranslateInitialized = 'true';

            const buttons = {
                smaller: container.querySelector(".quran-font-smaller"),
                larger: container.querySelector(".quran-font-larger"),
                nightMode: container.querySelector(".quran-toggle-night-mode"),
            };

            if (buttons.smaller) {
                buttons.smaller.addEventListener("click", () => {
                    container.classList.remove("quran-font-larger");
                    container.classList.add("quran-font-smaller");
                });
            }

            if (buttons.larger) {
                buttons.larger.addEventListener("click", () => {
                    container.classList.remove("quran-font-smaller");
                    container.classList.add("quran-font-larger");
                });
            }

            if (buttons.nightMode) {
                buttons.nightMode.addEventListener("click", () => {
                    container.classList.toggle("quran-night-mode");
                });
            }
        },

        initializeForContainer(containerElement) {
            if (containerElement) {
                this.setupQuranEventListeners(containerElement);
            }
        },

        initAllInstances() {
            document.querySelectorAll(".quran-table-container").forEach(container => {
                this.initializeForContainer(container);
            });
        }
    };

    // =================================================================================
    // == بخش مقداردهی اولیه کلی (تلفیق هر دو اسکریپت)
    // =================================================================================

    // اجرای اولیه پس از بارگذاری کامل صفحه
    $(function () {
        initQuranTabViewFeatures();
        QuranTextTranslate.initAllInstances();
    });

    // برای محتوای بارگذاری شده به صورت پویا (مانند VisualEditor یا AJAX)
    mw.hook('wikipage.content').add(function ($contentNode) {
        // اجرای منطق اسکریپت اول برای محتوای جدید
        if ($contentNode.find('.quran-TripleTabView').length > 0 || $contentNode.find('.translator-list').length > 0) {
            script1Initialized = false; // اجازه اجرای مجدد برای محتوای جدید
            initQuranTabViewFeatures();
        }

        // اجرای منطق اسکریپت دوم برای محتوای جدید
        $contentNode.find(".quran-table-container").each(function () {
            QuranTextTranslate.initializeForContainer(this);
        });
    });

})(mediaWiki, jQuery);