1var stop = false; 2var mweb = mweb || 0; 3var giveUpDelay = 0; 4var gameTimer; 5var startTime; 6var origSecs; 7var origQuizTime; 8var timerSecs; 9var pausedTime = 0; 10var pausedStartTime; 11var overrideSecs = -1; 12var timerCorrection; 13var inProgress = false; 14var didGiveUp = false; 15var stopwatch = false; 16var scoreAsPercentage = false; 17var numRight = 0; 18var currDisplayScore = 0; 19var oldTime = null; 20var delayCount = 0; 21var isTournamentQuiz = false; 22const JUST_GO_PLAY_THRESHOLD = 15; 23 24$j(function () { 25 oldTime = $j('#time').html().trim(); 26}); 27 28function selectTimerSetting(selection, isShowdown) { 29 const isSporcleAppView = (window._payload || {}).is_flagship_app_view; 30 31 if (selection == 'stopwatch' && !userID) { 32 window.SporcleLib.Modal.openRegisterModal({ 33 regPath : 'quiz_page_practice_mode', 34 }); 35 return; 36 } 37 38 let timeEl = mweb ? $j('#reckoning-time') : $j('#time'); 39 40 if (inProgress) { 41 return; 42 } 43 44 if (typeof isShowdown === 'undefined') { 45 isShowdown = false; 46 } 47 48 $j('#time-box-dropdown-menu button').each(function(index, element) { 49 const $el = $j(element); 50 if ($el.data('value') === selection) { 51 $el.addClass('active'); 52 } else { 53 $el.removeClass('active'); 54 } 55 }); 56 57 // Only do the things if the mode actually changes 58 if (selection === 'timer' && stopwatch) { 59 stopwatch = false; 60 timeEl.html(oldTime); 61 62 if (!mweb) { 63 $j('#timeBox .dropdown-trigger').html('Timer'); 64 $j('#timeBox #practice-mode-container').addClass('disabled'); 65 } 66 67 $j('#timeBox, #scoreBox, #upTime').removeClass('timePast'); 68 69 if (userID) { 70 if (isShowdown) { 71 $j('#share-game-challenge').addClass('disabled'); 72 window.Sporcle.scoreboards.showdown.params.mode = 'showdownFriends'; 73 showCollapsedQuizScoreboard($j('#showdown-scoreboard'), window.Sporcle.scoreboards.showdown); 74 } else { 75 window.Sporcle.scoreboards.quiz.params.mode = 'quizFriends'; 76 showCollapsedQuizScoreboard($j('#quiz-scoreboard'), window.Sporcle.scoreboards.quiz); 77 } 78 } 79 80 if (isSporcleAppView && Sporcle.gameData.quizHelpers) { 81 if (!Sporcle.gameData.quizHelpers.filter(qh => qh.id !== 'the-extender').length) { 82 $j('#quiz-helpers-open').show(); 83 } 84 } 85 } else if (selection === 'stopwatch' && !stopwatch) { 86 stopwatch = true; 87 timeEl.html('00:00'); 88 89 if (!mweb) { 90 $j('#timeBox .dropdown-trigger').html('Practice Mode'); 91 $j('#timeBox #practice-mode-container').removeClass('disabled'); 92 } 93 94 $j('#share-game-challenge').addClass('disabled'); 95 $j('#timeBox, #scoreBox, #upTime').addClass('timePast'); 96 97 if (userID) { 98 window.Sporcle.scoreboards.quiz.params.mode = 'quizStopwatch'; 99 showCollapsedQuizScoreboard($j('#quiz-scoreboard'), window.Sporcle.scoreboards.quiz); 100 } 101 102 if (isSporcleAppView && Sporcle.gameData.quizHelpers) { 103 if (!Sporcle.gameData.quizHelpers.filter(qh => qh.id !== 'the-extender').length) { 104 $j('#quiz-helpers-open').hide(); 105 } 106 } 107 } 108} 109 110function selectScoreSetting(selection) { 111 if (!window.userID) { 112 window.SporcleLib.Modal.openRegisterModal({ 113 regPath : 'quiz_page_score_settings', 114 }); 115 return; 116 } 117 118 $j('#score-box-dropdown-menu button').each(function(index, element) { 119 const $el = $j(element); 120 if ($el.data('value') === selection) { 121 $el.addClass('active'); 122 } else { 123 $el.removeClass('active'); 124 } 125 }); 126 127 scoreAsPercentage = (selection === 'percentage'); 128 129 displayScore(); 130} 131 132function startTimer() { 133 timerSecs = stopwatch ? 0 : origSecs; 134 startTime = new Date().getTime(); 135 gameTimer = setTimeout(redo, 1000); 136 137 $j('#time-progress').css({ 138 'animation-duration' : `${origSecs * 1000}ms`, 139 'animation-play-state' : stopwatch ? 'paused' : 'running' 140 }); 141} 142 143function displayScore(numCorrect, showMax = true) { 144 const isSporcleAppView = (window._payload || {}).is_flagship_app_view; 145 146 // if (isSporcleAppView) { 147 // window.SporcleApp.api.playHaptic('soft'); 148 // } 149 150 if (typeof numCorrect === 'undefined') { 151 numCorrect = currDisplayScore; 152 } else { 153 currDisplayScore = numCorrect; 154 } 155 156 let maxScore = answers.length; 157 158 if (window.pinpoint) { 159 maxScore = maxScore * 100; 160 } 161 162 var score; 163 if (scoreAsPercentage) { 164 var userPct = _.round((numCorrect/maxScore), 2); 165 score = _.round(100 * userPct) + '%'; 166 } else { 167 if (showMax) { 168 score = numCorrect + "/" + maxScore; 169 170 if (mweb) { 171 score = numCorrect + " / " + maxScore; 172 } 173 } else { 174 score = numCorrect; 175 } 176 } 177 178 $j('.correctnum').text(numCorrect); 179 $j('#scoreBox .currentScore').text(score); 180} 181 182function displayTime(totalSecs) { 183 var min = Math.floor(totalSecs/60); 184 var sec = totalSecs%60; 185 return (min<10?"0":"")+min+":"+(sec<10?"0":"")+sec; 186} 187 188function endGame() { 189 if (!inProgress) return; 190 191 inProgress = false;
192 193 clearTimeout(gameTimer); 194 195 // Defer this block to reduce INP 196 window.setTimeout(async function() { 197 showMissed(); 198 199 if (window.Sporcle.postQuizAnimation) { 200 await window.Sporcle.postQuizAnimation; 201 } 202 203 $j(window).trigger('endgame'); 204 $j('body').removeClass('active').addClass('over'); 205 206 callSPHooks('end'); 207 208 }, 0); 209} 210 211function giveUp() { 212 didGiveUp = true; 213 logPlay(); 214 endGame(); 215 216 if (typeof gameID !== 'undefined') { 217 $j.get('/games/includes/event.php', { 218 g: gameID, 219 mw: mweb, 220 sw: stopwatch ? 1 : 0, 221 a: 'gu' 222 }); 223 } 224} 225 226function addGiveUp() { 227 const $giveUp = $j('#giveUp'); 228 $giveUp.prop('disabled', false) 229 $giveUp.one('click', giveUp); 230 $giveUp.show(); 231} 232 233async function redo() { 234 if (stop || didGiveUp) { 235 return; 236 } 237 238 timerSecs += stopwatch ? 1 : -1; 239 240 if (stopwatch) { 241 if (timerSecs == giveUpDelay) { 242 addGiveUp(); 243 } 244 } else { 245 if ((origSecs - timerSecs) == giveUpDelay) { 246 addGiveUp(); 247 } 248 249 if (timerSecs == 10) { 250 $j('#timeBox').addClass('timeTicking'); 251 } 252 } 253 254 // Pinpoint quizzes have a delay to show the scoring radius. Crossover quizzes also have a delay. We don't want these checks to cut this short 255 if (!window.pinpoint && !window.crossover) { 256 if (guessesRemaining == 0) { 257 /* Process the end of a click box or picture click game where they ran out of guesses (regardless if they got them all right) */ 258 logPlay(); 259 endGame(); 260 261 return; 262 } 263 264 if (inProgress && endOnAllCorrect && found.length == answers.length) { 265 /* Process the end of normal game where they got them all right before the time ran out */ 266 logPlay(); 267 endGame(); 268 return; 269 } 270 } 271 272 $j('#time').html(displayTime(timerSecs)); 273 274 restoreFocus(); 275 276 if (timerSecs == 5999) { 277 // End the game if the timer ever hits 99:59. This is for stopwatch 278 logPlay(); 279 endGame(); 280 return; 281 } 282 283 if (stopwatch) { 284 if (timerSecs == origSecs) { 285 /* They ran out of time, log a timer play but keep counting up */ 286 logTimerPlay(); 287 288 $j.get('/games/includes/event.php', {g:gameID, mw:mweb, sw:stopwatch?1:0, a:'av'}); 289 } 290 timerCorrection = (new Date().getTime() - (startTime+pausedTime)) - (timerSecs*1000); 291 gameTimer = setTimeout(redo, 1000-timerCorrection); 292 } else { 293 if (timerSecs == 0) { 294 $j('#time-progress').css({ 295 'animation-play-state' : 'paused' 296 }); 297 298 let showAddTime = true; 299 let timeToAdd; 300 301 if (origQuizTime < 60) { 302 timeToAdd = 15; // 15 seconds 303 } else if (origQuizTime < 240) { 304 timeToAdd = 30; // 30 seconds 305 } else { 306 timeToAdd = 60; // 60 seconds 307 } 308 309 if (window.orderup && window.orderup.choices.every((choice, index) => choice.data === window.orderup.correct[index].data)) { 310 showAddTime = false; // Don't show add time modal if they got everything right in order up 311 } 312 313 if (showAddTime && window.addTime && await window.addTime(timeToAdd)) { 314 doAddTime(timeToAdd); 315 316 pausedTime = (new Date().getTime() - startTime) - ((origSecs-timerSecs)*1000); 317 timerCorrection = 0; 318 319 return; 320 } 321 322 /* Process the end of any game where they ran out of time */ 323 logPlay(); 324 endGame(); 325 return; 326 } else { 327 /* Restart the timer */ 328 timerCorrection = (new Date().getTime() - (startTime+pausedTime)) - ((origSecs-timerSecs)*1000); 329 gameTimer = setTimeout(redo, (1000 - timerCorrection)); 330 } 331 } 332} 333 334function doAddTime(timeToAdd = 60) { 335 timerSecs += timeToAdd; 336 origSecs += timeToAdd; 337 338 let animationProgress = (origSecs - timerSecs) / origSecs; 339 340 $j('#time').html(displayTime(timerSecs)); 341 $j('#timeBox').removeClass('timeTicking'); 342 343 // All this just resets the animation to prevent it being buggy 344 $j('#time-progress').css('animation', 'none').height(); 345 346 $j('#time-progress').css({ 347 'animation-name': 'time-progress-bar', 348 'animation-timing-function': 'linear', 349 'animation-fill-mode': 'forwards', 350 'animation-play-state': 'paused' 351 }); 352 353 $j('#time-progress').css({ 354 'animation-duration' : `${origSecs * 1000}ms`, 355 'animation-play-state' : 'running', 356 'animation-delay' : `-${origSecs * animationProgress}s` 357 }); 358} 359 360function getLogData() { 361 var ls = getLogStrings(); 362 ls.logString = ls.logString || ''; 363 ls.logBonusString = ls.logBonusString || ''; 364 365 var logTime = stopwatch ? timerSecs : origSecs-timerSecs; 366 367 // Get homepage tracking data. Then clear it, we only want to log this on he very first play coming from the homepage 368 let homepageTrackingPayload = {}; 369 if (window.homepageTracking) { // This should be getting set, but don't want to break if it's not 370 homepageTrackingPayload = window.homepageTracking.getTrackingDataLogResultsPayload(); 371 window.homepageTracking.clearTrackingData(); 372 } else { 373 console.error('Homepage tracking not found'); 374 } 375 376 var logData = { 377 g : gameID, 378 a : ls.logString, 379 t : logTime, 380 b : ls.logBonusString, 381 tzo : (new Date()).getTimezoneOffset(), 382 es : Sporcle.embedSiteID, 383 ep : Sporcle.embedPartnerID, 384 pl : window.sessionStorage.getItem('playlistID'), 385 ho : typeof hasOrange !== 'undefined' ? hasOrange : 0, 386 hpm : typeof hasMembership !== 'undefined' ? hasMembership : 0, 387 ldtm : loadTime, 388 avg_pct : typeof averageScore !== 'undefined' ? averageScore : 0, 389 qs : typeof qScores !== 'undefined' ? qScores : {}, 390 ssr : window.Sporcle.gameData.subsetRows.join(','), 391 392 // Tracking keys for the homepage 393 ...homepageTrackingPayload, 394 }; 395 396 if (isEmbed) { 397 if (typeof spuid != 'undefined' && spuid) logData.spuid = spuid; 398 } 399 400 return logData; 401} 402 403function logPlay() { 404 trackJustGo(); 405 406 if (showdownActive) { 407 return; 408 } 409 410 if (stopwatch) { 411 logTimerPlay(); 412 logStopwatchPlay(); 413 } else { 414 logTimerPlay(); 415 } 416 417 return; 418} 419 420 421// Helper functions for "JustGo" mtc variable 422function trackJustGo() { 423 // Only for logged out users 424 if (typeof userKey !== 'undefined') { 425 return; 426 } 427 428 let playCount = getJustGoCount(); 429 playCount++; 430 window.localStorage.setItem('justGoPlayCount', playCount); 431 432 const params = new URLSearchParams(window.location.search); 433 434 if ((playCount > 0 && playCount % JUST_GO_PLAY_THRESHOLD === 0) || params.has('justgo')) { 435 triggerJustGo(); 436 } 437} 438 439function getJustGoCount() { 440 let playCount = 0; 441 442 // Only fetch the value if the user is not logged in 443 if (window.localStorage && typeof userKey === 'undefined') { 444 playCount = parseInt(window.localStorage.getItem('justGoPlayCount')) || 0; 445 } 446 447 return playCount; 448} 449 450// Check the user's JustGo play count and trigger JustGo if it's 20 451function checkJustGo() { 452 const count = getJustGoCount(); 453 if (count > 0 && count % JUST_GO_PLAY_THRESHOLD === 0) { 454 triggerJustGo(); 455 } 456} 457 458// Set the window variable and post an event for MTC to pick up 459function triggerJustGo() { 460 window.JustGo = true; 461 window.top.postMessage( 462 { source: "JustGo", message: true}, 463 "*" 464 ); 465} 466 467 468function logTimerPlay() { 469 // This is so postLog waits for us to get a response back from log_results before attempting to do postLog stuff 470 var deferredData = $j.Deferred(); 471 postLog = postLog.bind(null, deferredData); 472 473 var logData = getLogData(); 474 logData.swm = stopwatch ? 1 : 0; 475 logData.nur = typeof newUserRecommended !== 'undefined' && newUserRecommended ? 1 : 0; 476 477 $j.get("/games/log_results.php", logData, function(data) { 478 deferredData.resolve(data); 479 480 if (data.tracked_tournaments && data.tracked_tournaments.length) { 481 let message = data.tracked_tournaments.length > 1 482 ? `Your score has been recorded for all the tournaments this quiz is in. See how you stack up in <a href='/tournaments/'>your tournaments</a>.` 483 : `Your play is locked in â youâre #${data.tracked_tournaments[0].current_rank} out of ${data.tracked_tournaments[0].player_count} so far today. Check your progress on the <a href='/tournaments/${data.tracked_tournaments[0].encoded_tournament_id}'>Tournament Leaderboard</a>.`; 484 485 // Modal disabled for now
486 // window.SporcleLib.Modal.openTournamentPostQuizModal(); 487 window.addGameMessage(message, 'alertGood', 'tournament_message'); 488 } 489 490 if (data.play_is_valid && typeof userKey !== 'undefined' && !data.anonymized_play) { 491 window.updatePlayStreak(); 492 } 493 494 if (data.play_is_valid && typeof gameID !== 'undefined') { 495 var user = typeof userKey !== 'undefined' && userKey ? userKey : (typeof spuid !== 'undefined' && spuid ? spuid : false); 496 497 if (typeof window.sbData !== 'undefined') { 498 499 if ( 500 typeof sbData[2] !== 'undefined' && 501 typeof sbData[2]['playlist_game_ids'] !== 'undefined' && 502 sbData[2]['playlist_game_ids'].indexOf(gameID) >= 0 503 ) { 504 updateSuggestionsBar(2, 1); 505 } 506 507 if (typeof gameCategoryID !== 'undefined' && typeof updateSamplerPlatter === 'function') { 508 updateSamplerPlatter(gameCategoryID); 509 } 510 511 if ( 512 typeof window.sbData['update_baby_steps_on_play'] !== 'undefined' && 513 window.sbData['update_baby_steps_on_play'] && 514 typeof updateBabySteps === 'function' 515 ) { 516 updateBabySteps(); 517 } 518 } 519 } 520 }); 521 522 // We only log a timer play once, so short circuit any future calls 523 logTimerPlay = function(){}; 524 525 if (window.Sporcle.sendEmbedQuizEvents) { 526 // Basing the correct answer count on the logString because we don't update numRight on some quiz types until the showMissed() function 527 const correct = (logData.a.match(/1/g)||[]).length; 528 const answers = window.answers.length; 529 const time = window.origSecs-window.timerSecs; 530 531 let cause = 'completed_quiz'; 532 if (timerSecs === 0) { 533 cause = 'timed_out'; 534 } else if (didGiveUp) { 535 cause = 'gave_up'; 536 } else if (_spks && correct !== answers) { 537 cause = 'hit_mine'; 538 } 539 540 const msg = { 541 source : 'sporcle', 542 event : 'end', 543 end_cause : cause, 544 correct_count : correct, 545 question_count : answers, 546 time : time, 547 }; 548 window.parent.postMessage(msg, '*'); 549 } 550} 551 552function logStopwatchPlay() { 553 var logData = getLogData(); 554 555 logData.sw = 1; 556 logData.swm = stopwatch ? 1 : 0; 557 558 $j.get("/games/log_results.php", logData, function (data) { 559 if (data.play_is_valid && typeof userKey !== 'undefined' && !data.anonymized_play) { 560 window.updatePlayStreak(); 561 } 562 }); 563 564 // We only log a stopwatch play once, so short circuit any future calls 565 logStopwatchPlay = function(){}; 566} 567 568function runFinished() { 569 $j('#giveUp:not(.button-primary)').hide(); 570 $j('#pauseBox').css({visibility:'hidden'}); 571 postLog(); 572} 573 574function postLog(deferredData) { 575 if (typeof deferredData === 'object') { 576 // This means it is a normal play, and we are waiting for the call to log_results to finish 577 $j.when(deferredData).done(function(data) { 578 $j(window).trigger('postLog'); 579 580 var completedChallengeCount = 0; 581 var height = 270; 582 var userPct = 0; 583 584 if (answers.length >= 1) { 585 userPct = _.round(window.numRight/answers.length, 2); 586 587 if (window.pinpoint && window.qScores) { 588 let score = Object.values(qScores).reduce((a, cv) => { 589 return a + cv; 590 }, 0); 591 userPct = _.round(score/(answers.length * 100), 2); 592 } 593 594 if (userPct === 0 && window.numRight > 0 ) { 595 userPct = 0.01; 596 } else if (userPct > 1) { 597 userPct = 1; 598 } 599 } 600 601 if (data.hasOwnProperty('challenge_status') && data.challenge_status.logged_in) { 602 challenge = data.challenge_status; 603 604 completedChallengeCount = challenge.record.wins + challenge.record.losses + challenge.record.ties; 605 606 if (!challenge.rollup) { 607 height += 50 * completedChallengeCount; 608 } 609 610 if (challenge.met > 0 && completedChallengeCount === 0) { 611 $j('#game-message').html('<a href="/challenge/pending/">Challenge met!</a>'); 612 $j('#game-message-box').attr('class', 'alertNeutral').fadeIn({duration: 0.25}); 613 } 614 } 615 616 if (completedChallengeCount > 0) { 617 $j('#game-message-box').hide(); 618
619 if (!window._payload.is_flagship_app_view) { 620 window.SporcleLib.Modal.openPostChallengeModal({ 621 results : data.challenge_status, 622 gid : gameID 623 }); 624 } else { 625 let userPctCopy = userPct; // a copy is needed to prevent the value from changing during the 50ms timeout 626 627 window.SporcleLib.Modal.openPostChallengeModal({ 628 results : data.challenge_status, 629 gid : gameID, 630 onClose : () => { 631 setTimeout(() => { 632 openFlagshipPostGameModal(userPctCopy); 633 }, 50); 634 } 635 }); 636 } 637 } else if (window._payload?.is_flagship_app_view) { 638 openFlagshipPostGameModal(userPct); 639 } 640 641 if (!isEmbed) { 642 if (window.userID && window.numRight > 0) { 643 let score = window.numRight; 644 let maxScore = answers.length; 645 646 if (window.pinpoint && window.qScores) { 647 score = Object.values(qScores).reduce((a, cv) => { 648 return a + cv; 649 }, 0); 650 maxScore = answers.length * 100; 651 userPct = _.round(100*score/maxScore, 2); 652 } else { 653 userPct = _.round(userPct*100, 0); 654 } 655 if (!stopwatch) { 656 // Update the played tag 657 658 if (typeof window.bestPercent === 'undefined' || window.bestPercent < userPct) { 659 $j('#played-tag') 660 .html('Best: '+score+'/'+maxScore+' ('+userPct+'%)') 661 .show(); 662 } 663 } 664 updateLastPlayedDate(); 665 666 // Update the friend scores 667 var mode = stopwatch ? 'quizStopwatch' : 'quizFriends'; 668 var playTime = stopwatch ? timerSecs : origSecs-timerSecs; 669 addPlayToFriendScoresCache(mode, score, playTime); 670 window.Sporcle.scoreboards.quiz.params.mode = mode; 671 showCollapsedQuizScoreboard($j('#quiz-scoreboard'), window.Sporcle.scoreboards.quiz); 672 } 673 } 674 }); 675 } else { 676 // This means it is a showdown play, nothing was logged. 677 // openFlagshipPostGameModal happens in getReckoning if it's a flagship app showdown play 678 } 679} 680 681 682function showAnswerBoxes() { 683 $j('.answer').css({visibility:'visible'}); 684} 685 686async function startGame(secsStart) 687{ 688 window.Sporcle.gameData.startTime = Math.floor(Date.now() / 1000); 689 inProgress = true; 690 691 callSPHooks('start'); 692 693 if (overrideSecs >= 0) { 694 origSecs = overrideSecs; 695 giveUpDelay -= (secsStart - overrideSecs); 696 } else { 697 origSecs = secsStart; 698 } 699 origSecs = (overrideSecs >=0) ? overrideSecs : secsStart; 700 origQuizTime = origSecs; // If time is added with the app addTime modal origSecs will be updated, keep original value 701 702 $j('#button-showdown-lower').remove(); 703 $j('#button-showdown-upper').remove(); 704 705 $j('#preHide').hide(); 706 $j('#preHideContent').show(); 707 708 $j('#playPadding, .embeddable-start-button').hide(); 709 710 $j('#answer-wrapper').show(); 711 712 $j('#skip').css({visibility:'visible'}); 713 714 $j('#gameinput').prop('disabled',false); 715 716 if (!showdownActive) { 717 $j('#pauseBox').css({visibility:'visible'}); 718 } 719 disableStopwatchPick(); 720 721 if (typeof gameID !== 'undefined' && !showdownActive) { 722 var eventData = { 723 g: gameID, 724 mw: mweb, 725 sw: stopwatch ? 1 : 0, 726 a: 'st', 727 ldtm : loadTime 728 }; 729 $j.get('/games/includes/event.php', eventData); 730 } 731 732 $j('body').addClass('play active'); 733 734 // The `startgame` event kicks off a bunch of stuff on mobile web, so we want to yield execution before and after this 735 await window.yieldToMain(); 736 $j(window).trigger('startgame'); 737 await window.yieldToMain(); 738 739 740 $j('#gameinput').trigger('focus'); 741 742 if (window.fakeInput) { 743 window.fakeInput.remove(); 744 } 745 746 startTimer(); 747 748 if (mweb) { 749 $j('#pauseButton').on('click', function(e) { 750 pauseGame(); 751 }); 752 753 const qhOpenEl = document.querySelector('#quiz-helpers-open'); 754 755 if (qhOpenEl) { 756 $j('#quiz-helpers-open').on('click', () => { 757 window.SporcleLib.Modal.openQuizHelpersModal(); 758 }); 759 760 let idleTimeout = null 761 762 function resetIdleTimer() { 763 clearTimeout(idleTimeout); 764 765 idleTimeout = setTimeout(() => { 766 qhOpenEl.classList.remove('glisten-active'); 767 void qhOpenEl.offsetWidth; 768 qhOpenEl.classList.add('glisten-active'); 769 resetIdleTimer(); 770 }, 10000); 771 } 772 773 window.addEventListener('touchstart', resetIdleTimer) 774 resetIdleTimer(); 775 } 776 } 777 778 if (giveUpDelay <= 0) { addGiveUp(); } 779 780 if (window.Sporcle.sendEmbedQuizEvents) { 781 const msg = {source: 'sporcle', event: 'start'}; 782 window.parent.postMessage(msg, '*'); 783 } 784} 785
786function pauseGame(fromGiveUp) 787{ 788 /* Separated out to account for instances where we want to stop game play without displaying the pause screen */ 789 delayGame(); 790 791 if (typeof gameID != 'undefined') { 792 $j.get('/games/includes/event.php', { 793 g : gameID, 794 mw : mweb, 795 a : fromGiveUp ? 'gup' : 'p' 796 }); 797 } 798 799 showPause(fromGiveUp); 800} 801 802function delayGame() { 803 delayCount += 1; 804 805 if (stop) { 806 return; 807 } 808 809 $j('#time-progress').css({ 810 'animation-play-state' : 'paused' 811 }); 812 813 pausedStartTime = new Date().getTime(); 814 stop = true; 815} 816 817// Grid quizzes for MW will automatically pause on being rotated horizontally if the view is too small. 818// We don't want those to be counted towards pauses or resumes in gamereport_mobile.php so break this 819// out into two separate parts 820function unPauseGame() { 821 resumeGame() 822 823 if (typeof gameID != 'undefined') { 824 $j.get('/games/includes/event.php', {g:gameID, mw:mweb, a:'r'}); 825 } 826} 827 828function resumeGame() { 829 delayCount -= 1; 830 831 if (!stop) { 832 return; 833 } 834 835 if (delayCount > 0) { 836 return; 837 } 838 839 $j('#time-progress').css({ 840 'animation-play-state' : stopwatch ? 'paused' : 'running' 841 }); 842 843 stop = false; 844 pausedTime += new Date().getTime() - pausedStartTime; 845 846 if (stopwatch) { 847 timerCorrection = (new Date().getTime() - (startTime+pausedTime)) - (timerSecs*1000); 848 } else { 849 timerCorrection = (new Date().getTime() - (startTime+pausedTime)) - ((origSecs-timerSecs)*1000); 850 } 851 852 gameTimer = setTimeout(redo, 1000-timerCorrection); 853 854 if (!mweb) { 855 $j('.instructions object, .instructions embed').show(); 856 $j('#gameinput').trigger('focus'); 857 $j('#pause-ad').html(""); 858 } 859} 860
861function showPause(fromGiveUp) { 862 let pauseData = { 863 time : displayTime(timerSecs), 864 quiz_length : answers.length, 865 correct : currDisplayScore, 866 quiz_name : Sporcle.gameData.name, 867 quiz_description : Sporcle.gameData.description, 868 give_up : fromGiveUp, 869 show_score : (Sporcle.gameData.isGrid ? 0 : 1), 870 hideQuizHelp : (Sporcle.gameData.quizHelpers.length == 0 ? 1 : 0), 871 }; 872 873 if (mweb) { 874 $j(window).trigger('showPause', pauseData); 875 } else { 876 $j('.tynt-close-btn').trigger('click'); // does not behave well with pause screen 877 $j('.instructions object, .instructions embed').hide(); 878 SporcleLib.Modal.openPauseModal(pauseData); 879 } 880} 881 882function decodeFlag(f) { 883 return (parseInt(f, 36) % 2 == 0); 884} 885 886function getAnswer(slotNum, tryShort) { 887 if (slotNum >= answers.length) { 888 thisSlot = hidden[slotNum-answers.length]; 889 } else { 890 thisSlot = answers[slotNum]; 891 } 892 893 // Don't split at closing tags "</" 894 var garbledGator = (asta['<'] == ']' ? "\\" : "") + asta['<']; 895 var slashNoGator = thisSlot.search("[^"+garbledGator+"]\/"); 896 slashNoGator = slashNoGator == -1 ? -1 : slashNoGator + 1; // Position of first slash that has no "<" before it 897 slash = thisSlot.search("^\/") == 0 ? 0 : slashNoGator; // If string starts with slash, go with that, otherwise use first slash not preceded by "<" 898 899 if (slash != -1 && !tryShort) { 900 thisSlot = thisSlot.slice(0, slash); 901 } 902 903 slotinfo = thisSlot; 904 var e=''; 905 for(var i=0;i < slotinfo.length;i++){var c=slotinfo.slice(i,i+1);var d=false;for(var k in asta)if(asta[k]==c){ e+=k;d=true;}if(!d) e+=c;} 906 907 if (tryShort) { 908 var parts = e.split('/'); 909 910 if ((parts.length > 2) && (parts[1].length >= 39)) { 911 return parts[2]; 912 } else { 913 return parts[0]; 914 } 915 } 916 917 return e; 918} 919 920function escapeHTML(unsafe) { 921 return $j('<div>').text(unsafe).html(); 922} 923 924function unescapeHTML(safe) { 925 return $j('<div>').html(safe).text(); 926} 927 928function disableStopwatch() { 929 // Disable switching the timer settings 930 $j('#timeBox .timer-controls-btn').attr('disabled', true).off('click keyup'); 931 932 // Hide "Practice Mode" button 933 $j('#button-play.button-play-stopwatch').hide(); 934 935 // Seems most instances we disable stopwatch is a good time to disable quiz helpers 936 $j('#quiz-helpers-open').hide(); 937 stopwatch = false; 938} 939 940function enableStopwatchPick() { 941 $j('#timeBox .timer-controls-btn').attr('disabled', false).on({ 942 click: function(e) { 943 selectTimerSetting($j(e.currentTarget).data('value')); 944 }, 945 keyup: function(e) { 946 if (e.keycode === 13) { 947 selectTimerSetting($j(e.currentTarget).data('value')); 948 } 949 }, 950 }); 951} 952 953function disableStopwatchPick() { 954 $j('#timeBox .timer-controls-btn').attr('disabled', true).off('click keyup'); 955} 956 957function shuffle(array) { 958 var tmp, current, top = array.length; 959 960 if(top) while(--top) { 961 current = Math.floor(Math.random() * (top + 1)); 962 tmp = array[current]; 963 array[current] = array[top]; 964 array[top] = tmp; 965 } 966 967 return array; 968} 969 970function colorLum(hex, lum) { 971 972 hex = String(hex).replace(/[^0-9a-f]/gi, ''); 973 if (hex.length < 6) { 974 hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; 975 } 976 lum = lum || 0; 977 var rgb = "#", c, i; 978 for (i = 0; i < 3; i++) { 979 c = parseInt(hex.substr(i*2,2), 16); 980 c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16); 981 rgb += ("00"+c).substr(c.length); 982 } 983 return rgb; 984} 985 986function getDarkerColor(color) 987{ 988 return colorLum(color, -0.2); 989} 990 991function restoreFocus() { 992 const isSporcleAppView = (window._payload || {}).is_flagship_app_view; 993 994 if (isSporcleAppView) { 995 // No in-page ads in the app so don't run. Does this chunk of code interact with the code I added 996 // to sporcle/build/includes/sporcle-ui/game/play/common.js that pulls focus away quickly 997 // sometimes causing an infinite loop? What's causing the infinite loop? 998 // 999 // Regardless, the app doesn't have in-page ads so we don't ever need to run restoreFocus for it 1000 return; 1001 } 1002 1003 // If the active element is inside an iframe (that is not inside #embedMedia) then return focus to the game input 1004 1005 if (document.activeElement.getAttribute('id') === 'gameinput') { 1006 return; 1007 } 1008 1009 var iframeParent = document.activeElement.closest('iframe'); 1010 1011 if (!iframeParent) { 1012 return; 1013 } 1014 1015 if (iframeParent.closest('#embedMedia')) { 1016 return; 1017 } 1018 1019 $j('#gameinput').trigger('focus'); 1020}
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.