@@ -20,18 +20,23 @@ let startTime = Date.now(); // Time this lab started.
2020let lastHintTime = null ; // Last time we showed a hint.
2121let lastHintTarget = null ; // Last hint button user used.
2222
23- // Has the input changed since we showed a hint?
24- // We track this so people can re-see a hint they've already seen.
25- // This initial value of "true" forces users to wait a delay time before
26- // they are allowed to see their first hint on an unchanged page.
23+ /**
24+ * True iff the input has changed since we showed a hint.
25+ * We track this so people can re-see a hint they've already seen.
26+ * This initial value of "true" forces users to wait a delay time before
27+ * they are allowed to see their first hint on an unchanged page. */
2728let changedInputSinceHint = true ;
2829
2930let BACKQUOTE = "`" ; // Make it easy to use `${BACKQUOTE}`
3031let DOLLAR = "$" ; // Make it easy to use `${DOLLAR}`
3132
32- // Current language. Guess English until we learn otherwise.
33+ /** Current language. Guess English until we learn otherwise. */
3334let lang = "en" ;
3435
36+ /**
37+ * Resources for localizations, in particular, for translations.
38+ * This intentionally mimics the format of library i18next.
39+ */
3540const resources = {
3641 en : {
3742 translation : {
@@ -90,7 +95,8 @@ const resources = {
9095 } ,
9196} ;
9297
93- /** Provide an "assert" (JavaScript doesn't have one built-in).
98+ /**
99+ * Provide an "assert" function (JavaScript doesn't have one built-in).
94100 * This one uses "Error" to provide a stack trace.
95101 */
96102function myAssert ( condition , message ) {
@@ -99,13 +105,17 @@ function myAssert(condition, message) {
99105 }
100106}
101107
102- // Format a string, replacing {NUM} with item NUM.
103- // We use this function to simplify internationalization.
104- // Use as: myFormat("Demo {0} result", ["Name"])
105- // https://www.geeksforgeeks.org/what-are-the-equivalent-of-printf-string-format-in-javascript/
106- // This is *not* set as a property on String; if we did that,
107- // we'd modify the global namespace, possibly messing up something
108- // already there.
108+ /**
109+ * Format a string, replacing {NUM} with item NUM.
110+ * We use this function to simplify internationalization.
111+ * Use as: myFormat("Demo {0} result", ["Name"]). See:
112+ * https://www.geeksforgeeks.org/what-are-the-equivalent-of-printf-string-format-in-javascript/
113+ * This is *not* set as a property on String; if we did that,
114+ * we'd modify the global namespace, possibly messing up something
115+ * already there.
116+ * @param s {string} - string to format, where {NUM} is to be replaced
117+ * @param replacements {Array} - Array of strings for replacements
118+ */
109119function myFormat ( s , replacements ) {
110120 return s . replace ( / { ( \d + ) } / g, function ( match , number ) {
111121 return typeof replacements [ number ] != 'undefined'
@@ -119,7 +129,10 @@ myAssert(myFormat("Hello", []) === "Hello");
119129myAssert ( myFormat ( "Hello {0}, are you {1}?" , [ "friend" , "well" ] ) ===
120130 "Hello friend, are you well?" ) ;
121131
122- // Retrieve translation for given key from resources.
132+ /** Retrieve translation for given key from resources.
133+ * @param key {string} - key to be retrieved
134+ * @returns {string } - translated key for the current local `lang`
135+ */
123136function t ( key ) {
124137 let result = resources [ lang ] [ 'translation' ] [ key ] ;
125138
@@ -129,7 +142,8 @@ function t(key) {
129142 return result ;
130143}
131144
132- // Retrieve translation from object for given field
145+ /** Retrieve translation from object for given field
146+ */
133147function retrieve_t ( obj , field ) {
134148 let result = obj [ field + "_" + lang ] ;
135149
@@ -139,7 +153,7 @@ function retrieve_t(obj, field) {
139153 return result ;
140154}
141155
142- // Determine language of document. Set with <html lang="...">.
156+ /** Return language of document. Set with <html lang="...">. */
143157function determine_locale ( ) {
144158 let lang = document . documentElement . lang ;
145159 if ( ! lang ) {
@@ -148,25 +162,27 @@ function determine_locale() {
148162 return lang ;
149163}
150164
151- // This array contains the default pattern preprocessing commands, in order.
152- // We process every pattern through these (in order) to create a final regex
153- // to be used to match a pattern.
154- //
155- // We preprocess regexes to (1) simplify the pattern language and
156- // (2) optimize performance.
157- // Each item in this array has two elements:
158- // a regex and its replacement string on match.
159- // Yes, these preprocess patterns are regexes that process regexes.
160- //
161- // People can instead define their *own* sequence of
162- // preprocessing commands, to make their language easier to handle
163- // (e.g., Python). Do this by setting `info.preprocessing`.
164- // Its format is a sequence of arrays, each element is an array of
165- // 2 or 3 strings of form pattern, replacementString [, flags]
166- //
167- // Our default pattern preprocessing commands include some optimizations;
168- // we want people to get rapid feedback even with complex correct patterns.
169- //
165+ /**
166+ * Array that containing the pattern preprocessing commands, in order.
167+ * We set it to its default value to start with.
168+ * We process every pattern through these (in order) to create a final regex
169+ * to be used to match a pattern.
170+ *
171+ * We preprocess regexes to (1) simplify the pattern language and
172+ * (2) optimize performance.
173+ * Each item in this array has two elements:
174+ * a regex and its replacement string on match.
175+ * Yes, these preprocess patterns are regexes that process regexes.
176+ *
177+ * People can instead define their *own* sequence of
178+ * preprocessing commands, to make their language easier to handle
179+ * (e.g., Python). Do this by setting `info.preprocessing`.
180+ * Its format is a sequence of arrays, each element is an array of
181+ * 2 or 3 strings of form pattern, replacementString [, flags]
182+ *
183+ * Our default pattern preprocessing commands include some optimizations;
184+ * we want people to get rapid feedback even with complex correct patterns.
185+ */
170186let preprocessRegexes = [
171187 // Remove end-of-line characters (\n and \r)
172188 [ / [ \n \r ] + / g, '' ] ,
@@ -218,9 +234,9 @@ function escapeHTML(unsafe) {
218234 . replace ( / \' / g, "'" ) ) ;
219235}
220236
221- /* Compute Set difference lhs \ rhs.
222- * @lhs - Set to start with
223- * @rhs - Set to remove from the lhs
237+ /** Compute Set difference lhs \ rhs.
238+ * @param lhs - Set to start with
239+ * @param rhs - Set to remove from the lhs
224240 * Set difference is in Firefox nightly, but is not yet released.
225241 * So we compute it ourselves. This is equivalent to lhs.difference(rhs)
226242 */
@@ -230,16 +246,17 @@ function setDifference(lhs, rhs) {
230246 return new Set ( result ) ;
231247}
232248
233- /* Return differences between two objects
249+ /** Return differences between two objects
250+ * (this is useful for debugging)
234251 */
235252function objectDiff ( obj1 , obj2 ) {
236253 let diff = { } ;
237-
254+
238255 function compare ( obj1 , obj2 , path = '' ) {
239256 for ( const key in obj1 ) {
240257 if ( obj1 . hasOwnProperty ( key ) ) {
241258 const newPath = path ? `${ path } .${ key } ` : key ;
242-
259+
243260 if ( ! obj2 . hasOwnProperty ( key ) ) {
244261 diff [ newPath ] = [ obj1 [ key ] , undefined ] ;
245262 } else if ( typeof obj1 [ key ] === 'object' && typeof obj2 [ key ] === 'object' ) {
@@ -249,23 +266,23 @@ function objectDiff(obj1, obj2) {
249266 }
250267 }
251268 }
252-
269+
253270 for ( const key in obj2 ) {
254271 if ( obj2 . hasOwnProperty ( key ) && ! obj1 . hasOwnProperty ( key ) ) {
255272 const newPath = path ? `${ path } .${ key } ` : key ;
256273 diff [ newPath ] = [ undefined , obj2 [ key ] ] ;
257274 }
258275 }
259276 }
260-
277+
261278 compare ( obj1 , obj2 ) ;
262279 return diff ;
263280}
264281
265- /*
282+ /**
266283 * Show debug output in debug region and maybe via alert box
267- * @debugOutput - the debug information to show
268- * @alwaysAlert - if true, ALWAYS show an alert
284+ * @param debugOutput - the debug information to show
285+ * @param alwaysAlert - if true, ALWAYS show an alert
269286 * This does *not* raise or re-raise an exception; it may be just informative.
270287 */
271288function showDebugOutput ( debugOutput , alwaysAlert = true ) {
@@ -290,8 +307,8 @@ function showDebugOutput(debugOutput, alwaysAlert = true) {
290307 * Given take a regex string, preprocess it (using our array of
291308 * definitions and preprocessing regexes),
292309 * and return a processed regex as a String.
293- * @regexString - String to be converted into a compiled Regex
294- * @fullMatch - require full match (insert "^" at beginning , "$" at end).
310+ * @param { string } regexString - String to be converted into a compiled Regex
311+ * @param { Boolean } fullMatch - require full match ("^" at start , "$" at end)
295312 */
296313function processRegexToString ( regexString , fullMatch = true ) {
297314 // Replace all definitions. This makes regexes much easier to use,
@@ -314,9 +331,9 @@ function processRegexToString(regexString, fullMatch = true) {
314331/**
315332 * Given take a regex string, preprocess it (using our array of
316333 * preprocessing regexes), and return a final compiled Regexp.
317- * @regexString - String to be converted into a compiled Regexp
318- * @description - Description of @regexString's purpose (for error reports)
319- * @fullMatch - require full match (insert "^" at beginning , "$" at end).
334+ * @param { String } regexString - String to be converted into a compiled Regexp
335+ * @param description - Description of its purpose (for error reports)
336+ * @param fullMatch - require full match? (insert "^" at start , "$" at end).
320337 */
321338function processRegex ( regexString , description , fullMatch = true ) {
322339 let processedRegexString = processRegexToString ( regexString , fullMatch ) ;
@@ -331,9 +348,9 @@ function processRegex(regexString, description, fullMatch = true) {
331348 }
332349}
333350
334- /*
351+ /**
335352 * Determine if preprocessing produces the expected final regex answer.
336- * @example - 2-element array. LHS is to be processed, RHS is expected result
353+ * @param example - 2-element array [ to be processed, expected result]
337354 */
338355function validProcessing ( example ) {
339356 let [ unProcessed , expectedProcessed ] = example ;
@@ -345,19 +362,19 @@ function validProcessing(example) {
345362
346363/**
347364 * Return true iff the indexed attempt matches the indexed correct.
348- * @attempt - Array of strings that might be correct
349- * @index - Integer index (0+)
350- * @correct - Array of compiled regexes describing correct answer
365+ * @param attempt - Array of strings that might be correct
366+ * @param index - Integer index (0+)
367+ * @param correct - Array of compiled regexes describing correct answer
351368 */
352369function calcOneMatch ( attempt , index = 0 , correct = correctRe ) {
353370 return correct [ index ] . test ( attempt [ index ] ) ;
354371}
355372
356373/**
357374 * Return true iff all of attempt matches all of correct.
358- * @attempt - Array of strings that might be correct
359- * @correct - Array of compiled regexes describing correct answer
360- * @validIndexes - Array of indexes to check (default: all indexes)
375+ * @param attempt - Array of strings that might be correct
376+ * @param correct - Array of compiled regexes describing correct answer
377+ * @param validIndexes - Array of indexes to check (default: all indexes)
361378 */
362379function calcMatch ( attempt , correct = correctRe , validIndexes = null ) {
363380 if ( ! correct ) { // Defensive test, should never happen.
@@ -391,8 +408,8 @@ function retrieveAttempt() {
391408
392409const attemptIdPattern = / ^ a t t e m p t ( \d + ) $ / ;
393410
394- /*
395- * Given Node @ form in document, return array of indexes of input/textareas
411+ /**
412+ * Given Node form in document, return array of indexes of input/textareas
396413 * that are relevant for that form.
397414 * The values retrieved are *input* field indexes (`inputIndexes`),
398415 * starting at 0 for the first user input.
@@ -544,7 +561,7 @@ function runCheck() {
544561}
545562
546563/** Return the best-matching hint string given an attempt.
547- * @attempt - array of strings of attempt to give hints on
564+ * @param attempt - array of strings of attempt to give hints on
548565 */
549566function findHint ( attempt , validIndexes = undefined ) {
550567 // Find a matching hint (matches present and NOT absent)
@@ -621,7 +638,7 @@ function maybeShowAnswer(e) {
621638 }
622639}
623640
624- // Return true iff target is the same hint button as last time, without edits.
641+ /** Return true iff target is same hint button as last time & no edits. */
625642function sameHint ( target ) {
626643 return ( target == lastHintTarget ) && ! changedInputSinceHint ;
627644}
@@ -720,9 +737,10 @@ function processHints(requestedHints) {
720737}
721738
722739/** Set global values based on other than "correct" and "expected" values.
723- * The correct and expected values may come from elsewhere, but we have to set up the
740+ * The correct and expected values may come from elsewhere,
741+ * but we have to set up the
724742 * info-based values first, because info can change how those are interpreted.
725- * @configurationInfo : Data to use
743+ * @param configurationInfo - Data to use
726744 */
727745function processInfo ( configurationInfo ) {
728746 const allowedInfoFields = new Set ( [
@@ -907,6 +925,7 @@ function setupInfo() {
907925 } ;
908926}
909927
928+ /** Initialize the whole HTML page */
910929function initPage ( ) {
911930 // Set current locale
912931 lang = determine_locale ( ) ;
0 commit comments