# Interactive Free-Response Quiz (JavaScript-only)
from IPython.display import display, HTML, Javascript
import ipywidgets as widgets

print("🎯 JavaScript String Basics - Free-Response Quiz")
print("Type your answers below and click Check Answers.\n")

# Question 1
q1_label = widgets.HTML("<b>1. In JavaScript, which property is used to get the length of a string?</b><br><i>Answer with the property name (e.g., length or string.length)</i>")
q1_input = widgets.Text(placeholder="e.g., length or string.length")

# Question 2
q2_label = widgets.HTML("<b>2. Which expression can concatenate two strings?</b><br><i>Type the expression or operator (e.g., \"Hello\" + \"World\" or +)</i>")
q2_input = widgets.Text(placeholder='e.g., "Hello" + "World" or +')

# Question 3
q3_label = widgets.HTML("<b>3. What does 'JavaScript'.substring(0, 4) return?</b><br><i>Type the exact substring</i>")
q3_input = widgets.Text(placeholder='e.g., Java')

# Question 4
q4_label = widgets.HTML("<b>4. Which method converts a string to uppercase?</b><br><i>Type the method name</i>")
q4_input = widgets.Text(placeholder='e.g., toUpperCase()')

# True/False Questions
q5_label = widgets.HTML("<b>5. String indexes start at 1 in JavaScript</b><br><i>Answer True or False</i>")
q5_input = widgets.Text(placeholder='True/False')

q6_label = widgets.HTML("<b>6. 'hello'.length returns 5</b><br><i>Answer True or False</i>")
q6_input = widgets.Text(placeholder='True/False')

# Submit Button
submit_btn = widgets.Button(
    description="Check Answers",
    button_style='success',
    icon='check'
)

output = widgets.Output()

# Helper functions for tolerant checking

def clean_token(s: str) -> str:
    s = s.strip().lower()
    s = s.strip('`')
    if s.endswith('()'):
        s = s[:-2]
    return s


def strip_quotes_spaces(s: str) -> str:
    return s.replace('"', '').replace("'", '').replace(' ', '').strip().lower()


def parse_bool_like(s: str):
    v = s.strip().lower()
    if v in {'true', 't', 'yes', 'y', '1'}:
        return True
    if v in {'false', 'f', 'no', 'n', '0'}:
        return False
    return None


def check_answers(btn):
    with output:
        output.clear_output()
        score = 0
        total = 6

        print("📊 QUIZ RESULTS")
        print("=" * 40)

        # Q1: length property
        a1 = clean_token(q1_input.value)
        if a1 == 'length' or a1.endswith('.length'):
            score += 1
            print("✅ Q1: CORRECT")
        else:
            print("❌ Q1: INCORRECT - Expected: length (property)")

        # Q2: concatenation using +
        raw2 = q2_input.value
        a2_clean = strip_quotes_spaces(raw2)
        if ('+' in raw2) or ('plus' in raw2.lower()) or (a2_clean == 'hello+world'):
            score += 1
            print("✅ Q2: CORRECT")
        else:
            print("❌ Q2: INCORRECT - Example: \"Hello\" + \"World\" (use +)")

        # Q3: substring(0,4) -> "Java"
        a3 = q3_input.value.strip().strip('"').strip("'")
        if a3.lower() == 'java':
            score += 1
            print("✅ Q3: CORRECT")
        else:
            print("❌ Q3: INCORRECT - Correct: Java")

        # Q4: toUpperCase()
        a4 = clean_token(q4_input.value).replace('_', '')
        if a4 == 'touppercase':
            score += 1
            print("✅ Q4: CORRECT")
        else:
            print("❌ Q4: INCORRECT - Correct: toUpperCase()")

        # Q5: False (indexes start at 0)
        a5 = parse_bool_like(q5_input.value)
        if a5 is None:
            print("⚠️ Q5: Could not parse. Please answer True/False.")
        elif a5 is False:
            score += 1
            print("✅ Q5: CORRECT")
        else:
            print("❌ Q5: INCORRECT - Correct: False (indexes start at 0)")

        # Q6: True ('hello'.length == 5)
        a6 = parse_bool_like(q6_input.value)
        if a6 is None:
            print("⚠️ Q6: Could not parse. Please answer True/False.")
        elif a6 is True:
            score += 1
            print("✅ Q6: CORRECT")
        else:
            print("❌ Q6: INCORRECT - Correct: True")

        print("=" * 40)
        print(f"🎯 Your Score: {score}/{total}")
        print(f"📈 Percentage: {score/total*100:.1f}%")

        # Persist a completion flag to localStorage (optional)
        try:
            display(Javascript("""localStorage.setItem('csp34_hw_quiz_tried','true');"""))
        except Exception:
            pass


submit_btn.on_click(check_answers)

# Display all questions and inputs
display(
    q1_label, q1_input,
    q2_label, q2_input,
    q3_label, q3_input,
    q4_label, q4_input,
    q5_label, q5_input,
    q6_label, q6_input,
    submit_btn, output,
)
🎯 JavaScript String Basics - Free-Response Quiz
Type your answers below and click Check Answers.




