مستخدم:لوقا/PendingEditsChecker.js

ملاحظة: بعد الحفظ، قد يلزمك إفراغ الكاش لرؤية التغييرات.

/**
 * PendingEditsChecker is a singleton class that checks for pending edits
 * on a Wikipedia page and displays a message box with details about the pending edits
 * and the last review date.
 */
class PendingEditsChecker {
    constructor() {
        // If an instance already exists, return it to maintain the singleton pattern
        if (PendingEditsChecker.instance) {
            return PendingEditsChecker.instance;
        }

        // Initialize the page title and API instance
        this.pageTitle = mw.config.get('wgPageName');
        this.api = new mw.Api();
        PendingEditsChecker.instance = this;

        // Initialize the checker
        this.init();
    }

    /**
     * Gets the singleton instance of the PendingEditsChecker class.
     * @returns {PendingEditsChecker} The singleton instance.
     */
    static getInstance() {
        if (!PendingEditsChecker.instance) {
            PendingEditsChecker.instance = new PendingEditsChecker();
        }
        return PendingEditsChecker.instance;
    }

    /**
     * Initializes the checker by setting up an event listener
     * that triggers the check for pending edits when the page is in view mode.
     */
    init() {
        // Check if the page is in view mode
        if (mw.config.get('wgAction') === 'view') {
            document.addEventListener('DOMContentLoaded', this.checkPendingEdits.bind(this));
        }
    }

    /**
     * Checks for pending edits by comparing the latest revision with the last reviewed revision.
     * If there are pending edits, it calls the method to display the message box.
     */
    checkPendingEdits() {
        this.api.get({
            action: 'query',
            prop: 'revisions',
            titles: this.pageTitle,
            rvprop: 'ids|timestamp',
            rvlimit: 'max',
            formatversion: 2,
        }).then((data) => {
            const page = data.query.pages[0];
            const revisions = page.revisions;
            const latestRevisionId = revisions[0].revid;
            const latestRevisionTimestamp = this.formatTimestamp(revisions[0].timestamp);

            // Fetch the latest reviewed revision (if available)
            this.api.get({
                action: 'query',
                prop: 'flagged',
                titles: this.pageTitle,
                formatversion: 2,
            }).then((flaggedData) => {
                const flaggedPage = flaggedData.query.pages[0];
                const reviewedRevisionId = flaggedPage.flagged.stable_revid;

                // Fetch and format the last review date
                this.getFormattedLastReviewDate().then((reviewedRevisionTimestamp) => {
                    // Count the number of pending edits
                    const pendingEdits = revisions.filter(revision => revision.revid > reviewedRevisionId).length;

                    if (pendingEdits > 0) {
                        this.displayMessageBox(latestRevisionId, reviewedRevisionId, latestRevisionTimestamp, reviewedRevisionTimestamp, pendingEdits);
                    }
                });
            });
        });
    }

    /**
     * Fetches the last review log data from the MediaWiki API.
     * @returns {Promise<Object|null>} A promise that resolves to the last review log entry or null if not found.
     */
    fetchLastReviewLog() {
        return this.api.get({
            action: 'query',
            list: 'logevents',
            letype: 'review',
            letitle: this.pageTitle,
            lelimit: 1,
            leprop: 'user|timestamp|comment|details',
            formatversion: 2,
        }).then((data) => {
            if (data.query.logevents && data.query.logevents.length > 0) {
                return data.query.logevents[0];
            } else {
                return null;
            }
        }).catch((error) => {
            console.error("Error fetching review log data:", error);
            return null;
        });
    }

    /**
     * Returns the Arabic name of a month given its numeric value.
     * @param {string} month - The numeric month value (e.g., '01' for January).
     * @returns {string} The Arabic name of the month.
     */
    getArabicMonth(month) {
        switch (month) {
            case '01': return 'يناير';
            case '02': return 'فبراير';
            case '03': return 'مارس';
            case '04': return 'أبريل';
            case '05': return 'مايو';
            case '06': return 'يونيو';
            case '07': return 'يوليو';
            case '08': return 'أغسطس';
            case '09': return 'سبتمبر';
            case '10': return 'أكتوبر';
            case '11': return 'نوفمبر';
            case '12': return 'ديسمبر';
            default: return 'غير معروف';
        }
    }

