Actions

MediaWiki

MediaWiki:ResourcePage.js

From Dune Awakening DB

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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.
/**
 * ResourcePage.js - ResourceLoader Module
 * This file should be created as MediaWiki:ResourcePage.js
 * 
 * Dependencies: jquery, mediawiki.api, mediawiki.util
 */

( function ( $, mw ) {
    'use strict';

    /**
     * Resource Page Enhancement Module
     * Provides interactive features for resource pages including
     * search functionality and popup displays for recipes
     */
    var ResourcePage = {
        
        /**
         * Configuration
         */
        config: {
            selectors: {
                craftedWithSearch: '#craftedWithSearch',
                craftedFromSearch: '#craftedFromSearch',
                craftedWithTable: '#craftedWithTable',
                craftedFromTable: '#craftedFromTable',
                viewAllCraftedWith: '.view-all-crafted-with',
                viewAllCraftedFrom: '.view-all-crafted-from',
                popupOverlay: '.recipe-popup-overlay',
                popupClose: '.popup-close',
                popupSearchInput: '.popup-search-input',
                popupTable: '.recipe-popup-table'
            },
            api: new mw.Api(),
            animationDuration: 300
        },

        /**
         * Initialize the module
         */
        init: function () {
            // Only initialize if we're on a resource page
            if ( !this.isResourcePage() ) {
                return;
            }

            mw.log( 'ResourcePage: Initializing' );
            
            this.bindEvents();
            this.initializeSearch();
            
            // Fire a custom event to signal initialization
            mw.hook( 'resourcePage.initialized' ).fire();
        },

        /**
         * Check if current page is a resource page
         * @return {boolean}
         */
        isResourcePage: function () {
            return $( '.crafting-section' ).length > 0 || 
                   $( '[class*="crafted-with"], [class*="crafted-from"]' ).length > 0;
        },

        /**
         * Bind event handlers
         */
        bindEvents: function () {
            var self = this;

            // View All buttons
            $( document )
                .on( 'click', this.config.selectors.viewAllCraftedWith, function ( e ) {
                    e.preventDefault();
                    var resourceName = $( this ).data( 'resource' );
                    self.showCraftedWithPopup( resourceName );
                } )
                .on( 'click', this.config.selectors.viewAllCraftedFrom, function ( e ) {
                    e.preventDefault();
                    var resourceName = $( this ).data( 'resource' );
                    self.showCraftedFromPopup( resourceName );
                } );

            // Popup interactions
            $( document )
                .on( 'click', this.config.selectors.popupClose + ', ' + this.config.selectors.popupOverlay, function ( e ) {
                    if ( e.target === this || $( e.target ).hasClass( 'popup-close' ) ) {
                        self.closePopup();
                    }
                } )
                .on( 'input', this.config.selectors.popupSearchInput, function () {
                    var searchTerm = $( this ).val().toLowerCase();
                    self.filterPopupTable( searchTerm );
                } );

            // Keyboard shortcuts
            $( document ).on( 'keydown', function ( e ) {
                if ( e.key === 'Escape' ) {
                    self.closePopup();
                }
            } );
        },

        /**
         * Initialize search functionality for preview tables
         */
        initializeSearch: function () {
            var self = this;

            // Crafted With search
            $( this.config.selectors.craftedWithSearch ).on( 'input', function () {
                var searchTerm = $( this ).val().toLowerCase();
                self.filterTable( self.config.selectors.craftedWithTable, searchTerm );
            } );

            // Crafted From search
            $( this.config.selectors.craftedFromSearch ).on( 'input', function () {
                var searchTerm = $( this ).val().toLowerCase();
                self.filterTable( self.config.selectors.craftedFromTable, searchTerm );
            } );
        },

        /**
         * Filter table rows based on search term
         * @param {string} tableSelector - Table selector
         * @param {string} searchTerm - Search term
         */
        filterTable: function ( tableSelector, searchTerm ) {
            var $table = $( tableSelector ),
                $rows = $table.find( 'tbody tr' );

            if ( !searchTerm ) {
                $rows.removeClass( 'hidden' ).show();
                return;
            }

            $rows.each( function () {
                var $row = $( this ),
                    text = $row.text().toLowerCase();

                if ( text.indexOf( searchTerm ) > -1 ) {
                    $row.removeClass( 'hidden' ).show();
                } else {
                    $row.addClass( 'hidden' ).hide();
                }
            } );
        },

        /**
         * Show popup with recipes that use this resource
         * @param {string} resourceName - Name of the resource
         */
        showCraftedWithPopup: function ( resourceName ) {
            var self = this;

            this.showLoadingPopup( 'Loading recipes that use ' + resourceName + '...' );

            this.config.api.parse(
                '{{ResourceCraftedWith|resource=' + resourceName + '|full=yes}}',
                {
                    contentmodel: 'wikitext',
                    disablelimitreport: true,
                    wrapoutputclass: ''
                }
            ).done( function ( html ) {
                self.displayRecipePopup(
                    'Recipes Using ' + resourceName,
                    html
                );
            } ).fail( function () {
                self.showErrorPopup( 'Failed to load recipe data' );
            } );
        },

        /**
         * Show popup with recipes that produce this resource
         * @param {string} resourceName - Name of the resource
         */
        showCraftedFromPopup: function ( resourceName ) {
            var self = this;

            this.showLoadingPopup( 'Loading recipes that produce ' + resourceName + '...' );

            this.config.api.parse(
                '{{ResourceCraftedFrom|resource=' + resourceName + '|full=yes}}',
                {
                    contentmodel: 'wikitext',
                    disablelimitreport: true,
                    wrapoutputclass: ''
                }
            ).done( function ( html ) {
                self.displayRecipePopup(
                    'Recipes Producing ' + resourceName,
                    html
                );
            } ).fail( function () {
                self.showErrorPopup( 'Failed to load recipe data' );
            } );
        },

        /**
         * Show loading popup
         * @param {string} message - Loading message
         */
        showLoadingPopup: function ( message ) {
            var $popup = $( '<div>' ).addClass( 'recipe-popup-overlay active' ).append(
                $( '<div>' ).addClass( 'recipe-popup loading' ).append(
                    $( '<div>' ).addClass( 'popup-content' ).css( {
                        'text-align': 'center',
                        'padding': '50px'
                    } ).append(
                        $( '<div>' ).addClass( 'tech-loader' ),
                        $( '<p>' ).css( {
                            'margin-top': '20px',
                            'color': '#fce7c8'
                        } ).text( message )
                    )
                )
            );

            $( 'body' ).append( $popup );
        },

        /**
         * Show error popup
         * @param {string} message - Error message
         */
        showErrorPopup: function ( message ) {
            $( this.config.selectors.popupOverlay ).remove();

            var $popup = $( '<div>' ).addClass( 'recipe-popup-overlay active' ).append(
                $( '<div>' ).addClass( 'recipe-popup error' ).append(
                    $( '<div>' ).addClass( 'popup-header' ).append(
                        $( '<h3>' ).addClass( 'popup-title' ).text( 'Error' ),
                        $( '<button>' ).addClass( 'popup-close' ).html( '&times;' )
                    ),
                    $( '<div>' ).addClass( 'popup-content' ).css( {
                        'text-align': 'center',
                        'padding': '50px'
                    } ).append(
                        $( '<p>' ).css( 'color', '#ff6b6b' ).text( message )
                    )
                )
            );

            $( 'body' ).append( $popup );
        },

        /**
         * Display recipe popup with content
         * @param {string} title - Popup title
         * @param {string} content - HTML content
         */
        displayRecipePopup: function ( title, content ) {
            var self = this;

            // Remove any existing popups
            $( this.config.selectors.popupOverlay ).remove();

            // Create popup structure
            var $popup = $( '<div>' ).addClass( 'recipe-popup-overlay' ).append(
                $( '<div>' ).addClass( 'recipe-popup' ).append(
                    $( '<div>' ).addClass( 'popup-header' ).append(
                        $( '<h3>' ).addClass( 'popup-title' ).text( title ),
                        $( '<button>' ).addClass( 'popup-close' ).html( '&times;' )
                    ),
                    $( '<div>' ).addClass( 'popup-content' ).append(
                        $( '<div>' ).addClass( 'popup-search-container' ).append(
                            $( '<input>' )
                                .attr( 'type', 'text' )
                                .addClass( 'popup-search-input' )
                                .attr( 'placeholder', 'Search recipes...' )
                        ),
                        $( '<div>' ).addClass( 'popup-table-wrapper' ).html( content )
                    )
                )
            );

            $( 'body' ).append( $popup );

            // Activate popup with animation
            setTimeout( function () {
                $popup.addClass( 'active' );
                // Focus search input
                $popup.find( '.popup-search-input' ).focus();
            }, 10 );
        },

        /**
         * Filter popup table based on search
         * @param {string} searchTerm - Search term
         */
        filterPopupTable: function ( searchTerm ) {
            var $table = $( this.config.selectors.popupTable ),
                $rows = $table.find( 'tbody tr' ),
                $noResults = $table.find( '.no-results' );

            // Remove any existing no-results message
            $noResults.remove();

            if ( !searchTerm ) {
                $rows.show();
                return;
            }

            var visibleCount = 0;
            $rows.each( function () {
                var $row = $( this ),
                    text = $row.text().toLowerCase();

                if ( text.indexOf( searchTerm ) > -1 ) {
                    $row.show();
                    visibleCount++;
                } else {
                    $row.hide();
                }
            } );

            // Show no results message
            if ( visibleCount === 0 ) {
                $table.find( 'tbody' ).append(
                    $( '<tr>' ).addClass( 'no-results' ).append(
                        $( '<td>' )
                            .attr( 'colspan', '6' )
                            .css( {
                                'text-align': 'center',
                                'color': '#666',
                                'padding': '20px'
                            } )
                            .text( 'No recipes found matching "' + searchTerm + '"' )
                    )
                );
            }
        },

        /**
         * Close popup
         */
        closePopup: function () {
            var self = this,
                $popup = $( this.config.selectors.popupOverlay );

            $popup.removeClass( 'active' );

            setTimeout( function () {
                $popup.remove();
            }, self.config.animationDuration );
        }
    };

    // Initialize when DOM is ready
    $( function () {
        // Use mw.hook to allow other scripts to know when we're ready
        mw.hook( 'wikipage.content' ).add( function () {
            ResourcePage.init();
        } );
    } );

    // Export for debugging
    mw.duneResourcePage = ResourcePage;

}( jQuery, mediaWiki ) );