HTML(value='<b>1. In JavaScript, which property is used to get the length of a string?</b><br><i>Answer with t…



Text(value='', placeholder='e.g., length or string.length')



HTML(value='<b>2. Which expression can concatenate two strings?</b><br><i>Type the expression or operator (e.g…



Text(value='', placeholder='e.g., "Hello" + "World" or +')



HTML(value="<b>3. What does 'JavaScript'.substring(0, 4) return?</b><br><i>Type the exact substring</i>")



Text(value='', placeholder='e.g., Java')



HTML(value='<b>4. Which method converts a string to uppercase?</b><br><i>Type the method name</i>')



Text(value='', placeholder='e.g., toUpperCase()')



HTML(value='<b>5. String indexes start at 1 in JavaScript</b><br><i>Answer True or False</i>')



Text(value='', placeholder='True/False')



HTML(value="<b>6. 'hello'.length returns 5</b><br><i>Answer True or False</i>")



Text(value='', placeholder='True/False')



Button(button_style='success', description='Check Answers', icon='check', style=ButtonStyle())



Output()

Part C: JavaScript Password Strength Practice

Here you will make a password strength analyzer in JavaScript. Complete the program so it checks:

  • Password length ≥ 8 characters
  • Contains uppercase letters
  • Contains lowercase letters
  • Contains numbers
  • Contains special characters (!@#$%^&*)

Then calculate a strength score and display appropriate feedback.

Try to fix the code to set your own password level.

%%javascript

console.log("=== Password Strength Analyzer ===");

function analyzePassword(password, minLength = 8, specialChars = "!@#$%^&*") {
    const passwordLength = password.length;
    const isLongEnough = passwordLength >= minLength;
    const hasUppercase = /[A-Z]/.test(password);
    const hasLowercase = /[a-z]/.test(password);
    const hasNumbers = /\d/.test(password);
    const hasSpecial = new RegExp("[" + specialChars.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "]").test(password);

    // Common and repeating patterns
    const commonPatterns = ["password", "123456789", "987654321", "hello", "goodmorning", "letmein", "food", "planes", "eat", "donuts"];
    const repeatingPatterns = ["abc", "123", "qwerty", "111", "aaa", "xyz"];
    const inappropriatePatterns = ["f_bomb", "hitler", "nazism", "communism"];

    // Check for restricted or common content
    const lowerPw = password.toLowerCase();
    const hasCommon = commonPatterns.concat(repeatingPatterns).some(p => lowerPw.includes(p));
    const notAllowed = inappropriatePatterns.some(p => lowerPw.includes(p));

    // Calculate strength score
    let strengthScore = 0;
    if (isLongEnough) strengthScore++;
    if (hasUppercase) strengthScore++;
    if (hasLowercase) strengthScore++;
    if (hasNumbers) strengthScore++;
    if (hasSpecial) strengthScore++;

    // Determine rating
    let strengthRating = "";
    if (notAllowed) {
        strengthRating = "Not Allowed";
    } else if (hasCommon) {
        strengthRating = "Weak";
    } else if (strengthScore <= 2) {
        strengthRating = "Weak";
    } else if (strengthScore <= 3) {
        strengthRating = "Medium";
    } else {
        strengthRating = "Strong";
    }

    // Suggestions
    const suggestions = [];
    if (!isLongEnough) suggestions.push(`Make password longer (at least ${minLength} characters)`);
    if (!hasUppercase) suggestions.push("Add uppercase letters");
    if (!hasLowercase) suggestions.push("Add lowercase letters");
    if (!hasNumbers) suggestions.push("Add numbers");
    if (!hasSpecial) suggestions.push(`Add special characters: ${specialChars}`);
    if (hasCommon) suggestions.push("Avoid common words or patterns");
    if (notAllowed) suggestions.push("Avoid inappropriate or banned terms");

    return {
        password,
        length: passwordLength,
        score: strengthScore,
        rating: strengthRating,
        isLongEnough,
        hasUppercase,
        hasLowercase,
        hasNumbers,
        hasSpecial,
        hasCommonPatterns: hasCommon,
        hasInappropriatePatterns: notAllowed,
        suggestions
    };
}

// Print results
function printAnalysis(result, hidePassword = false) {
    const pw = hidePassword ? "*".repeat(result.length) : result.password;
    let output = `\nPassword: ${pw}\n`;
    output += `Length: ${result.length} characters\n`;
    output += `Strength Score: ${result.score}/5 - ${result.rating}\n`;
    output += `Requirements met:\n`;
    output += `  ✓ Length >= 8: ${result.isLongEnough}\n`;
    output += `  ✓ Has uppercase: ${result.hasUppercase}\n`;
    output += `  ✓ Has lowercase: ${result.hasLowercase}\n`;
    output += `  ✓ Has numbers: ${result.hasNumbers}\n`;
    output += `  ✓ Has special chars: ${result.hasSpecial}\n\n`;

    if (result.suggestions.length > 0) {
        output += "Suggestions for improvement:\n";
        for (const s of result.suggestions) output += `  • ${s}\n`;
    } else if (result.rating === "Not Allowed") {
        output += "⚠️ Password contains restricted terms — not allowed.\n";
    } else {
        output += "🎉 Excellent! Your password meets all security requirements!\n";
    }

    console.log(output);
}

// Example passwords
const examplePasswords = [
    "adolfhitler123",
    "David123456789",
    "987654321",
    "zacfsd",
    "Berna@3#56",
    "Wakers!",
    "Doors&Rooms2",
    "Debuggers20252026"
];

console.log("\nRunning examples:");
for (const p of examplePasswords) {
    const result = analyzePassword(p);
    printAnalysis(result, false);
}
<IPython.core.display.Javascript object>