@@ -16,7 +16,14 @@ let page_definitions = {}; // Definitions used when preprocessing regexes
1616let user_solved = false ; // True if user has *ever* solved it on this load
1717let 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
2128let BACKQUOTE = "`" ; // Make it easy to use `${BACKQUOTE}`
2229let 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 */
456467function 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 */
536552function 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). */
549594function 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