1/** 2 * ITAMDE Games Catalog - Frontend JavaScript 3 * Version: 1.0.0 4 */ 5 6(function($) { 7 'use strict'; 8 9 class ITAMDEGamesCatalog { 10 constructor() { 11 this.init(); 12 } 13 14 init() { 15 this.bindEvents(); 16 this.initResponsiveFrames(); 17 this.initGameCards(); 18 } 19 20 bindEvents() { 21 // Fullscreen button events 22 $(document).on('click', '.itamde-fullscreen-btn', this.toggleFullscreen.bind(this)); 23 24 // Game version button events 25 $(document).on('click', '.game-version-btn', this.handleVersionButton.bind(this)); 26 27 // Escape key for fullscreen exit 28 $(document).on('keydown', this.handleKeydown.bind(this)); 29 30 // Window resize for responsive frames 31 $(window).on('resize', this.handleResize.bind(this)); 32 } 33 34 initResponsiveFrames() { 35 $('.itamde-game-responsive').each(function() { 36 const $container = $(this); 37 const $iframe = $container.find('.itamde-game-frame'); 38 39 // Just ensure iframe fills the container width 40 // Height is controlled by the shortcode inline style 41 $iframe.css({ 42 'width': '100%', 43 'display': 'block' 44 }); 45 }); 46 } 47 48 initGameCards() { 49 // Add loading state to game cards 50 $('.itamde-game-card').each(function() { 51 const $card = $(this); 52 const $thumbnail = $card.find('.game-thumbnail img'); 53 54 if ($thumbnail.length) { 55 $thumbnail.on('load', function() { 56 $card.addClass('loaded'); 57 }).on('error', function() { 58 $card.addClass('error'); 59 $(this).attr('src', 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="300" height="200"><rect width="100%" height="100%" fill="%23f0f0f0"/><text x="50%" y="50%" text-anchor="middle" dy="0.3em" fill="%23999">Game Image</text></svg>'); 60 }); 61 62 // Trigger load event if image is already cached 63 if ($thumbnail[0].complete) { 64 $thumbnail.trigger('load'); 65 } 66 } 67 }); 68 } 69 70 toggleFullscreen(event) { 71 event.preventDefault(); 72 73 const $button = $(event.currentTarget); 74 const $container = $button.closest('.itamde-game-container'); 75 const isFullscreen = $container.hasClass('itamde-game-fullscreen'); 76 77 if (isFullscreen) { 78 this.exitFullscreen($container, $button); 79 } else { 80 this.enterFullscreen($container, $button); 81 } 82 } 83 84 enterFullscreen($container, $button) { 85 // Store original styles 86 $container.data('originalStyles', { 87 position: $container.css('position'), 88 top: $container.css('top'), 89 left: $container.css('left'), 90 width: $container.css('width'), 91 height: $container.css('height'), 92 zIndex: $container.css('z-index'), 93 margin: $container.css('margin') 94 }); 95 96 // Apply fullscreen styles 97 $container.addClass('itamde-game-fullscreen'); 98 $('body').addClass('itamde-games-fullscreen-active'); 99 100 // Update button text/icon 101 $button.html('â').attr('title', itamde_games_ajax.strings.fullscreen_exit); 102 103 // Focus on iframe and notify game of fullscreen 104 const iframe = $container.find('.itamde-game-frame')[0]; 105 setTimeout(() => { 106 if (iframe) { iframe.focus(); iframe.contentWindow.postMessage('fullscreen-enter', '*'); } 107 }, 100); 108 109 // Trigger custom event 110 $container.trigger('itamde_games_fullscreen_enter'); 111 } 112 113 exitFullscreen($container, $button) { 114 const originalStyles = $container.data('originalStyles'); 115 116 // Restore original styles 117 if (originalStyles) { 118 $container.css(originalStyles); 119 } 120 121 // Notify game of fullscreen exit 122 const iframe = $container.find('.itamde-game-frame')[0]; 123 if (iframe) { iframe.contentWindow.postMessage('fullscreen-exit', '*'); } 124 125 // Remove fullscreen classes 126 $container.removeClass('itamde-game-fullscreen'); 127 $('body').removeClass('itamde-games-fullscreen-active'); 128 129 // Update button text/icon 130 $button.html('â¶').attr('title', itamde_games_ajax.str
130ings.fullscreen_enter); 131 132 // Trigger custom event 133 $container.trigger('itamde_games_fullscreen_exit'); 134 } 135 136 handleVersionButton(event) { 137 event.preventDefault(); 138 139 const $button = $(event.currentTarget); 140 const shortcode = $button.data('shortcode'); 141 142 if (shortcode) { 143 // Copy shortcode to clipboard 144 this.copyToClipboard(shortcode); 145 146 // Show feedback 147 this.showFeedback($button, 'Shortcode copiato!', 'success'); 148 } 149 } 150 151 handleKeydown(event) { 152 // Exit fullscreen on ESC key 153 if (event.keyCode === 27) { 154 const $fullscreenContainer = $('.itamde-game-fullscreen'); 155 if ($fullscreenContainer.length) { 156 const $button = $fullscreenContainer.find('.itamde-fullscreen-btn'); 157 this.exitFullscreen($fullscreenContainer, $button); 158 } 159 } 160 } 161 162 handleResize() { 163 // Recalculate responsive frames on resize 164 this.initResponsiveFrames(); 165 } 166 167 copyToClipboard(text) { 168 // Modern clipboard API 169 if (navigator.clipboard && window.isSecureContext) { 170 navigator.clipboard.writeText(text); 171 return; 172 } 173 174 // Fallback method 175 const textArea = document.createElement('textarea'); 176 textArea.value = text; 177 textArea.style.position = 'fixed'; 178 textArea.style.left = '-999999px'; 179 textArea.style.top = '-999999px'; 180 document.body.appendChild(textArea); 181 textArea.focus(); 182 textArea.select(); 183 184 try { 185 document.execCommand('copy'); 186 } catch (error) { 187 console.warn('Fallback clipboard copy failed:', error); 188 } 189 190 document.body.removeChild(textArea); 191 } 192 193 showFeedback($element, message, type = 'info', duration = 2000) { 194 const $feedback = $('<div class="itamde-feedback itamde-feedback-' + type + '">' + message + '</div>'); 195 196 $feedback.css({ 197 position: 'absolute', 198 top: '-40px', 199 left: '50%', 200 transform: 'translateX(-50%)', 201 background: type === 'success' ? '#28a745' : '#007cba', 202 color: 'white', 203 padding: '5px 10px', 204 borderRadius: '3px', 205 fontSize: '12px', 206 whiteSpace: 'nowrap', 207 zIndex: '9999', 208 opacity: '0' 209 }); 210 211 $element.css('position', 'relative').append($feedback); 212 213 $feedback.animate({ opacity: 1, top: '-35px' }, 200).delay(duration).animate({ 214 opacity: 0, 215 top: '-40px' 216 }, 200, function() { 217 $(this).remove(); 218 }); 219 } 220 221 // Public methods for external access 222 getGameContainer(gameId) { 223 return $('.itamde-game-container[data-game-id="' + gameId + '"]'); 224 } 225 226 reloadGame(gameId) { 227 const $container = this.getGameContainer(gameId); 228 const $iframe = $container.find('.itamde-game-frame'); 229 230 if ($iframe.length) { 231 const src = $iframe.attr('src'); 232 $iframe.attr('src', '').attr('src', src); 233 } 234 } 235 236 // Game analytics (optional) 237 trackGameInteraction(gameId, action, data = {}) { 238 if (typeof gtag !== 'undefined') { 239 gtag('event', action, { 240 event_category: 'ITAMDE Games', 241 event_label: gameId, 242 custom_map: data 243 }); 244 } 245 246 // Custom tracking hook for other analytics 247 $(document).trigger('itamde_games_track', { 248 gameId: gameId, 249 action: action, 250 data: data 251 }); 252 } 253 } 254 255 // Initialize when DOM is ready 256 $(document).ready(function() { 257 window.ITAMDEGames = new ITAMDEGamesCatalog(); 258 259 // Expose some methods globally for theme integration 260 window.itamdeGamesReload = function(gameId) { 261 window.ITAMDEGames.reloadGame(gameId); 262 }; 263 264 window.itamdeGamesTrack = function(gameId, action, data) { 265 window.ITAMDEGames.trackGameInteraction(gameId, action, data); 266 }; 267 });
268 269 // CSS injection for dynamic styles 270 $('<style>') 271 .prop('type', 'text/css') 272 .html(` 273 body.itamde-games-fullscreen-active { 274 overflow: hidden !important; 275 } 276 277 .itamde-feedback { 278 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important; 279 } 280 281 @media (max-width: 768px) { 282 .itamde-game-fullscreen .itamde-fullscreen-btn { 283 top: 20px; 284 right: 20px; 285 padding: 10px 15px; 286 font-size: 18px; 287 } 288 } 289 `) 290 .appendTo('head'); 291 292})(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.