diff --git a/build/bundle.js b/build/bundle.js index 94bc190..30b0030 100644 --- a/build/bundle.js +++ b/build/bundle.js @@ -141,6 +141,7 @@ let privateKey; let publicKey; let message; let signature; +let $backButton; // For lesson 7 let transaction = lessonLogic.mockTxToSign; @@ -204,6 +205,14 @@ function startLesson(newLessonNumber) { $("#nextButton").prop("hidden", false); $("#nextButton").prop("disabled", true); } + + // Back button: show for lessons after the first, hide on welcome and final pages + if (newLessonNumber > 1 && finalLesson === false) { + $("#backButton").prop("hidden", false); + $("#backButton").prop("disabled", false); + } else { + $("#backButton").prop("hidden", true); + } } // Increment the current lesson counter, save to local storage, and call @@ -211,6 +220,14 @@ function startLesson(newLessonNumber) { function advanceLesson() { startLesson(currentLesson + 1); } + +// Decrement the current lesson counter and refresh the lesson page +function goToPreviousLesson() { + const previousLesson = currentLesson - 1; + if (previousLesson >= 1) { + startLesson(previousLesson); + } +} function saveToLocalStorage(key, value) { localData[key] = value; localStorage.setItem('localData', JSON.stringify(localData)); @@ -311,7 +328,7 @@ function printResult($userInput, $consolePrompt, isError, aResult) { const userInputString = $('.console-input').val(); // take the user input and dispaly as a label - $('.prompt-completed').clone().removeClass('prompt-completed').insertBefore($consolePrompt).find('code').text(userInputString); + $('.prompt-completed').clone().removeClass('prompt-completed').addClass('console-line').insertBefore($('.console-input-container')).find('code').text(userInputString); // clear the user input $userInput.val(''); @@ -320,7 +337,7 @@ function printResult($userInput, $consolePrompt, isError, aResult) { } // print the result - $('.prompt-result').clone().removeClass('prompt-result').insertBefore($consolePrompt).find('code')[isError ? 'html' : 'text'](result); + $('.prompt-result').clone().removeClass('prompt-result').addClass('console-line').insertBefore($('.console-input-container')).find('code')[isError ? 'html' : 'text'](result); } // This is the same opening line as 'document ready()' @@ -336,6 +353,7 @@ $(function () { const $consolePrompt = $('.console-prompt'); const $userInput = $('.console-input'); const $nextButton = $('#nextButton'); + $backButton = $('#backButton'); const $lessonArea = $('.lessons'); // Focus on the user input box @@ -359,10 +377,23 @@ $(function () { $nextButton.prop("disabled", true); }); - // If the user clicks anywhere in the console box, focus the cursor on the input line - $console.on('click', function (e) { - $userInput.trigger('focus'); + // Click-to-copy: clicking any past console line copies its text + $console.on('click', '.console-line code', async function () { + const textToCopy = $(this).text(); + if (navigator.clipboard && navigator.clipboard.writeText) { + try { + await navigator.clipboard.writeText(textToCopy); + } catch (e) {} + } + }); + + // When the back button is pressed, go to the previous lesson + $backButton.on('click', function () { + goToPreviousLesson(); + // Disable the next button in the lesson we just navigated to until it is completed + $nextButton.prop("disabled", true); }); + $userInput.on('keydown', async function (e) { const userInputString = $('.console-input').val(); @@ -414,6 +445,29 @@ $(function () { if (lowercaseUserInputString.includes('showanswer()')) { // TODO show the answer } + + // Allow users to type 'next' to move to the next lesson, + // but only if the current lesson has been completed and the + // Next button is enabled. + if (lowercaseUserInputString === 'next') { + let result = ''; + let error = true; + + if (!$nextButton.prop('disabled')) { + error = false; + advanceLesson(); + $nextButton.prop('disabled', true); + } else { + result = 'Please complete this lesson before moving to the next one.'; + } + + printResult($userInput, $consolePrompt, error, result); + + // Auto scroll to the bottom of the console + const newConsoleHeight = $console[0].scrollHeight; + $console.scrollTop(newConsoleHeight); + return; + } let result = ''; let error = true; const sanityCheckResult = validation.userInputSanityCheck(currentLesson, lowercaseUserInputString); diff --git a/build/main.css b/build/main.css index 92f4841..eead86c 100644 --- a/build/main.css +++ b/build/main.css @@ -1 +1 @@ -html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}html,body{padding:0;margin:0;height:100%}body{display:flex;flex-direction:column;font-family:"Open Sans",sans-serif;color:#222;background-image:url('../assets/proofofword-background-light.png');background-size:30%, 30%;background-attachment:fixed;background-position:25% 75%}header{margin-bottom:40px;height:120px;min-height:80px;display:flex;flex-direction:column;justify-content:center;background-color:#f5f2e3;text-align:center}header h1{text-transform:uppercase;font-size:48pt;font-weight:bold;color:#ed602b}.main{display:flex;flex:1;flex-direction:row;width:calc(100% - 60px);min-height:350px;margin:0 auto;gap:20px}.main>div{flex:1}.main ::-webkit-scrollbar{width:5px}.main ::-webkit-scrollbar-track{background:#f5f2e3}.main ::-webkit-scrollbar-thumb{background:#f3baa6}.main ::-webkit-scrollbar-thumb:hover{background:#555}.main pre{font-size:13px;font-family:monospace}.main textarea{border-style:dashed;border-color:#ed602b;outline:none;padding:10px;width:100%;word-wrap:break-word}@media all and (max-width:640px){.main{flex-direction:column}.main .console{padding:0 50px 0 20px}.main .tutorial{flex:3;margin-bottom:20px;order:-1}}.main .console{padding-right:20px;font-family:monospace;overflow:auto;background-color:#fbfaf3;border-style:dashed;border-color:#ed602b;border-width:thin;overflow-y:scroll;flex:3}.main .console .console-header{color:#ed602b}.main .console .console-text{padding-left:10px;padding-top:10px}.main .console .info{color:#222;margin-bottom:10px}.main .console code{white-space:pre-wrap;word-break:break-all;color:#ed602b}.main .console .prompt+code{color:#222;font-size:13px}.main .console .prompt{color:#ed602b;font-size:13px}.main .console input{font-size:13px;font-family:monospace;border:0;outline:none;background:transparent;width:calc(100% - 30px);margin-left:-1px}.main .console li:nth-child(even){margin:5px 16px}.main .console li:nth-child(odd){margin:5px 0px}.main .console .error{color:#d14;font-size:13px}.main .instructions{display:flex;flex-direction:column;flex:4}.main .instructions strong{font-weight:bold}.main .instructions p.note{background-color:#f5f2e3;border-left:solid 4px #B7CF99;line-height:18px;overflow:hidden;padding:12px;font-style:italic}.main .instructions hr.dashed{border-style:none none dashed;color:#ed602b}.main .instructions .lessons{padding:0 50px 0 50px;overflow:auto}.main .instructions .lessons h1{font-size:18pt;font-weight:600;margin-bottom:20px;color:#087B75}.main .instructions .lessons h2{font-size:18pt;font-weight:600;margin-bottom:20px;color:#ed602b}.main .instructions .lessons h3{font-size:14pt;font-weight:600;margin-bottom:20px;color:#ed602b}.main .instructions .lessons p,.main .instructions .lessons pre{line-height:1.4;margin-bottom:10px}.main .instructions .lessons li{line-height:1.4;margin-bottom:5px;list-style:circle;margin-left:20px}.main .instructions .lessons code{font-family:monospace;white-space:pre;border-radius:3px;padding:3px;color:#222;font-weight:bold}.main .instructions .lessons consoleCommand{font-family:monospace;white-space:pre;border-radius:3px;background:#fbfaf3;padding:3px;color:#ed602b}.main .instructions .lessons em{font-style:italic}.main .instructions .lessons .lesson{background-color:white}.main .instructions .lessons .lesson-image{max-width:100%;height:auto}.main .instructions .lessons .lesson:not(.lesson1){display:none}.main .instructions .lessonFooter{display:flex;flex:1;flex-direction:row;width:calc(100% - 60px);min-height:50px;margin:0 auto}.main .instructions .lessonFooter>div{flex:1}.main .instructions .lessonFooter .navigationButtonArea{padding:20px 10px 0 10px;font-size:12px}.main .instructions .lessonFooter button:enabled{background-color:#ed602b;color:white;font-family:'Courier New',Courier,monospace;border:none}.main .instructions .lessonFooter button:hover:enabled{transform:translate(2px, 2px);box-shadow:2px 2px #B7CF99}.main .instructions .lessonFooter .progressIndicator{padding:20px 0 0 0;color:#ed602b;font-family:'Courier New',Courier,monospace;font-size:12px}footer{margin-top:30px;height:90px;display:flex;flex-direction:column;justify-content:center;background-color:#f5f2e3;text-align:center}@media all and (max-width:500px){.gh-ribbon{display:none}}.hidden-storage{display:none} \ No newline at end of file +html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}html,body{padding:0;margin:0;height:100%}body{display:flex;flex-direction:column;font-family:"Open Sans",sans-serif;color:#222;background-image:url('../assets/proofofword-background-light.png');background-size:30%, 30%;background-attachment:fixed;background-position:25% 75%}header{margin-bottom:40px;height:120px;min-height:80px;display:flex;flex-direction:column;justify-content:center;background-color:#f5f2e3;text-align:center}header h1{text-transform:uppercase;font-size:48pt;font-weight:bold;color:#ed602b}.main{display:flex;flex:1;flex-direction:row;width:calc(100% - 60px);min-height:350px;margin:0 auto;gap:20px}.main>div{flex:1}.main ::-webkit-scrollbar{width:5px}.main ::-webkit-scrollbar-track{background:#f5f2e3}.main ::-webkit-scrollbar-thumb{background:#f3baa6}.main ::-webkit-scrollbar-thumb:hover{background:#555}.main pre{font-size:13px;font-family:monospace}.main textarea{border-style:dashed;border-color:#ed602b;outline:none;padding:10px;width:100%;word-wrap:break-word}@media all and (max-width:640px){.main{flex-direction:column}.main .console{padding:0 50px 0 20px}.main .tutorial{flex:3;margin-bottom:20px;order:-1}}.main .console{padding-right:20px;font-family:monospace;overflow:auto;background-color:#fbfaf3;border-style:dashed;border-color:#ed602b;border-width:thin;overflow-y:scroll;flex:3;user-select:text}.main .console .console-header{color:#ed602b}.main .console .console-text{padding-left:10px;padding-top:10px}.main .console .info{color:#222;margin-bottom:10px}.main .console code{white-space:pre-wrap;word-break:break-all;color:#ed602b}.main .console .prompt+code{color:#222;font-size:13px}.main .console .prompt{color:#ed602b;font-size:13px}.main .console input{font-size:13px;font-family:monospace;border:0;outline:none;background:transparent;width:calc(100% - 30px);margin-left:-1px}.main .console li:nth-child(even){margin:5px 16px}.main .console li:nth-child(odd){margin:5px 0px}.main .console .error{color:#d14;font-size:13px}.main .instructions{display:flex;flex-direction:column;flex:4}.main .instructions strong{font-weight:bold}.main .instructions p.note{background-color:#f5f2e3;border-left:solid 4px #B7CF99;line-height:18px;overflow:hidden;padding:12px;font-style:italic}.main .instructions hr.dashed{border-style:none none dashed;color:#ed602b}.main .instructions .lessons{padding:0 50px 0 50px;overflow:auto}.main .instructions .lessons h1{font-size:18pt;font-weight:600;margin-bottom:20px;color:#087B75}.main .instructions .lessons h2{font-size:18pt;font-weight:600;margin-bottom:20px;color:#ed602b}.main .instructions .lessons h3{font-size:14pt;font-weight:600;margin-bottom:20px;color:#ed602b}.main .instructions .lessons p,.main .instructions .lessons pre{line-height:1.4;margin-bottom:10px}.main .instructions .lessons li{line-height:1.4;margin-bottom:5px;list-style:circle;margin-left:20px}.main .instructions .lessons code{font-family:monospace;white-space:pre;border-radius:3px;padding:3px;color:#222;font-weight:bold}.main .instructions .lessons consoleCommand{font-family:monospace;white-space:pre;border-radius:3px;background:#fbfaf3;padding:3px;color:#ed602b}.main .instructions .lessons em{font-style:italic}.main .instructions .lessons .lesson{background-color:white}.main .instructions .lessons .lesson-image{max-width:100%;height:auto}.main .instructions .lessons .lesson:not(.lesson1){display:none}.main .instructions .lessonFooter{display:flex;flex:1;flex-direction:row;width:calc(100% - 60px);min-height:50px;margin:0 auto}.main .instructions .lessonFooter>div{flex:1}.main .instructions .lessonFooter .navigationButtonArea{padding:20px 10px 0 10px;font-size:12px}.main .instructions .lessonFooter button:enabled{background-color:#ed602b;color:white;font-family:'Courier New',Courier,monospace;border:none}.main .instructions .lessonFooter #backButton:enabled{background-color:#000;color:#fff}.main .instructions .lessonFooter button:hover:enabled{transform:translate(2px, 2px);box-shadow:2px 2px #B7CF99}.main .instructions .lessonFooter .progressIndicator{padding:20px 0 0 0;color:#ed602b;font-family:'Courier New',Courier,monospace;font-size:12px}footer{margin-top:30px;height:90px;display:flex;flex-direction:column;justify-content:center;background-color:#f5f2e3;text-align:center}@media all and (max-width:500px){.gh-ribbon{display:none}}.hidden-storage{display:none} \ No newline at end of file diff --git a/index.html b/index.html index cfb991f..e884574 100644 --- a/index.html +++ b/index.html @@ -238,7 +238,7 @@