Skip to content

Commit 133f2b7

Browse files
Merge pull request #784 from ossf/hint_timer
Add delay for a hint
2 parents 651bf91 + b7c663a commit 133f2b7

File tree

1 file changed

+70
-11
lines changed

1 file changed

+70
-11
lines changed

docs/labs/checker.js

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,14 @@ let page_definitions = {}; // Definitions used when preprocessing regexes
1616
let user_solved = false; // True if user has *ever* solved it on this load
1717
let user_gave_up = false; // True if user ever gave up before user solved it
1818

19-
let startTime = Date.now();
19+
let startTime = Date.now(); // Time this lab started.
20+
let lastHintTime = null; // Last time we showed a hint.
21+
22+
// Has the input changed since we showed a hint?
23+
// We track this so people can re-see a hint they've already seen.
24+
// This initial value of "true" forces users to wait a delay time before
25+
// they are allowed to see their first hint on an unchanged page.
26+
let changedInputSinceHint = true;
2027

2128
let BACKQUOTE = "`"; // Make it easy to use `${BACKQUOTE}`
2229
let DOLLAR = "$"; // Make it easy to use `${DOLLAR}`
@@ -39,7 +46,8 @@ const resources = {
3946
no_matching_hint: 'Sorry, I cannot find a hint that matches your attempt.',
4047
reset_title: 'Reset initial state (throwing away current attempt).',
4148
to_be_completed: 'to be completed',
42-
try_harder: "Try harder! Don't give up so soon. Current time spent (in seconds): {0}",
49+
try_harder_give_up: "Try harder! Don't give up so soon. Current time spent since start or last hint (in seconds): {0}",
50+
try_harder_hint: "Try harder! Don't ask for a hint so soon, wait at least {0} seconds.",
4351
},
4452
},
4553
ja: {
@@ -56,7 +64,8 @@ const resources = {
5664
no_matching_hint: '申し訳ありませんが、あなたの試みに一致するヒントが見つかりません。',
5765
reset_title: '初期状態をリセットします (現在の試行を破棄します)。',
5866
to_be_completed: '完成する',
59-
try_harder: '「もっと頑張ってください! すぐに諦めないでください。現在の所要時間 (秒): {0}」',
67+
try_harder_give_up: 'もっと頑張れ!そんなにすぐに諦めないでください。開始または最後のヒントから経過した現在の時間 (秒単位): {0}',
68+
try_harder_hint: "もっと頑張れ!すぐにヒントを求めず、少なくとも {0} 秒待ちます。",
6069
},
6170
},
6271
fr: {
@@ -73,7 +82,9 @@ const resources = {
7382
no_matching_hint: "Désolé, je ne trouve pas d'indice correspondant à votre tentative.",
7483
reset_title: "Réinitialiser l'état initial (abandonner la tentative actuelle).",
7584
to_be_completed: 'à compléter',
76-
try_harder: "Essayez plus fort ! N'abandonnez pas si tôt. Temps actuel passé (en secondes) : {0}",
85+
try_harder_give_up: "Essayez plus fort ! N'abandonnez pas si tôt. Temps actuel passé (en secondes) : {0}",
86+
try_harder_give_up: "Essayez plus fort ! N'abandonnez pas si tôt. Temps actuel passé depuis le début ou le dernier indice (en secondes) : {0}",
87+
try_harder_hint: "Essayez plus fort ! Ne demandez pas d'indice si tôt, attendez au moins {0} secondes.",
7788
},
7889
},
7990
};
@@ -454,6 +465,10 @@ function makeStamp() {
454465
* Then set "grade" in document depending on that answer.
455466
*/
456467
function runCheck() {
468+
// This is only called when *something* has changed in the input.
469+
// From now on, enforce hint delays.
470+
changedInputSinceHint = true;
471+
457472
let attempt = retrieveAttempt();
458473

459474
// Calculate grade and set in document.
@@ -533,6 +548,7 @@ function showHint(e) {
533548
}
534549
}
535550

551+
/** Show the answer to the user */
536552
function showAnswer(e) {
537553
// Get indexes in *this* form.
538554
let formIndexes = JSON.parse(e.target.dataset.inputIndexes);
@@ -543,19 +559,62 @@ function showAnswer(e) {
543559
alert(t('expecting').format(goodAnswer));
544560
}
545561

546-
// "Give up" only shows the answer after this many seconds have elapsed.
547-
const MIN_DELAY_TIME = 60;
562+
// "Give up" only shows the answer after this many seconds have elapsed
563+
// since a clue (lab start or a hint given).
564+
const GIVE_UP_DELAY_TIME = 60;
565+
566+
// "Hint" only shows hint after this many seconds have elapsed
567+
// since a clue (lab start or a hint given).
568+
// WARNING: If you change this value, you *may* need to adjust some of
569+
// the translated texts for try_harder_hint.
570+
// Pluralization rules vary depending on the natural language,
571+
// yet we want to tell the user the exact delay value for hints.
572+
// English, French, German, and some others have two forms, "one" and "other"
573+
// (aka "singular" and "plural" forms of words).
574+
// For Chinese and Japanese it doesn't matter (there's no difference).
575+
// In those cases, you only need to change translations if you change between
576+
// not-1 to 1, which is unlikely. However,
577+
// for some languages like Arabic, Hebrew, and Russian it's more complicated.
578+
// See: https://localizely.com/language-plural-rules/
579+
// If we needed to, we could use JavaScript's Intl.PluralRules
580+
// which is widely supported and addresses this (unless you use IE),
581+
// but at this point there's no evidence we need it. See:
582+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules
583+
// https://caniuse.com/mdn-javascript_builtins_intl_pluralrules
584+
const HINT_DELAY_TIME = 15;
585+
586+
/** return time (in seconds) since start and/or last hint */
587+
function elapsedTimeSinceClue() {
588+
let currentTime = Date.now();
589+
let lastTime = (lastHintTime == null) ? startTime : lastHintTime;
590+
return ((currentTime - lastTime) / 1000); // in seconds
591+
}
548592

593+
/** Maybe show the answer to the user (depending on timer). */
549594
function maybeShowAnswer(e) {
550-
let currentTime = Date.now();
551-
let elapsedTime = (currentTime - startTime) / 1000; // in seconds
552-
if (elapsedTime < MIN_DELAY_TIME) {
553-
alert(t('try_harder').format(elapsedTime.toString()));
595+
let elapsedTime = elapsedTimeSinceClue();
596+
if (elapsedTime < GIVE_UP_DELAY_TIME) {
597+
alert(t('try_harder_give_up').format(elapsedTime.toString()));
554598
} else {
555599
showAnswer(e);
556600
}
557601
}
558602

603+
/** Maybe show a hint to the user (depending on timer). */
604+
function maybeShowHint(e) {
605+
let elapsedTime = elapsedTimeSinceClue();
606+
// Only enforce delay timer if changedInputSinceHint is true. That way,
607+
// people can re-see a previously-seen hint as long as they
608+
// have not changed anything since seeing the hint.
609+
if (changedInputSinceHint && (elapsedTime < HINT_DELAY_TIME)) {
610+
alert(t('try_harder_hint').format(HINT_DELAY_TIME.toString()));
611+
} else {
612+
lastHintTime = Date.now(); // Set new delay time start
613+
changedInputSinceHint = false; // Allow redisplay of hint
614+
showHint(e);
615+
}
616+
}
617+
559618
/**
560619
* Reset form.
561620
* We have to implement this in JavaScript to ensure that the final
@@ -822,7 +881,7 @@ function initPage() {
822881
current++;
823882
}
824883
for (let hintButton of document.querySelectorAll("button.hintButton")){
825-
hintButton.addEventListener('click', (e) => { showHint(e); });
884+
hintButton.addEventListener('click', (e) => { maybeShowHint(e); });
826885
// Precompute inputIndexes to work around problems that occur
827886
// if a user uses a browser's built-in natural language translation.
828887
// Presumes button's parent is the form

0 commit comments

Comments
 (0)