1"use strict"; 2 3 4function ShowEmbedWidget( ) 5{ 6 $J('#widget_create').show(); 7 $J('#widget_finished').hide(); 8 9 var $Content = $J('#EmbedModal'); 10 $Content.detach(); 11 $Content.show(); 12 13 var deferred = new jQuery.Deferred(); 14 var fnOK = function() { deferred.resolve(); }; 15 16 var Modal = _BuildDialog( "\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e27\u0e34\u0e14\u0e40\u0e08\u0e47\u0e15\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e19\u0e33\u0e44\u0e1b\u0e1d\u0e31\u0e07\u0e15\u0e31\u0e27", $Content, [], fnOK, {} ); 17 deferred.always( function() { Modal.Dismiss(); } ); 18 Modal.Show(); 19 20 // attach the deferred's events to the modal 21 deferred.promise( Modal ); 22 23 Modal.always( 24 function() { 25 // save it away again for later 26 $Content.hide(); 27 $J(document.body).append( $Content ); 28 } 29 ); 30} 31 32function CreateWidget( nAppId ) 33{ 34 $J('#widget_create').hide(); 35 36 var nSubId = $J('input[name=w_subid]').val(); 37 if( !nSubId ) 38 nSubId = $J('input:radio[name=w_rsubid]:checked').val(); 39 40 var strDesc = $J('textarea[name=w_text]').val(); 41 42 var strWidgetURL; 43 if( nSubId ) 44 strWidgetURL = 'https://store.steampowered.com/widget/' + nAppId + '/' + nSubId + '/'; 45 else 46 strWidgetURL = 'https://store.steampowered.com/widget/' + nAppId + '/'; 47 48 if( strDesc ) 49 strWidgetURL = strWidgetURL + '?t=' + encodeURIComponent( strDesc ); 50 51 var strWidgetCode = '<iframe src="' + strWidgetURL + '" frameborder="0" width="646" height="190"></iframe>'; 52 53 var $iframe = $J(strWidgetCode); 54 55 $J('#widget_container').empty(); 56 $J('#widget_container').append($iframe); 57 $J('#widget_code').val( strWidgetCode ); 58 59 $J('#widget_finished').show(); 60} 61 62// appid_for_follow can be different than store_appid in cases such as when viewing a standalone demo page where we want the follow button to follow the parent app 63function InitQueueControls( store_appid, appid_for_follow, next_in_queue_appid, snr ) 64{ 65 var $FollowBtn = $J('.queue_control_button.queue_btn_follow .queue_btn_inactive'); 66 var $UnFollowBtn = $J('.queue_control_button.queue_btn_follow .queue_btn_active'); 67 var $IgnoreBtn = $J('.queue_control_button.queue_btn_ignore .queue_btn_inactive'); 68 var $UnIgnoreBtn = $J('.queue_control_button.queue_btn_ignore .queue_btn_active'); 69 70 var $IgnoreMenuBtn = $J('#queue_ignore_menu_arrow'); 71 var $IgnoreMenu = $J('#queue_btn_ignore_menu'); 72 var $IgnoreMenuContent = $J('#ignore_menu_flyout > div'); 73 var $IgnoreMenuOption_NotInterested = $J('#queue_ignore_menu_option_not_interested' ); 74 var $IgnoreMenuOption_OwnedElsewhere = $J('#queue_ignore_menu_option_owned_elsewhere' ); 75 76 var funcUpdateIgnoredReasonClasses = function() { 77 $IgnoreMenu.removeClass( "not_interested owned_elsewhere" ); 78 var curIgnoredReason = $IgnoreMenu.data( "ignoredreason" ); 79 if ( curIgnoredReason !== undefined ) 80 { 81 $IgnoreMenuBtn.removeClass( "queue_btn_inactive" ); 82 $IgnoreMenuBtn.addClass( "queue_btn_active" ); 83 switch( curIgnoredReason ) 84 { 85 case 0: 86 $IgnoreMenu.addClass( "not_interested" ); 87 break; 88 case 2: 89 $IgnoreMenu.addClass( "owned_elsewhere" ); 90 break; 91 } 92 } 93 }; 94 funcUpdateIgnoredReasonClasses(); 95 96 var funcIgnoreGame = function( ignore_reason ) { 97 $J.post( 'https://store.steampowered.com/recommended/ignorerecommendation/', { 98 sessionid: g_sessionID, 99 appid: store_appid, 100 snr: snr, 101 ignore_reason: ignore_reason 102 }).done( function( data ) { 103 const bWasFocused = document.activeElement === $IgnoreBtn[0]; 104 $IgnoreBtn.hide(); 105 $UnIgnoreBtn.show(); 106 $IgnoreMenuBtn.attr( 'aria-expanded', false ); 107 if ( bWasFocused ) 108 { 109 $UnIgnoreBtn.focus(); 110 } 111 112 $IgnoreMenu.data( "ignoredreason", ignore_reason ); 113 funcUpdateIgnoredReasonClasses(); 114 115 GDynamicStore.InvalidateCache(); 116 if ( data && data.nSaleTaskCompleted ) { NewStickerPackModal( 'à¸à¸³à¹à¸à¸£à¸·à¹à¸à¸à¸«à¸¡à¸²à¸¢à¸à¸²à¸à¸ªà¸´à¹à¸à¹à¸à¹à¸à¹à¸¡à¹à¸ªà¸à¹à¸' ); } // SummerSale2017 117 }).fail( function( jqXHR ) { 118 ShowAlertDialog( 'ละà¹à¸§à¹à¸à¹à¸à¸', 'à¸à¸£à¸§à¸à¸à¸à¸à¸±à¸à¸«à¸²à¹à¸à¸à¸²à¸£à¸à¸±à¸à¸à¸¶à¸à¸à¸²à¸£à¹à¸à¸¥à¸µà¹à¸¢à¸à¹à¸à¸¥à¸à¸à¸à¸à¸à¸¸à¸ à¹à¸à¸£à¸à¸¥à¸à¸à¹à¸«à¸¡à¹à¸à¸µà¸à¸à¸£à¸±à¹à¸à¸ ายหลัà¸' ); 119 }); 120 }; 121 122 $IgnoreBtn.click( function() { 123 funcIgnoreGame( 0 ); 124 } ); 125 $IgnoreMenuOption_NotInterested.click( function() { 126 var curIgnoredReason = $IgnoreMenu.data( "ignoredreason" ); 127 if ( curIgnoredReason === 0 ) 128 { 129 $UnIgnoreBtn.click(); 130 $IgnoreMenuBtn.attr( 'aria-expanded', false ); 131 } 132 else 133 { 134 funcIgnoreGame( 0 ); 135 } 136 } ); 137 $IgnoreMenuOption_OwnedElsewhere.click( function() { 138 var curIgnoredReason = $IgnoreMenu.data( "ignoredreason" ); 139 if ( curIgnoredReason === 2 ) 140 { 141 $UnIgnoreBtn.click(); 142 $IgnoreMenuBtn.attr( 'aria-expanded', false ); 143 } 144 else 145 { 146 funcIgnoreGame( 2 ); 147 } 148 } ); 149 150 $IgnoreMenuBtn.click( function() { 151 const bIsExpanded = $IgnoreMenuBtn.attr( 'aria-expanded' ) == "true"; 152 $IgnoreMenuBtn.attr( 'aria-expanded', !bIsExpanded ); 153 if ( !bIsExpanded ) 154 { 155 $IgnoreMenuContent[0].children[0].focus(); 156 } 157 } ); 158 $IgnoreMenuContent.focusout( function(e) { 159 if ( !$IgnoreMenuContent[0].contains( e.relatedTarget ) ) 160 { 161 $IgnoreMenuBtn.attr( 'aria-expanded', false ); 162 } 163 } ); 164 165 $UnIgnoreBtn.click( function() {
166 $J.post( 'https://store.steampowered.com/recommended/ignorerecommendation/', { 167 sessionid: g_sessionID, 168 appid: store_appid, 169 snr: snr, 170 remove: 1 171 }).done( function() { 172 const bWasFocused = document.activeElement === $UnIgnoreBtn[0]; 173 $IgnoreBtn.show(); 174 $UnIgnoreBtn.hide(); 175 if ( bWasFocused ) 176 { 177 $IgnoreBtn.focus(); 178 } 179 $IgnoreMenu.removeClass( "not_interested owned_elsewhere" ); 180 $IgnoreMenuBtn.removeClass( "queue_btn_active" ); 181 $IgnoreMenuBtn.addClass( "queue_btn_inactive" ); 182 GDynamicStore.InvalidateCache(); 183 }).fail( function() { 184 ShowAlertDialog( 'ละà¹à¸§à¹à¸à¹à¸à¸', 'à¸à¸£à¸§à¸à¸à¸à¸à¸±à¸à¸«à¸²à¹à¸à¸à¸²à¸£à¸à¸±à¸à¸à¸¶à¸à¸à¸²à¸£à¹à¸à¸¥à¸µà¹à¸¢à¸à¹à¸à¸¥à¸à¸à¸à¸à¸à¸¸à¸ à¹à¸à¸£à¸à¸¥à¸à¸à¹à¸«à¸¡à¹à¸à¸µà¸à¸à¸£à¸±à¹à¸à¸ ายหลัà¸' ); 185 }); 186 }); 187 188 $FollowBtn.click( function() { 189 $J.post( 'https://store.steampowered.com/explore/followgame/', { 190 sessionid: g_sessionID, 191 appid: appid_for_follow 192 }).done( function() { 193 const bWasFocused = document.activeElement === $FollowBtn[0]; 194 $FollowBtn.hide(); 195 $UnFollowBtn.show(); 196 if ( bWasFocused ) 197 { 198 $UnFollowBtn.focus(); 199 } 200 }).fail( function() { 201 ShowAlertDialog( 'à¸à¸´à¸à¸à¸²à¸¡', 'à¸à¸£à¸§à¸à¸à¸à¸à¸±à¸à¸«à¸²à¹à¸à¸à¸²à¸£à¸à¸±à¸à¸à¸¶à¸à¸à¸²à¸£à¹à¸à¸¥à¸µà¹à¸¢à¸à¹à¸à¸¥à¸à¸à¸à¸à¸à¸¸à¸ à¹à¸à¸£à¸à¸¥à¸à¸à¹à¸«à¸¡à¹à¸à¸µà¸à¸à¸£à¸±à¹à¸à¸ ายหลัà¸' ); 202 }); 203 }); 204 205 $UnFollowBtn.click( function() { 206 $J.post( 'https://store.steampowered.com/explore/followgame/', { 207 sessionid: g_sessionID, 208 appid: appid_for_follow, 209 unfollow: 1 210 }).done( function() { 211 const bWasFocused = document.activeElement === $UnFollowBtn[0]; 212 $FollowBtn.show(); 213 $UnFollowBtn.hide(); 214 if ( bWasFocused ) 215 { 216 $FollowBtn.focus(); 217 } 218 }).fail( function() { 219 ShowAlertDialog( 'à¸à¸´à¸à¸à¸²à¸¡', 'à¸à¸£à¸§à¸à¸à¸à¸à¸±à¸à¸«à¸²à¹à¸à¸à¸²à¸£à¸à¸±à¸à¸à¸¶à¸à¸à¸²à¸£à¹à¸à¸¥à¸µà¹à¸¢à¸à¹à¸à¸¥à¸à¸à¸à¸à¸à¸¸à¸ à¹à¸à¸£à¸à¸¥à¸à¸à¹à¸«à¸¡à¹à¸à¸µà¸à¸à¸£à¸±à¹à¸à¸ ายหลัà¸' ); 220 }); 221 }); 222 223 // discovery queue settings dialog 224 var bQueueIsValid = true; 225 var $NextInQueueBtn = $J('.btn_next_in_queue_trigger'); 226 227 $NextInQueueBtn.click( function() { 228 if ( bQueueIsValid ) 229 $J('#next_in_queue_form').submit(); 230 else 231 window.location = 'https://store.steampowered.com/explore/startnew'; 232 }); 233 234 $J('a.dq_settings_link').click( function() { 235 CDiscoveryQueue.ShowCustomizeDialog( function( data ) { 236 if ( data && data.queue && ( data.queue.indexOf( store_appid ) == -1 || 237 ( next_in_queue_appid && data.queue.indexOf( next_in_queue_appid ) == -1 ) ) ) 238 { 239 ShowConfirmDialog( 'à¸à¸£à¸±à¸à¹à¸à¹à¸à¸à¸´à¸§à¸à¹à¸à¸à¸à¸à¸à¸à¸à¸¸à¸', 'à¸à¸²à¸à¹à¸£à¸²à¹à¸à¹à¸ªà¸£à¹à¸²à¸à¸à¸´à¸§à¸à¹à¸à¸à¸à¹à¸«à¸¡à¹à¹à¸à¸¢à¸à¸²à¸£à¹à¸à¸´à¹à¸¡à¸à¸²à¸£à¸à¸£à¸±à¸à¹à¸à¹à¸à¸à¸à¸à¸à¸¸à¸ à¸à¸¸à¸à¸à¹à¸à¸à¸à¸²à¸£à¸à¸³à¸à¸°à¹à¸£?', 240 'à¹à¸£à¸´à¹à¸¡à¸ªà¸³à¸£à¸§à¸à¸à¸´à¸§à¹à¸«à¸¡à¹','à¸à¸¢à¸¹à¹à¸à¸µà¹à¸à¸µà¹' 241 ).done( function() { 242 window.location = 'https://store.steampowered.com/explore/startnew/' + g_eDiscoveryQueueType + '/'; 243 }); 244 bQueueIsValid = false; 245 } 246 else 247 { 248 bQueueIsValid = true; 249 } 250 }, g_eDiscoveryQueueType ); 251 } ); 252} 253 254/** OK button handler for collapsed `game_page_autocollapse` sections. Triggers expansion of the content. */ 255function HandleAutoCollapseOKButton( event ) 256{ 257 var $Container = $J( event.currentTarget ); 258 if ( $Container.hasClass( 'collapsed' ) ) 259 { 260 var $Content = $Container.find( '.game_page_autocollapse' ); 261 $Content.trigger('gamepage_autocollapse_expand'); 262 return true; 263 } 264 265 return false; 266} 267 268 269function InitAutocollapse() 270{ 271 $J('.game_page_autocollapse').each( function() { 272 var content = this; 273 var $Content = $J(content); 274 $Content.wrap( $J('<div/>', {'class': 'game_page_autocollapse_ctn' } ) ); 275 276 var $Container = $Content.parent(); 277 278 var $ReadMore = $J('<div/>', {'class': 'game_page_autocollapse_readmore' }).text( 'à¸à¹à¸²à¸à¹à¸à¸´à¹à¸¡à¹à¸à¸´à¸¡' ); 279 var $Fade = $J('<div/>', {'class': 'game_page_autocollapse_fade' } ).append( $ReadMore ); 280 $Container.append( $Fade ); 281 282 var nInterval = 0; 283 var nMaxHeight = parseInt( $Content.css('max-height') ); 284 var bMaxHeightSet = true; 285 286 $Content.on( 'gamepage_autocollapse_expand', function() { 287 if ( $Container.hasClass( 'collapsed' ) ) 288 {
289 // Stop suppressing gamepad focus on the expandable content now that it is expanded 290 $Content.data( 'gpFocusDisabled', false ); 291 $Container.removeClass( 'collapsed' ); 292 $Container.addClass( 'expanded' ); 293 294 if ( bMaxHeightSet ) 295 { 296 $Content.animate( {'max-height': content.scrollHeight + 20 + 'px'}, 'fast', null, function() { $Content.css('max-height', 'none' ); } ); 297 } 298 window.clearInterval( nInterval ); 299 } 300 }); 301 302 $ReadMore.click( function() { $Content.trigger('gamepage_autocollapse_expand'); } ); 303 304 var fnCheckHeight = function () { 305 if ( content.scrollHeight > nMaxHeight + 30 ) 306 { 307 $Content.css( 'max-height', nMaxHeight + 'px' ); 308 $Container.addClass( 'collapsed' ); 309 window.clearInterval( nInterval ); 310 bMaxHeightSet = true; 311 312 // The content div becomes a panel with focus temporarily disabled. This allows the container Panel to get focus and handle 313 // expansion via its OK button activation. Once expanded the content panel lets focus pass through to its children (if there are any). 314 if ( $Content.attr( 'data-panel' ) === undefined ) 315 $Content.attr( 'data-panel', '[]' ); 316 317 $Content.data( 'gpFocusDisabled', true ); 318 $Container.attr( 'data-panel', '{"onOKButton":"HandleAutoCollapseOKButton( event )","focusableIfEmpty":true}' ); 319 } 320 else if ( bMaxHeightSet ) 321 { 322 $Content.css( 'max-height', 'none' ); 323 bMaxHeightSet = false; 324 } 325 }; 326 327 nInterval = window.setInterval( fnCheckHeight, 250 ); 328 fnCheckHeight(); 329 330 }); 331} 332 333function AddScrollToAccessoriesBlock() 334{ 335 function scrollToAccessoriesIfNeeded() 336 { 337 if ( window.location.hash == "#accessories" ) 338 { 339 document.querySelector( "#accessory_block" ).scrollIntoView( { 340 behavior: 'smooth', 341 block: 'center', 342 inline: 'center', 343 } ); 344 345 document.querySelector( "#accessory_block" ).classList.add( "highlighted" ); 346 } 347 else 348 { 349 document.querySelector( "#accessory_block" ).classList.remove( "highlighted" ); 350 } 351 } 352 353 window.addEventListener( "hashchange", scrollToAccessoriesIfNeeded, false ); 354 window.addEventListener( "load", scrollToAccessoriesIfNeeded, false ); 355} 356 357function ShowEULA( elLink ) 358{ 359 if ( window.UseGamepadScreenMode && window.UseGamepadScreenMode() ) 360 { 361 // it's a better user experience on Deck if we navigate to the EULA instead of opening a new window 362 window.location = elLink.href; 363 } 364 else 365 { 366 var win = window.open( elLink.href,'eula','height=584,width=648,resize=yes,scrollbars=yes'); 367 win && win.focus(); 368 } 369} 370 371 372// formerly user_reviews_store.js 373 374var g_recommendationContents = [ 'friend', 'summary', 'recent_short', 'all', 'positive', 'negative', 'recent', 'funny' ]; 375 376 377function OnRecommendationVotedUp( recommendationid ) 378{ 379 for ( var i = 0; i < g_recommendationContents.length; ++i ) 380 { 381 $J( '#RecommendationVoteUpBtn' + g_recommendationContents[i] + recommendationid ).addClass( 'btn_active' ); 382 $J( '#RecommendationVoteDownBtn' + g_recommendationContents[i] + recommendationid ).removeClass( "btn_active" ); 383 $J( '#RecommendationVoteTagBtn' + g_recommendationContents[i] + recommendationid + '_' + 1 ).removeClass( 'btn_active' ); 384 } 385} 386 387function OnRecommendationVotedDown( recommendationid ) 388{ 389 for ( var i = 0; i < g_recommendationContents.length; ++i ) 390 { 391 $J( '#RecommendationVoteUpBtn' + g_recommendationContents[i] + recommendationid ).removeClass( 'btn_active' ); 392 $J( '#RecommendationVoteDownBtn' + g_recommendationContents[i] + recommendationid ).addClass( "btn_active" ); 393 $J( '#RecommendationVoteTagBtn' + g_recommendationContents[i] + recommendationid + '_' + 1 ).removeClass( 'btn_active' ); 394 } 395} 396 397function OnRecommendationVotedTag( recommendationid, tagID, bRateUp ) 398{ 399 for ( var i = 0; i < g_recommendationContents.length; ++i ) 400 { 401 if ( bRateUp ) 402 { 403 $J( '#RecommendationVoteTagBtn' + g_recommendationContents[i] + recommendationid + '_' + tagID ).addClass( 'btn_active' ); 404 $J( '#RecommendationVoteUpBtn' + g_recommendationContents[i] + recommendationid ).removeClass( 'btn_active' ); 405 $J( '#RecommendationVoteDownBtn' + g_recommendationContents[i] + recommendationid ).removeClass( "btn_active" ); 406 } 407 else 408 { 409 $J( '#RecommendationVoteTagBtn' + g_recommendationContents[i] + recommendationid + '_' + tagID ).removeClass( 'btn_active' ); 410 } 411 } 412} 413 414function OnRecommendationAward( recommendationid, award ) 415{ 416 for ( var i = 0; i < g_recommendationContents.length; ++i ) 417 { 418 var review = $J( "#ReviewContent" + g_recommendationContents[i] + recommendationid ); 419 var rewardsCtn = review.find( ".review_award_ctn" ); 420 if ( rewardsCtn.length != 0 ) 421 { 422 var bFoundExisting = false;
423 var rewards = rewardsCtn.find( ".review_award" ); 424 for ( var j = 0; j < rewards.length; ++j ) 425 { 426 var reward = $J( rewards[j] ); 427 if ( reward.data( "reaction" ) == award ) 428 { 429 bFoundExisting = true; 430 431 var count = parseInt( reward.data( "reactioncount" ) ); 432 var countElem = reward.find( ".review_award_count" ); 433 countElem.text( count + 1 ); 434 435 reward.data( "reactioncount", count + 1 ); 436 break; 437 } 438 } 439 440 if ( !bFoundExisting ) 441 { 442 var reward = $J( "<div>", { class: "review_award" } ); 443 var img = $J( "<img>", { class: "review_award_icon tooltip", src: "https://store.fastly.steamstatic.com/public/images/loyalty/reactions/still/" + award + ".png" } ); 444 reward.append( img ); 445 446 var countElem = $J( "<span>", { class: "review_award_count", text: "1" } ); 447 reward.append( countElem ); 448 449 reward.data( "reaction", award ); 450 reward.data( "reactioncount", 1 ); 451 452 rewardsCtn.append( reward ); 453 review.find( ".vote_info" ).show(); 454 } 455 } 456 } 457} 458 459function RequestCurrentUserRecommendationVotes( recommendationIDs ) 460{ 461 if ( recommendationIDs.length == 0 ) 462 { 463 return; 464 } 465 466 $J.post( 'https://store.steampowered.com//userreviews/ajaxgetvotes/', { 467 'recommendationids' : recommendationIDs 468 } 469 ).done( function( response ) { 470 if ( response.success == 1 ) 471 { 472 var votes = response.votes; 473 for ( var i = 0; i < votes.length; ++i ) 474 { 475 var vote = votes[i]; 476 if ( vote.voted_up ) 477 { 478 OnRecommendationVotedUp( vote.recommendationid ); 479 } 480 else if ( vote.voted_down ) 481 { 482 OnRecommendationVotedDown( vote.recommendationid ); 483 } 484 if ( vote.voted_funny ) 485 { 486 OnRecommendationVotedTag( vote.recommendationid, 1, true ); 487 } 488 } 489 } 490 } ); 491} 492 493function UserReviewVoteUp( bLoggedIn, strLoginURL, id ) 494{ 495 if ( !bLoggedIn ) 496 { 497 var dialog = ShowConfirmDialog( 'à¸à¹à¸à¸à¸´à¸à¸à¸¥à¸²à¸', 'à¸à¸¸à¸à¸à¹à¸à¸à¹à¸à¹à¸²à¸ªà¸¹à¹à¸£à¸°à¸à¸à¹à¸à¸·à¹à¸à¸à¸£à¸°à¸à¸³à¸à¸²à¸£à¸à¸±à¹à¸', 'à¹à¸à¹à¸²à¸ªà¸¹à¹à¸£à¸°à¸à¸' ); 498 dialog.done( function() { 499 top.location.href = strLoginURL; 500 } ); 501 return; 502 } 503 504 UserReview_Rate( id, true, 'https://store.steampowered.com/', 505 function( rgResults ) { 506 if ( rgResults.nSaleTaskCompleted ) { NewStickerPackModal( 'à¸à¸³à¹à¸à¸£à¸·à¹à¸à¸à¸«à¸¡à¸²à¸¢à¸à¸à¸§à¸´à¸à¸²à¸£à¸à¹à¸§à¹à¸²à¹à¸à¹à¸à¸à¸£à¸°à¹à¸¢à¸à¸à¹ ... หรืà¸à¹à¸¡à¹à¹à¸à¹à¸');} 507 OnRecommendationVotedUp( id ); 508 } 509 ); 510} 511 512function UserReviewVoteDown( bLoggedIn, strLoginURL, id ) 513{ 514 if ( !bLoggedIn ) 515 { 516 var dialog = ShowConfirmDialog( 'à¸à¹à¸à¸à¸´à¸à¸à¸¥à¸²à¸', 'à¸à¸¸à¸à¸à¹à¸à¸à¹à¸à¹à¸²à¸ªà¸¹à¹à¸£à¸°à¸à¸à¹à¸à¸·à¹à¸à¸à¸£à¸°à¸à¸³à¸à¸²à¸£à¸à¸±à¹à¸', 'à¹à¸à¹à¸²à¸ªà¸¹à¹à¸£à¸°à¸à¸' ); 517 dialog.done( function() { 518 top.location.href = strLoginURL; 519 } ); 520 return; 521 } 522 523 UserReview_Rate( id, false, 'https://store.steampowered.com/', 524 function( rgResults ) { 525 if ( rgResults.nSaleTaskCompleted ) { NewStickerPackModal( 'à¸à¸³à¹à¸à¸£à¸·à¹à¸à¸à¸«à¸¡à¸²à¸¢à¸à¸à¸§à¸´à¸à¸²à¸£à¸à¹à¸§à¹à¸²à¹à¸à¹à¸à¸à¸£à¸°à¹à¸¢à¸à¸à¹ ... หรืà¸à¹à¸¡à¹à¹à¸à¹à¸');} 526 OnRecommendationVotedDown( id ); 527 } 528 ); 529} 530 531function UserReviewVoteTag( bLoggedIn, strLoginURL, id, tagID, elemID ) 532{ 533 if ( !bLoggedIn ) 534 { 535 var dialog = ShowConfirmDialog( 'à¸à¹à¸à¸à¸´à¸à¸à¸¥à¸²à¸', 'à¸à¸¸à¸à¸à¹à¸à¸à¹à¸à¹à¸²à¸ªà¸¹à¹à¸£à¸°à¸à¸à¹à¸à¸·à¹à¸à¸à¸£à¸°à¸à¸³à¸à¸²à¸£à¸à¸±à¹à¸', 'à¹à¸à¹à¸²à¸ªà¸¹à¹à¸£à¸°à¸à¸' ); 536 dialog.done( function() { 537 top.location.href = strLoginURL; 538 } ); 539 return; 540 } 541 542 var elem = $J( '#' + elemID ); 543 var bRateUp = !elem.hasClass( 'btn_active' ); 544 UserReview_VoteTag( id, tagID, bRateUp, 'https://store.steampowered.com/', 545 function( rgResults ) { 546 OnRecommendationVotedTag( id, tagID, bRateUp ); 547 } 548 ); 549} 550 551function UserReviewShowMore( id, context ) 552{ 553 $J('#ReviewContent'+context+id).parent().removeClass('partial'); 554 $J('#ReviewContent'+context+id).parent().addClass('expanded'); 555 556 // GamepadUI: Enable links in the review body as focus targets once 557 // the review is expanded. 558 $J('#ReviewContent'+context+id).find( 'div.content a' ).data( 'gpFocusDisabled', false ); 559} 560 561function UpdateDateFilterText( date_filter_text ) 562{ 563 var dateFilterText = $J( "#review_selected_histogram_date_range_text" ); 564 dateFilterText.text( date_filter_text ); 565 dateFilterText.show(); 566} 567 568function LoadMoreReviews( appid, cursor, dayRange, startDate, endDate, context ) 569{ 570 $J( "#ViewAllReviews" + context ).remove(); 571 $J( "#LoadMoreReviews" + context ).remove(); 572 $J( "#LoadingMoreReviews" + context ).show(); 573 574 var container = $J( "#Reviews_" + context ); 575 576 var reviewType = $J('input[name="review_type"]:checked').val(); 577 var purchaseType = $J('input[name="purchase_type"]:checked').val(); 578 var language = $J('input[name="review_language"]:checked').val(); 579 var filterOfftopicActivity = $J( "#reviews_offtopic_activity_checkbox" ).is( ":checked" ) ? 1 : 0; 580 var dateRangeType = $J('input[name="review_date_range"]:checked').val(); 581 var summaryNumPositiveReviews = $J( "#review_summary_num_positive_reviews" ).val(); 582 var summaryNumReviews = $J( "#review_summary_num_reviews" ).val(); 583 var playtimeFilterMin = $J( "#app_reviews_playtime_range_min" ).val(); 584 var playtimeFilterMax = $J( "#app_reviews_playtime_range_max" ).val(); 585 var playtimeType = $J( 'input[name="review_playtime_type"]:checked' ).val(); 586 var topics = []; 587 $J( 'input[name="review_topic"]:checked' ).each( ( index, elem ) => 588 { 589 topics.push( $J( elem ).val() ); 590 }); 591 // hardware 592 let hardwareOS = $J( 'input[name="review_hardware_os"]:checked' ).val(); 593 let hardwareCPU = $J( 'input[name="review_hardware_cpu_name"]:checked' ).val(); 594 let hardwareGPU = $J( 'input[name="review_hardware_gfx_adapter_description"]:checked' ).val(); 595 let hardwareDeviceType = $J( 'input[name="review_hardware_gaming_device_type"]:checked' ).val(); 596 597 var filteredReviewScore = $J( "#user_reviews_filter_score" ); 598 filteredReviewScore.removeClass( "visible" ); 599 600 $J.get( 'https://store.steampowered.com/appreviews/' + appid,{ 601 'use_review_quality': $J('input[name="use_review_quality"]').is( ":checked" ) ? 1 : 0, 602 'cursor' : cursor, 603 'day_range' : dayRange, 604 'start_date' : startDate, 605 'end_date' : endDate, 606 'date_range_type' : dateRangeType, 607 'filter' : context, 608 'language' : language, 609 'l' : 'thai',
610 'review_type' : reviewType, 611 'purchase_type' : purchaseType, 612 'playtime_filter_min' : playtimeFilterMin, 613 'playtime_filter_max' : playtimeFilterMax, 614 'playtime_type' : playtimeType, 615 'topics' : topics, 616 'filter_offtopic_activity' : filterOfftopicActivity, 617 'summary_num_positive_reviews' : summaryNumPositiveReviews, 618 'summary_num_reviews' : summaryNumReviews, 619 'hardware_os' : hardwareOS, 620 'hardware_cpu' : hardwareCPU, 621 'hardware_gpu' : hardwareGPU, 622 'hardware_device_type' : hardwareDeviceType, 623 }).done( function( data ) { 624 625 RecordAJAXPageView( this.url ); 626 627 if ( data.success == 1 ) 628 { 629 $J( "#Reviews_loading" ).hide(); 630 $J( "#Reviews_" + context ).show(); 631 632 if ( cursor == "" || cursor == "*" ) 633 { 634 var filteredReviewScore = $J( "#user_reviews_filter_score" ); 635 if ( data.review_score ) 636 { 637 filteredReviewScore.addClass( "visible" ); 638 filteredReviewScore.html( data.review_score ); 639 } 640 else 641 { 642 filteredReviewScore.removeClass( "visible" ); 643 } 644 } 645 $J( "#LoadingMoreReviews" + context ).remove(); 646 647 // remove duplicates 648 var recommendationIDs = []; 649 var temp = $J('<div></div>'); 650 temp.append( data.html ); 651 for ( var i = 0; i < data.recommendationids.length; ++i ) 652 { 653 var recommendationid = data.recommendationids[i]; 654 var elemID = "#ReviewContent" + context + recommendationid; 655 if ( $J( elemID ).length != 0 ) 656 { 657 temp.find( elemID ).parent().remove(); 658 } 659 else 660 { 661 recommendationIDs.push( recommendationid ); 662 } 663 } 664 665 if ( data.date_filter_text ) 666 { 667 UpdateDateFilterText( data.date_filter_text ); 668 } 669 670 // all dupes, request more 671 if ( data.recommendationids.length != 0 && recommendationIDs.length == 0 && data.cursor != '*' ) 672 { 673 LoadMoreReviews( appid, data.cursor, data.dayrange, data.start_date, data.end_date, context ); 674 } 675 else if ( recommendationIDs.length != 0 ) 676 { 677 container.append( temp.children() ); 678 CollapseLongReviews(); 679 RequestCurrentUserRecommendationVotes( recommendationIDs ); 680 } 681 682 // if customer is on a mobile size screen make content changes in the reviews section 683 ReparentReviewsForSmallScreens(); 684 } 685 else 686 { 687 ShowAlertDialog( 'à¸à¹à¸à¸à¸´à¸à¸à¸¥à¸²à¸', 'à¸à¸£à¸§à¸à¸à¸à¸à¹à¸à¸à¸´à¸à¸à¸¥à¸²à¸à¹à¸à¸à¸²à¸£à¸à¸¢à¸²à¸¢à¸²à¸¡à¸à¸£à¸°à¸¡à¸§à¸¥à¸à¸¥à¸à¸³à¸£à¹à¸à¸à¸à¸à¸à¸à¸¸à¸: ' + data.error ); 688 $J( "#Reviews_" + context ).show(); 689 $J( "#LoadingMoreReviews" + context ).remove(); 690 } 691 } ); 692} 693 694function SelectReviews( appid, context, reviewDayRange, startDate, endDate, forceClear ) 695{ 696 $J( "#ReviewsTab_summary" ).removeClass( "active" ); 697 $J( "#ReviewsTab_all" ).removeClass( "active" ); 698 $J( "#ReviewsTab_recent" ).removeClass( "active" ); 699 $J( "#ReviewsTab_positive" ).removeClass( "active" ); 700 $J( "#ReviewsTab_negative" ).removeClass( "active" ); 701 $J( "#ReviewsTab_funny" ).removeClass( "active" ); 702 $J( "#ReviewsTab_" + context ).addClass( "active" ); 703 704 $J( "#Reviews_summary" ).hide(); 705 $J( "#Reviews_all" ).hide(); 706 $J( "#Reviews_recent" ).hide(); 707 $J( "#Reviews_positive" ).hide(); 708 $J( "#Reviews_negative" ).hide(); 709 $J( "#Reviews_funny" ).hide(); 710 $J( "#Reviews_loading" ).show(); 711 712 var container = $J( "#Reviews_" + context ); 713 if ( forceClear ) 714 { 715 container.empty() 716 } 717 if ( container.children().length == 0 ) 718 { 719 LoadMoreReviews( appid, "*", reviewDayRange, startDate, endDate, context ); 720 } 721} 722 723function FilterReviewsToGraph( bCountAllReviews, startDate, endDate ) 724{ 725 if ( bCountAllReviews ) 726 { 727 $J('#purchase_type_all').attr( 'checked', true ); 728 } 729 else 730 { 731 $J('#purchase_type_steam').attr( 'checked', !bCountAllReviews ); 732 } 733 734 $J('#review_start_date').val( startDate ); 735 $J('#review_end_date').val( endDate ); 736 if ( !$J('#review_date_range_histogram').attr( 'checked' ) && !$J('#review_date_range_exclude_histogram').attr( 'checked') ) 737 { 738 $J('#review_date_range_histogram').attr('checked', true); 739 } 740 $J('#review_date_range_histogram').attr( 'disabled', false ); 741 $J('#review_date_range_exclude_histogram').attr( 'disabled', false ); 742 ShowFilteredReviews(); 743} 744 745function FilterReviewsGraph( bCountAllReviews, startDate, endDate, bExclude ) 746{ 747 if ( bCountAllReviews ) 748 { 749 $J('#purchase_type_all').attr( 'checked', true ); 750 } 751 else 752 { 753 $J('#purchase_type_steam').attr( 'checked', !bCountAllReviews ); 754 } 755 756 $J('#review_start_date').val( startDate ); 757 $J('#review_end_date').val( endDate ); 758 759 $J('#review_date_range_histogram').attr( 'disabled', false ); 760 $J('#review_date_range_exclude_histogram').attr( 'disabled', false ); 761 762 if ( bExclude ) 763 { 764 $J('#review_date_range_exclude_histogram').attr( 'checked', true ); 765 } 766 else 767 { 768 $J('#review_date_range_histogram').attr( 'checked', true ); 769 } 770 771 ShowFilteredReviews(); 772} 773 774function ClearReviewTypeFilter() 775{ 776 $J('#review_type_all').attr( 'checked', true ); 777 ShowFilteredReviews(); 778} 779 780function ClearReviewPurchaseTypeFilter() 781{ 782 $J('#purchase_type_all').attr( 'checked', true ); 783 ShowFilteredReviews(); 784} 785 786function ClearReviewLanguageFilter() 787{ 788 $J('#review_language_all').attr( 'checked', true ); 789 ShowFilteredReviews(); 790} 791 792function ClearReviewDateRangeFilter() 793{ 794 $J('#review_date_range_all').attr( 'checked', true ); 795 ClearReviewDateFilter(); 796} 797 798function ClearHardwareFilter( category ) 799{ 800 $J( 'input[name="review_' + category + '"][value="all"]' ).prop('checked', true); 801 ShowFilteredReviews(); 802} 803 804function EditUserReviewScorePreference() 805{ 806 if ( g_AccountID == 0 ) 807 { 808 ShowAlertDialog( 'à¸à¹à¸à¸à¸´à¸à¸à¸¥à¸²à¸', 'à¸à¸¸à¸à¸à¹à¸à¸à¹à¸à¹à¸²à¸ªà¸¹à¹à¸£à¸°à¸à¸à¹à¸à¸·à¹à¸à¸à¸£à¸°à¸à¸³à¸à¸²à¸£à¸à¸±à¹à¸' ); 809 return; 810 } 811 812 $J.post( 813 'https://store.steampowered.com/account/edituserreviewscorepreference/', { sessionid: g_sessionID } 814 ).done( function( response ) { 815 var dialogContent = $J( response.html ); 816 var dialog = ShowAlertDialog( 'à¸à¸²à¸£à¸à¸±à¹à¸à¸à¹à¸²à¸à¸°à¹à¸à¸à¸à¸à¸§à¸´à¸à¸²à¸£à¸à¹', dialogContent, 'ยà¸à¹à¸¥à¸´à¸' ); 817 }); 818} 819 820function SetUserReviewScorePreference( pref ) 821{ 822 if ( g_AccountID == 0 ) 823 { 824 ShowAlertDialog( 'à¸à¹à¸à¸à¸´à¸à¸à¸¥à¸²à¸', 'à¸à¸¸à¸à¸à¹à¸à¸à¹à¸à¹à¸²à¸ªà¸¹à¹à¸£à¸°à¸à¸à¹à¸à¸·à¹à¸à¸à¸£à¸°à¸à¸³à¸à¸²à¸£à¸à¸±à¹à¸' ); 825 return; 826 } 827 828 var rgData = { 829 preference: pref, 830 sessionid : g_sessionID 831 }; 832 $J.post( 'https://store.steampowered.com/account/saveuserreviewscorepreference', rgData ).done( 833 function( json ) 834 { 835 var h = window.location.href.substr( 0, window.location.href.indexOf('#') ); 836 window.location.href = h + '#app_reviews_hash'; 837 window.location.reload( true ); 838 } 839 ).fail( 840 function( json ) 841 { 842 ShowAlertDialog( "\u0e02\u0e49\u0e2d\u0e1c\u0e34\u0e14\u0e1e\u0e25\u0e32\u0e14", "\u0e01\u0e32\u0e23\u0e1b\u0e23\u0e3
8421\u0e1a\u0e41\u0e15\u0e48\u0e07\u0e02\u0e2d\u0e07\u0e04\u0e38\u0e13\u0e44\u0e21\u0e48\u0e44\u0e14\u0e49\u0e23\u0e31\u0e1a\u0e01\u0e32\u0e23\u0e1a\u0e31\u0e19\u0e17\u0e36\u0e01 \u0e42\u0e1b\u0e23\u0e14\u0e25\u0e2d\u0e07\u0e43\u0e2b\u0e21\u0e48\u0e2d\u0e35\u0e01\u0e04\u0e23\u0e31\u0e49\u0e07\u0e20\u0e32\u0e22\u0e2b\u0e25\u0e31\u0e07" ); 843 } 844 ); 845} 846 847function IntervalDistance( min1, max1, min2, max2 ) 848{ 849 return Math.max( 0, Math.max( min2 - max1, min1 - max2 ) ); 850} 851 852function DrawPastEvents( flotRollup, rgPastEvents ) 853{ 854 var ctx = flotRollup.getCanvas().getContext("2d"); 855 var rollupPoints = flotRollup.getData(); 856 var seriesPositive = rollupPoints[0]; 857 var axes = flotRollup.getAxes(); 858 var dateOptions = { day: 'numeric', month: 'long', year: 'numeric', timeZone: "UTC" }; 859 860 for ( var i = 0; i < rgPastEvents.length; ++i ) 861 { 862 var event = rgPastEvents[i]; 863 var midDate = ( event.start_date + ( event.end_date - event.start_date ) / 2 ) * 1000; 864 865 var o = flotRollup.pointOffset( { x: midDate, y: axes.yaxis.max } ); 866 867 var topY = seriesPositive.yaxis.p2c( event.recommendations_up ); 868 869 var eventIconID = flotRollup.getPlaceholder()[0].id + "_review_anomaly_" + event.start_date; 870 var eventIcon = $J( "#" + eventIconID ); 871 var newStyle = { 872 left: (o.left - 6) + 'px', 873 top: (o.top - 10) + 'px' 874 }; 875 876 if ( eventIcon.length == 0 ) 877 { 878 eventIcon = $J( '<div/>', { id: eventIconID, class: 'review_event_graph_icon', style: JSON.stringify( newStyle ), html: '*' } ); 879 eventIcon.data( 'startDate', event.start_date ); 880 eventIcon.data( 'endDate', event.end_date ); 881 var startDate = new Date( event.start_date * 1000 ); 882 var endDate = new Date( event.end_date * 1000 ); 883 var strToolTip = 'à¸à¸£à¸§à¸à¸à¸à¸à¸´à¸à¸à¸£à¸£à¸¡à¸à¸à¸§à¸´à¸à¸²à¸£à¸à¹à¸à¸à¸à¹à¸£à¸·à¹à¸à¸: <br><br>' + startDate.toLocaleDateString( undefined, dateOptions ) + " - " + endDate.toLocaleDateString( undefined, dateOptions ) + '<br><br>à¸à¸¥à¸´à¸à¸ªà¸³à¸«à¸£à¸±à¸à¸£à¸²à¸¢à¸¥à¸°à¹à¸à¸µà¸¢à¸à¹à¸à¸´à¹à¸¡à¹à¸à¸´à¸¡'; 884 eventIcon.data( 'tooltipContent', strToolTip ); 885 eventIcon.v_tooltip(); 886 flotRollup.getPlaceholder().append( eventIcon ); 887 } 888 else 889 { 890 eventIcon.css( newStyle ); 891 } 892 893 o.top += 10; 894 895 ctx.beginPath(); 896 ctx.moveTo(o.left-1, o.top); 897 ctx.lineTo(o.left, topY); 898 ctx.lineTo(o.left+1, o.top); 899 ctx.lineTo(o.left-1, o.top); 900 ctx.fillStyle = "#fff"; 901 ctx.fill(); 902 } 903} 904 905function AddOrUpdatePastEvent( rgPastEvents, event ) 906{ 907 for ( var i = 0; i < rgPastEvents.length; ++i ) 908 { 909 if ( rgPastEvents[i].start_date == event.start_date ) 910 { 911 rgPastEvents[i].recommendations_up = Math.max( rgPastEvents[i].recommendations_up, event.recommendations_up ); 912 return; 913 } 914 } 915 916 rgPastEvents.push( { start_date: event.start_date, end_date: event.end_date, recommendations_up: event.recommendations_up } ); 917} 918 919function BuildReviewHistogram() 920{ 921 if( $J( "#review_histograms_container" ).length == 0 ) 922 return; 923 924 var appid = $J( "#review_appid" ).val(); 925 926 var review_score_preference = 0; 927 if ( typeof GDynamicStore != 'undefined' ) 928 { 929 review_score_preference = ( !GDynamicStore.s_preferences['review_score_preference'] ? 0 : GDynamicStore.s_preferences['review_score_preference'] ); 930 } 931 932 $J.get( 'https://store.steampowered.com/appreviewhistogram/' + appid, { l: 'thai', review_score_preference: review_score_preference } 933 ).done( function( data ) { 934 935 $J( "#review_histograms_container" ).addClass( "has_data" ); 936 937 // remove so we can draw to the canvas 938 $J( "#review_histograms_container" ).removeClass( "collapsed" ); 939 $J( "#reviews_filter_options" ).removeClass( "graph_collapsed" ); 940 941 var bCountAllReviews = data.count_all_reviews; 942 var bExpandGraph = data.expand_graph; 943 var numTotalDays = ( data.results.end_date - data.results.start_date ) / 86400; 944 var numReviewsRecent = 0; 945 for ( var i = 0; i < data.results.recent.length; ++i ) 946 { 947 var recentDay = data.results.recent[i]; 948 numReviewsRecent += recentDay.recommendations_up + recentDay.recommendations_down; 949 } 950 951 if ( numTotalDays < 7 ) 952 { 953 $J( "#app_reviews_hash" ).addClass( "graph_hidden" ); 954 $J( "#review_histogram_rollup_container" ).hide(); 955 $J( "#review_histogram_rollup_section" ).addClass( "recent" ); 956 $J( "#review_histogram_rollup_section" ).addClass( "hidden" ); 957 $J( "#review_histogram_recent_section" ).hide(); 958 return; 959 } 960 else if ( numReviewsRecent == 0 ) 961 { 962 data.results.recent = null;
963 $J( "#review_histogram_rollup_section" ).addClass( "recent" ); 964 $J( "#review_histogram_recent_section" ).hide(); 965 } 966 else if ( numTotalDays < 30 ) 967 { 968 $J( "#review_histogram_rollup_section" ).addClass( "recent" ); 969 $J( "#review_histogram_recent_section" ).hide(); 970 971 data.results.rollups = data.results.recent; 972 data.results.recent = null; 973 data.results.rollup_type = 'day'; 974 } 975 976 var maxPositive = 0; 977 var chartDataPositive = []; 978 var chartDataNegative = []; 979 var chartDataAll = []; 980 981 var chartDataPositiveEvent = []; 982 var chartDataNegativeEvent = []; 983 var chartDataAllEvent = []; 984 var rgPastEventsRollup = []; 985 var rgPastEventsRecent = []; 986 987 for ( var i = 0; i < data.results.rollups.length; ++i ) 988 { 989 var rollup = data.results.rollups[i]; 990 maxPositive = Math.max( rollup.recommendations_up, maxPositive ); 991 var barDataUp = [ rollup.date * 1000, rollup.recommendations_up ]; 992 var barDataDown = [ rollup.date * 1000, -rollup.recommendations_down ]; 993 chartDataPositive.push( barDataUp ); 994 chartDataNegative.push( barDataDown ); 995 chartDataAll.push( [ rollup.recommendations_up, rollup.recommendations_down ] ); 996 997 if ( data.past_events && data.past_events.length != 0 ) 998 { 999 for ( var j = 0; j < data.past_events.length; ++j ) 1000 { 1001 var event = data.past_events[j]; 1002 var rollupDateEnd = new Date( rollup.date * 1000 ); 1003 if ( data.results.rollup_type == 'week' ) 1004 { 1005 rollupDateEnd = new Date( ( rollup.date + 86400*7 - 1 ) * 1000 ); 1006 } 1007 else if ( data.results.rollup_type == 'month' ) 1008 { 1009 rollupDateEnd.setMonth( rollupDateEnd.getMonth() + 1 ); 1010 } 1011 if ( IntervalDistance( event.start_date, event.end_date, rollup.date, rollupDateEnd.getTime() / 1000 ) == 0 ) 1012 { 1013 chartDataPositiveEvent.push( barDataUp ); 1014 chartDataNegativeEvent.push( barDataDown ); 1015 chartDataAllEvent.push( [ rollup.recommendations_up, rollup.recommendations_down ] ); 1016 1017 AddOrUpdatePastEvent( rgPastEventsRollup, { start_date: event.start_date, end_date : event.end_date, recommendations_up: rollup.recommendations_up } ); 1018 break; 1019 } 1020 } 1021 } 1022 } 1023 var seriesRollup = [ 1024 { label: "à¹à¸à¹à¸à¸§à¸", color: "#66c0f4", fillColor: "#66c0f4", data: chartDataPositive, dataAll: chartDataAll }, 1025 { label: "à¹à¸à¹à¸¥à¸", color: "#A34C25", fillColor: "#A34C25", data: chartDataNegative, dataAll: chartDataAll }, 1026 { label: "à¹à¸à¹à¸à¸§à¸", color: "#FFFFFF", fillColor: "#FFFFFF", data: chartDataPositiveEvent, dataAll: chartDataAllEvent }, 1027 { label: "à¹à¸à¹à¸¥à¸", color: "#FFFFFF", fillColor: "#FFFFFF", data: chartDataNegativeEvent, dataAll: chartDataAllEvent }, 1028 ]; 1029 1030 var seriesRecent = null; 1031 if ( data.results.recent ) 1032 { 1033 chartDataPositive = []; 1034 chartDataNegative = []; 1035 chartDataAll = [] 1036 chartDataPositiveEvent = []; 1037 chartDataNegativeEvent = []; 1038 chartDataAllEvent = [] 1039 for ( var i = 0; i < data.results.recent.length; ++i ) 1040 { 1041 var recentDay = data.results.recent[i]; 1042 var barDataUp = [ recentDay.date * 1000, recentDay.recommendations_up ]; 1043 var barDataDown = [ recentDay.date * 1000, -recentDay.recommendations_down ]; 1044 chartDataPositive.push( barDataUp ); 1045 chartDataNegative.push( barDataDown ); 1046 chartDataAll.push( [ recentDay.recommendations_up, recentDay.recommendations_down ] ); 1047 if ( data.past_events && data.past_events.length != 0 ) 1048 { 1049 for ( var j = 0; j < data.past_events.length; ++j ) 1050 { 1051 var event = data.past_events[j]; 1052 if ( IntervalDistance( event.start_date, event.end_date, recentDay.date, recentDay.date + 86400 - 1 ) == 0 ) 1053 { 1054 chartDataPositiveEvent.push( barDataUp ); 1055 chartDataNegativeEvent.push( barDataDown ); 1056 chartDataAllEvent.push( [ recentDay.recommendations_up, recentDay.recommendations_down ] ); 1057 1058 AddOrUpdatePastEvent( rgPastEventsRecent, { start_date: event.start_date, end_date : event.end_date, recommendations_up: recentDay.recommendations_up } ); 1059 break; 1060 } 1061 } 1062 } 1063 } 1064 seriesRecent = [ 1065 { color: "#66c0f4", label: "à¹à¸à¹à¸à¸§à¸", data: chartDataPositive, dataAll: chartDataAll }, 1066 { color: "#A34C25", label: "à¹à¸à¹à¸¥à¸", data: chartDataNegative, dataAll: chartDataAll }, 1067 { label: "à¹à¸à¹à¸à¸§à¸", color: "#FFFFFF", fillColor: "#FFFFFF", data: chartDataPositiveEvent, dataAll: chartDataAllEvent }, 1068 { label: "à¹à¸à¹à¸¥à¸", color: "#FFFFFF", fillColor: "#FFFFFF", data: chartDataNegativeEvent, dataAll: chartDataAllEvent }, 1069 ]; 1070 } 1071 1072 var options = { 1073 series: { 1074 stack: 0, 1075 bars: { 1076 show: 1, 1077 fill: 1, 1078 lineWidth: 0, 1079 barWidth: 86400*1000 * 0.5,
1080 align: "left" 1081 }, 1082 lines: { 1083 show: 0 1084 }, 1085 highlightColor: 'rgb(255,255,255)' 1086 }, 1087 legend: { 1088 show: 0 1089 }, 1090 xaxis: { 1091 mode: "time", 1092 timeformat: "%b %d", 1093 tickLength: 0 1094 }, 1095 yaxis: { 1096 tickFormatter : function( val, axis ) { 1097 return ( val < 0 ) ? -val : val; 1098 }, 1099 tickLength: 0, 1100 tickDecimals: 0, 1101 autoscaleMargin: 0, 1102 }, 1103 grid: { 1104 hoverable: true, 1105 clickable: true, 1106 borderWidth: 0, 1107 margin: 0, 1108 mouseActiveRadius: 10, 1109 autoHighlight: true, 1110 markings: [ { yaxis: { from: 0, to: 0 }, color: "#4582A5" } ] 1111 }, 1112 selection: { 1113 mode: "x", 1114 color: "#ffffff", 1115 } 1116 }; 1117 1118 // week/month rollup 1119 var rollupOptions = $J.extend( true, {}, options ); 1120 if ( data.results.rollup_type == 'week' ) 1121 { 1122 if ( numTotalDays > 365 / 2 ) 1123 { 1124 rollupOptions.xaxis.timeformat = "%b"; 1125 } 1126 else 1127 { 1128 rollupOptions.xaxis.timeformat = "%b %d"; 1129 } 1130 rollupOptions.series.bars.barWidth = 86400*1000 * 7 * 0.5; 1131 } 1132 else if ( data.results.rollup_type == 'month' ) 1133 { 1134 rollupOptions.xaxis.timeformat = numTotalDays > 365 ? "%b %Y": "%b"; 1135 rollupOptions.series.bars.barWidth = 86400*1000 * 30 * 0.5; 1136 } 1137 1138 // leave room for the asterisk 1139 if ( rgPastEventsRollup.length != 0 ) 1140 { 1141 rollupOptions.yaxis.max = maxPositive * 1.1; 1142 } 1143 1144 var graphRollup = $J( "#review_histogram_rollup" ); 1145 var flotRollup = $J.plot( graphRollup, seriesRollup, rollupOptions ); 1146 1147 if ( rgPastEventsRollup.length != 0 ) 1148 { 1149 var funcDrawPastEvents = function() { 1150 DrawPastEvents( flotRollup, rgPastEventsRollup ); 1151 }; 1152 flotRollup.hooks.draw.push( funcDrawPastEvents ); 1153 funcDrawPastEvents(); 1154 1155 $J("#review_graph_canvas").resize( funcDrawPastEvents ); 1156 } 1157 1158 // recent 1159 var graphRecent = null; 1160 if ( seriesRecent ) 1161 { 1162 var recentOptions = $J.extend( true, {}, options ); 1163 recentOptions.series.bars.barWidth = 86400*1000 * 0.5; 1164 recentOptions.xaxis.timeformat = "%b %d"; 1165 recentOptions.yaxis.autoscaleMargin = rgPastEventsRecent.length != 0 ? 0.2 : null; 1166 var graphRecent = $J( "#review_histogram_recent" ); 1167 var flotRecent = $J.plot( graphRecent, seriesRecent, recentOptions ); 1168 1169 var recentGraphStartDate = data.results.recent[0].date * 1000; 1170 1171 var rollupPoints = flotRollup.getData(); 1172 var seriesPositive = rollupPoints[0]; 1173 { 1174 var startX = seriesPositive.xaxis.p2c( recentGraphStartDate ); 1175 var endPoint = seriesPositive.data[seriesPositive.data.length-1]; 1176 var endX = seriesPositive.xaxis.p2c( endPoint[0] + rollupOptions.series.bars.barWidth ); 1177 var highlightWidth = endX - startX; 1178 1179 // get min/max of the yaxis 1180 var axes = flotRollup.getAxes(); 1181 var startY = seriesPositive.yaxis.p2c( axes.yaxis.max ); 1182 var endY = seriesPositive.yaxis.p2c( axes.yaxis.min ); 1183 var highlightHeight = endY - startY; 1184 1185 var funcDrawGraphOverlay = function() { 1186 1187 var c = document.getElementById("review_graph_canvas"); 1188 var ctx = c.getContext("2d"); 1189 // resize the canvas to the same size as the element, so our drawing doesn't look blurry 1190 c.width = $J(c).width(); 1191 c.height = $J(c).height(); 1192 ctx.clearRect(0, 0, c.width, c.height); 1193 // these should be 1-to-1 now, but for correctness, we need to 1194 var scaleX = c.width / c.offsetWidth; 1195 var scaleY = c.height / c.offsetHeight; 1196 1197 // draw rect on the graph 1198 ctx.fillStyle = 'rgba(148,217,255,0.2)'; 1199 var offsets = flotRollup.getPlotOffset(); 1200 var offsetLeft = ( offsets.left + startX ) + $J(flotRollup.getCanvas()).offsetParent().position().left; 1201 var offsetTop = ( offsets.top + startY ) + $J(flotRollup.getCanvas()).offsetParent().position().top; 1202 ctx.fillRect(offsetLeft * scaleX, offsetTop * scaleY, highlightWidth, highlightHeight); 1203 1204 // now draw the "pop-out" on our other canvas 1205 offsetLeft = ( offsets.left + endX ) + $J(flotRollup.getCanvas()).offsetParent().position().left; 1206 offsetTop = ( offsets.top + startY ) + $J(flotRollup.getCanvas()).offsetParent().position().top; 1207 var offsetBottom = ( offsets.top + endY ) + $J(flotRollup.getCanvas()).offsetParent().position().top; 1208 var recentSection = $J("#review_histogram_recent_section"); 1209 1210 ctx.fillStyle = 'rgba(33,44,61,1)'; 1211 ctx.beginPath(); 1212 ctx.moveTo(offsetLeft * scaleX, offsetTop * scaleY); 1213 ctx.lineTo(recentSection.position().left * scaleX, recentSection.position().top * scaleY); 1214 ctx.lineTo(recentSection.position().left * scaleX, ( recentSection.position().top + recentSection.height() ) * scaleY); 1215 ctx.lineTo(offsetLeft * scaleX, offsetBottom * scaleY); 1216 ctx.lineTo(offsetLeft * scaleX, offsetTop * scaleY); 1217 ctx.fill(); 1218 }; 1219 1220 var funcDrawPastEvents = function() { 1221 DrawPastEvents( flotRecent, rgPastEventsRecent ); 1222 }; 1223 flotRecent.hooks.draw.push( funcDrawPastEvents ); 1224 funcDrawPastEvents(); 1225 1226 $J("#review_graph_canvas").resize( funcDrawGraphOverlay ); 1227 $J("#review_graph_canvas").resize( funcDrawPastEvents ); 1228 funcDrawGraphOverlay(); 1229 } 1230 } 1231 1232 // tooltip 1233 $J( 1234 `<div id='review_histogram_tooltip'> 1235 <div class='date'></div> 1236 <div class='positive'><span></span> à¹à¸à¹à¸à¸§à¸ (<span class='percentage'></span>%)</div> 1237 <div class='negative'><span></span>
1237à¹à¸à¹à¸¥à¸</div> 1238 </div>` 1239 ).appendTo("body"); 1240 var funcTooltip = function (event, pos, item) { 1241 var tooltip = $J("#review_histogram_tooltip"); 1242 if ( item ) 1243 { 1244 var idx = item.dataIndex; 1245 var reviewData = item.series.dataAll[idx]; 1246 var numPositiveReviews = reviewData[0]; 1247 var numNegativeReviews = reviewData[1]; 1248 var percentage = ( 100.0 * numPositiveReviews ) / Math.max( 1, numPositiveReviews + numNegativeReviews ); 1249 var x = item.datapoint[0].toFixed(2); 1250 var y = item.datapoint[1].toFixed(2); 1251 var bNegativeReviews = y < 0; 1252 var yDelta = bNegativeReviews ? 10 : ( -20 - tooltip.height() ); 1253 var xDelta = 5; 1254 1255 var date = new Date( parseInt(x) ); 1256 1257 var dateOptions = { day: 'numeric', month: 'long', year: 'numeric', timeZone: "UTC" }; 1258 var strDate = date.toLocaleDateString( undefined, dateOptions ); 1259 tooltip.find( ".positive span:first-child" ).html( v_numberformat( numPositiveReviews ) ); 1260 tooltip.find( ".negative span:first-child" ).html( v_numberformat( numNegativeReviews ) ); 1261 tooltip.find( ".percentage" ).html( percentage.toFixed( 0 ) ); 1262 tooltip.find( ".date" ).html( strDate ); 1263 tooltip.css( {top: item.pageY+yDelta, left: item.pageX+xDelta} ); 1264 tooltip.fadeIn( 10 ); 1265 } 1266 else 1267 { 1268 tooltip.hide(); 1269 } 1270 }; 1271 1272 // click 1273 var funcClick = function( plot, rollupType ) { 1274 return function (event, pos, item) { 1275 // ignore for selection 1276 if ( plot.getSelection() ) 1277 { 1278 return; 1279 } 1280 1281 if ( item ) 1282 { 1283 var x = item.datapoint[0].toFixed(2); 1284 var y = item.datapoint[1].toFixed(2); 1285 var numReviews = parseInt( y ); 1286 var bNegativeReviews = numReviews < 0; 1287 1288 var date = new Date( parseInt(x) ); 1289 var startDate = date.getTime() / 1000; 1290 var endDate = startDate + 86400; 1291 if ( rollupType == 'week' ) 1292 { 1293 endDate = startDate + 86400 * 7; 1294 } 1295 else if ( rollupType == 'month' ) 1296 { 1297 var lastDayOfMonth = new Date( date.getUTCFullYear(), date.getUTCMonth() + 1, 0 ); 1298 endDate = lastDayOfMonth.getTime() / 1000; 1299 } 1300 1301 $J( bNegativeReviews ? '#review_type_negative' : '#review_type_positive').attr( 'checked', true ); 1302 FilterReviewsToGraph( bCountAllReviews, startDate, endDate ); 1303 1304 flotRollup.clearSelection(); 1305 if ( flotRecent ) 1306 { 1307 flotRecent.clearSelection(); 1308 } 1309 } 1310 } 1311 }; 1312 1313 var funcSelected = function( plot ) { 1314 return function (event, ranges) { 1315 1316 var startDate = Math.floor( ranges.xaxis.from.toFixed(1) / ( 86400 * 1000 ) ) * 86400; 1317 var endDate = Math.ceil( ranges.xaxis.to.toFixed(1) / ( 86400 * 1000 ) ) * 86400; 1318 1319 $J( '#review_type_all' ).attr( 'checked', true ); 1320 FilterReviewsToGraph( bCountAllReviews, startDate, endDate ); 1321 if ( plot == flotRollup && flotRecent ) 1322 { 1323 flotRecent.clearSelection(); 1324 } 1325 else if ( plot == flotRecent ) 1326 { 1327 flotRollup.clearSelection(); 1328 } 1329 }; 1330 }; 1331 1332 var funcUnSelected = function ( event ) { 1333 1334 }; 1335 1336 graphRollup.bind("plothover", funcTooltip); 1337 graphRollup.bind("plotclick", funcClick( flotRollup, data.results.rollup_type ) ); 1338 graphRollup.bind("plotselected", funcSelected( flotRollup )); 1339 graphRollup.bind("plotunselected", funcUnSelected ); 1340 if ( graphRecent ) 1341 { 1342 graphRecent.bind("plothover", funcTooltip); 1343 graphRecent.bind("plotclick", funcClick( flotRecent, 'day' ) ); 1344 graphRecent.bind("plotselected", funcSelected( flotRecent ) ); 1345 graphRecent.bind("plotunselected", funcUnSelected ); 1346 } 1347 1348 var funcViewReviewsDuringEvent = function( bCountAllReviews, startDate, endDate ) { 1349 $J( '#review_type_all' ).attr( 'checked', true ); 1350 $J('#reviews_offtopic_activity_checkbox').attr( 'checked', false ); 1351 FilterReviewsGraph( bCountAllReviews, startDate, endDate, false ); 1352 if ( flotRecent ) 1353 { 1354 flotRecent.clearSelection(); 1355 } 1356 flotRollup.clearSelection(); 1357 }; 1358 1359 var funcViewReviewsExcludingEvent = function( bCountAllReviews, startDate, endDate ) { 1360 $J( '#review_type_all' ).attr( 'checked', true ); 1361 $J('#reviews_offtopic_activity_checkbox').attr( 'checked', false ); 1362 FilterReviewsGraph( bCountAllReviews, startDate, endDate, true ); 1363 if ( flotRecent ) 1364 { 1365 flotRecent.clearSelection(); 1366 } 1367 flotRollup.clearSelection(); 1368 }; 1369 1370 // recent events 1371 if ( bExpandGraph ) 1372 { 1373 var container = $J( "#review_recent_events_container" ); 1374 container.show(); 1375 1376 flotRollup.draw(); 1377 if ( flotRecent ) 1378 { 1379 flotRecent.draw(); 1380 } 1381 } 1382 else 1383 { 1384 $J( "#review_histograms_container" ).addClass( "collapsed" ); 1385 $J( "#reviews_filter_options" ).addClass( "graph_collapsed" ); 1386 } 1387 1388 $J( ".review_event_graph_icon" ).each( function( index, value ) { 1389 var $elem = $J( value ); 1390 $elem.click( function() { 1391 var startDate = $J( this ).data( 'startDate' ); 1392 var endDate = $J( this ).data( 'endDate' ); 1393 1394 var startDateObj = new Date( startDate * 1000 ); 1395 var endDateObj = new Date( endDate * 1000 ); 1396 var dateOptions = { day: 'numeric', month: 'long', year: 'numeric', timeZone: "UTC" }; 1397 var strDateRange = startDateObj.toLocaleDateString( undefined, dateOptions ) + " - " + endDateObj.toLocaleDateString( undefined, dateOptions ); 1398 1399 var dialogContent = $J( "<div/>", { class: "review_anomalous_events_dialog" } ); 1400 dialogContent.append( $J( "<div/>", { class: 'review_anomalous_events_icon', html: '*' } ) ); 1401 dialogContent.append( $J( "<div/>", { class: 'review_anomalous_events_title', html: strDateRange } ) ); 1402 dialogContent.append( $J( "<div/>", { style: "clear: left" } ) ); 1403 dialogContent.append( $J( "<div/>", { html: '<p>à¸à¹à¸§à¸à¹à¸§à¸¥à¸²à¸à¸µà¹à¸à¸¹à¸à¸à¸³à¹à¸à¸£à¸·à¹à¸à¸à¸«à¸¡à¸²à¸¢à¸§à¹à¸² มีà¸à¸¸à¸à¸à¸à¸§à¸´à¸à¸²à¸£à¸à¹à¸à¸µà¹à¹à¸¡à¹à¸à¸à¸à¸´ à¸à¸¶à¹à¸à¹à¸£à¸²à¹à¸à¸·à¹à¸à¸§à¹à¸²à¸ªà¹à¸§à¸à¹à¸«à¸à¹à¹à¸¡à¹à¹à¸à¸µà¹à¸¢à¸§à¸à¹à¸à¸à¸à¸±à¸à¹à¸à¸·à¹à¸à¸«à¸²à¸à¸µà¹à¸à¸¸à¸à¸à¹à¸à¸à¸à¸²à¸£à¸£à¸±à¸à¸à¸²à¸à¸à¸¥à¸´à¸à¸ ัà¸à¸à¹à¸à¸±à¹à¸ à¸à¸à¸§à¸´à¸à¸²à¸£à¸à¹à¸ ายà¹à¸à¸à¹à¸§à¸à¹à¸§à¸¥à¸²à¸à¸µà¹à¸à¸°à¸à¸¹à¸à¸¥à¸°à¹à¸§à¹à¸à¸à¸²à¸à¸à¸°à¹à¸à¸à¸à¸à¸§à¸´à¸à¸²à¸£à¸à¹à¸à¹à¸§à¸¢à¸à¹à¸²à¹à¸£à¸´à¹à¸¡à¸à¹à¸</p><p>à¸à¸¸à¸à¸ªà¸²à¸¡à¸²à¸£à¸à¹à¸£à¸µà¸¢à¸à¸£à¸¹à¹à¹à¸à¸´à¹à¸¡à¹à¸à¸´à¸¡à¹à¸à¸µà¹à¸¢à¸§à¸à¸±à¸à¸§à¸´à¸à¸µà¸à¸²à¸£à¹à¸¥à¸·à¸à¸à¸à¹à¸§à¸à¹à¸§à¸¥à¸²à¹à¸«à¸¥à¹à¸²à¸à¸µà¹à¹à¸à¸¢à¸à¸²à¸£à¸à¹à¸²à¸à¸à¸ <a href="https://steamcommunity.com/games/593110/announcements/detail/1808664240333155775">à¹à¸à¸ªà¸à¹à¸à¸¥à¹à¸à¸ Steam à¸à¸à¸à¹à¸£à¸²</a></p><p>หาà¸à¸à¸¸à¸à¸à¹à¸à¸à¸à¸²à¸£à¸à¹à¸à¸¡à¸¹à¸¥à¹à¸à¸´à¸à¸¥à¸¶à¸ à¸à¸¸à¸à¸¢à¸±à¸à¸à¸à¸ªà¸²à¸¡à¸²à¸£à¸à¸à¹à¸²à¸à¸à¸à¸§à¸´à¸à¸²à¸£à¸à¹à¸ ายà¹à¸à¸à¹à¸§à¸à¹à¸§à¸¥à¸²à¸à¸µà¹ หรืà¸à¹à¸à¹à¹à¸à¸à¸²à¸£à¸à¸£à¸±à¸à¹à¸à¹à¸à¸à¸à¸à¸à¸¸à¸à¹à¸à¸·à¹à¸à¹à¸«à¹à¸£à¸§à¸¡à¸à¹à¸§à¸à¹à¸§à¸¥à¸²à¸à¸à¸à¸à¸à¸§à¸´à¸à¸²à¸£à¸à¹à¸à¸à¸à¹à¸£à¸·à¹à¸à¸à¹à¸à¸à¸°à¹à¸à¸à¸à¸à¸§à¸´à¸à¸²à¸£à¸à¹</p>' } ) ); 1404 var dialog = ShowConfirmDialog( 'à¸à¸´à¸à¸à¸£à¸£à¸¡à¸à¸à¸§à¸´à¸à¸²à¸£à¸à¹à¸à¸à¸à¹à¸£à¸·à¹à¸à¸', dialogContent, 'à¸à¹à¸²à¸à¸à¸à¸§à¸´à¸à¸²à¸£à¸à¹', 'ยà¸à¹à¸¥à¸´à¸', 'à¹à¸à¹à¹à¸à¸à¸²à¸£à¸à¸£à¸±à¸à¹à¸à¹à¸' ); 1405 dialog.done( function( type ) { 1406 if ( type == 'OK' ) 1407 { 1408 funcViewReviewsDuringEvent( bCountAllReviews, startDate, endDate ); 1409 } 1410 else if ( type == 'SECONDARY' ) 1411 { 1412 EditUserReviewScorePreference(); 1413 } 1414 } ); 1415 } ); 1416 } ); 1417 1418 } ); 1419} 1420 1421function SetReviewsGraphVisibility( bVisible ) 1422{ 1423 const previousFocusedElement = document.activeElement; 1424 $J( "#review_histograms_container" ).toggleClass( "collapsed", !bVisible ); 1425 $J( "#reviews_filter_options" ).toggleClass( "graph_collapsed", !bVisible ); 1426 1427 const showButton = document.getElementById( "review_show_graph_button" ); 1428 const hideButton = document.getElementById( "review_hide_graph_button" ); 1429 if ( previousFocusedElement === showButton ) 1430 { 1431 hideButton.focus(); 1432 } 1433 else if(previousFocusedElement === hideButton ) 1434 { 1435 showButton.focus(); 1436 } 1437} 1438 1439function ClearReviewDateFilter() 1440{ 1441 $J('#review_start_date').val( -1 ); 1442 $J('#review_end_date').val( -1 ); 1443 $J('#review_date_range_histogram').attr( 'disabled', true ); 1444 $J('#review_date_range_exclude_histogram').attr( 'disabled', true ); 1445 $J( "#review_selected_histogram_date_range_text" ).hide(); 1446 ShowFilteredReviews(); 1447} 1448 1449function OnLoadReviews() 1450{ 1451 BuildReviewHistogram(); 1452 ShowFilteredReviews(); 1453} 1454 1455function LocalizeDateRange( startDate, endDate ) 1456{ 1457 let options = { month: 'short', day: 'numeric', year: 'numeric' }; 1458 return new Date( startDate * 1000 ).toLocaleDateString( undefined, options ) + ' - ' + new Date( endDate * 1000 ).toLocaleDateString( undefined, options ); 1459} 1460 1461function UpdateHardwareFilter( category ) 1462{ 1463 var hardwareValue = $J( 'input[name="review_' + category + '"]:checked' ).val(); 1464 var elemFilter = $J( "#reviews_filter_" + category ); 1465 if ( !hardwareValue || hardwareValue == 'all' ) 1466 { 1467 elemFilter.hide(); 1468 } 1469 else 1470 { 1471 elemFilter.find( "span:first-child" ).text( hardwareValue ); 1472 elemFilter.show(); 1473 } 1474} 1475 1476function UpdateActiveFilters() 1477{ 1478 var bAnyActiveFilters = false;
1479 var startDate = $J( "#review_start_date" ).val(); 1480 var endDate = $J( "#review_end_date" ).val(); 1481 1482 // type 1483 if ( $J( "#review_type_positive" ).attr( "checked" ) ) 1484 { 1485 bAnyActiveFilters = true; 1486 $J( "#reviews_filter_type" ).show(); 1487 $J( "#reviews_filter_type" ).text( 'à¹à¸à¹à¸à¸§à¸' ); 1488 } 1489 else if ( $J( "#review_type_negative" ).attr( "checked" ) ) 1490 { 1491 bAnyActiveFilters = true; 1492 $J( "#reviews_filter_type" ).show(); 1493 $J( "#reviews_filter_type" ).text( 'à¹à¸à¹à¸¥à¸' ); 1494 } 1495 else 1496 { 1497 $J( "#reviews_filter_type" ).hide(); 1498 } 1499 1500 // purchase type 1501 if ( $J( "#purchase_type_steam" ).attr( "checked" ) ) 1502 { 1503 bAnyActiveFilters = true; 1504 $J( "#reviews_filter_purchase_type" ).show(); 1505 $J( "#reviews_filter_purchase_type" ).text( 'à¸à¸¹à¹à¸ªà¸±à¹à¸à¸à¸·à¹à¸à¸à¸ Steam' ); 1506 } 1507 else if ( $J( "#purchase_type_non_steam" ).attr( "checked" ) ) 1508 { 1509 bAnyActiveFilters = true; 1510 $J( "#reviews_filter_purchase_type" ).show(); 1511 $J( "#reviews_filter_purchase_type" ).text( 'à¹à¸¡à¹à¹à¸à¹à¸ªà¸±à¹à¸à¸à¸·à¹à¸à¸à¸ Steam' ); 1512 } 1513 else 1514 { 1515 $J( "#reviews_filter_purchase_type" ).hide(); 1516 } 1517 1518 // language 1519 var selectedLanguage = $J('input[name="review_language"]:checked').val(); 1520 if ( $J( "#review_language_mine" ).attr( "checked" ) ) 1521 { 1522 bAnyActiveFilters = true; 1523 $J( "#reviews_filter_language" ).show(); 1524 $J( "#reviews_filter_language" ).text( 'ภาษาà¸à¸à¸à¸à¸¸à¸' ); 1525 } 1526 else if ( false ) 1527 { 1528 bAnyActiveFilters = true; 1529 $J( "#reviews_filter_language" ).show(); 1530 $J( "#reviews_filter_language" ).text( 'à¸à¸µà¸à¸à¸±à¸§à¸¢à¹à¸' ); 1531 } 1532 else if ( selectedLanguage != 'all' ) 1533 { 1534 $J( "#reviews_filter_language" ).show(); 1535 var strDisplay = $J('input[name="review_language"]:checked').data( 'language' ); 1536 $J( "#reviews_filter_language" ).text( strDisplay ); 1537 } 1538 else 1539 { 1540 $J( "#reviews_filter_language" ).hide(); 1541 } 1542 1543 // graph 1544 if ( $J( "#review_date_range_histogram" ).attr( "checked" ) ) 1545 { 1546 bAnyActiveFilters = true; 1547 $J( "#reviews_filter_graph" ).show(); 1548 $J( "#review_selected_histogram_date_range_prefix" ).text( 'à¸à¸¹à¹à¸à¸à¸²à¸° ' ); 1549 1550 UpdateDateFilterText( LocalizeDateRange( startDate, endDate ) ); 1551 } 1552 else if ( $J( "#review_date_range_exclude_histogram" ).attr( "checked" ) ) 1553 { 1554 bAnyActiveFilters = true; 1555 $J( "#reviews_filter_graph" ).show(); 1556 $J( "#review_selected_histogram_date_range_prefix" ).text( 'ละà¹à¸§à¹à¸ ' ); 1557 1558 UpdateDateFilterText( LocalizeDateRange( startDate, endDate ) ); 1559 } 1560 else 1561 { 1562 $J( "#reviews_filter_graph" ).hide(); 1563 } 1564 1565 // off-topic review activity 1566 if ( $J( "#user_reviews_offtopic_activity_menu" ).is( ":visible" ) && $J( "#reviews_offtopic_activity_checkbox" ).attr( "checked" ) ) 1567 { 1568 bAnyActiveFilters = true; 1569 $J( "#reviews_filter_offtopic_activity" ).show(); 1570 } 1571 else 1572 { 1573 $J( "#reviews_filter_offtopic_activity" ).hide(); 1574 } 1575 1576 // playtime 1577 var playtimeFilterMin = $J( "#app_reviews_playtime_range_min" ).val(); 1578 var playtimeFilterMax = $J( "#app_reviews_playtime_range_max" ).val(); 1579 if ( playtimeFilterMin != 0 || playtimeFilterMax != 0 ) 1580 { 1581 var elemPlaytimeDesc = $J( "#review_playtime_preset_text" ); 1582 if ( playtimeFilterMax == 0 ) 1583 { 1584 if ( playtimeFilterMin == 1 ) 1585 { 1586 elemPlaytimeDesc.text( 'มาà¸à¸à¸§à¹à¸² 1 à¸à¸±à¹à¸§à¹à¸¡à¸' ); 1587 } 1588 else 1589 { 1590 elemPlaytimeDesc.text( 'มาà¸à¸à¸§à¹à¸² %2$s à¸à¸±à¹à¸§à¹à¸¡à¸'.replace( "%2$s", playtimeFilterMin ) ); 1591 } 1592 } 1593 else 1594 { 1595 elemPlaytimeDesc.text( '%1$s à¸à¸±à¹à¸§à¹à¸¡à¸ à¸à¸¶à¸ %2$s à¸à¸±à¹à¸§à¹à¸¡à¸'.replace( "%1$s", playtimeFilterMin ).replace( "%2$s", playtimeFilterMax ) ); 1596 } 1597 1598 bAnyActiveFilters = true; 1599 $J( "#reviews_filter_playtime" ).show(); 1600 } 1601 else 1602 { 1603 $J( "#reviews_filter_playtime" ).hide(); 1604 } 1605 1606 if ( $J( "#review_playtime_type_deck" ).attr( "checked" ) ) 1607 { 1608 $J( "#reviews_filter_deck_playtime" ).show(); 1609 } 1610 else 1611 { 1612 $J( "#reviews_filter_deck_playtime" ).hide(); 1613 } 1614 1615 UpdateHardwareFilter( 'hardware_os' ); 1616 UpdateHardwareFilter( 'hardware_cpu_name' ); 1617 UpdateHardwareFilter( 'hardware_gfx_adapter_description' ); 1618 UpdateHardwareFilter( 'hardware_gaming_device_type' ); 1619 1620 // topic 1621 $J( ".review_topic_filter" ).hide(); 1622 $J( 'input[name="review_topic"]:checked' ).each( ( index, elem ) => 1623 { 1624 $J( "#review_topic_filter" + $J( elem ).val() ).show(); 1625 } ); 1626 1627 $J( "#reviews_filter_title" ).toggle( bAnyActiveFilters ); 1628} 1629 1630function ShowFilteredReviews() 1631{ 1632 const params = new URLSearchParams( window.location.search ); 1633 const bLoadReviews = !!g_bUseOldReviewDisplay; 1634 1635 UpdateActiveFilters(); 1636 1637 if ( $J( "#review_appid" ).length == 0 ) 1638 return; 1639 1640 var appid = $J( "#review_appid" ).val(); 1641 var context = $J( "input[name=review_context]:checked" ).val(); 1642 var defaultDayRange = $J( "#review_default_day_range" ).val(); 1643 var startDate = $J( "#review_start_date" ).val(); 1644 var endDate = $J( "#review_end_date" ).val(); 1645 1646 // Notify React of the change to filters. If we haven't rendered React because 1647 // we're forcing old-style display, this will still post the message but
1648 // nobody's listening. 1649 let reviewType = $J( 'input[name="review_type"]:checked' ).val(); 1650 let purchaseType = $J( 'input[name="purchase_type"]:checked' ).val(); 1651 let language = $J( 'input[name="review_language"]:checked' ).val(); 1652 let filterOfftopicActivity = $J( "#reviews_offtopic_activity_checkbox" ).is( ":checked" ) ? 1 : 0; 1653 let dateRangeType = $J( 'input[name="review_date_range"]:checked' ).val(); 1654 let summaryNumPositiveReviews = $J( "#review_summary_num_positive_reviews" ).val(); 1655 let summaryNumReviews = $J( "#review_summary_num_reviews" ).val(); 1656 let playtimeFilterMin = $J( "#app_reviews_playtime_range_min" ).val(); 1657 let playtimeFilterMax = $J( "#app_reviews_playtime_range_max" ).val(); 1658 let playtimeType = $J( 'input[name="review_playtime_type"]:checked' ).val(); 1659 1660 // hardware 1661 let hardwareOS = $J( 'input[name="review_hardware_os"]:checked' ).val(); 1662 let hardwareCPU = $J( 'input[name="review_hardware_cpu_name"]:checked' ).val(); 1663 let hardwareGPU = $J( 'input[name="review_hardware_gfx_adapter_description"]:checked' ).val(); 1664 let hardwareDeviceType = $J( 'input[name="review_hardware_gaming_device_type"]:checked' ).val(); 1665 1666 let topics = []; 1667 $J( 'input[name="review_topic"]:checked' ).each( ( index, elem ) => 1668 { 1669 topics.push( $J( elem ).val() ); 1670 }); 1671 1672 var useReviewQuality = $J( 'input[name="use_review_quality"]' ).is( ":checked" ) ? 1 : 0; 1673 1674 window.postMessage( JSON.stringify( { 1675 message: 'ReviewFilterChange', 1676 args: {
1677 review_type: reviewType, 1678 filter: context, 1679 day_range: defaultDayRange, 1680 start_date: startDate, 1681 end_date: endDate, 1682 purchase_type: purchaseType, 1683 language, 1684 filter_offtopic_activity: filterOfftopicActivity, 1685 date_range_type: dateRangeType, 1686 summary_num_positive_reviews: summaryNumPositiveReviews, 1687 summary_num_reviews: summaryNumReviews, 1688 playtime_filter_min: playtimeFilterMin, 1689 playtime_filter_max: playtimeFilterMax, 1690 playtime_type: playtimeType, 1691 use_review_quality: useReviewQuality, 1692 hardware_os: hardwareOS, 1693 hardware_cpu: hardwareCPU, 1694 hardware_gpu: hardwareGPU, 1695 hardware_device_type: hardwareDeviceType, 1696 topics, 1697 } 1698 } ) ); 1699 1700 if ( bLoadReviews ) 1701 { 1702 SelectReviews( appid, context, defaultDayRange, startDate, endDate, true ); 1703 } 1704} 1705 1706function ChangeReviewPurchaseTypeFilter() 1707{ 1708 var purchaseType = $J('input[name="purchase_type"]:checked').val(); 1709 V_SetCookie( "review_purchase_type_filter", purchaseType, purchaseType == 'all' ? 1 : 14, "app/" ); 1710 ShowFilteredReviews(); 1711} 1712 1713function OnReviewPlaytimeFilterSliderChanged( hourMin, hourMax, maxHours ) 1714{ 1715 if ( hourMin == 0 && hourMax == maxHours ) 1716 { 1717 $J('input[name="review_playtime_preset"][value="0"]').attr( 'checked', true ); 1718 } 1719 else 1720 { 1721 $J('input[name="review_playtime_preset"]').attr( 'checked', false ); 1722 } 1723 1724 ShowFilteredReviews(); 1725} 1726 1727function SelectPlaytimeFilterPreset( playtimeMinHours ) 1728{ 1729 var maxHours = 100; 1730 var maxSeconds = maxHours * 60 * 60; 1731 1732 $J("#app_reviews_playtime_slider").slider('values', 0, playtimeMinHours * 60 * 60 ); 1733 $J("#app_reviews_playtime_slider").slider('values', 1, maxSeconds ); 1734 1735 UpdatePlaytimeFilterValues( playtimeMinHours, maxHours ); 1736 1737 ShowFilteredReviews(); 1738} 1739 1740function ChangedOfftopicReviewActivityFilter() 1741{ 1742 ShowFilteredReviews(); 1743} 1744 1745function ClearOfftopicReviewActivityFilter() 1746{ 1747 $J('#reviews_offtopic_activity_checkbox').attr( 'checked', false ); 1748 ShowFilteredReviews(); 1749} 1750 1751function ClearReviewPlaytimeFilter() 1752{ 1753 $J('#review_playtime_preset_0').attr( 'checked', true ); 1754 SelectPlaytimeFilterPreset( 0 ); 1755} 1756 1757function ClearDeckPlaytimeFilter() 1758{ 1759 $J('#review_playtime_type_all').attr( 'checked', true ); 1760 SelectPlaytimeFilterPreset( 0 ); 1761} 1762 1763function SetReviewTopicFilter( strTopicId, bSet ) 1764{ 1765 $J('input[name="review_topic"][value="' + strTopicId + '"]').prop( 'checked', bSet ); // check the menu item (or not) 1766 $J('input[name="review_topic"][value="' + strTopicId + '"]').parent().show(); // make sure we're showing the menu item 1767 ShowFilteredReviews(); 1768} 1769 1770function HighlightReviewTopic( strParentElementId, nTopicId, bHighlight ) 1771{ 1772 CSS.highlights.delete("TopicHighlight"); 1773 1774 if ( bHighlight ) 1775 { 1776 const parent = $J( "#" + strParentElementId ); 1777 const nParentBottom = parent[0].getBoundingClientRect().bottom; 1778 1779 let bHiddenElement = false; 1780 let jqFragments = parent.find( "span.generic_highlight" ).filter( ( _, span ) => 1781 { 1782 try 1783 { 1784 const rTypes = $J( span ).data( "type" ); 1785 const bMatching = rTypes.includes( nTopicId ); 1786 if ( bMatching && span.getBoundingClientRect().top > nParentBottom ) 1787 bHiddenElement = true; 1788 return bMatching; 1789 } 1790 catch 1791 { 1792 return false; 1793 } 1794 } ); 1795 1796 const ranges = $J( jqFragments ).map( ( _, node ) => 1797 { 1798 const range = document.createRange(); 1799 range.selectNodeContents( node ); 1800 return range; 1801 } ).get(); 1802 1803 if ( bHiddenElement ) 1804 { 1805 const range = document.createRange(); 1806 range.selectNodeContents( parent.find( ".view_more" )[0] ); 1807 ranges.push( range ) 1808 } 1809 const highlight = new Highlight( ...ranges ); 1810 CSS.highlights.set( "TopicHighlight", highlight ); 1811 } 1812} 1813 1814function CollapseLongReviews() 1815{ 1816 $J('.review_box').each( function(j, i){ 1817 if( $J(i).outerHeight() > 400 ) 1818 { 1819 if ( !$J(i).hasClass('expanded') ) 1820 { 1821 $J(i).addClass('partial'); 1822 1823 // In gamepadui links inside the review content should not be focusable if the review 1824 // is collapsed. This prevents focus going to links the user cannot see. 1825 $J(i).find( 'div.content a' ).data( 'gpFocusDisabled', true ); 1826 } 1827 } 1828 }); 1829} 1830 1831function UpdatePlaytimeFilterValues( hourMin, hourMax ) 1832{ 1833 var maxHours = 100; 1834 $J( "#app_reviews_playtime_range_min" ).val( hourMin ); 1835 $J( "#app_reviews_playtime_range_max" ).val( hourMax == maxHours ? 0 : hourMax ); 1836 1837 var strHoursMinNumber = v_numberformat( hourMin, 0 ); 1838 var strHourMin = hourMin > 0 ? '%1$s à¸à¸±à¹à¸§à¹à¸¡à¸'.replace( /%1\$s/g, strHoursMinNumber ) : 'à¹à¸¡à¹à¸¡à¸µà¸à¹à¸³à¸ªà¸¸à¸'; 1839 $J( "#app_reviews_playtime_range_text_min" ).text( strHourMin ); 1840 1841 var strHoursMaxNumber = v_numberformat( hourMax, 0 ); 1842 var strHourMax = hourMax > 0 && hourMax != maxHours ? '%1$s à¸à¸±à¹à¸§à¹à¸¡à¸'.replace( /%1\$s/g, strHoursMaxNumber ) : 'à¹à¸¡à¹à¸¡à¸µà¸ªà¸¹à¸à¸ªà¸¸à¸'; 1843 $J( "#app_reviews_playtime_range_text_max" ).text( strHourMax ); 1844 1845 1846 const handles = [...document.querySelectorAll( "#app_reviews_playtime_slider .ui-slider-handle" )]; 1847 if ( handles.length > 0 ) 1848 { 1849 handles[0].ariaValueNow = hourMin; 1850 handles[0].ariaValueText = strHourMin; 1851 handles[1].ariaValueNow = hourMax; 1852 handles[1].ariaValueText = strHourMax; 1853 } 1854} 1855 1856 1857function InitPlaytimeFilterSlider() 1858{ 1859 // Element doesn't exist playtime filters is empty 1860 if ( $J( "#app_reviews_playtime_slider" ).length === 0 ) 1861 return; 1862 1863 var maxHours = 100; 1864 var maxSeconds = maxHours * 60 * 60; 1865 const slider = $J( "#app_reviews_playtime_slider" ).slider({ 1866 range: true, 1867 min: 0, 1868 max: maxSeconds, 1869 values: [0, maxSeconds], 1870 step: 3600, 1871 slide: function (event, ui) { 1872 var minSecs = ui.values[0]; 1873 var maxSecs = ui.values[1]; 1874 1875 var hourMin = parseInt( minSecs / ( 60 * 60 ), 10 ); 1876 var hourMax = parseInt( maxSecs / ( 60 * 60 ), 10 ); 1877 1878 UpdatePlaytimeFilterValues( hourMin, hourMax ); 1879 }, 1880 change: function( event, ui ) { 1881 if ( event.originalEvent ) 1882 { 1883 var minSecs = ui.values[0]; 1884 var maxSecs = ui.values[1]; 1885 1886 var hourMin = parseInt( minSecs / ( 60 * 60 ), 10 ); 1887 var hourMax = parseInt( maxSecs / ( 60 * 60 ), 10 ); 1888 1889 OnReviewPlaytimeFilterSliderChanged( hourMin, hourMax, maxHours ); 1890 } 1891 } 1892 }); 1893 UpdatePlaytimeFilterValues( 0, maxHours ); 1894 const handles = [...slider[0].querySelectorAll(".ui-slider-handle")]; 1895 for ( const handle of handles ) 1896 { 1897 handle.role = "slider";
1898 handle.ariaValueMin = 0; 1899 handle.ariaValueMax = maxHours; 1900 } 1901 handles[0].ariaLabel = 'à¸à¸³à¸à¸§à¸à¸à¸±à¹à¸§à¹à¸¡à¸à¸à¹à¸³à¸ªà¸¸à¸à¸à¸µà¹à¹à¸¥à¹à¸'; 1902 handles[1].ariaLabel = 'à¸à¸³à¸à¸§à¸à¸à¸±à¹à¸§à¹à¸¡à¸à¸ªà¸¹à¸à¸ªà¸¸à¸à¸à¸µà¹à¹à¸¥à¹à¸'; 1903} 1904 1905//formerly app_reporting.js 1906 1907var gReportedApp = false; 1908function ShowReportDialog( nAppId ) 1909{ 1910 if ( gReportedApp ) 1911 { 1912 return; 1913 } 1914 1915 var content = $J('<div/>', {'class': 'app_report_dialog' } ); 1916 1917 content.append( $J('<div/>', {'class': 'app_report_dialog_intro' } ).text('à¹à¸à¸£à¸à¹à¸¥à¸·à¸à¸à¹à¸«à¸à¸¸à¸à¸¥à¸§à¹à¸²à¸à¸³à¹à¸¡à¸à¸¸à¸à¸à¸¶à¸à¸à¸³à¸à¸²à¸£à¸£à¸²à¸¢à¸à¸²à¸à¸à¸¥à¸´à¸à¸ ัà¸à¸à¹à¸à¸µà¹') ); 1918 1919 var rgReportOptions = {"1":"\u0e09\u0e49\u0e2d\u0e42\u0e01\u0e07 - <span class=\"sub\">\u0e0b\u0e2d\u0e1f\u0e15\u0e4c\u0e41\u0e27\u0e23\u0e4c\u0e19\u0e35\u0e49\u0e1e\u0e22\u0e32\u0e22\u0e32\u0e21\u0e2b\u0e25\u0e2d\u0e01\u0e25\u0e27\u0e07\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e40\u0e01\u0e47\u0e1a\u0e23\u0e27\u0e1a\u0e23\u0e27\u0e21\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e2a\u0e33\u0e04\u0e31\u0e0d \u0e15\u0e31\u0e27\u0e2d\u0e22\u0e48\u0e32\u0e07\u0e40\u0e0a\u0e48\u0e19 \u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e23\u0e31\u0e1a\u0e23\u0e2d\u0e07 Steam \u0e2b\u0e23\u0e37\u0e2d\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e17\u0e32\u0e07\u0e01\u0e32\u0e23\u0e40\u0e07\u0e34\u0e19 (\u0e40\u0e0a\u0e48\u0e19 \u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e1a\u0e31\u0e15\u0e23\u0e40\u0e04\u0e23\u0e14\u0e34\u0e15)<\/span>","2":"\u0e40\u0e1b\u0e47\u0e19\u0e2d\u0e31\u0e19\u0e15\u0e23\u0e32\u0e22 - <span class=\"sub\">\u0e0b\u0e2d\u0e1f\u0e15\u0e4c\u0e41\u0e27\u0e23\u0e4c\u0e19\u0e35\u0e49\u0e14\u0e31\u0e14\u0e41\u0e1b\u0e25\u0e07\u0e41\u0e01\u0e49\u0e44\u0e02\u0e04\u0e2d\u0e21\u0e1e\u0e34\u0e27\u0e40\u0e15\u0e2d\u0e23\u0e4c\u0e02\u0e2d\u0e07\u0e1c\u0e39\u0e49\u0e0b\u0e37\u0e49\u0e2d\u0e43\u0e19\u0e17\u0e32\u0e07\u0e17\u0e35\u0e48\u0e44\u0e21\u0e48\u0e04\u0e32\u0e14\u0e04\u0e34\u0e14\u0e2b\u0e23\u0e37\u0e2d\u0e40\u0e1b\u0e47\u0e19\u0e2d\u0e31\u0e19\u0e15\u0e23\u0e32\u0e22 (\u0e40\u0e0a\u0e48\u0e19 \u0e40\u0e1b\u0e47\u0e19\u0e21\u0e31\u0e25\u0e41\u0e27\u0e23\u0e4c\u0e2b\u0e23\u0e37\u0e2d\u0e44\u0e27\u0e23\u0e31\u0e2a)<\/span>","5":"\u0e40\u0e19\u0e37\u0e49\u0e2d\u0e2b\u0e32\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e1c\u0e39\u0e49\u0e43\u0e2b\u0e0d\u0e48 - <span class=\"sub\">\u0e1a\u0e23\u0e23\u0e08\u0e38\u0e40\u0e19\u0e37\u0e49\u0e2d\u0e2b\u0e32\u0e2a\u0e33\u0e2b\u0e23\u0e31\u0e1a\u0e1c\u0e39\u0e49\u0e43\u0e2b\u0e0d\u0e48\u0e17\u0e35\u0e48\u0e44\u0e21\u0e48\u0e16\u0e39\u0e01\u0e17\u0e33\u0e40\u0e04\u0e23\u0e37\u0e48\u0e2d\u0e07\u0e2b\u0e21\u0e32\u0e22\u0e2d\u0e22\u0e48\u0e32\u0e07\u0e40\u0e2b\u0e21\u0e32\u0e30\u0e2a\u0e21\u0e41\u0e25\u0e30\u0e44\u0e21\u0e48\u0e21\u0e35\u0e01\u0e32\u0e23\u0e01\u0e23\u0e2d\u0e07\u0e2d\u0e32\u0e22\u0e38<\/span>","6":"\u0e2b\u0e21\u0e34\u0e48\u0e19\u0e1b\u0e23\u0e30\u0e21\u0e32\u0e17 - <span class=\"sub\">\u0e1a\u0e23\u0e23\u0e08\u0e38\u0e02\u0e49\u0e2d\u0e04\u0e27\u0e32\u0e21\u0e17\u0e35\u0e48\u0e2b\u0e21\u0e34\u0e48\u0e19\u0e1b\u0e23\u0e30\u0e21\u0e32\u0e17\u0e2b\u0e23\u0e37\u0e2d\u0e43\u0e2a\u0e48\u0e23\u0e49\u0e32\u0e22\u0e1b\u0e49\u0e32\u0e22\u0e2a\u0e35<\/span>","8":"\u0e01\u0e32\u0e23\u0e41\u0e2a\u0e27\u0e07\u0e2b\u0e32\u0e1b\u0e23\u0e30\u0e42\u0e22\u0e0a\u0e19\u0e4c\u0e08\u0e32\u0e01\u0e40\u0e14\u0e47\u0e01 - <span class=\"sub\">\u0e1a\u0e23\u0e23\u0e08\u0e38\u0e40\u0e19\u0e37\u0e49\u0e2d\u0e2b\u0e32\u0e17\u0e35\u0e48\u0e41\u0e2a\u0e27\u0e07\u0e2b\u0e32\u0e1b\u0e23\u0e30\u0e42\u0e22\u0e0a\u0e19\u0e4c\u0e08\u0e32\u0e01\u0e40\u0e14\u0e47\u0e01\u0e43\u0e19\u0e17\u0e32\u0e07\u0e43\u0e14\u0e17\u0e32\u0e07\u0e2b\u0e19\u0e36\u0e48\u0e07<\/span>","11":"\u0e01\u0e32\u0e23\u0e25\u0e30\u0e40\u0e21\u0e34\u0e14\u0e01\u0e0e\u0e2b\u0e21\u0e32\u0e22 - <span class=\"sub\">\u0e1a\u0e23\u0e23\u0e08\u0e38\u0e40\u0e19\u0e37\u0e49\u0e2d\u0e2b\u0e32\u0e17\u0e35\u0e48\u0e25\u0e30\u0e40\u0e21\u0e34\u0e14\u0e01\u0e0e\u0e2b\u0e21\u0e32\u0e22\u0e43\u0e19\u0e40\u0e02\u0e15\u0e2d\u0e33\u0e19\u0e32\u0e08\u0e28\u0e32\u0e25\u0e02\u0e2d\u0e07\u0e04\u0e38\u0e13<\/span>","13":"\u0e44\u0e21\u0e48\u0e2a\u0e21\u0e1a\u0e39\u0e23\u0e13\u0e4c - <span class=\"sub\">\u0e44\u0e21\u0e48\u0e2a\u0e32\u0e21\u0e32\u0e23\u0e16\u0e14\u0e32\u0e27\u0e19\u0e4c\u0e42\u0e2b\u0e25\u0e14 \u0e40\u0e23\u0e34\u0e48\u0e21\u0e40\u0e1b\u0e34\u0e14 \u0e2b\u0e23\u0e37\u0e2d\u0e43\u0e0a\u0e49\u0e07\u0e32\u0e19\u0e44\u0e14\u0e49\u0e2d\u0e22\u0e48\u0e32\u0e07\u0e16\u0e39\u0e01\u0e15\u0e49\u0e2d\u0e07 \u0e41\u0e21\u0e49\u0e27\u0e48\u0e32\u0e40\u0e04\u0e23\u0e37\u0e48\u0e2d\u0e07\u0e19\u0e31\u0e49\u0e19\u0e1c\u0e48\u0e32\u0e19\u0e04\u0e27\u0e32\u0e21\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23\u0e23\u0e30\u0e1a\u0e1a\u0e02\u0e3
19191\u0e49\u0e19\u0e15\u0e48\u0e33\u0e41\u0e25\u0e49\u0e27<\/span>"}; 1920 var rgReportOptionElements = []; 1921 for ( var eReportType in rgReportOptions ) 1922 { 1923 var $ReportOption = $J('<div/>', {'class': 'app_report_dialog_option' } ); 1924 $ReportOption.append( $J('<div/>', {'class': 'app_report_dialog_option_input' }).append( $J('<input/>', {'type': 'radio', 'name':'report_type', 'value': eReportType, 'id': 'report_type_' + eReportType } ) ) ); 1925 $ReportOption.append( $J('<div/>', {'class': 'app_report_dialog_option_text' }).append( $J('<label/>', {'for': 'report_type_' + eReportType }).html( rgReportOptions[eReportType] ) ) ); 1926 rgReportOptionElements.push( $ReportOption ); 1927 } 1928 1929 for ( var j, x, i = rgReportOptionElements.length; i; j = parseInt(Math.random() * i), x = rgReportOptionElements[--i], rgReportOptionElements[i] = rgReportOptionElements[j], rgReportOptionElements[j] = x ); 1930 for ( var i = 0; i < rgReportOptionElements.length; ++i ) 1931 { 1932 content.append( rgReportOptionElements[i] ); 1933 } 1934 1935 var textArea = $J('<textarea/>', { 'class': 'app_report_dialog_reason grey_bevel fullwidth', 'id' : 'ReportReason', 'name' : 'report_reason' } ); 1936 var maxReasonLength = 512; 1937 textArea.attr( 'maxlength', maxReasonLength ); 1938 textArea.bind( "keyup change", 1939 function() 1940 { 1941 var str = $J(this).val() 1942 var mx = parseInt($J(this).attr('maxlength')) 1943 if (str.length > mx) 1944 { 1945 $J(this).val(str.substr(0, mx)) 1946 return false; 1947 } 1948 } 1949 ); 1950 content.append( $J('<div/>', {'class': 'app_report_dialog_intro' }
1950 ).text('à¸à¸¸à¸à¸ªà¸²à¸¡à¸²à¸£à¸à¸à¸£à¸à¸à¸à¹à¸à¸¡à¸¹à¸¥à¹à¸à¸´à¹à¸¡à¹à¸à¸´à¸¡à¸à¸µà¹à¸à¸¸à¸à¸£à¸¹à¹à¸ªà¸¶à¸à¸§à¹à¸²à¸¡à¸µà¸à¸§à¸²à¸¡à¹à¸à¸µà¹à¸¢à¸§à¸à¹à¸à¸à¹à¸à¹à¸à¸µà¹à¸à¸µà¹: ') ); 1951 content.append( textArea ); 1952 1953 content.append( $J('<div/>', {'class': 'app_report_dialog_dmca' } ).html('หาà¸à¸à¸¸à¸à¸à¹à¸à¸à¸à¸²à¸£à¸à¸°à¸£à¸²à¸¢à¸à¸²à¸à¸à¸²à¸£à¸¥à¸°à¹à¸¡à¸´à¸à¸¥à¸´à¸à¸ªà¸´à¸à¸à¸´à¹ à¹à¸¥à¸°à¸à¸¸à¸à¹à¸à¹à¸à¹à¸à¹à¸²à¸à¸à¸à¸ªà¸´à¸à¸ªà¸´à¸à¸à¸´à¹à¸à¸µà¹ à¹à¸à¸£à¸à¸à¸³à¹à¸à¸´à¸à¸à¸²à¸£à¸à¹à¸à¸à¸µà¹à¹à¸à¸à¸à¸à¸£à¹à¸¡à¹à¸à¹à¸à¸à¸²à¸£à¸£à¹à¸à¸à¹à¸£à¸µà¸¢à¸ DMCA à¹à¸à¸µà¹à¸¢à¸§à¸à¸±à¸à¸à¸²à¸£à¸¥à¸°à¹à¸¡à¸´à¸à¸¥à¸´à¸à¸ªà¸´à¸à¸à¸´à¹ <a href="https://steamcommunity.com/dmca/create/">à¸à¸µà¹à¸à¸µà¹</a>') ); 1954 content.append( $J('<div/>', {'class': 'app_report_dialog_dmca' } ).html('หาà¸à¸à¸¸à¸à¸à¹à¸à¸à¸à¸²à¸£à¸¢à¸·à¹à¸à¸à¸³à¸£à¹à¸à¸à¹à¸£à¸µà¸¢à¸à¹à¸à¸µà¹à¸¢à¸§à¸à¸±à¸à¹à¸à¸£à¸·à¹à¸à¸à¸«à¸¡à¸²à¸¢à¸à¸²à¸£à¸à¹à¸² à¹à¸à¸£à¸à¸à¸£à¸à¸à¹à¸à¸à¸à¸à¸£à¹à¸¡<a href="https://steamcommunity.com/trademark/createtrademarkcomplaint/">à¸à¸µà¹à¸à¸µà¹</a>') ); 1955 1956 var dialog = ShowConfirmDialog( 'รายà¸à¸²à¸à¸à¸¥à¸´à¸à¸ ัà¸à¸à¹à¸à¸µà¹', content, 'รายà¸à¸²à¸'); 1957 1958 dialog.done( function() { 1959 var eReportTypeSelected = content.find( 'input[type=radio]:checked' ).val(); 1960 if ( eReportTypeSelected ) 1961 { 1962 $J.post( 1963 'https://store.steampowered.com/appreport/' + nAppId + '/report/', 1964 { 1965 'report_type' : eReportTypeSelected, 1966 'report_reason' : content.find( 'textarea' ).val(), 1967 'sessionid': g_sessionID 1968 } 1969 ).done( function( json ) { 1970 gReportedApp = true; 1971 $J( "#ReportAppBtn").addClass( 'btn_active' ); 1972 } 1973 ).fail( function( jqXHR ) { 1974 var json = jqXHR.responseText.evalJSON(); 1975 if ( json.success == 29 ) 1976 { 1977 ShowAlertDialog( 'à¸à¹à¸à¸à¸´à¸à¸à¸¥à¸²à¸', 'à¸à¸¸à¸à¹à¸à¹à¸£à¸²à¸¢à¸à¸²à¸à¸à¸¥à¸´à¸à¸ ัà¸à¸à¹à¸à¸µà¹à¹à¸£à¸µà¸¢à¸à¸£à¹à¸à¸¢à¹à¸¥à¹à¸§!' ); 1978 gReportedApp = true; 1979 $J( "#ReportAppBtn").addClass( 'btn_active' ); 1980 } 1981 else 1982 { 1983 ShowAlertDialog( 'à¸à¹à¸à¸à¸´à¸à¸à¸¥à¸²à¸', 'à¸à¸£à¸§à¸à¸à¸à¸à¸±à¸à¸«à¸²à¹à¸à¸à¸²à¸£à¸à¸±à¸à¸à¸¶à¸à¸£à¸²à¸¢à¸à¸²à¸à¸à¸à¸à¸à¸¸à¸ à¹à¸à¸£à¸à¸¥à¸à¸à¹à¸«à¸¡à¹à¸à¸µà¸à¸à¸£à¸±à¹à¸à¸ ายหลัà¸' ); 1984 } 1985 1986 } ); 1987 } 1988 else 1989 { 1990 ShowAlertDialog( 'à¸à¹à¸à¸à¸´à¸à¸à¸¥à¸²à¸', 'à¸à¸¸à¸à¸à¹à¸à¸à¹à¸¥à¸·à¸à¸à¹à¸«à¸à¸¸à¸à¸¥à¸à¸µà¹à¸à¸¸à¸à¸£à¸²à¸¢à¸à¸²à¸à¸à¸¥à¸´à¸à¸ ัà¸à¸à¹à¸à¸µà¹!' ); 1991 } 1992 }); 1993} 1994 1995function ShowNavigatorShare() 1996{ 1997 if ( typeof navigator === 'object' && typeof navigator.share === 'function' ) 1998 { 1999 navigator.share( { url: window.location.href } ); 2000 } 2001 else if ( 'ReactNativeWebView' in window && typeof window.ReactNativeWebView === 'object' && typeof window.ReactNativeWebView.postMessage === 'function' ) 2002 { 2003 window.ReactNativeWebView.postMessage( JSON.stringify( { event_name: 'share', link: window.location.href } ) ); 2004 } 2005} 2006 2007function ShowGotSteamModal( strSteamURL, strAppName, strPlayLaunchVerb ) 2008{ 2009 var $ModalContent = $J("<dialog class=\"gotsteamModal\">\n\t<div class=\"got_steam_ctn\">\n\t<div class=\"got_steam_box\">\n\t\t<h1>\u0e14\u0e32\u0e27\u0e19\u0e4c\u0e42\u0e2b\u0e25\u0e14 Steam \u0e2b\u0e23\u0e37\u0e2d\u0e22\u0e31\u0e07?<\/h1>\n\t\t<p>\u0e04\u0e38\u0e13\u0e08\u0e33\u0e40\u0e1b\u0e47\u0e19\u0e15\u0e49\u0e2d\u0e07\u0e15\u0e34\u0e14\u0e15\u0e31\u0e49\u0e07<a href=\"https:\/\/store.steampowered.com\/about\/\">\u0e41\u0e2d\u0e1b\u0e1e\u0e25\u0e34\u0e40\u0e04\u0e0a\u0e31\u0e19 Steam \u0e1a\u0e19\u0e40\u0e14\u0e2a\u0e01\u0e4c\u0e17\u0e47\u0e2d\u0e1b<\/a> \u0e01\u0e48\u0e2d\u0e19\u0e17\u0e35\u0e48\u0e04\u0e38\u0e13\u0e08\u0e30\u0e2a\u0e32\u0e21\u0e32\u0e23\u0e16\u0e15\u0e34\u0e14\u0e15\u0e31\u0e49\u0e07\u0e41\u0e25\u0e30\u0e40\u0e23\u0e34\u0e48\u0e21 <strong class=\"gotSteam_AppName\"><\/strong> \u0e44\u0e14\u0e49 \u0e04\u0e38\u0e13\u0e44\u0e14\u0e49\u0e15\u0e34\u0e14\u0e15\u0e31\u0e49\u0e07 Steam \u0e1a\u0e19\u0e04\u0e2d\u0e21\u0e1e\u0e34\u0e27\u0e40\u0e15\u0e2d\u0e23\u0e4c\u0e19\u0e35\u0e49\u0e41\u0e25\u0e49\u0e27\u0e43\u0e0a\u0e48\u0e44\u0e2b\u0e21?<\/p>\n\t\t<div class=\"gotsteam_buttons\">\n\t\t\t<a class=\"gotSteam_SteamURL btn_blue leftbtn\" href=\"\">\n\t\t\t\t<h3>\u0e43\u0e0a\u0e48 Steam \u0e15\u0e34\u0e14\u0e15\u0e31\u0e49\u0e07\u0e2d\u0e22\u0e39\u0e48\u0e41\u0e25\u0e49\u0e27<\/h3>\n\t\t\t\t<h5 class=\"gotsteam_action\"><\/h5>\n\t\t\t<\/a>
2009\n\t\t\t<a href=\"https:\/\/store.steampowered.com\/about\/\" class=\"btn_blue\">\n\t\t\t\t<h3>\u0e44\u0e21\u0e48 \u0e09\u0e31\u0e19\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23 Steam<\/h3>\n\t\t\t\t<h5>\u0e14\u0e32\u0e27\u0e19\u0e4c\u0e42\u0e2b\u0e25\u0e14\u0e41\u0e25\u0e30\u0e2d\u0e48\u0e32\u0e19\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a Steam<\/h5>\n\t\t\t<\/a>\n\t\t\t<div style=\"clear: left;\"><\/div>\n\t\t<\/div>\n\t\t<div class=\"got_steam_low_block\">\n\t\t\t<div class=\"gotsteam_steam_ico\"><img src=\"https:\/\/store.fastly.steamstatic.com\/public\/images\/v6\/steam_ico.png\" width=\"40\" height=\"40\" border=\"0\" \/><\/div>\n\t\t\tSteam \u0e04\u0e37\u0e2d\u0e41\u0e1e\u0e25\u0e15\u0e1f\u0e2d\u0e23\u0e4c\u0e21\u0e40\u0e01\u0e21\u0e1a\u0e19\u0e40\u0e14\u0e2a\u0e01\u0e4c\u0e17\u0e47\u0e2d\u0e1b\u0e23\u0e30\u0e14\u0e31\u0e1a\u0e1e\u0e23\u0e35\u0e40\u0e21\u0e35\u0e22\u0e23\u0e4c \u0e2a\u0e21\u0e31\u0e04\u0e23\u0e1f\u0e23\u0e35\u0e41\u0e25\u0e30\u0e43\u0e0a\u0e49\u0e07\u0e32\u0e19\u0e07\u0e48\u0e32\u0e22 <a href=\"https:\/\/store.steampowered.com\/about\/\">\u0e40\u0e23\u0e35\u0e22\u0e19\u0e23\u0e39\u0e49\u0e40\u0e1e\u0e34\u0e48\u0e21\u0e40\u0e15\u0e34\u0e21\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a Steam<\/a>\n\t\t<\/div><\/div>\n\t<\/div>\n<\/dialog>"); 2010 $ModalContent.find('.gotSteam_AppName').text( strAppName ); 2011 $ModalContent.find('.gotsteam_action').text( strPlayLaunchVerb ); 2012 $ModalContent.find( '.gotSteam_SteamURL').attr( 'href', strSteamURL ); 2013 var Modal = new CModal( $ModalContent ); 2014 Modal.Show(); 2015 //ShowDialog( 'Got Steam?', $ModalContent ); 2016} 2017 2018function ChangeSeason( el, season ) 2019{ 2020 // switch active season indicator 2021 $J( '.series_seasons .season_selector .season_name' ).removeClass( 'active' ); 2022 $J( el ).addClass( 'active' ); 2023 2024 // switch which episodes are shown 2025 $J( '.season_episode_list_wrapper .season_episode_list' ).removeClass( 'active' ); 2026 $J( '.season_episode_list_wrapper .season_episode_list[data-season="' + season + '"]' ).addClass( 'active' ); 2027 2028 // load the images 2029 $J( '.season_episode_list.active .episode_image_col img').each( function( index, value ) 2030 { 2031 $J( this ).attr( 'src', $J( this ).data( 'src' ) ); 2032 }); 2033} 2034 2035function ShowRecommendedMoreInfoModal() 2036{ 2037 var strTemplate = "<div class=\"recommended_more_info_modal\">\n\t\t\t\t\t\t\t\t<p class=\"intro\">\u0e40\u0e23\u0e32\u0e44\u0e14\u0e49\u0e1e\u0e1a\u0e1a\u0e32\u0e07\u0e2a\u0e34\u0e48\u0e07\u0e17\u0e35\u0e48\u0e14\u0e39\u0e44\u0e21\u0e48\u0e40\u0e2b\u0e21\u0e37\u0e2d\u0e19\u0e2a\u0e34\u0e48\u0e07\u0e2d\u0e37\u0e48\u0e19\u0e43\u0e14\u0e17\u0e35\u0e48\u0e04\u0e38\u0e13\u0e44\u0e14\u0e49\u0e40\u0e04\u0e22\u0e43\u0e0a\u0e49\u0e07\u0e32\u0e19\u0e21\u0e32\u0e01\u0e48\u0e2d\u0e19\u0e2b\u0e19\u0e49\u0e32\u0e19\u0e35\u0e49 Steam \u0e08\u0e30\u0e40\u0e23\u0e35\u0e22\u0e19\u0e23\u0e39\u0e49\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e01\u0e32\u0e23\u0e1b\u0e23\u0e31\u0e1a\u0e41\u0e15\u0e48\u0e07\u0e02\u0e2d\u0e07\u0e04\u0e38\u0e13\u0e08\u0e32\u0e01\u0e40\u0e01\u0e21\u0e17\u0e35\u0e48\u0e04\u0e38\u0e13\u0e40\u0e25\u0e48\u0e19 \u0e20\u0e32\u0e1e\u0e22\u0e19\u0e15\u0e23\u0e4c\u0e17\u0e35\u0e48\u0e04\u0e38\u0e13\u0e23\u0e31\u0e1a\u0e0a\u0e21 \u0e41\u0e25\u0e30\u0e0b\u0e2d\u0e1f\u0e15\u0e4c\u0e41\u0e27\u0e23\u0e4c\u0e17\u0e35\u0e48\u0e04\u0e38\u0e13\u0e43\u0e0a\u0e49\u0e07\u0e32\u0e19\u0e1a\u0e19 Steam \u0e40\u0e23\u0e32\u0e08\u0e30\u0e43\u0e0a\u0e49\u0e01\u0e34\u0e08\u0e01\u0e23\u0e23\u0e21\u0e40\u0e2b\u0e25\u0e48\u0e32\u0e19\u0e35\u0e49\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e1b\u0e23\u0e31\u0e1a\u0e41\u0e15\u0e48\u0e07\u0e04\u0e33\u0e41\u0e19\u0e30\u0e19\u0e33\u0e02\u0e2d\u0e07\u0e04\u0e38\u0e13\u0e43\u0e2b\u0e49\u0e40\u0e2b\u0e21\u0e32\u0e30\u0e01\u0e31\u0e1a\u0e23\u0e2a\u0e19\u0e34\u0e22\u0e21\u0e02\u0e2d\u0e07\u0e04\u0e38\u0e13<\/p>\n\t\t\t\t\t\t\t\t<h2>\u0e0a\u0e2d\u0e1a\u0e1c\u0e25\u0e34\u0e15\u0e20\u0e31\u0e13\u0e11\u0e4c\u0e19\u0e35\u0e49?<\/h2>\n\t\t\t\t\t\t\t\t<p>\u0e2b\u0e32\u0e01\u0e04\u0e38\u0e13\u0e0a\u0e2d\u0e1a\u0e1c\u0e25\u0e34\u0e15\u0e20\u0e31\u0e13\u0e11\u0e4c\u0e19\u0e35\u0e49\u0e41\u0e25\u0e30\u0e15\u0e49\u0e2d\u0e07\u0e01\u0e32\u0e23\u0e08\u0e30\u0e2a\u0e31\u0e48\u0e07\u0e0b\u0e37\u0e49\u0e2d\u0e41\u0e25\u0e30\u0e40\u0e25\u0e48\u0e19 \u0e40\u0e23\u0e32\u0e08\u0e30\u0e19\u0e33\u0e02\u0e49\u0e2d\u0e21\u0e39\u0e25\u0e19\u0e35\u0e49\u0e44\u0e1b\u0e1b\u0e23\u0e3
20371\u0e1a\u0e43\u0e0a\u0e49\u0e01\u0e31\u0e1a\u0e1a\u0e31\u0e0d\u0e0a\u0e35\u0e40\u0e21\u0e37\u0e48\u0e2d\u0e14\u0e33\u0e40\u0e19\u0e34\u0e19\u0e01\u0e32\u0e23\u0e2a\u0e23\u0e49\u0e32\u0e07\u0e04\u0e33\u0e41\u0e19\u0e30\u0e19\u0e33\u0e43\u0e19\u0e2d\u0e19\u0e32\u0e04\u0e15<\/p>\n\t\t\t\t\t\t\t\t<h2>\u0e44\u0e21\u0e48\u0e2a\u0e19\u0e43\u0e08\u0e43\u0e19\u0e1c\u0e25\u0e34\u0e15\u0e20\u0e31\u0e13\u0e11\u0e4c\u0e25\u0e31\u0e01\u0e29\u0e13\u0e30\u0e19\u0e35\u0e49?<\/h2>\n\t\t\t\t\t\t\t\t<p>\u0e04\u0e38\u0e13\u0e22\u0e31\u0e07\u0e2a\u0e32\u0e21\u0e32\u0e23\u0e16\u0e01\u0e33\u0e2b\u0e19\u0e14\u0e04\u0e48\u0e32\u0e01\u0e32\u0e23\u0e1b\u0e23\u0e31\u0e1a\u0e41\u0e15\u0e48\u0e07\u0e23\u0e49\u0e32\u0e19\u0e04\u0e49\u0e32\u0e02\u0e2d\u0e07\u0e04\u0e38\u0e13\u0e40\u0e1e\u0e37\u0e48\u0e2d\u0e1a\u0e2d\u0e01 Steam \u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e41\u0e17\u0e47\u0e01\u0e2b\u0e23\u0e37\u0e2d\u0e1b\u0e23\u0e30\u0e40\u0e20\u0e17\u0e02\u0e2d\u0e07\u0e1c\u0e25\u0e34\u0e15\u0e20\u0e31\u0e13\u0e11\u0e4c\u0e17\u0e35\u0e48\u0e04\u0e38\u0e13\u0e44\u0e21\u0e48\u0e44\u0e14\u0e49\u0e2a\u0e19\u0e43\u0e08\u0e44\u0e14\u0e49 <a href=\"https:\/\/store.steampowered.com\/account\/preferences\/\">\u0e01\u0e32\u0e23\u0e40\u0e22\u0e35\u0e48\u0e22\u0e21\u0e0a\u0e21\u0e01\u0e32\u0e23\u0e1b\u0e23\u0e31\u0e1a\u0e41\u0e15\u0e48\u0e07\u0e23\u0e49\u0e32\u0e19\u0e04\u0e49\u0e32<\/a><\/p>\n\t\t\t\t\t\t\t<\/div>"; 2038 ShowAlertDialog( "\u0e40\u0e1e\u0e34\u0e48\u0e21\u0e40\u0e15\u0e34\u0e21\u0e40\u0e01\u0e35\u0e48\u0e22\u0e27\u0e01\u0e31\u0e1a\u0e04\u0e33\u0e41\u0e19\u0e30\u0e19\u0e33", strTemplate); 2039 2040} 2041 2042function CollapseLongStrings( strSelector ) 2043{ 2044 2045 $J(strSelector).each(function(i, j){ 2046 var $target = $J(j); 2047 if( j.scrollWidth > $target.innerWidth() ) 2048 { 2049 //$target.css({ 'overflow': 'hidden', 'white-space': 'nowrap', 'text-overflow': 'ellipsis' }); 2050 var elMoreBtn = document.createElement('div'); 2051 2052 elMoreBtn.classList.add('more_btn'); 2053 elMoreBtn.textContent = '+'; 2054 elMoreBtn.addEventListener('click', function($target, elButton, event){ 2055 2056 $target.css({'overflow': 'visible', 'white-space': 'normal'}); 2057 elButton.remove(); 2058 2059 }.bind(null, $target, elMoreBtn )); 2060 2061 j.parentNode.appendChild(elMoreBtn); 2062 } 2063 }) 2064} 2065 2066function JSReportProductAction( appId, pageAction, snr ) 2067{ 2068 $J.post( 'https://store.steampowered.com//ajaxreportproductaction/' + appId + '/', { 2069 'page_action' : pageAction, 2070 'snr' : snr 2071 } 2072 ); 2073} 2074 2075function ToggleBannerContentVisibility( divContentID, divIconID ) 2076{ 2077 var $element = $J( divContentID ); 2078 if ( $element !== null ) 2079 { 2080 $element.slideToggle( 'fast' ); 2081 } 2082 2083 var $icon = $J( divIconID ) 2084 if ( $icon !== null ) 2085 { 2086 $icon.toggleClass('expanded'); 2087 } 2088}; 2089 2090// The review detail section is simplified for mobile screens 2091// which requires some section moves here, and formatting changes in .css 2092function ReparentReviewsForSmallScreens() 2093{ 2094 var bUseGamepadScreenMode = window.UseGamepadScreenMode && window.UseGamepadScreenMode(); 2095 var fn_reparent = bUseGamepadScreenMode ? Responsive_ReparentItemsInGamepadMode : Responsive_ReparentItemsInMobileMode; 2096 2097 var $MoveReviewSections = $J('.user_reviews_container'); 2098 $MoveReviewSections.each( function() { 2099 2100 // move content within each review detail section for small screen layout 2101 var $SummarySection = $J(this).find('[id^="ReviewContent"]'); 2102 $SummarySection.each( function() { 2103 2104 var $LeftColContent = $J(this).find('.leftcol'); 2105 var $NewLeftLocation = $J(this).find('.responsive_review_leftcol_
2105ctn'); 2106 var $ReviewAwardCtn = $J(this).find('.review_award_ctn'); 2107 var $NewAwardLocation = $J(this).find('.rightcol'); 2108 2109 // if we don't have the required destination containers bail (this shouldn't happen) 2110 if ( $NewLeftLocation.length == 0 || $NewAwardLocation.length == 0 ) 2111 return; 2112 2113 fn_reparent( '#' + $LeftColContent.attr('id'), $J( '#' + $NewLeftLocation.attr('id') ) ); 2114 fn_reparent( '#' + $ReviewAwardCtn.attr('id'), $J( '#' + $NewAwardLocation.attr('id') ) ); 2115 } ); 2116 } ); 2117} 2118 2119// Share dialog calls this to copy the store page link to the clipboard 2120function ShareDialogCopyToClipboard() 2121{ 2122 // select the text and copy to clipboard 2123 var $res = false; 2124 var $linkText = document.getElementById('shareDialogLinkStoreLink'); 2125 if ( $linkText ) 2126 { 2127 // using .select() instead causes the copy command to fail on iOS 12 2128 $linkText.setSelectionRange(0, $linkText.value.length); 2129 $res = document.execCommand( "copy" ); 2130 } 2131 2132 // report the result 2133 $J('#shareDialogResult').text( $res ? 'Link copied' : 'ลà¹à¸¡à¹à¸«à¸¥à¸§à¹à¸à¸à¸²à¸£à¸à¸±à¸à¸¥à¸à¸à¸¥à¸´à¸à¸à¹à¸£à¹à¸²à¸à¸à¹à¸²à¹à¸à¸¢à¸±à¸à¸à¸¥à¸´à¸à¸à¸à¸£à¹à¸'); 2134 2135 // clear the selection 2136 document.getSelection().removeAllRanges(); 2137} 2138 2139// A very basic scroll helper which only supports vertical scrolling 2140// Added to translate gamepad navigation events into scroll up/down 2141function ScrollElement( elementID, nAmount ) 2142{ 2143 var element = $J( elementID )[0]; 2144 if ( !element ) 2145 return false; 2146 2147 if ( nAmount > 0 && element.scrollTop + element.clientHeight < element.scrollHeight ) 2148 element.scrollBy( 0, nAmount ); 2149 else if ( nAmount < 0 && element.scrollTop > 0 ) 2150 element.scrollBy( 0, nAmount ); 2151 else 2152 return false; 2153 2154 return true; 2155} 2156 2157// initially only used by tablet 2158function ShowEarlyAccessModal( contentID ) 2159{ 2160 var $Content = $J( contentID ); 2161 $Content.detach(); 2162 $Content.show(); 2163 2164 ShowDialog( "\u0e0b\u0e2d\u0e1f\u0e15\u0e4c\u0e41\u0e27\u0e23\u0e4c\u0e23\u0e30\u0e2b\u0e27\u0e48\u0e32\u0e07\u0e01\u0e32\u0e23\u0e1e\u0e31\u0e12\u0e19\u0e32", $Content ).always( 2165 function() { 2166 2167 // save it away again for later 2168 $Content.hide(); 2169 $J( document.body ).append( $Content ); 2170 } 2171 ); 2172} 2173 2174// applies layout changes for mobile and gamepad screens 2175function ReparentAppLandingPageForSmallScreens() 2176{ 2177 // copy the review summary content to the review details section 2178 // CSS will set when to make these visible 2179 var $appReviewsAll = $J('#appReviewsAll_responsive').clone(); 2180 $appReviewsAll.attr( 'id', 'appReviewsAll_Detail' ); 2181 $appReviewsAll.css( 'display', 'none' ); 2182 $appReviewsAll.appendTo('.reviews_info_ctn'); 2183 var $appReviewsRecent = $J('#appReviewsRecent_responsive').clone(); 2184 $appReviewsRecent.attr( 'id', 'appReviewsRecent_Detail' ); 2185 $appReviewsRecent.css( 'display', 'none' ); 2186 $appReviewsRecent.appendTo('.reviews_info_ctn'); 2187 2188 // on iOS use the iOS share icon. the default is Android. 2189 if ( navigator.userAgent.toLowerCase().indexOf( 'iphone' ) != -1 ) 2190 { 2191 $J('#shareImg').attr('src', '../images/icon-share-ios.svg' ); 2192 } 2193 2194 var bUseGamepadScreenMode = window.UseGamepadScreenMode && window.UseGamepadScreenMode(); 2195 2196 ReparentPurchaseOptionsForGamepad( '#purchaseOptionsContent' ); 2197 2198 var fn_reparent = bUseGamepadScreenMode ? Responsive_ReparentItemsInGamepadMode : Responsive_ReparentItemsInMobileMode; 2199 2200 if ( bUseGamepadScreenMode ) 2201 { 2202 // move the page header image to the top of purchase options 2203 fn_reparent( $J('#page_header_img'), $J('#purchaseOptionsContent') ); 2204 } 2205 2206 // move early access content into the purchase options parent 2207 if ( bUseGamepadScreenMode ) 2208 { 2209 // on tablet we provide a learn more link which opens a dialog containing early access details 2210 fn_reparent( $J('#earlyAccessBody'), $J('#earlyAccessTabletDialogContent') ); 2211 fn_reparent( $J('#earlyAccessTabletContent'), $J('#earlyAccessHeader_Gamepad') ); 2212 } 2213 else 2214 { 2215 fn_reparent( '.early_access_header', $J('#purchaseOptionsContent') ); 2216 } 2217 2218 // move purchase options into a new container for responsive UX 2219 if ( !bUseGamepadScreenMode ) 2220 { 2221 fn_reparent( '#game_area_purchase', $J( '#purchaseOptionsContent' ) ); 2222 } 2223 else 2224 { 2225 // Hide it 2226 $J( '#game_area_purchase' ).hide(); 2227 } 2228 2229 // order the action buttons 2230 fn_reparent( '#reportBtn', $J('#rowBtnActions') ); 2231 fn_reparent( '#queueBtnFollow', $J('#rowBtnActions') ); 2232 fn_reparent( '#ignoreBtn', $J('#rowBtnActions') ); 2233 fn_reparent( '#shareBtn', $J('#rowBtnActions') ); 2234 fn_reparent( '#rowBtnActions', $J('#queueActionsCtn') ); 2235 2236 // place discovery queue below the action buttons 2237 fn_reparent( '#nextInDiscoveryQueue', $J('#queueCtn') ); 2238 2239 // place banners and game details into the links and info section 2240 fn_reparent( '#bannerAchievements', $J( '#appLinksAndInfo' ) ); 2241 fn_reparent( '#bannerPointsShop', $J( '#appLinksAndInfo' ) ); 2242 fn_reparent( '#bannerItemStore', $J( '#appLinksAndInfo' ) ); 2243 fn_reparent( '#bannerCommunity', $J( '#appLinksAndInfo' ) ); 2244 2245 // on tablet these go into a dropdown
2246 fn_reparent( '#appDetailsUnderlinedLinks', bUseGamepadScreenMode ? $J( '#appLinksAndInfo_TabletDropdownContent' ) : $J( '#appLinksAndInfo' ) ); 2247 2248 // place the active review filter list in the review details section 2249 fn_reparent( '#reviews_active_filters', $J('.reviews_info_ctn') ); 2250 2251 // populate the reviews settings popup 2252 fn_reparent( '#review_histograms_container', $J( '#reviewSettingsPopupContent' ) ); 2253 fn_reparent( '#reviews_filter_options', $J( '#reviewSettingsPopupContent' ) ); 2254 2255 // move some of the links and info content to the bottom 2256 // testing this - we may need to move this again 2257 fn_reparent( '#genresAndManufacturer', $J( '#appLinksAndInfo' ) ); 2258 2259 // the window resize message handler is for layout adjustements that require more logic than reparenting based on screen size 2260 var defaultShareFlex = $J('#shareBtn').css('flex-grow'); 2261 var defaultFollowFlex = $J('#queueBtnFollow').css('flex-grow'); 2262 var defaultReportFlex = $J('#reportBtn').css('flex-grow'); 2263 var defaultLanguageTableDisplay = $J('#languageTable').css('display'); 2264 2265 var msgWatch = bUseGamepadScreenMode ? 'Responsive_GamepadScreenModeToggled' : 'Responsive_MobileScreenModeToggled'; 2266 $J(window).on( msgWatch, function() { 2267 2268 var bUseNewUX = ( bUseGamepadScreenMode || ( window.UseMobileScreenMode && window.UseMobileScreenMode() ) ); 2269 2270 // if one of the wishlist buttons are visible make the action buttons flex grow so the two rows of buttons match width. 2271 if ( bUseNewUX && ( $J('#add_to_wishlist_area').is(':visible') 2272 || $J('#add_to_wishlist_area_success').is(':visible') 2273 || $J('#add_to_wishlist_area_fail').is(':visible') ) ) 2274 { 2275 $J('#shareBtn').css('flex-grow', '1'); 2276 $J('#queueBtnFollow').css('flex-grow', '1'); 2277 $J('#reportBtn').css('flex-grow', '1'); 2278 } 2279 else 2280 { 2281 $J('#shareBtn').css('flex-grow', defaultShareFlex); 2282 $J('#queueBtnFollow').css('flex-grow', defaultFollowFlex); 2283 $J('#reportBtn').css('flex-grow', defaultReportFlex); 2284 } 2285 2286 // if we're in a small screen layout reveal all language options within the languageTable 2287 // don't bother trying to re-hide these if the user later resizes outside small screen 2288 if ( bUseNewUX ) 2289 { 2290 var $LanguageOptions = $J('#languageTable').find('tr'); 2291 $LanguageOptions.each( function() { 2292 $J(this).css('display',''); 2293 } ); 2294 2295 // hide the 'see all X languages" link since all languages are now visible 2296 $J('#languageTable').find('.all_languages').css('display', 'none'); 2297 } 2298 else 2299 { 2300 $J('#reviews_active_filters').attr( 'role', 'group' ); 2301 } 2302 2303 // hide the language table on small screens since a banner is clicked to reveal it 2304 // 2305 // putting a display:none in CSS for small screen sizes doesn't work well because the 2306 // CSS values don't override the display state set by the language banner click handler 2307 $J('#languageTable').css('display', bUseNewUX ? 'none' : defaultLanguageTableDisplay ); 2308 }); 2309 $J(window).trigger( msgWatch ); 2310 2311 // the vertical space available to display purchase options can change when the page scrolls. Check if we need to update the height. 2312 if ( bUseGamepadScreenMode ) 2313 $J(window).on( 'scroll', UpdateGamepadPurchaseOptionsHeight ); 2314} 2315 2316// Update the height of the sticky, scrollable purchase options container. 2317function UpdateGamepadPurchaseOptionsHeight() 2318{ 2319 if ( window.UseGamepadScreenMode && window.UseGamepadScreenMode() ) 2320 { 2321 let $purchaseContentHeight = parseInt( window.innerHeight ) - parseInt( GetResponsiveHeaderFixedOffsetAdjustment() ) - parseInt( $J('#purchaseOptionsContentTablet')[0].getBoundingClientRect().top ); 2322 $purchaseContentHeight += 'px'; 2323 let $stickyDiv = $J('#purchaseOptionsContentTablet'); 2324 if ( $stickyDiv && $stickyDiv.css( 'height') != $purchaseContentHeight ) 2325 { 2326 $stickyDiv.css( 'height', $purchaseContentHeight ); 2327 } 2328 } 2329} 2330 2331function ReparentPurchaseOptionsForGamepad( idPurchaseOptions ) 2332{ 2333 // gamepad has its own purchase options container (shown on right side of screen) 2334 if ( window.UseGamepadScreenMode && window.UseGamepadScreenMode() ) 2335 { 2336 Responsive_ReparentItemsInGamepadMode( idPurchaseOptions, $J('#purchaseOptionsContentTablet') ); 2337 2338 $J('#purchaseOptionsContentTablet').css('top', parseInt( GetResponsiveHeaderFixedOffsetAdjustment() ) + 'px' ); 2339 $J('#purchaseOptionsContentTablet').css('scrollTop', 0); 2340 UpdateGamepadPurchaseOptionsHeight(); 2341 } 2342} 2343 2344 2345// Popup for customers to optin/out of the new mobile UX 2346var g_newMobileUXPopup = null; 2347function ShowUseNewMobileUXPopup() 2348{ 2349 var $ModalContent = $J('#popupNewMobileUX'); 2350 if ( 0 == $ModalContent.length ) 2351 return; 2352
2353 g_newMobileUXPopup = new CModal( $ModalContent ); 2354 g_newMobileUXPopup.Show(); 2355} 2356 2357// check if customer selection for viewing the new mobile UX has changed 2358function OnNewMobileUXPopupClosed() 2359{ 2360 if ( typeof g_newMobileUXPopup !== 'undefined' ) 2361 g_newMobileUXPopup.Dismiss(); 2362} 2363 2364// called by Mobile UX to show a settings dialog with Review filters 2365var g_reviewSettingsPopup = null; 2366function ShowReviewSettingsModal() 2367{ 2368 // bail if we can't find the popup content, or it's not populated 2369 var $ModalContent = $J('#reviewSettingsPopupCtn'); 2370 if ( 0 == $ModalContent.length || 0 == $ModalContent.find('#reviews_filter_options').length ) 2371 { 2372 return; 2373 } 2374 2375 g_reviewSettingsPopup = new CModal( $ModalContent ); 2376 g_reviewSettingsPopup.Show(); 2377} 2378function CloseReviewSettingsModal() 2379{ 2380 if ( typeof g_reviewSettingsPopup !== 'undefined' ) 2381 g_reviewSettingsPopup.Dismiss(); 2382} 2383 2384function BindFocusVideoOnTablet() 2385{ 2386 $J( '.highlight_movie' ).on( "focusout", function( event ) { 2387 this.pause(); 2388 } ); 2389} 2390 2391function AddRightNavStickyPaddingOnTablet() 2392{ 2393 const elSticky = document.querySelector('.purchase_options_content_tablet' ); 2394 const observer = new IntersectionObserver( 2395 ([e]) => e.target.classList.toggle('isCurrentlySticky', e.isIntersecting ), 2396 {threshold: [1]} 2397 ); 2398 observer.observe( elSticky ) 2399} 2400 2401function ToggleShowAllPackageContentsText( event ) 2402{ 2403 const elContainer = event.target.parentNode.parentNode; 2404 elContainer.classList.toggle( 'package_contents_collapsed'); 2405} 2406 2407function SetupReviewFilterMenus() 2408{ 2409 [...document.querySelectorAll( "#reviews_filter_options button[aria-expanded]" )].forEach( ( button ) => { 2410 const flyout = button.ariaControlsElements?.[0] ?? document.getElementById(button.getAttribute("aria-controls")); 2411 button.addEventListener( "click", () => { 2412 if ( button.ariaExpanded === "true" ) 2413 { 2414 button.ariaExpanded = "false"; 2415 } 2416 else { 2417 button.ariaExpanded = "true"; 2418 } 2419 2420 const currentFocus = document.activeElement; 2421 let nextFocusTarget = flyout?.firstElementChild; 2422 const visited = new Set(); 2423 while ( nextFocusTarget && nextFocusTarget !== flyout ) 2424 { 2425 nextFocusTarget.focus(); 2426 if ( document.activeElement !== currentFocus ) 2427 { 2428 // successfully moved focus 2429 break; 2430 } 2431 visited.add( nextFocusTarget ); 2432 if ( nextFocusTarget.firstElementChild && !visited.has( nextFocusTarget.firstElementChild ) ) 2433 { 2434 nextFocusTarget = nextFocusTarget.firstElementChild; 2435 } 2436 else if ( nextFocusTarget.nextElementSibling && !visited.has( nextFocusTarget.nextElementSibling ) ) 2437 { 2438 nextFocusTarget = nextFocusTarget.nextElementSibling; 2439 } 2440 else 2441 { 2442 nextFocusTarget = nextFocusTarget.parentElement; 2443 } 2444 } 2445 } ); 2446 2447 flyout?.addEventListener( "focusout", ( e ) => { 2448 if ( !flyout.contains( e.relatedTarget ) ) 2449 { 2450 button.ariaExpanded = "false"; 2451 } 2452 } ); 2453 } ); 2454 2455 // $J( 'input[name="review_topic"]' ).not( '[always-show]' ).each( function() 2456 // { 2457 // $J(this).hide(); 2458 // $J('label[for="' + this.id + '"]').hide(); 2459 // } ); 2460 2461} 2462 2463function CheckClientBootProtection( strType ) 2464{ 2465 let element = document.querySelector( `[data-bootprotection="${ strType }"]` ); 2466 if ( !element ) 2467 return; 2468 2469 if ( typeof SteamClient === "undefined" || !SteamClient.Compat || !SteamClient.Compat.CheckBootProtectionEnabled ) 2470 return; 2471 2472 if ( strType != 'secureboot_tpm2' ) 2473 return; 2474 2475 SteamClient.Compat.CheckBootProtectionEnabled( '2', true ).then( ( bEnabled ) => { 2476 if ( !bEnabled ) 2477 element.style.display = 'block'; 2478 } ); 2479} 2480 2481// Go through images on the store page and add: 2482// - Click to play/pause for animated images. 2483// - Magnifying glass to zoom in when an image has a bigger version available. 2484$J(document).ready(function() { 2485 // TODO: add back initial paused state is based reduced motion preference. We were using 2486 // `window.matchMedia("(prefers-reduced-motion: reduce)").matches` but it turns out that this is set when users 2487 // don't expect it, such as if they have some animations turned off in Windows or other things. We're going to 2488 // add a setting to client and then we can plumb it through here, which should help. 2489 const bInitiallyPaused = false;
2490 let bPaused = bInitiallyPaused; 2491 2492 function addActionBar($el) { 2493 const $iconArea = $J(`<div class='bb_img_icons' />`); 2494 $el.append($iconArea); 2495 return $iconArea; 2496 } 2497 2498 function addPlayPauseIcon($outerDiv, $actionBar) { 2499 const strTooltip = "\u0e40\u0e1a\u0e23\u0e32\u0e27\u0e4c\u0e40\u0e0b\u0e2d\u0e23\u0e4c\u0e02\u0e2d\u0e07\u0e04\u0e38\u0e13\u0e40\u0e1b\u0e34\u0e14\u0e43\u0e0a\u0e49\u0e07\u0e32\u0e19\u0e01\u0e32\u0e23\u0e15\u0e31\u0e49\u0e07\u0e04\u0e48\u0e32\u0e01\u0e32\u0e23\u0e25\u0e14\u0e01\u0e32\u0e23\u0e40\u0e04\u0e25\u0e37\u0e48\u0e2d\u0e19\u0e44\u0e2b\u0e27\u0e1a\u0e19\u0e2b\u0e19\u0e49\u0e32\u0e08\u0e2d\u0e44\u0e27\u0e49 \u0e14\u0e31\u0e07\u0e19\u0e31\u0e49\u0e19\u0e20\u0e32\u0e1e\u0e40\u0e04\u0e25\u0e37\u0e48\u0e2d\u0e19\u0e44\u0e2b\u0e27\u0e19\u0e35\u0e49\u0e08\u0e36\u0e07\u0e2b\u0e22\u0e38\u0e14\u0e0a\u0e31\u0e48\u0e27\u0e04\u0e23\u0e32\u0e27\u0e15\u0e32\u0e21\u0e04\u0e48\u0e32\u0e40\u0e23\u0e34\u0e48\u0e21\u0e15\u0e49\u0e19"; 2500 const strPanel = 'data-panel="{"focusable":true,"clickOnActivate":true}" role="button"'; 2501 const $btnPlayPause = $J(`<div class='bb_img_play_pause' ${strPanel} data-tooltip='${strTooltip}' data-tooltip-visible=${bInitiallyPaused}> 2502 <img width="18" height="18" draggable="false" /> 2503 </div>`); 2504 $actionBar.append($btnPlayPause); 2505 2506 $btnPlayPause.on("click", function(e) { 2507 e.stopPropagation(); // so it doesn't double trigger outer click 2508 $outerDiv.trigger("click"); 2509 }); 2510 2511 return $btnPlayPause; 2512 } 2513 2514 function addExpandIcon($actionBar, $img, bigSrc) { 2515 const strPanel = 'data-panel="{"focusable":true,"clickOnActivate":true}" role="button"'; 2516 const $btnExpand = $J(`<div class='bb_img_expand' ${strPanel}>ð</div>`); 2517 $actionBar.append($btnExpand); 2518 2519 $btnExpand.on("click", function(e) { 2520 const newSrc = bigSrc && bigSrc.length ? bigSrc : $img.attr("src"); 2521 window.open(newSrc, "_blank"); 2522 }); 2523 2524 return $btnExpand; 2525 } 2526 2527 function updatePlayPauseStyle($el, bPaused, bInitial) { 2528 $el.toggleClass("paused", bPaused); 2529 $el.toggleClass("playing", !bPaused); 2530 const $img = $el.find('img'); 2531 $img.attr('src', bPaused ? "/public/images/ico/media_playback/play.svg" : "/public/images/ico/media_playback/pause.svg"); 2532 $img.attr('alt', bPaused ? "\u0e40\u0e25\u0e48\u0e19\u0e41\u0e2d\u0e19\u0e34\u0e40\u0e21\u0e0a\u0e31\u0e19" : "\u0e2b\u0e22\u0e38\u0e14\u0e41\u0e2d\u0e19\u0e34\u0e40\u0e21\u0e0a\u0e31\u0e19\u0e0a\u0e31\u0e48\u0e27\u0e04\u0e23\u0e32\u0e27"); 2533 if (!bInitial) { 2534 // hide the initial tooltip, that should only show if the image started paused and has never been played 2535 $el.removeAttr("data-tooltip-visible"); 2536 } 2537 } 2538 2539 function updatePlayPauseOnAll() 2540 { 2541 bPaused = !bPaused; 2542 2543 $J(".bb_img_ctn").each(function() { 2544 const $outerDiv = $J(this); 2545 2546 const $pic = $outerDiv.find("picture"); 2547 const $video = $outerDiv.find("video"); 2548 2549 const bVideoWithSources = $video.length !== 0 && $video.find("source").length > 0; 2550 2551 const $btnPlayPause = $outerDiv.find('.bb_img_play_pause'); 2552 2553 if ($pic.length > 0) { 2554 // handle click-to-play for picture elements 2555 const $source = $pic.find("source"); 2556 const $img = $pic.find("img"); 2557 2558 const pausedSrc = $img.attr("src"); 2559 let animatedSrc = $source.attr("srcset"); 2560 2561 // store the source's srcset the first time we come through here so we have it after we change it 2562 if (!$source.data('original-srcset')) { 2563 $source.data('original-srcset', animatedSrc); 2564 } else { 2565 animatedSrc = $source.data('original-srcset'); 2566 } 2567 2568 // on first click, drop the media attribute so the source always applies 2569 if ($source.attr("media")) { 2570 $source.removeAttr("media"); 2571 } 2572 2573 $source.attr("srcset", bPaused ? pausedSrc : animatedSrc); 2574 updatePlayPauseStyle($btnPlayPause, bPaused, false); 2575 2576 // force re-evaluation by replacing <img src> with a dummy and then back 2577 const temp = $img.attr("src"); 2578 $img.attr("src", "").attr("src", temp); 2579 } else if (bVideoWithSources) { 2580 // handle click-to-play for video elements 2581 const elVideo = $video[0]; 2582 2583 if (bPaused) { 2584 elVideo.pause(); 2585 } else { 2586 elVideo.play(); 2587 } 2588 2589 updatePlayPauseStyle($btnPlayPause, bPaused, false); 2590 } 2591 }); 2592 } 2593 2594 $J(".bb_img_ctn").each(function() { 2595 const $outerDiv = $J(this); 2596 2597 const $pic = $outerDiv.find("picture"); 2598 const $video = $outerDiv.find("video"); 2599 const $img = $outerDiv.find("img"); 2600 2601 const $actionBar = addActionBar($outerDiv); 2602 2603 // only imgs can have expand buttons 2604 let $btnExpand = null; 2605 const nColumnWidth = 780;
2606 if ($img.length !== 0 && ($img.data("big-src") !== undefined || $img.attr("width") > nColumnWidth * 1.25)) { 2607 $btnExpand = addExpandIcon($actionBar, $img, $img.data("big-src")); 2608 } 2609 2610 const bVideoWithSources = $video.length !== 0 && $video.find("source").length > 0; 2611 2612 // only picture and video elements have play/pause buttons 2613 let $btnPlayPause = null; 2614 if ($pic.length !== 0 || bVideoWithSources) { 2615 $btnPlayPause = addPlayPauseIcon($outerDiv, $actionBar); 2616 updatePlayPauseStyle($btnPlayPause, bPaused, true); 2617 } 2618 2619 if ($pic.length > 0) { 2620 // handle click-to-play for picture elements 2621 $outerDiv.on("click", function() { 2622 updatePlayPauseOnAll(); 2623 }); 2624 } else if (bVideoWithSources) { 2625 // handle click-to-play for video elements 2626 const elVideo = $video[0]; 2627 2628 if (bPaused) { 2629 elVideo.removeAttribute("autoplay"); 2630 } 2631 2632 $outerDiv.on("click", function() { 2633 updatePlayPauseOnAll(); 2634 }); 2635 } 2636 }); 2637}); 2638
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.