    /**
     * Formats a given timestamp into a readable date string in Arabic.
     * @param {string} timestamp - The timestamp to format.
     * @returns {string} The formatted date string in the format "day month year".
     */
    formatTimestamp(timestamp) {
        try {
            const date = new Date(timestamp);
            const day = date.getDate();
            const month = this.getArabicMonth(String(date.getMonth() + 1).padStart(2, '0'));
            const year = date.getFullYear();

            return `${day} ${month} ${year}`;
        } catch (error) {
            console.error('Error formatting timestamp:', error);
            return 'تاريخ غير متوفر';
        }
    }

    /**
     * Fetches the last review log data and formats the timestamp of the last review.
     * @returns {Promise<string>} A promise that resolves to the formatted last review date string.
     */
    async getFormattedLastReviewDate() {
        const lastReview = await this.fetchLastReviewLog();
        if (lastReview) {
            return this.formatTimestamp(lastReview.timestamp);
        } else {
            return 'تاريخ غير متوفر';
        }
    }

    /**
     * Displays a message box with details about the pending edits and the last review date.
     * @param {number} latestRevisionId - The ID of the latest revision.
     * @param {number} reviewedRevisionId - The ID of the last reviewed revision.
     * @param {string} latestRevisionTimestamp - The timestamp of the latest revision.
     * @param {string} reviewedRevisionTimestamp - The timestamp of the last reviewed revision.
     * @param {number} pendingEdits - The number of pending edits.
     */
    displayMessageBox(latestRevisionId, reviewedRevisionId, latestRevisionTimestamp, reviewedRevisionTimestamp, pendingEdits) {
        // Create a box to display the message with a link
        const messageBox = document.createElement('div');
        messageBox.style.marginBottom = '10px';
        messageBox.style.marginTop = '10px';
        messageBox.classList.add(
            'mw-message-box',
            'cdx-message',
            'cdx-message--block',
        );

        // Determine the correct text based on the number of pending edits
        let pendingEditsText = '';
        if (pendingEdits === 1) {
            pendingEditsText += 'تعديل واحد';
        } else if (pendingEdits === 2) {
            pendingEditsText += 'تعديلان';
        } else if (pendingEdits > 2) {
            pendingEditsText += `${pendingEdits} تعديلات`;
        }

        // Create the message text with the stable version link embedded in the "فحصت" part
        const messageText = document.createElement('span');
        messageText.innerHTML = `${pendingEditsText} في هذه النسخه  <a href="https://ar.wikipedia.org/wiki/%D8%AA%D8%B9%D8%AF%D9%8A%D9%84%D8%A7%D8%AA_%D9%85%D8%B9%D9%84%D9%82%D8%A9">معلق للمراجعة</a>.<a href="${mw.util.getUrl(this.pageTitle, { oldid: reviewedRevisionId })}"> فحصت النسخة المستقرة</a> في ${reviewedRevisionTimestamp}.`;

        // Create a link with the diff URL
        const diffLink = document.createElement('a');
        diffLink.href = mw.util.getUrl(this.pageTitle, {
            diff: latestRevisionId,
            oldid: reviewedRevisionId
        });
        diffLink.textContent = ' [اظهر الفرق] ';

        // Create a new span element for the icon
        const spanElement = document.createElement('span');
        spanElement.className = 'cdx-message__icon';

        // Append elements to the message box
        messageBox.appendChild(spanElement);
        messageBox.appendChild(diffLink);
        messageBox.appendChild(messageText);
        messageBox.appendChild(document.createElement('br')); // Add line break

        // Insert the box before the content
        const content = document.getElementById('mw-content-text');
        if (content) {
            content.parentNode.insertBefore(messageBox, content);
        }
    }
}

// Instantiate the singleton
PendingEditsChecker.getInstance().checkPendingEdits();