Jump to content

User:Asked42/bnwikinews.js

From Wikimedia Incubator

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// <nowiki>

/**
* WikiDialog - Qucik action functionalities for Bengali Wikinews
* @version 2.0
* 
* A user interface script that provides quick action buttons and menu items for common Wikinews tasks.
* Currently supports three main actions:
* - "Submit for Review": Submits an article for peer review
* - "Self Publish": Publishes an article with appropriate templates
* - "Archive": Marks an article as archived
* 
* Features:
* - Adds action buttons where 'wikidialog-submit/selfpublish' class tags are placed
* - Creates menu items in the main menu
* - The archive option will auto-add when an article is ready
* - Provides confirmation dialogs before performing actions
* - Shows loading states and success/error notifications
* 
* Documentation can be found at [[Wn/bn/উইকিসংবাদ:উইকিকার্য]]
* 
* @requires Vue 3
* @requires Wikimedia Codex
*/

/**
 * Sources and Inspirations:
 * @source https://incubator.wikimedia.org/wiki/User:Syunsyunminmin/wikinews/easy-dialog.js
 * @source https://www.mediawiki.org/wiki/Codex
 * @source https://meta.wikimedia.org/wiki/User:দিব্য_দত্ত/Script/Shorturl.js
 */


const i18n = {
    // Admin: When Wikinews is promoted to its own site, remove the 'Wn/bn' prefix from these template names
    // and change 'Category' to 'বিষয়শ্রেণী', 'Image' to 'চিত্র'
    maintinenceTemplates: [ "Wn/bn/উন্নয়ন চলছে", "Wn/bn/কাজ চলছে", "Wn/bn/সক্রিয়ভাবে সম্পাদিত হচ্ছে", "Wn/bn/করণীয়", "Wn/bn/পরিষ্করণ", "Wn/bn/স্বল্প উৎস", "Wn/bn/রচনাগত সমস্যা", "Wn/bn/কপিরাইট", "Wn/bn/বিতর্কিত", "Wn/bn/প্রাযুক্তিক সমস্যা", "Wn/bn/নিরপেক্ষতা", "Wn/bn/একাধিক সমস্যা"],
    reviewTemplate: "Wn/bn/নিরীক্ষা",
    selfPublishedTemplate: "Wn/bn/অনিরীক্ষিত প্রকাশ",
    publishTemplate: "Wn/bn/প্রকাশিত",
    oldPublishTemplate: "Wn/bn/প্রকাশ করুন",
    missingImageTemplate: 'Wn/bn/চিত্র অনুপস্থিত',
    archivedTemplate: "Wn/bn/সংগ্রহশালা",
    categoryLabel: "Category",
    filePrefix: 'File',
    imagePrefix: 'Image',

    // Messages
    loginRequired: "আপনাকে এই কার্যটি সঞ্চালন করার জন্য প্রবেশ (লগ ইন) করতে হবে।",
    namespaceError: "এই কার্যটি শুধুমাত্র নিবন্ধের নামস্থানে ব্যবহার করা যেতে পারে।",
    confirmSubmitReview: "আপনি কি এই নিবন্ধটিকে নিরীক্ষণের জন্য জমা করার বিষয়ে নিশ্চিত?",
    alreadySubmitted: 'এই নিবন্ধটি ইতিমধ্যে নিরীক্ষার জন্য জমা দেওয়া হয়েছে।',
    cannotBeSubmitted: 'এই নিবন্ধটি নিরীক্ষণের জন্য জমা দেওয়া যাবে না কারণ এটি ইতিমধ্যে প্রকাশিত হয়েছে।',
    confirmSelfPublish: "আপনি কি নিবন্ধটিকে স্ব-প্রকাশিত করার বিষয়ে নিশ্চিত?",
    alreadyPublished: "এই নিবন্ধটি ইতিমধ্যে প্রকাশিত হয়েছে।",
    confirmArchive: "আপনি কি নিবন্ধটিকে সংগ্রহশালাভুক্ত করার বিষয়ে নিশ্চিত?",
    alreadyArchived: "এই নিবন্ধটিকে ইতিমধ্যেই সংগ্রহশালা ভুক্ত করা হয়েছে।",
    editFailed: "দুঃখিত! সম্পাদনা ব্যর্থ হয়েছে। কিছু পরে পুনরায় চেষ্টা করুন।",
    editSuccess: "সম্পাদনা সফল হয়েছে।",
    fetchContentFailed: "দুঃখিত! পৃষ্ঠার বিষয়বস্তু উপলব্ধি করতে ব্যর্থ হয়েছে৷ পুনরায় চেষ্টা করুন; এই ত্রুটি বজায় থাকলে কোনো প্রশাসকের সাথে যোগাযোগ করুন।",
    bengaliWikinewsNotice: "সুপ্রিয় সম্পাদক, আপনি যে কার্যটি সঞ্চালন করতে চান তার জন্য মিডিয়াউইকি:Common.js পৃষ্ঠা থেকে জাভাস্ক্রিপ্ট প্রয়োজন। কিন্তু সেই জাভাস্ক্রিপ্টটি ইনকিউবেটরের জন্য লেখা, এবং এটিকে bn.wikinews-এ ব্যবহার করার জন্য হালনাগাদ করার প্রয়োজন। অনুগ্রহ করে একজন স্থানীয় ইন্টারফেস প্রশাসক বা গ্লোবাল স্টুয়ার্ডের সাথে যোগাযোগ করুন এবং উনাদের কাজগুলি করতে বলুন, যেমন 'Wn/bn' উপসর্গটি অপসারণ করা এবং 'বিষয়শ্রেণী' দিয়ে 'Category' পরিবর্তন করা। ধন্যবাদ",

    // Labels
    loadingLabel: 'লোড হচ্ছে...',
    submitForReviewLabel: 'নিরীক্ষণের জন্য জমাদান',
    submitForReviewTitle: 'নিবন্ধটিকে নিরীক্ষণের জন্য জমা করুন',
    selfPublishLabel: 'প্রকাশিত করুন',
    selfPublishTitle: 'নিবন্ধটিকে স্ব-প্রকাশিত করুন',
    archiveArticleLabel: 'সংগ্রহশালা ভুক্ত করুন',
    archiveArticleTitle: 'নিবন্ধটিকে সংগ্রহশালা ভুক্ত করুন',
    warningTitle: "সতর্কীকরণ",
    confirmLabel: "নিশ্চিত",
    cancelLabel: "বাতিল করুন",
    closeLabel: "বন্ধ করুন",
    okLabel: "ঠিক আছে",

    // Dialog titles
    confirmSubmitReviewTitle: "নিরীক্ষার জন্য জমা নিশ্চিত করুন",
    confirmSelfPublishTitle: "স্ব-প্রকাশ নিশ্চিত করুন",
    confirmArchiveTitle: "সংগ্রহশালাভুক্ত করা নিশ্চিত করুন",

    // Edit summaries
    submitForReviewSummary: "নিরীক্ষণের জন্য জমা দেওয়া হয়েছে।",
    selfPublishSummary: "স্ব-প্রকাশিত করা হয়েছে।",
    archiveArticleSummary: "সংগ্রহশালা ভুক্ত করা হয়েছে।",

    // Error messages
    hostError: "এই কার্যটিকে শুধুমাত্র ইনকিউবেটরে সঞ্চালন করা যাবে।",
    
    // Easy share 
    shareText: "বাংলা উইকিসংবাদ থেকে একটি তথ্যপূর্ণ নিবন্ধ: ",
    shareLabel: "শেয়ার করুন!",
    copyUrlLabel: "ইউআরএল অনুলিপি করুন",
    pdfLabel: "পিডিএফ ডাউনলোড",
    emailLabel: "ইমেইল",
    facebookLabel: "ফেসবুক",
    whatsappLabel: "হোয়াটসঅ্যাপ",
    telegramLabel: "টেলিগ্রাম",
    copySuccess: "সংক্ষিপ্ত ইউআরএল ক্লিপবোর্ডে অনুলিপি করা হয়েছে!",
    copyPrompt: "এই সংক্ষিপ্ত ইউআরএলটি অনুলিপি করুন:",
    emailSubject: "বাংলা উইকিসংবাদ থেকে একটি নিবন্ধ: ",
    shortLabel: "কপি",
    pdfShortLabel: "পিডিএফ"
};

