Skip to content

Commit 72a9c54

Browse files
Merge pull request #785 from ossf/refactor_format
Refactor format() so it's not a global String property
2 parents 133f2b7 + 63b0d95 commit 72a9c54

File tree

1 file changed

+35
-10
lines changed

1 file changed

+35
-10
lines changed

docs/labs/checker.js

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
105122
function 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
*/
383408
function 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() {
594619
function 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

Comments
 (0)