PageSourceSearch

https://game-stat.com/wp-content/plugins/steam-player-stats/js/additional-modules.js?ver=1.8.18

js game-stat.com collected 2026-10-02 00:50:11 UTC 20,840 bytes, 512 lines download raw bytes

1/**
2 * Steam Player Stats - Additional Modules JavaScript
3 * Handles specific functionality for additional modules (achievements, inventory checks, etc.) 
4 */
5
6// Version must match ?ver= used by wp_enqueue_script('steam-player-stats-shared-utils', ...) so this
7// import resolves to the same URL as the separately enqueued copy instead of fetching a duplicate module.
8import {
9    SteamConfig,
10    formatPlaytime,
11    formatNumber,
12    escapeHtml,
13    makeAjaxRequest,
14    getGameIconUrl,
15    getErrorMessage,
16    showNotification,
17    resetButton
18} from './shared-utils.js?ver=1.8.17';
19
20(function($) {
21    'use strict';
22
23    // Module initialization
24    $(document).ready(function() {
25        setupEventHandlers();
26    });
27
28    /**
29     * Setup event handlers for module interactions
30     */
31    function setupEventHandlers() {
32        // Handle module creation events from steam-player-stats-public.js
33        $(document).on('steamModuleCreated', function(event, moduleName, steamId, $container) {
34            initializeModuleSpecificFunctionality(moduleName, steamId, $container);
35        });
36
37        // Handle inventory module specific initialization
38        $(document).on('steamInventoryModuleCreated', function(event, steamId, $module) {
39            loadTopGamesForInventory($module, steamId);
40        });
41
42        // Handle achievements buttons
43        $(document).on('click', '.show-achievements', function() {
44            const $button = $(this);
45            const steamId = $button.data('steam-id');
46            const appId = $button.data('appid');
47            const gameName = $button.data('game-name');
48            
49            loadGameAchievements($button, steamId, appId, gameName);
50        });
51
52        // Handle inventory check buttons
53        $(document).on('click', '.steam-inventory-check-btn', function() {
54            const $button = $(this);
55            const steamId = $button.data('steam-id');
56            const appId = $button.data('app-id');
57            const gameName = $button.data('game-name');
58            
59            checkGameInventory($button, steamId, appId, gameName);
60        });
61    }
62
63    /**
64     * Initialize module-specific functionality
65     */
66    function initializeModuleSpecificFunctionality(moduleName, steamId, $container) {
67        switch (moduleName) {
68            case 'games':
69                // Games module is mostly handled by the main file
70                break;
71            case 'friends':
72                // Friends module is mostly handled by the main file
73                break;
74            case 'inventory':
75                // Inventory module needs special handling
76                const $module = $container.find('.steam-inventory-module');
77                if ($module.length) {
78                    loadTopGamesForInventory($module, steamId);
79                }
80                break;
81        }
82    }
83
84    /**
85     * Load top 5 games for inventory checking
86     */
87    function loadTopGamesForInventory($module, steamId) {
88        makeAjaxRequest('get_player_games', { steamId: steamId })
89            .done(function(response) {
90                if (response.success && response.data.top5Games) {
91                    const $gameInventories = $module.find('.steam-game-inventories');
92                    $gameInventories.html(renderInventoryButtons(response.data.top5Games, steamId));
93                } else {
94                    const $gameInventories = $module.find('.steam-game-inventories');
95                    $gameInventories.html('<p class="steam-no-data">' + SteamConfig.getString('noGames', 'No games found') + '</p>');
96                }
97            })
98            .fail(function() {
99                const $gameInventories = $module.find('.steam-game-inventories');
100                $gameInventories.html('<p class="steam-no-data">' + SteamConfig.getString('error', 'Error loading data') + '</p>');
101            });
102    }
103
104    /**
105     * Render inventory check buttons
106     */
107    function renderInventoryButtons(games, steamId) {
108        if (!games || games.length === 0) {
109            return '<p class="steam-no-data">' + SteamConfig.getString('noGames', 'No games found') + '</p>';
110        }
111
112        let html = '<div class="steam-inventory-buttons">';
113        
114        games.forEach(function(game) {
115            html += `
116                <button class="steam-btn steam-btn-primary steam-inventory-check-btn" 
117                        data-steam-id="${steamId}" 
118                        data-app-id="${game.appid}" 
119                        data-game-name="${escapeHtml(game.name)}">
120                    ${SteamConfig.getString('checkInventory', 'Check Inventory')}: ${escapeHtml(game.name)}
121                </button>
122            `;
123        });
124        
125        html += '</div>';
126        return html;
127    }
128
129    /**
130     * Render inventory grid with grouping by rarity
131     */
132    function renderInventoryGrid(data, gameName, appId) {
133        const items = data.items || [];
134
135        // Group items by rarity
136        const rarityOrder = ['rare', 'mythical', 'legendary', 'ancient', 'immortal', 'arcana', 'common', 'uncommon'];
137        const groupedItems = {};
138        const rarityCounts = {};
139
140        items.forEach(item => {
141            const rarity = item.rarity || 'common';
142            if (!groupedItems[rarity]) {
143                groupedItems[rarity] = [];
144                rarityCounts[rarity] = 0;
145            }
146            groupedItems[rarity].push(item);
147            rarityCounts[rarity]++;
148        });
149
150        // Sort rarity groups by custom order
151        const sortedRarities = Object.keys(groupedItems).sort((a, b) => {
152            const indexA = rarityOrder.indexOf(a.toLowerCase());
153            const indexB = rarityOrder.indexOf(b.toLowerCase());
154            if (indexA === -1 && indexB === -1) return a.localeCompare(b);
155            if (indexA === -1) return 1;
156            if (indexB === -1) return -1;
157            return indexA - indexB;
158        });
159
160        let html = `<div class="steam-inventory-display" data-inventory-for="${appId}">`;
161
162        // Statistics header
163        html += `<div class="steam-inventory-header">`;
164        html += `<h5 class="steam-inventory-title">${escapeHtml(gameName)} - ${SteamConfig.getString('inventory', 'Inventory')}</h5>`;
165        html += `<div class="steam-inventory-stats">`;
166        html += `<span>${SteamConfig.getString('showing', 'Showing')} ${data.displayedCount} ${SteamConfig.getString('of', 'of')} ${data.itemCount} ${SteamConfig.getString('items', 'items')}</span>`;
167        if (data.hasMore) {
168            html += `<span class="steam-inventory-more"> (${SteamConfig.getString('moreAvailable', 'more available')})</span>`;
169        }
170        html += `</div>`;
171        html += `</div>`;
172
173        // Rarity groups
174        sortedRarities.forEach(rarity => {
175            const rarityItems = groupedItems[rarity];
176            const firstItem = rarityItems[0];
177            const rarityName = firstItem.rarity_name || rarity;
178            const rarityColor = firstItem.rarity_color || '';
179
180            html += `<div class="steam-rarity-group">`;
181            html += `<div class="steam-rarity-header" data-rarity="${escapeHtml(rarity)}">`;
182            html += `<span class="steam-rarity-indicator" style="background-color: #${rarityColor}"></span>`;
183            html += `<span class="steam-rarity-name">${escapeHtml(rarityName)}</span>`;
184            html += `<span class="steam-rarity-count">(${rarityCounts[rarity]})</span>`;
185            html += `<span class="steam-rarity-toggle">▼</span>`;
186            html += `</div>`;
187
188            html += `<div class="steam-inventory-grid">`;
189            rarityItems.forEach(item => {
190                html += renderInventoryItem(item, rarityColor);
191            });
192            html += `</div>`;
193            html += `</div>`;
194        });
195
196        html += `</div>`;
197        return html;
198    }
199
200    /**
201     * Render single inventory item
202     */
203    function renderInventoryItem(item, rarityColor) {
204        const borderColor = rarityColor ? `#${rarityColor}` : '#2a475e';
205        const nameColor = item.name_color ? `#${item.name_color}` : '#d2d2d2';
206        const bgColor = item.background_color ? `#${item.background_color}` : '';
207
208        let html = `<div class="steam-inventory-item"
209                         data-item-name="${escapeHtml(item.name)}"
210                         data-item-type="${escapeHtml(item.type)}"
211                         data-item-rarity="${escapeHtml(item.rarity_name)}"
212                         data-item-description="${escapeHtml(item.description)}"
213                         data-item-tradable="${item.tradable}"
214                         data-item-marketable="${item.marketable}"
215                         style="border-color: ${borderColor};">`;
216
217        html += `<div class="steam-item-icon" style="${bgColor ? 'background-color: ' + bgColor : ''}">`;
218        if (item.icon_url) {
219            html += `<img src="${escapeHtml(item.icon_url)}" alt="${escapeHtml(item.name)}" loading="lazy" onerror="this.style.display='none'">`;
220        } else {
221            html += `<div class="steam-item-no-icon">?</div>`;
222        }
223        if (item.amount > 1) {
224            html += `<span class="steam-item-amount">x${item.amount}</span>`;
225        }
226        html += `</div>`;
227
228        html += `<div class="steam-item-name" style="color: ${nameColor}">${escapeHtml(item.name)}</div>`;
229        html += `</div>`;
230
231        return html;
232    }
233
234    /**
235     * Initialize inventory tooltips
236     */
237    function initializeInventoryTooltips() {
238        const $items = $('.steam-inventory-item');
239
240        // Remove old event handlers
241        $items.off('mouseenter mouseleave click');
242
243        // Click handler to show tooltip
244        $items.on('click', function(e) {
245            e.stopPropagation();
246            const $item = $(this);
247
248            // Remove any existing tooltips
249            $('.steam-item-tooltip').remove();
250
251            const name = $item.data('item-name');
252            const type = $item.data('item-type');
253            const rarity = $item.data('item-rarity');
254            const description = $item.data('item-description');
255            const tradable = $item.data('item-tradable');
256            const marketable = $item.data('item-marketable');
257
258            // Don't show tooltip if we don't have a name
259            if (!name) return;
260
261            // Build tooltip HTML with close button
262            let tooltipHtml = `<div class="steam-item-tooltip">`;
263            tooltipHtml += `<button class="steam-tooltip-close" aria-label="Close">&times;</button>`;
264            tooltipHtml += `<div class="steam-tooltip-name">${escapeHtml(name)}</div>`;
265            if (type) {
266                tooltipHtml += `<div class="steam-tooltip-type">${escapeHtml(type)}</div>`;
267            }
268            if (rarity) {
269                tooltipHtml += `<div class="steam-tooltip-rarity">${escapeHtml(rarity)}</div>`;
270            }
271            if (description) {
272                tooltipHtml += `<div class="steam-tooltip-divider"></div>`;
273                tooltipHtml += `<div class="steam-tooltip-description">${escapeHtml(description)}</div>`;
274            }
275
276            // Add badges
277            const badges = [];
278            if (tradable) badges.push(SteamConfig.getString('tradable', 'Tradable'));
279            if (marketable) badges.push(SteamConfig.getString('marketable', 'Marketable'));
280            if (badges.length > 0) {
281                tooltipHtml += `<div class="steam-tooltip-badges">`;
282                badges.forEach(badge => {
283                    tooltipHtml += `<span class="steam-tooltip-badge">${badge}</span>`;
284                });
285                tooltipHtml += `</div>`;
286            }
287
288            tooltipHtml += `</div>`;
289
290            const $tooltip = $(tooltipHtml);
291            $('body').append($tooltip);
292
293            // Position tooltip using getBoundingClientRect for fixed positioning
294            const rect = this.getBoundingClientRect();
295            const tooltipWidth = $tooltip.outerWidth();
296            const tooltipHeight = $tooltip.outerHeight();
297
298            // Center tooltip above item
299            let left = rect.left + (rect.width / 2) - (tooltipWidth / 2);
300            let top = rect.top - tooltipHeight - 10;
301
302            // Adjust if tooltip goes off screen horizontally
303            if (left < 10) left = 10;
304            if (left + tooltipWidth > window.innerWidth - 10) {
305                left = window.innerWidth - tooltipWidth - 10;
306            }
307
308            // If tooltip goes off top of screen, show below item
309            if (top < 10) {
310                top = rect.bottom + 10;
311            }
312
313            $tooltip.css({
314                left: left + 'px',
315                top: top + 'px',
316                display: 'block',
317                opacity: '0'
318            });
319
320            // Fade in animation
321            setTimeout(() => {
322                $tooltip.css('opacity', '1');
323            }, 10);
324
325            // Close button handler
326            $tooltip.find('.steam-tooltip-close').on('click', function(e) {
327                e.stopPropagation();
328                $tooltip.fadeOut(200, function() {
329                    $(this).remove();
330                });
331            });
332
333            // Stop propagation when clicking on tooltip
334            $tooltip.on('click', function(e) {
335                e.stopPropagation();
336            });
337        });
338
339        // Close tooltip when clicking outside
340        $(document).off('click.tooltip').on('click.tooltip', function() {
341            $('.steam-item-tooltip').fadeOut(200, function() {
342                $(this).remove();
343            });
344        });
345
346        // Close tooltip on ESC key
347        $(document).off('keydown.tooltip').on('keydown.tooltip', function(e) {
348            if (e.key === 'Escape') {
349                $('.steam-item-tooltip').fadeOut(200, function() {
350                    $(this).remove();
351                });
352            }
353        });
354
355        // Toggle rarity groups
356        $(document).off('click', '.steam-rarity-header').on('click', '.steam-rarity-header', function() {
357            const $header = $(this);
358            const $grid = $header.next('.steam-inventory-grid');
359            const $toggle = $header.find('.steam-rarity-toggle');
360
361            $grid.slideToggle(300);
362            $toggle.text($toggle.text() === '▼' ? '▶' : '▼');
363        });
364    }
365
366    /**
367     * Check specific game inventory
368     */
369    function checkGameInventory($button, steamId, appId, gameName) {
370        const originalText = $button.text();
371        const $container = $button.closest('.steam-inventory-buttons');
372
373        // Check if inventory is already displayed for this game
374        const existingInventory = $container.find(`[data-inventory-for="${appId}"]`);
375        if (existingInventory.length > 0) {
376            // Toggle visibility
377            existingInventory.slideToggle(300);
378            return;
379        }
380
381        $button.text(SteamConfig.getString('checking', 'Checking...')).prop('disabled', true);
382
383        makeAjaxRequest('get_game_inventory', {
384            steamId: steamId,
385            appId: appId
386        }, 30000)
387            .done(function(response) {
388                if (response.success) {
389                    const data = response.data;
390
391                    if (data.accessible && data.hasItems && data.items && data.items.length > 0) {
392                        // Render inventory grid
393                        const inventoryHtml = renderInventoryGrid(data, gameName, appId);
394                        $button.after(inventoryHtml);
395
396                        // Update button text
397                        const itemText = data.itemCount === 1 ?
398                            SteamConfig.getString('item', 'item') :
399                            SteamConfig.getString('items', 'items');
400                        $button.text(`${gameName}: ${data.itemCount} ${itemText}`).removeClass('steam-btn-primary').addClass('steam-btn-success');
401
402                        // Initialize tooltips
403                        initializeInventoryTooltips();
404
405                    } else if (data.accessible) {
406                        const message = `${gameName}: ${data.message || SteamConfig.getString('noItems', 'No items found')}`;
407                        $button.text(message).removeClass('steam-btn-primary').addClass('steam-btn-success');
408                    } else {
409                        const message = `${gameName}: ${data.message}`;
410                        $button.text(message).removeClass('steam-btn-primary').addClass('steam-btn-success');
411                    }
412                } else {
413                    $button.text(`${gameName}: ${response.data.message}`).removeClass('steam-btn-primary').addClass('steam-btn-error');
414                }
415            })
416            .fail(function(xhr, status, error) {
417                const message = getErrorMessage(status, error);
418                $button.text(`${gameName}: ${message}`).removeClass('steam-btn-primary').addClass('steam-btn-error');
419            })
420            .always(function() {
421                $button.prop('disabled', false);
422            });
423    }
424
425    /**
426     * Load achievements for specific game
427     */
428    function loadGameAchievements($button, steamId, appId, gameName) {
429        const $achievementSpan = $button.siblings('.achievement-info');
430        
431        if ($achievementSpan.html()) return; // Если данные уже загружены
432
433        $button.prop('disabled', true).text(SteamConfig.getString('loadingAchievements', 'Loading...'));
434
435        makeAjaxRequest('get_game_achievements', {
436            steamId: steamId,
437            appid: appId
438        }, 15000)
439            .done(function(response) {
440                if (response.success) {
441                    const data = response.data;
442                    $achievementSpan.html(`
443                        <div class="achievement-progress">
444                            <span>${SteamConfig.getString('achievementProgress', 'Achievement Progress')}: ${data.percentage}% (${data.completedCount}/${data.totalCount})</span>
445                            <div class="achievement-bar">
446                                <div class="achievement-fill" style="width: ${data.percentage}%"></div>
447                            </div>
448                        </div>
449                    `);
450                } else {
451                    $achievementSpan.html(`<span class="achievement-error">${response.data.message || SteamConfig.getString('noAchievements', 'No achievements available')}</span>`);
452                }
453            })
454            .fail(function(xhr, status, error) {
455                const message = getErrorMessage(status, error);
456                $achievementSpan.html(`<span class="achievement-error">${SteamConfig.getString('errorAchievements', 'Failed to load achievements')}</span>`);
457            })
458            .always(function() {
459                $button.remove(); // Remove button after loading
460            });
461    }
462
463    // Legacy functions for backward compatibility (if needed)
464    // These are now handled by the main file, but keeping for potential external calls
465
466    /**
467     * @deprecated Use events instead
468     * Legacy function for initializing additional modules
469     */
470    window.initializeAdditionalModules = function() {
471        console.warn('initializeAdditionalModules is deprecated. Modules are now initialized via events.');
472        // Event-based initialization is automatic
473    };
474
475    /**
476     * Populate Games Module with data (legacy support)
477     * @deprecated This is now handled by the main file
478     */
479    function populateGamesData($module, data, steamId) {
480        console.warn('populateGamesData is deprecated. Use the main file rendering.');
481        // Implementation moved to steam-player-stats-public.js
482    }
483
484    /**
485     * Populate Friends Module with data (legacy support)
486     * @deprecated This is now handled by the main file
487     */
488    function populateFriendsData($module, data) {
489        console.warn('populateFriendsData is deprecated. Use the main file rendering.');
490        // Implementation moved to steam-player-stats-public.js
491    }
492
493    /**
494     * Populate Inventory Module with data (legacy support)
495     * @deprecated This is now handled by the main file
496     */
497    function populateInventoryData($module, data, steamId) {
498        console.warn('populateInventoryData is deprecated. Use loadTopGamesForInventory.');
499        if ($module && steamId) {
500            loadTopGamesForInventory($module, steamId);
501        }
502    }
503
504    // Export functions for potential external use (if needed)
505    window.SteamAdditionalModules = {
506        loadTopGamesForInventory: loadTopGamesForInventory,
507        checkGameInventory: checkGameInventory,
508        loadGameAchievements: loadGameAchievements,
509        renderInventoryButtons: renderInventoryButtons
510    };
511
512})(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.