const config = {
    allowedHosts: ['test.wikipedia.org', 'test.m.wikipedia.org', 'bn.wikinews.org', 'bn.m.wikinews.org'],
    bengaliWikinews: ['bn.wikinews.org', 'bn.m.wikinews.org'],
    articleNamespace: 0,
    portletId: mw.config.get('skin') === 'vector-2022' ? "p-cactions" : "p-tb",
    
    icons: {
        share: '//upload.wikimedia.org/wikipedia/commons/4/40/Ic_share_48px.svg',
        loading: '//upload.wikimedia.org/wikipedia/commons/9/92/Loading_icon_cropped.gif',
        socials: {
            copyUrl: '//upload.wikimedia.org/wikipedia/commons/b/b6/Linkiconred.png',
            facebook: '//upload.wikimedia.org/wikipedia/commons/b/b8/2021_Facebook_icon.svg',
            whatsapp: '//upload.wikimedia.org/wikipedia/commons/c/cc/WhatsApp_Logo.svg',
            telegram: '//upload.wikimedia.org/wikipedia/commons/8/82/Telegram_logo.svg',
            pdf: '//upload.wikimedia.org/wikipedia/commons/6/6c/Adobe_PDF_icon.svg',
            email: '//upload.wikimedia.org/wikipedia/commons/7/7f/OOjs_UI_icon_message.svg'
        }
    }
};

