@@ -89,18 +89,35 @@ const resources = {
8989 } ,
9090} ;
9191
92- // Create a "format" method to simplify internationalization.
93- // Use as: "Demo {0} result"".format(name);
92+ /** Provide an "assert" (JavaScript doesn't have one built-in).
93+ * This one uses "Error" to provide a stack trace.
94+ */
95+ function myAssert ( condition , message ) {
96+ if ( ! condition ) {
97+ throw new Error ( message || "Assertion failed" ) ;
98+ }
99+ }
100+
101+ // Format a string, replacing {NUM} with item NUM.
102+ // We use this function to simplify internationalization.
103+ // Use as: myFormat("Demo {0} result", ["Name"])
94104// https://www.geeksforgeeks.org/what-are-the-equivalent-of-printf-string-format-in-javascript/
95- String . prototype . format = function ( ) {
96- const args = arguments ;
97- return this . replace ( / { ( \d + ) } / g, function ( match , number ) {
98- return typeof args [ number ] != 'undefined'
99- ? args [ number ]
105+ // This is *not* set as a property on String; if we did that,
106+ // we'd modify the global namespace, possibly messing up something
107+ // already there.
108+ function myFormat ( s , replacements ) {
109+ return s . replace ( / { ( \d + ) } / g, function ( match , number ) {
110+ return typeof replacements [ number ] != 'undefined'
111+ ? replacements [ number ]
100112 : match ;
101113 } ) ;
102114} ;
103115
116+ // Run some built-in tests on startup, to ensure all is okay.
117+ myAssert ( myFormat ( "Hello" , [ ] ) === "Hello" ) ;
118+ myAssert ( myFormat ( "Hello {0}, are you {1}?" , [ "friend" , "well" ] ) ===
119+ "Hello friend, are you well?" ) ;
120+
104121// Retrieve translation for given key from resources.
105122function t ( key ) {
106123 let result = resources [ lang ] [ 'translation' ] [ key ] ;
@@ -370,15 +387,23 @@ const attemptIdPattern = /^attempt(\d+)$/;
370387
371388/*
372389 * Given Node @form in document, return array of indexes of input/textareas
390+ * that are relevant for that form.
373391 * The values retrieved are *input* field indexes (`inputIndexes`),
374392 * starting at 0 for the first user input.
375393 *
394+ * When there's only one form, this is simply the array of all valid indexes.
395+ * However, a page can have multiple forms; in that case this returns
396+ * only the indexes valid for this specific form.
397+ *
376398 * Note: At one time we ran this calculation when a user pressed
377399 * a button. However, if you *translate* the page using Chrome's translator,
378400 * that will cause this routine to fail because querySelectorAll will fail.
379401 * To work around this,
380402 * it's better to calculate all of these values on page load and store it
381403 * (e.g., as dataset.inputIndexes values on the buttons).
404+ * If you run this early, users can use the web browser's built-in translator,
405+ * see the translated HTML, and have the lab work (though the lab
406+ * responses won't be translated).
382407 */
383408function findIndexes ( form ) {
384409 try {
@@ -556,7 +581,7 @@ function showAnswer(e) {
556581 if ( ! user_solved ) {
557582 user_gave_up = true ;
558583 }
559- alert ( t ( 'expecting' ) . format ( goodAnswer ) ) ;
584+ alert ( myFormat ( t ( 'expecting' ) , [ goodAnswer ] ) ) ;
560585}
561586
562587// "Give up" only shows the answer after this many seconds have elapsed
@@ -594,7 +619,7 @@ function elapsedTimeSinceClue() {
594619function maybeShowAnswer ( e ) {
595620 let elapsedTime = elapsedTimeSinceClue ( ) ;
596621 if ( elapsedTime < GIVE_UP_DELAY_TIME ) {
597- alert ( t ( 'try_harder_give_up' ) . format ( elapsedTime . toString ( ) ) ) ;
622+ alert ( myFormat ( t ( 'try_harder_give_up' ) , [ elapsedTime . toString ( ) ] ) ) ;
598623 } else {
599624 showAnswer ( e ) ;
600625 }
@@ -607,7 +632,7 @@ function maybeShowHint(e) {
607632 // people can re-see a previously-seen hint as long as they
608633 // have not changed anything since seeing the hint.
609634 if ( changedInputSinceHint && ( elapsedTime < HINT_DELAY_TIME ) ) {
610- alert ( t ( 'try_harder_hint' ) . format ( HINT_DELAY_TIME . toString ( ) ) ) ;
635+ alert ( myFormat ( t ( 'try_harder_hint' ) , [ HINT_DELAY_TIME . toString ( ) ] ) ) ;
611636 } else {
612637 lastHintTime = Date . now ( ) ; // Set new delay time start
613638 changedInputSinceHint = false ; // Allow redisplay of hint
0 commit comments