// Load required MediaWiki modules before initializing
mw.loader.using(['mediawiki.api', 'mediawiki.util', 'vue', '@wikimedia/codex']).then(() => {

    const wikiDialog = window.wikiDialog || {};
    window.wikiDialog = wikiDialog;
    wikiDialog.api = new mw.Api();
    
    // Import required Vue components
    const Vue = mw.loader.require('vue');
    const { CdxDialog, CdxMessage, CdxButton, CdxProgressBar } = mw.loader.require('@wikimedia/codex');
    
    // Constants for UI timing
    const NOTIFICATION_TIMEOUT = 3000;
    const RELOAD_DURATION = 1500;
    
    // Creates and appends loading overlay to document
    const createLoadingPopup = () => {
        const popup = document.createElement('div');
        popup.id = 'loadingPopup';
        popup.style.display = 'none';
        popup.innerHTML = `
            <div class="wiki-dialog-overlay">
                <div class="wiki-dialog-loading-container">
                    <img src="${config.icons.loading}" alt="Loading..." class="wiki-dialog-loading-icon">
                    <div class="wiki-dialog-loading-text">${i18n.loadingLabel}</div>
                </div>
            </div>
        `;
        document.body.appendChild(popup);
        return popup;
    };

    // Handles template-related operations
    const TemplateManager = {
        // Generate regex patterns for finding templates
        patterns: (templateName) => [
            new RegExp(`{{\\s*${templateName}\\s*(?:\\|[^}]*)?}}`, 'gi'),
            new RegExp(`{{\\s*:?[Tt]emplate:${templateName}\\s*(?:\\|[^}]*)?}}`, 'gi'),
            new RegExp(`{{[\n\r\s]*${templateName}[\n\r\s]*(?:\\|[^}]*)?}}`, 'gi')
        ],

        // Check if template exists in content
        find(content, templateName) {
            return this.patterns(templateName).some(pattern => pattern.test(content));
        },

        // Get template position and content
        getPosition(content, templateName) {
            for (const pattern of this.patterns(templateName)) {
                pattern.lastIndex = 0;
                const match = pattern.exec(content);
                if (match) {
                    return {
                        start: match.index,
                        end: match.index + match[0].length,
                        template: match[0]
                    };
                }
            }
            return null;
        },

        // Create template string with optional parameters
        wrap(template, params = '') {
            return `{{${template}${params ? '|' + params : ''}}}`;
        }
    };
    
    const ContentProcessor = {
	    // Remove HTML comments from content
	    removeHtmlComments(content) {
	        return content.replace(/<!--[\s\S]*?-->/g, '');
	    },
	
	    // Wrap file links with Missing Image template
	      wrapFilesWithMissingImage(content, missingImageTemplate) {
		    return content
		        .replace(
		            new RegExp(`\\[\\[${i18n.filePrefix}:([^\\]]*)\\]\\]`, 'g'),
		            (match, fileName) => `{{${missingImageTemplate}|${i18n.filePrefix}:${fileName}}}`
		        )
		        .replace(
		            new RegExp(`\\[\\[${i18n.imagePrefix}:([^\\]]*)\\]\\]`, 'g'),
		            (match, imageName) => `{{${missingImageTemplate}|${i18n.imagePrefix}:${imageName}}}`
		        );
		}
	};

    // Manages content modifications
    const ContentManager = {
        // Remove specified maintenance templates from content
        removeMaintenanceTemplates(content, templates) {
            return templates.reduce((acc, template) => {
                const position = TemplateManager.getPosition(acc, template);
                return position ? 
                    acc.slice(0, position.start) + acc.slice(position.end) :
                    acc;
            }, content).trim();
        },

        // Insert template before category
        insertTemplateBeforeCategories(content, templateName, categoryLabel) {
            const categoryPattern = new RegExp(`\\[\\[${categoryLabel}:[^\\]]*\\]\\]`);
            const categoryMatch = content.match(categoryPattern);
            
            if (categoryMatch) {
                const categoryIndex = content.indexOf(categoryMatch[0]);
                return `${content.slice(0, categoryIndex)}${TemplateManager.wrap(templateName)}\n${content.slice(categoryIndex)}`;
            }
            return `${content}\n${TemplateManager.wrap(templateName)}`;
        },

        // Add archive template after publish template
        addArchiveTemplate(content, publishTemplate, archivedTemplate, missingImageTemplate) {
	        // First process the content
	        let processedContent = ContentProcessor.removeHtmlComments(content);
	        processedContent = ContentProcessor.wrapFilesWithMissingImage(processedContent, missingImageTemplate);
	
	        const wrappedPublishTemplate = TemplateManager.wrap(publishTemplate);
	        const publishIndex = processedContent.indexOf(wrappedPublishTemplate);
	
	        return publishIndex !== -1 ?
	            `${processedContent.slice(0, publishIndex + wrappedPublishTemplate.length)}\n${TemplateManager.wrap(archivedTemplate)}\n${processedContent.slice(publishIndex + wrappedPublishTemplate.length)}` :
	            this.insertTemplateBeforeCategories(processedContent, archivedTemplate);
	    }
    };
    
    const DateManager = {
        async getPageCreationDate() {
            try {
                const result = await wikiDialog.api.get({
                    action: "query",
                    format: "json",
                    prop: "revisions",
                    titles: mw.config.get('wgPageName'),
                    formatversion: "2",
                    rvprop: "timestamp",
                    rvlimit: 1,
                    rvdir: "newer" 
                });
                
                const page = result.query.pages[0];
                if (page.revisions && page.revisions[0]) {
                    return new Date(page.revisions[0].timestamp);
                }
                throw new Error('Could not find page creation date');
            } catch (error) {
                console.error('Error getting page creation date:', error);
                return null;
            }
        },

        isOneMonthOld(creationDate) {
            if (!creationDate) return false;
            const oneMonthAgo = new Date();
            oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
            return creationDate <= oneMonthAgo;
        }
    };

	    // Handles security validations
	    const SecurityManager = {
	        checks: [
	            {
	                check: () => mw.user.isAnon(),
	                message: i18n.loginRequired
	            },
	            {
	                check: () => !config.allowedHosts.includes(window.location.host),
	                message: i18n.hostError
	            },
	            {
	                check: () => config.bengaliWikinews.includes(window.location.host),
	                message: i18n.bengaliWikinewsNotice
	            },
	            {
	                check: () => mw.config.get("wgNamespaceNumber") !== config.articleNamespace,
	                message: i18n.namespaceError
	            }
	        ],

	    performChecks() {
	        const failedCheck = this.checks.find(({ check }) => check());
	        return { passed: !failedCheck, message: failedCheck?.message };
	    }
    };

    // Create and configure Vue application
    const createApp = () => {
        const mountPoint = document.createElement('div');
        mountPoint.id = 'wiki-dialog-app';
        document.body.appendChild(mountPoint);

        return Vue.createMwApp({
            data: () => ({
                i18n,
                config,
                dialogOpen: false,
                dialogTitle: '',
                dialogContent: '',
                showMessage: false,
                messageType: '',
                messageContent: '',
                confirmCallback: null,
                showProgressBar: false,
                isWarningDialog: false,
                isInitialLoading: false,
                isArchiveEnabled: false
            }),

            methods: {
                // Toggle loading overlay
                toggleLoading(show) {
                    document.getElementById('loadingPopup').style.display = show ? 'block' : 'none';
                    this.isInitialLoading = show;
                },

                // Fetch page content from API
                async getPageContent() {
                    try {
                        const result = await wikiDialog.api.get({
                            action: "query",
                            format: "json",
                            prop: "revisions",
                            titles: mw.config.get('wgPageName'),
                            formatversion: "2",
                            rvprop: "content",
                            rvslots: "*"
                        });
                        return result.query.pages[0].revisions[0].slots.main.content;
                    } catch (error) {
                        throw new Error(i18n.fetchContentFailed);
                    }
                },

                // Save edited content via API
                async editPageContent(content, summary) {
                    try {
                        const result = await wikiDialog.api.postWithToken('csrf', {
                            action: "edit",
                            format: "json",
                            title: mw.config.get('wgPageName'),
                            formatversion: "2",
                            nocreate: 1,
                            text: content,
                            summary
                        });
                        
                        if (result.error) throw new Error(result.error.info);
                        
                        this.showNotification(i18n.editSuccess, 'success');
                        setTimeout(() => location.reload(), RELOAD_DURATION);
                    } catch (error) {
                        this.showNotification(`${i18n.editFailed}\n\n${error.message}`, 'error');
                    }
                },

                // UI Dialog management methods
                showDialog(title, content, callback, isWarning = false) {
                    Object.assign(this, {
                        dialogTitle: title,
                        dialogContent: content,
                        confirmCallback: callback,
                        dialogOpen: true,
                        showProgressBar: false,
                        isWarningDialog: isWarning
                    });
                },

                closeDialog() {
                    this.dialogOpen = false;
                },

                showWarning(message) {
                    this.showDialog(i18n.warningTitle, message, null, true);
                },

                // Handle dialog confirmation
                async confirmAction() {
                    this.showProgressBar = true;
                    if (this.confirmCallback) {
                        try {
                            await this.confirmCallback();
                        } catch (error) {
                            console.error('Error in confirmCallback:', error);
                        }
                    }
                    if (this.isWarningDialog) {
                        this.closeDialog();
                    }
                },

                // Notification system methods
                showNotification(message, type = 'notice') {
                    this.messageContent = message;
                    this.messageType = type;
                    this.showMessage = true;

                    if (type === 'success') {
                        setTimeout(() => {
                            this.showMessage = false;
                        }, NOTIFICATION_TIMEOUT);
                    }
                },
                
                handleMessageDismiss() {
                    this.showMessage = false;
                },
                
                async isArchiveReady() {
	                try {
	                    const pageContent = await this.getPageContent();
	                    const creationDate = await DateManager.getPageCreationDate();
	                    
	                    // Check template conditions
	                    const hasPublish = TemplateManager.find(pageContent, i18n.publishTemplate) || 
	                                     TemplateManager.find(pageContent, i18n.oldPublishTemplate);
	                    const hasSelfPublished = TemplateManager.find(pageContent, i18n.selfPublishedTemplate);
	                    const hasArchived = TemplateManager.find(pageContent, i18n.archivedTemplate);
	                    
	                    // Check age condition
	                    const isOldEnough = DateManager.isOneMonthOld(creationDate);
	
	                    // All conditions must be met
	                    return hasPublish && !hasSelfPublished && isOldEnough && !hasArchived;
	                } catch (error) {
	                    console.error('Error checking archive conditions:', error);
	                    return false;
	                }
	            },

                // Main action handler for review/publish/archive operations
                async handleAction(actionType, title, content) {
                    const securityCheck = SecurityManager.performChecks();
                    if (!securityCheck.passed) {
                        this.showWarning(securityCheck.message);
                        return;
                    }

                    this.toggleLoading(true);
                    
                    try {
                        const pageContent = await this.getPageContent();
                        let newContent = ContentManager.removeMaintenanceTemplates(pageContent, i18n.maintinenceTemplates);
                        
                        // Template definitions
                        const templates = {
                            review: i18n.reviewTemplate,
                            selfPublished: i18n.selfPublishedTemplate,
                            publish: i18n.publishTemplate,
                            oldPublish: i18n.oldPublishTemplate,
                            archived: i18n.archivedTemplate
                        };
                        
                        // Check current template status
                        const templateStatus = {
                            hasNoChanges: newContent === pageContent,
                            hasSelfPublished: TemplateManager.find(newContent, templates.selfPublished),
                            hasPublished: TemplateManager.find(newContent, templates.publish) || 
                                        TemplateManager.find(newContent, templates.oldPublish),
                            hasArchived: TemplateManager.find(newContent, templates.archived),
                            hasReview: TemplateManager.find(newContent, templates.review)
                        };

                        // Define action handlers
                        const actions = {
                            review: () => {
                                if (templateStatus.hasReview) {
                                    throw new Error(i18n.alreadySubmitted);
                                }
                                if (templateStatus.hasPublished) {
                                    throw new Error(i18n.cannotBeSubmitted);
                                }
                                return async () => {
                                    const content = `${TemplateManager.wrap(templates.review)}\n${newContent}`;
                                    await this.editPageContent(content, i18n.submitForReviewSummary);
                                };
                            },
                            
                            publish: () => {
                                if (templateStatus.hasSelfPublished && templateStatus.hasPublished) {
                                    throw new Error(i18n.alreadyPublished);
                                }
                                return async () => {
                                    let content = newContent;
                                    
                                    if (templateStatus.hasReview) {
                                        const reviewPosition = TemplateManager.getPosition(content, templates.review);
                                        if (reviewPosition) {
                                            content = content.slice(0, reviewPosition.start) + 
                                                    content.slice(reviewPosition.end);
                                            content = content.trim();
                                        }
                                    }
                                    if (!templateStatus.hasSelfPublished) {
                                        content = `${TemplateManager.wrap(templates.selfPublished)}\n${content}`;
                                    }
                                    if (!templateStatus.hasPublished) {
                                        content = ContentManager.insertTemplateBeforeCategories(
                                            content, 
                                            templates.publish, 
                                            i18n.categoryLabel
                                        );
                                    }
                                    await this.editPageContent(content, i18n.selfPublishSummary);
                                };
                            },
                            
                            archive: () => {
							    if (templateStatus.hasArchived) {
							        throw new Error(i18n.alreadyArchived);
							    }
							    return async () => {
							        const content = ContentManager.addArchiveTemplate(
							            newContent,
							            templates.publish,
							            templates.archived,
							            i18n.missingImageTemplate
							        );
							        await this.editPageContent(content, i18n.archiveArticleSummary);
							    };
							}
                        };

                        try {
                            const callback = actions[actionType]();
                            this.toggleLoading(false);
                            this.showDialog(title, content, callback);
                        } catch (error) {
                            this.toggleLoading(false);
                            this.showWarning(error.message);
                        }
                    } catch (error) {
                        this.toggleLoading(false);
                        this.showNotification(error.message, 'error');
                    }
                },

                // Initialize UI buttons
                initializeButtons() {
                    const buttonConfigs = [
                        { 
                            className: 'wikidialog-submit',
                            action: () => this.handleAction('review', i18n.confirmSubmitReviewTitle, i18n.confirmSubmitReview)
                        },
                        {
                            className: 'wikidialog-selfpublish',
                            action: () => this.handleAction('publish', i18n.confirmSelfPublishTitle, i18n.confirmSelfPublish)
                        }
                    ];

                    buttonConfigs.forEach(this.createButton);
                    this.addPortletLinks();
                },

                // Create individual button elements
                createButton({ className, action }) {
                    document.querySelectorAll(`.${className}`).forEach(element => {
                        const text = element.getAttribute("data-button-text");
                        if (text) {
                            const button = document.createElement('button');
                            button.textContent = text;
                            button.className = `${className} ${element.getAttribute("data-class") || ''}`;
                            
                            const style = element.getAttribute("style");
                            if (style) button.setAttribute('style', style);
                            
                            element.innerHTML = '';
                            element.appendChild(button);
                            button.addEventListener("click", action);
                        }
                    });
                },

                // Add links to toolbar portlet
                async addPortletLinks() {
	                if (mw.config.get("wgNamespaceNumber") !== config.articleNamespace) return;
	
	                // Check if archive should be enabled
	                this.isArchiveEnabled = await this.isArchiveReady();
	
	                // Configure portlet links for different actions
	                const linkConfigs = [
	                    {
	                        label: i18n.submitForReviewLabel,
	                        id: "ca-review-action",
	                        title: i18n.submitForReviewTitle,
	                        action: () => this.handleAction('review', i18n.confirmSubmitReviewTitle, i18n.confirmSubmitReview)
	                    },
	                    {
	                        label: i18n.selfPublishLabel,
	                        id: "ca-replace-publish",
	                        title: i18n.selfPublishTitle,
	                        action: () => this.handleAction('publish', i18n.confirmSelfPublishTitle, i18n.confirmSelfPublish)
	                    }
	                ];
	
	                // Only add archive link if conditions are met
	                if (this.isArchiveEnabled) {
	                    linkConfigs.push({
	                        label: i18n.archiveArticleLabel,
	                        id: "ca-archive-article",
	                        title: i18n.archiveArticleTitle,
	                        action: () => this.handleAction('archive', i18n.confirmArchiveTitle, i18n.confirmArchive)
	                    });
	                }
	
	                linkConfigs.forEach(this.createPortletLink);
	                this.updatePortletLinkVisibility();
	            },
            
            // Create individual portlet link
                createPortletLink({ label, id, title, action }) {
                    const link = mw.util.addPortletLink(config.portletId, "javascript:void(0);", label, id, title);
                    if (link) link.addEventListener('click', action);
                },

            // Modify updatePortletLinkVisibility to only handle non-archive links
            updatePortletLinkVisibility() {
                const linkConfigs = [
                    { buttonClass: "wikidialog-submit", linkId: "ca-review-action" },
                    { buttonClass: "wikidialog-selfpublish", linkId: "ca-replace-publish" }
                ];

                linkConfigs.forEach(({ buttonClass, linkId }) => {
                    if (!document.querySelector(`.${buttonClass}`) && !document.getElementById(buttonClass)) {
                        const linkElement = document.getElementById(linkId);
                        if (linkElement) linkElement.style.display = "none";
                    }
                });
            },
            
                // Inject required CSS styles
                injectStyles() {
                    const styles = `
                        /* Overlay styles for loading state */
                        .wiki-dialog-overlay { display: flex; justify-content: center; align-items: center; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(250, 249, 249, 0.5); z-index: 10000; }
                        html.skin-theme-clientpref-night .wiki-dialog-overlay { background-color: rgba(0, 0, 0, 0.7); }
                        .wiki-dialog-loading-container { text-align: center; background-color: #FFFFFF; border: 1px solid #CCCCCC; padding: 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); }
                        .wiki-dialog-loading-icon { width: 36px; height: 36px; }
                        .wiki-dialog-loading-text { margin-top: 10px; font-size: 14px; color: #333; }
                        
                        /* Dialog styles */
                        .dialog-footer { display: flex; justify-content: flex-end; gap: 12px; margin-bottom: 10px; }
                        .cdx-dialog--warning .cdx-dialog__header, .cdx-dialog__header--warning { background-color: #fdf2d5; color: black; }
                        html.skin-theme-clientpref-night .cdx-dialog--warning .cdx-dialog__header, 
                        html.skin-theme-clientpref-night .cdx-dialog__header--warning { background-color: #453217; color: white; }
                        
                        /* Notification styles */
                        .wiki-dialog-notification { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); width: 60%; z-index: 999; }
                        @media screen and (max-width: 768px) { .wiki-dialog-notification { width: 90%; } }
                    `;

                    const styleElement = document.createElement('style');
                    styleElement.textContent = styles;
                    document.head.appendChild(styleElement);
                }
            },
            
            // Initialize UI on component mount
            mounted() {
                this.initializeButtons();
                this.injectStyles();
            },

            // Vue template for the dialog interface
            template: `
                <div>
                    <!-- Main dialog component -->
                    <cdx-dialog v-model:open="dialogOpen" :title="dialogTitle" :close-button-label="i18n.closeLabel" @close="closeDialog" :class="{ 'cdx-dialog--warning': isWarningDialog }">
                        <template #default><p>{{ dialogContent }}</p></template>
                        <template #footer>
                            <div class="dialog-footer">
                                <cdx-button v-if="!isWarningDialog" action="normal" @click="closeDialog">{{ i18n.cancelLabel }}</cdx-button>
                                <cdx-button v-if="isWarningDialog" action="normal" @click="closeDialog">{{ i18n.okLabel }}</cdx-button>
                                <cdx-button v-if="!isWarningDialog" action="progressive" weight="primary" @click="confirmAction">{{ i18n.confirmLabel }}</cdx-button>
                            </div>
                            <cdx-progress-bar v-if="showProgressBar" :inline="true" />
                        </template>
                    </cdx-dialog>

                    <!-- Notification component -->
                    <div v-if="showMessage" class="wiki-dialog-notification">
                        <cdx-message :type="messageType" :fade-in="true" :auto-dismiss="messageType === 'success'" :display-time="NOTIFICATION_TIMEOUT" :dismiss-button-label="i18n.closeLabel" @dismissed="handleMessageDismiss">{{ messageContent }}</cdx-message>
                    </div>
                </div>
            `
        })
        .component('cdx-dialog', CdxDialog)
        .component('cdx-message', CdxMessage)
        .component('cdx-button', CdxButton)
        .component('cdx-progress-bar', CdxProgressBar)
        .mount(mountPoint);
    };

    // Initialize application
    createLoadingPopup();
    createApp();
});


/**
* WikiDialog - Quick Share functionality
* @version 2.0
* 
* A script that adds a share functionality button to Wikinews articles.
* Creates a dropdown menu with multiple sharing options:
* - Copy shortened URL
* - Download as PDF
* - Share via Email
* - Share to social platforms (Facebook, WhatsApp, Telegram)
* 
* Features:
* - Automatically generates shortened URLs via Meta-Wiki API
* - Responsive dropdown positioning based on screen space
* - Clipboard support with fallback to manual copy
* 
* @requires MediaWiki API
* @requires MediaWiki ForeignApi
* @requires jQuery
*/

mw.loader.using(["mediawiki.api", 'mediawiki.ForeignApi']).then(() => {
    // Check for required DOM elements and namespace
    const hasRequiredIds = $('#wikidialog-quick-share').length && $('#publish').length;
    const isArticleNamespace = mw.config.get('wgNamespaceNumber') === config.articleNamespace;
    
    if (!hasRequiredIds || !isArticleNamespace) return;
    
    // Inject required styles for share functionality
    document.head.appendChild(Object.assign(document.createElement('style'), { textContent: `
    .share-container{display:inline-block;position:relative;}
    .share-icon{width:22px;height:22px;cursor:pointer;transition:transform 0.3s ease;}
    .share-icon:hover{transform:scale(1.1);}
    .share-dropdown{display:none;position:absolute;background:#fff;min-width:200px;box-shadow:0 2px 8px rgba(0,0,0,0.15);border:1px solid #a2a9b1;border-radius:4px;z-index:100;padding:12px;}
    .share-icons{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;}
    .share-icon-wrapper{display:flex;flex-direction:column;align-items:center;text-align:center;}
    .share-icon-item{width:32px;height:32px;cursor:pointer;transition:transform 0.2s;margin-bottom:4px;}
    .share-icon-item:hover{transform:scale(1.1);}
    .share-label{font-size:12px;color:#202122;}
`}));

    // Helper function to create icon elements with labels
    const createIcon = (src, alt, label) => {
        const wrapper = Object.assign(document.createElement('div'), { className: 'share-icon-wrapper' });
        const img = Object.assign(document.createElement('img'), { className: 'share-icon-item', src, alt, title: alt });
        const labelEl = Object.assign(document.createElement('span'), { className: 'share-label', textContent: label });
        wrapper.appendChild(img);
        wrapper.appendChild(labelEl);
        return wrapper;
    };

    // Create main container and share button
    const container = Object.assign(document.createElement('div'), { className: 'share-container' });
    const shareIcon = Object.assign(document.createElement('img'), { 
        className: 'share-icon skin-invert', 
        src: config.icons.share, 
        alt: 'Share' ,
        title: i18n.shareLabel
    });
    const dropdown = Object.assign(document.createElement('div'), { className: 'share-dropdown' });
    const iconsContainer = Object.assign(document.createElement('div'), { className: 'share-icons' });

    // Define share options with their labels and icons
    const icons = {
        copyUrl: [i18n.copyUrlLabel, config.icons.socials.copyUrl, i18n.shortLabel],
        pdf: [i18n.pdfLabel, config.icons.socials.pdf, i18n.pdfShortLabel],
        email: [i18n.emailLabel, config.icons.socials.email, i18n.emailLabel],
        facebook: [i18n.facebookLabel, config.icons.socials.facebook, i18n.facebookLabel],
        whatsapp: [i18n.whatsappLabel, config.icons.socials.whatsapp, i18n.whatsappLabel],
        telegram: [i18n.telegramLabel, config.icons.socials.telegram, i18n.telegramLabel]
    };

    // Create icon elements from configuration
    const iconElements = Object.entries(icons).reduce((acc, [key, [alt, src, label]]) => ({ 
        ...acc, 
        [key]: createIcon(src, alt, label) 
    }), {});

    // Assemble the dropdown menu
    Object.values(iconElements).forEach(icon => iconsContainer.appendChild(icon));
    dropdown.appendChild(iconsContainer);
    container.appendChild(shareIcon);
    container.appendChild(dropdown);

    // Calculate and set dropdown position based on available screen space
    const positionDropdown = () => {
        const rect = shareIcon.getBoundingClientRect();
        const space = {
            right: window.innerWidth - rect.right,
            left: rect.left,
            bottom: window.innerHeight - rect.bottom
        };

        Object.assign(dropdown.style, 
            space.right >= 220 
                ? { left: '0', right: 'auto', top: '100%', bottom: 'auto', transform: 'none' } 
                : space.left >= 220 
                ? { right: '0', left: 'auto', top: '100%', bottom: 'auto', transform: 'none' }
                : space.bottom >= 300 
                ? { left: '50%', right: 'auto', top: '100%', bottom: 'auto', transform: 'translateX(-50%)' }
                : { left: '50%', right: 'auto', top: 'auto', bottom: '100%', transform: 'translateX(-50%)' }
        );
    };

    // Handle share button click events
    let isOpen = false;
    shareIcon.addEventListener('click', e => {
        e.stopPropagation();
        isOpen = !isOpen;
        dropdown.style.display = isOpen ? 'block' : 'none';
        if (isOpen) positionDropdown();
        shareIcon.classList.toggle('active', isOpen);
    });

    // Close dropdown when clicking outside
    document.addEventListener('click', e => {
        if (!container.contains(e.target) && isOpen) {
            isOpen = false;
            dropdown.style.display = 'none';
            shareIcon.classList.remove('active');
        }
    });

    // Insert share button into the page
    $('#wikidialog-quick-share').append(container);
    
    // Get shortened URL from Meta-Wiki API
    new mw.ForeignApi('https://meta.wikimedia.org/w/api.php').post({
        action: 'shortenurl',
        url: location.href
    }).done(({ shortenurl: { shorturl } }) => {
        const pageTitle = mw.config.get('wgPageName');
        const text = `${i18n.shareText}"${pageTitle}" ${shorturl}`;
        
        // Define handlers for each share option
        const handlers = {
            copyUrl: () => navigator.clipboard.writeText(shorturl)
                .then(() => mw.notify(i18n.copySuccess, {type: 'success'}))
                .catch(() => prompt(i18n.copyPrompt, shorturl)),
            pdf: () => window.open(`${mw.config.get('wgServer')}${mw.config.get('wgScriptPath')}/index.php?title=Special:DownloadAsPdf&page=${encodeURIComponent(pageTitle)}&action=show-download-screen`),
            email: () => window.open(`mailto:?subject=${encodeURIComponent(`${i18n.emailSubject}${pageTitle}`)}&body=${encodeURIComponent(text)}`),
            facebook: () => window.open(`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(shorturl)}&quote=${encodeURIComponent(text)}`),
            whatsapp: () => window.open(`https://api.whatsapp.com/send?text=${encodeURIComponent(text)}`),
            telegram: () => window.open(`https://t.me/share/url?url=${encodeURIComponent(shorturl)}&text=${encodeURIComponent(text)}`)
        };

        Object.entries(handlers).forEach(([platform, handler]) => 
            iconElements[platform].addEventListener('click', () => {
                handler();
                isOpen = false;
                dropdown.style.display = 'none';
                shareIcon.classList.remove('active');
            }));
    });
    
});

// </nowiki>