From dd8c7e540764223fc1d6eb35b3ef8c26a402fa43 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sat, 2 Feb 2019 01:12:59 -0800 Subject: [PATCH 1/8] WIP work for advisor changes Signed-off-by: Matt Oswalt --- src/main/webapp/advisor/courseplan.html | 219 ++++++++++++++ src/main/webapp/advisor/index.html | 248 ++++++++++++++++ src/main/webapp/css/timeline.css | 134 +++++++++ src/main/webapp/js/antidote.js | 360 +++++++++++++++++------- src/main/webapp/labs/index.html | 34 +-- src/main/webapp/oldadvisor.html | 137 +++++++++ 6 files changed, 1010 insertions(+), 122 deletions(-) create mode 100644 src/main/webapp/advisor/courseplan.html create mode 100644 src/main/webapp/advisor/index.html create mode 100644 src/main/webapp/css/timeline.css create mode 100644 src/main/webapp/oldadvisor.html diff --git a/src/main/webapp/advisor/courseplan.html b/src/main/webapp/advisor/courseplan.html new file mode 100644 index 0000000..2ce4bd5 --- /dev/null +++ b/src/main/webapp/advisor/courseplan.html @@ -0,0 +1,219 @@ + + + + + + NRE Labs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+
+

2017

+

Lorem ipsum..

+
+
+
+
+

2016

+

Lorem ipsum..

+
+
+
+ +
+ + + + + + +
+ + + \ No newline at end of file diff --git a/src/main/webapp/advisor/index.html b/src/main/webapp/advisor/index.html new file mode 100644 index 0000000..f92c4ce --- /dev/null +++ b/src/main/webapp/advisor/index.html @@ -0,0 +1,248 @@ + + + + + + NRE Labs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
+
+
+

NRE Labs Advisor

+

+ Get a customized learning path. +

+
+
+
+
+ +
+ +
+

Search for what you want to learn above, or browse Lessons Below:

+
+ +
+
+
+

Fundamentals

+
+
Every Journey Has a Beginning.
+
These lessons are aimed at teaching the fundamental + technologies used needed by automation tools and NRE processes
+
    +
+
+
+
+

Tools

+
+
Building Blocks of Automation.
+
These lessons will introduce you to the specific tools + you're likely to use in your NRE journey.
+
    +
+
+
+
+

Workflows

+
+
Skills, applied.
+
These lessons will bring it all together and apply + fundamental skills and tools experience to accomplish a specific outcome.
+
    +
+
+
+
+
+ +
+ + + + + +
+ + + \ No newline at end of file diff --git a/src/main/webapp/css/timeline.css b/src/main/webapp/css/timeline.css new file mode 100644 index 0000000..9acccfe --- /dev/null +++ b/src/main/webapp/css/timeline.css @@ -0,0 +1,134 @@ +* { + box-sizing: border-box; +} + +/* Set a background color */ +body { + /* background-color: #474e5d; + font-family: Helvetica, sans-serif; */ +} + +/* The actual timeline (the vertical ruler) */ +.timeline { + position: relative; + max-width: 1200px; + margin: 0 auto; +} + +/* The actual timeline (the vertical ruler) */ +.timeline::after { + content: ''; + position: absolute; + width: 6px; + background-color: rgb(4, 214, 109); + top: 0; + bottom: 0; + left: 50%; + margin-left: -3px; +} + +/* Container around content */ +.timeline-container { + padding: 10px 40px; + position: relative; + background-color: inherit; + width: 50%; +} + +/* The circles on the timeline */ +.timeline-container::after { + content: ''; + position: absolute; + width: 25px; + height: 25px; + right: -12px; + background-color: white; + border: 4px solid #FF9F55; + top: 15px; + border-radius: 50%; + z-index: 1; +} + +/* Place the container to the left */ +.timeline-left { + left: 0; +} + +/* Place the container to the right */ +.timeline-right { + left: 50%; +} + +/* Add arrows to the left container (pointing right) */ +.timeline-left::before { + content: " "; + height: 0; + position: absolute; + top: 22px; + width: 0; + z-index: 1; + right: 30px; + border: medium solid red; + border-width: 10px 0 10px 10px; + border-color: transparent transparent transparent red; +} + +/* Add arrows to the right container (pointing left) */ +.timeline-right::before { + content: " "; + height: 0; + position: absolute; + top: 22px; + width: 0; + z-index: 1; + left: 30px; + border: medium solid red; + border-width: 10px 10px 10px 0; + border-color: transparent red transparent transparent; +} + +/* Fix the circle for containers on the right side */ +.timeline-right::after { + left: -16px; +} + +/* The actual content */ +.timeline-content { + padding: 20px 30px; + background-color: red; + position: relative; + border-radius: 6px; +} + +/* Media queries - Responsive timeline on screens less than 600px wide */ +@media screen and (max-width: 600px) { +/* Place the timelime to the left */ + .timeline::after { + left: 31px; + } + +/* Full-width containers */ + .timeline-container { + width: 100%; + padding-left: 70px; + padding-right: 25px; + } + +/* Make sure that all arrows are pointing leftwards */ + .timeline-container::before { + left: 60px; + border: medium solid white; + border-width: 10px 10px 10px 0; + border-color: transparent white transparent transparent; + } + +/* Make sure all circles are at the same spot */ + .timeline-left::after, .timeline-right::after { + left: 15px; + } + +/* Make all right containers behave like the left ones */ + .timeline-right { + left: 0%; + } +} \ No newline at end of file diff --git a/src/main/webapp/js/antidote.js b/src/main/webapp/js/antidote.js index 4fbdc9f..18566f5 100644 --- a/src/main/webapp/js/antidote.js +++ b/src/main/webapp/js/antidote.js @@ -1,5 +1,6 @@ // Will be overridden on page load. This is just the default var urlRoot = "https://labs.networkreliability.engineering" +var LESSONS = {}; // This function generates a unique session ID so we can make sure you consistently connect to your lab resources on the back-end. // We're not doing anything nefarious with this ID - this is just to make sure you have a good experience on the front-end. @@ -44,7 +45,7 @@ function runSnippetInTab(tabName, snippetIndex) { // is this really the best way? // For each character in the given string var snippetText = document.getElementById('labGuide').getElementsByTagName('pre')[parseInt(snippetIndex)].innerText; - for (var i=0; i < snippetText.length; i++) { + for (var i = 0; i < snippetText.length; i++) { // Get current codepoint var codepoint = snippetText.charCodeAt(i); @@ -80,7 +81,7 @@ function makeid() { } function getRandomModalMessage() { - + // Include memes? https://imgur.com/gallery/y0LQyOV var messages = [ "Sweeping technical debt under the rug...", @@ -97,43 +98,48 @@ function getRandomModalMessage() { return messages[Math.floor(Math.random() * messages.length)]; } -function renderLessonCategories() { +function getLessonCategories() { var xhttp = new XMLHttpRequest(); - xhttp.open("GET", urlRoot + "/syringe/exp/lessondef/all", false); + xhttp.open("GET", urlRoot + "/exp/lessondef", false); xhttp.setRequestHeader('Content-type', 'application/json; charset=utf-8'); xhttp.send(); + response = JSON.parse(xhttp.responseText); + if (xhttp.status != 200) { var errorMessage = document.getElementById("error-modal-body"); errorMessage.innerText = "Error retrieving lesson categories: " + response["error"]; $("#busyModal").modal("hide"); - $('#errorModal').modal({backdrop: 'static', keyboard: false}) + $('#errorModal').modal({ backdrop: 'static', keyboard: false }) return } - categories = JSON.parse(xhttp.responseText).lessonCategories; - console.log("Received lesson defs fom syringe: ") - console.log(categories) + // Store contents of response to global "LESSONS" variable as object indexed by lesson ID + LESSONS = {} + for (var i = 0; i < response.lessonDefs.length; i++) { + LESSONS[response.lessonDefs[i].LessonId] = response.lessonDefs[i] + } +} + +// renderLessonCategories downloads a full list of lesson definitions, and stores them in memory +// It will also populate the Advisor page with categorized lessons +function renderLessonCategories() { - for (var category in categories) { - var lessonDefs = categories[category].lessonDefs; + for (var i in LESSONS) { + if (LESSONS.hasOwnProperty(i)) { - for (var i = 0; i < lessonDefs.length; i++) { - console.log("Adding lesson to menu - " + lessonDefs[i].LessonName) + console.log("Adding lesson to menu - " + LESSONS[i].LessonName) var lessonLink = document.createElement('a'); - lessonLink.appendChild(document.createTextNode(lessonDefs[i].LessonName)); - lessonLink.classList.add('dropdown-item'); - lessonLink.href = "/labs/?lessonId=" + lessonDefs[i].LessonId + "&lessonStage=1"; - document.getElementById(category+"Menu").appendChild(lessonLink); - } + lessonLink.appendChild(document.createTextNode(LESSONS[i].LessonName)); + // lessonLink.classList.add('dropdown-item'); + lessonLink.href = "/labs/?lessonId=" + LESSONS[i].LessonId + "&lessonStage=1"; + + var lessonListItem = document.createElement('li') + lessonListItem.classList.add('list-group-item') + lessonListItem.appendChild(lessonLink) - // Populate quick start button with a random lesson - var quickStartButton = document.getElementById("btn"+category); - if (quickStartButton) { - var rand = Math.floor(Math.random() * categories[category].lessonDefs.length) - var randLessonId = categories[category].lessonDefs[rand].LessonId - quickStartButton.href = "/labs/?lessonId=" + randLessonId + "&lessonStage=1" + document.getElementById("lessonlist" + LESSONS[i].Category).appendChild(lessonListItem); } } } @@ -144,7 +150,7 @@ function renderLessonStages() { // TODO(mierdin): This is the first call to syringe, you should either here or elsewhere, handle errors and notify user. // Doing synchronous calls for now, need to convert to asynchronous - reqLessonDef.open("GET", urlRoot + "/syringe/exp/lessondef/" + getLessonId(), false); + reqLessonDef.open("GET", urlRoot + "/exp/lessondef/" + getLessonId(), false); reqLessonDef.setRequestHeader('Content-type', 'application/json; charset=utf-8'); reqLessonDef.send(); var lessonDefResponse = JSON.parse(reqLessonDef.responseText); @@ -153,7 +159,7 @@ function renderLessonStages() { var errorMessage = document.getElementById("error-modal-body"); errorMessage.innerText = "Error retrieving lesson stages: " + lessonDefResponse["error"]; $("#busyModal").modal("hide"); - $('#errorModal').modal({backdrop: 'static', keyboard: false}) + $('#errorModal').modal({ backdrop: 'static', keyboard: false }) return 0; } @@ -207,7 +213,7 @@ async function requestLesson() { // var xhttp = new XMLHttpRequest(); // // Doing synchronous calls for now, need to convert to asynchronous - // xhttp.open("POST", urlRoot + "/syringe/exp/livelesson", false); + // xhttp.open("POST", urlRoot + "/exp/livelesson", false); // xhttp.setRequestHeader('Content-type', 'application/json; charset=utf-8'); // xhttp.send(json); @@ -220,7 +226,7 @@ async function requestLesson() { // Send lesson request var xhttp = new XMLHttpRequest(); - xhttp.open("POST", urlRoot + "/syringe/exp/livelesson", false); + xhttp.open("POST", urlRoot + "/exp/livelesson", false); xhttp.setRequestHeader('Content-type', 'application/json; charset=utf-8'); xhttp.send(json); @@ -230,7 +236,7 @@ async function requestLesson() { var errorMessage = document.getElementById("error-modal-body"); errorMessage.innerText = "Error with initial lesson request: " + response["error"]; $("#busyModal").modal("hide"); - $('#errorModal').modal({backdrop: 'static', keyboard: false}) + $('#errorModal').modal({ backdrop: 'static', keyboard: false }) return } @@ -241,8 +247,8 @@ async function requestLesson() { // Here we go get the livelesson we requested, verify it's ready, and once it is, start wiring up endpoints. var xhttp2 = new XMLHttpRequest(); - xhttp2.open("GET", urlRoot + "/syringe/exp/livelesson/" + response.id, false); - // xhttp2.open("GET", "https://ptr.labs.networkreliability.engineering/syringe/exp/livelesson/12-jjtigg867ghr3gye", false); + xhttp2.open("GET", urlRoot + "/exp/livelesson/" + response.id, false); + // xhttp2.open("GET", "https://ptr.labs.networkreliability.engineering/exp/livelesson/12-jjtigg867ghr3gye", false); xhttp2.setRequestHeader('Content-type', 'application/json; charset=utf-8'); xhttp2.send(); @@ -250,7 +256,7 @@ async function requestLesson() { var errorMessage = document.getElementById("error-modal-body"); errorMessage.innerText = "Error retrieving requested lesson: " + response["error"]; $("#busyModal").modal("hide"); - $('#errorModal').modal({backdrop: 'static', keyboard: false}) + $('#errorModal').modal({ backdrop: 'static', keyboard: false }) return } @@ -264,7 +270,7 @@ async function requestLesson() { var errorMessage = document.getElementById("error-modal-body"); errorMessage.innerText = "Timeout waiting for lesson to become ready."; $("#busyModal").modal("hide"); - $('#errorModal').modal({backdrop: 'static', keyboard: false}) + $('#errorModal').modal({ backdrop: 'static', keyboard: false }) return } @@ -293,7 +299,7 @@ async function requestLesson() { if (liveLessonDetails.LessonVideo != null) { document.getElementById("btnOpenLessonVideo").style = "text-align: center;" document.getElementById("lessonVideoIframe").src = liveLessonDetails.LessonVideo; - document.getElementById("labGuide").style="padding-top: 10px;" + document.getElementById("labGuide").style = "padding-top: 10px;" } var nextLessonStage = parseInt(getLessonStage()) + 1 @@ -321,40 +327,40 @@ function updateProgressModal(liveLessonDetails) { var pBar = document.getElementById("liveLessonProgress"); var statusMessageElement = document.getElementById("lessonStatus"); - switch(liveLessonDetails.LiveLessonStatus) { + switch (liveLessonDetails.LiveLessonStatus) { case "INITIAL_BOOT": - totalEndpoints = 0; - reachableEndpoints = 0; - for (var property in liveLessonDetails.LiveEndpoints) { - totalEndpoints++; - if (liveLessonDetails.LiveEndpoints[property].Reachable == true) { - reachableEndpoints++; - } - } - statusMessageElement.innerText = "Waiting for lesson endpoints to become reachable...(" + reachableEndpoints + "/" + totalEndpoints + ")" - pBar.style = "width: 33%" - break; + totalEndpoints = 0; + reachableEndpoints = 0; + for (var property in liveLessonDetails.LiveEndpoints) { + totalEndpoints++; + if (liveLessonDetails.LiveEndpoints[property].Reachable == true) { + reachableEndpoints++; + } + } + statusMessageElement.innerText = "Waiting for lesson endpoints to become reachable...(" + reachableEndpoints + "/" + totalEndpoints + ")" + pBar.style = "width: 33%" + break; case "CONFIGURATION": - statusMessageElement.innerText = "Configuring endpoints for this lesson..." - pBar.style = "width: 66%" - break; + statusMessageElement.innerText = "Configuring endpoints for this lesson..." + pBar.style = "width: 66%" + break; case "READY": - statusMessageElement.innerText = "Almost ready!" - pBar.style = "width: 100%" - break; + statusMessageElement.innerText = "Almost ready!" + pBar.style = "width: 100%" + break; default: - // Shouldn't need this since we're getting rid of the default nil value on the syringe side, but just in case... - totalEndpoints = 0; - reachableEndpoints = 0; - for (var property in liveLessonDetails.LiveEndpoints) { - totalEndpoints++; - if (liveLessonDetails.LiveEndpoints[property].Reachable == true) { - reachableEndpoints++; - } - } - statusMessageElement.innerText = "Waiting for lesson endpoints to become reachable (" + reachableEndpoints + "/" + totalEndpoints + ")" - pBar.style = "width: 33%" - } + // Shouldn't need this since we're getting rid of the default nil value on the syringe side, but just in case... + totalEndpoints = 0; + reachableEndpoints = 0; + for (var property in liveLessonDetails.LiveEndpoints) { + totalEndpoints++; + if (liveLessonDetails.LiveEndpoints[property].Reachable == true) { + reachableEndpoints++; + } + } + statusMessageElement.innerText = "Waiting for lesson endpoints to become reachable (" + reachableEndpoints + "/" + totalEndpoints + ")" + pBar.style = "width: 33%" + } } function renderLabGuide(labGuideText) { @@ -426,7 +432,7 @@ function addTabs(endpoints) { } // newTabContent.height="350px"; // newTabContent.style.height = "350px"; - newTabContent.style="height: 100%;"; + newTabContent.style = "height: 100%;"; var newGuacDiv = document.createElement("DIV"); newGuacDiv.id = "display" + endpoints[i].Name @@ -488,7 +494,7 @@ function provisionLesson() { modal.removeChild(modal.firstChild); var modalMessage = document.createTextNode(getRandomModalMessage()); modal.appendChild(modalMessage); - $('#busyModal').modal({backdrop: 'static', keyboard: false}) + $('#busyModal').modal({ backdrop: 'static', keyboard: false }) requestLesson(); } @@ -532,7 +538,7 @@ function guacInit(endpoints) { thisTerminal.mouse.onmousedown = thisTerminal.mouse.onmouseup = - thisTerminal.mouse.onmousemove = function(id) { + thisTerminal.mouse.onmousemove = function (id) { return function (mouseState) { terminals[id].guac.sendMouseState(mouseState); } @@ -570,47 +576,14 @@ function guacInit(endpoints) { // Big honkin regex from https://stackoverflow.com/questions/11381673/detecting-a-mobile-browser function isMobile() { var check = false; - (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera); + (function (a) { if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; })(navigator.userAgent || navigator.vendor || window.opera); return check; }; -// Run all this once the DOM is fully rendered so we can get a handle on the DIVs -document.addEventListener('DOMContentLoaded', function () { - - urlRoot = window.location.href.split('/').slice(0, 3).join('/'); - - renderLessonCategories() - - if (getLessonId() != 0) { - if (isMobile() == true) { - alert("WARNING - NRE Labs doesn't yet support mobile. You can continue, but it likely won't work. Mobile support coming soon!") - } - - provisionLesson(); - } - - $('#videoModal').on('show.bs.modal', function () { - $("#videoModal iframe").attr("src", "https://www.youtube.com/embed/YhbWBX71yGQ?autoplay=1&rel=0"); - }); - - $("#videoModal").on('hidden.bs.modal', function (e) { - $("#videoModal iframe").attr("src", null); - }); - - $("#lessonVideoModal").on('hidden.bs.modal', function (e) { - // Just reset the `src` attribute, which will "un-play" the video - $("#lessonVideoModal iframe").attr("src", document.getElementById("lessonVideoIframe")); - }); - - if (urlRoot.substring(0,11) == "https://ptr") { - appendPTRBanner(); - } -}); - function appendPTRBanner() { var buildInfoReq = new XMLHttpRequest(); - buildInfoReq.open("GET", urlRoot + "/syringe/exp/syringeinfo", false); + buildInfoReq.open("GET", urlRoot + "/exp/syringeinfo", false); buildInfoReq.setRequestHeader('Content-type', 'application/json; charset=utf-8'); buildInfoReq.send(); @@ -623,7 +596,7 @@ function appendPTRBanner() { console.log(buildInfo) var scripts = document.getElementsByTagName('script'); - var lastScript = scripts[scripts.length-1]; + var lastScript = scripts[scripts.length - 1]; var scriptName = lastScript.src; var commits = { @@ -632,9 +605,9 @@ function appendPTRBanner() { "syringe": buildInfo.buildSha, } - var antidoteLink = "" + commits.antidote.substring(0,7) + "" - var antidoteWebLink = "" + commits.antidoteweb.substring(0,7) + "" - var syringeLink = "" + commits.syringe.substring(0,7) + "" + var antidoteLink = "" + commits.antidote.substring(0, 7) + "" + var antidoteWebLink = "" + commits.antidoteweb.substring(0, 7) + "" + var syringeLink = "" + commits.syringe.substring(0, 7) + "" var ptrBanner = document.createElement("DIV"); ptrBanner.id = "ptrBanner" @@ -644,3 +617,180 @@ function appendPTRBanner() { document.body.appendChild(ptrBanner) } + + +function searchBox(e) { + + searchResults = document.getElementById("searchResults") + + var options = { + shouldSort: true, + includeScore: true, + threshold: 0.6, + location: 0, + distance: 100, + maxPatternLength: 32, + minMatchCharLength: 1, + keys: [ + "LessonName" + ] + }; + + //TODO(mierdin): Need to figure out how to search on description and tags + + var fuse = new Fuse(LESSONS, options); // "list" is the item array + var results = fuse.search(e.target.value); + + while (searchResults.firstChild) { + searchResults.removeChild(searchResults.firstChild); + } + + + // TODO (mierdin): Limit this to like 3 or 4 + for (var result in results) { + var resultItem = results[result].item + + var searchResultItem = document.createElement('a'); + searchResultItem.classList.add('list-group-item'); + searchResultItem.classList.add('list-group-item-action'); + searchResultItem.style = "text-align:left;z-index: 1;" + searchResultItem.href = "/advisor/courseplan.html?lessonId=" + resultItem.LessonId; + + var lessonTitle = document.createElement('h4'); + lessonTitle.innerText = resultItem.LessonName + searchResultItem.appendChild(lessonTitle); + + var lessonTitle = document.createElement('p'); + lessonTitle.innerText = resultItem.Description + searchResultItem.appendChild(lessonTitle); + + var badgesDiv = document.createElement('div'); + + var categoryBadge = document.createElement('span'); + categoryBadge.classList.add('badge'); + // categoryBadge.classList.add('badge-pill'); + categoryBadge.classList.add('badge-primary'); + categoryBadge.style = "margin-right: 10px;" + categoryBadge.innerText = "Category: " + resultItem.Category + badgesDiv.appendChild(categoryBadge); + + var collectionBadge = document.createElement('span'); + collectionBadge.classList.add('badge'); + // collectionBadge.classList.add('badge-pill'); + collectionBadge.classList.add('badge-info'); + collectionBadge.style = "margin-right: 10px;" + collectionBadge.innerText = "Collection: " + resultItem.Collection + badgesDiv.appendChild(collectionBadge); + + searchResultItem.appendChild(badgesDiv); + + // Do something with this? + // results[result].score + searchResults.appendChild(searchResultItem) + } +} + +// getLessonDeps is designed to take a lesson ID as a parameter and return a list of lesson IDs +// that this lesson depends on. The returned list will include the provided lesson ID as well. +function getLessonDeps(lessonId) { + + for (var lesson in alldefs) { + + } + +} + + + + + +// // Run all this once the DOM is fully rendered so we can get a handle on the DIVs +// document.addEventListener('DOMContentLoaded', function () { + +// // urlRoot = window.location.href.split('/').slice(0, 3).join('/'); +// urlRoot = "http://127.0.0.1:8086" + +// renderLessonCategories() + +// if (getLessonId() != 0) { +// if (isMobile() == true) { +// alert("WARNING - NRE Labs doesn't yet support mobile. You can continue, but it likely won't work. Mobile support coming soon!") +// } + +// provisionLesson(); +// } + +// $('#videoModal').on('show.bs.modal', function () { +// $("#videoModal iframe").attr("src", "https://www.youtube.com/embed/YhbWBX71yGQ?autoplay=1&rel=0"); +// }); + +// $("#videoModal").on('hidden.bs.modal', function (e) { +// $("#videoModal iframe").attr("src", null); +// }); + +// $("#lessonVideoModal").on('hidden.bs.modal', function (e) { +// // Just reset the `src` attribute, which will "un-play" the video +// $("#lessonVideoModal iframe").attr("src", document.getElementById("lessonVideoIframe")); +// }); + +// if (urlRoot.substring(0,11) == "https://ptr") { +// appendPTRBanner(); +// } + +// $('#searchBox').on('input',function(e){ +// searchBox(e) +// }); +// }); + + +function getPrereqs(lessonId) { + var reqLessonPrereqs = new XMLHttpRequest(); + + // TODO(mierdin): This is the first call to syringe, you should either here or elsewhere, handle errors and notify user. + + // Doing synchronous calls for now, need to convert to asynchronous + reqLessonPrereqs.open("GET", urlRoot + "/exp/lessondef/" + getLessonId() + "/prereqs", false); + reqLessonPrereqs.setRequestHeader('Content-type', 'application/json; charset=utf-8'); + reqLessonPrereqs.send(); + var prereqs = JSON.parse(reqLessonPrereqs.responseText).prereqs; + + if (reqLessonPrereqs.status != 200) { + var errorMessage = document.getElementById("error-modal-body"); + errorMessage.innerText = "Error retrieving lesson stages: " + prereqs["error"]; + $("#busyModal").modal("hide"); + $('#errorModal').modal({ backdrop: 'static', keyboard: false }) + return 0; + } + + console.log("GOT PREREQS - " + prereqs) + + for (var i = 0; i < prereqs.length; i++) { + var strengthDiv = document.createElement('div'); + strengthDiv.id = "strength-" + prereqs[i] + strengthDiv.classList.add('modal-body'); + + var strengthQuestion = document.createElement('h4'); + strengthQuestion.innerText = "How well do you know " + LESSONS[prereqs[i]].Slug + "?" + strengthDiv.appendChild(strengthQuestion); + + var strengthSlider = document.createElement('input') + strengthSlider.id = "slider-" + prereqs[i] + strengthSlider.type="text" + console.log(strengthSlider) + strengthDiv.appendChild(strengthSlider); + + document.getElementById('strengthsFinder-body').appendChild(strengthDiv) + + var slider = new Slider("#" + "slider-" + prereqs[i], { + step: 1, + ticks: [1, 2, 3, 4, 5], + min: 1, + max: 5, + id: "slider-" + prereqs[i], + value: 3 + }); + } + +} + + diff --git a/src/main/webapp/labs/index.html b/src/main/webapp/labs/index.html index 987aa2a..3a7489c 100644 --- a/src/main/webapp/labs/index.html +++ b/src/main/webapp/labs/index.html @@ -9,7 +9,7 @@ - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - + @@ -196,7 +196,7 @@ you this lesson right now. Please post the details of what lesson you were accessing, as well as the above error message as a Github issue. In the meantime, try - refreshing this page, or trying + refreshing this page, or searching for another lesson. diff --git a/src/main/webapp/oldadvisor.html b/src/main/webapp/oldadvisor.html new file mode 100644 index 0000000..0c57bab --- /dev/null +++ b/src/main/webapp/oldadvisor.html @@ -0,0 +1,137 @@ + + + + + + + + + + Collapsible Tree Example + + + + + + + + I want to... + + + + + + + + \ No newline at end of file From 9fd8c50bacdcf6d77ea3ae952613567f80162a3a Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Sat, 2 Feb 2019 16:41:18 -0800 Subject: [PATCH 2/8] Added a lot more for the timeline Signed-off-by: Matt Oswalt --- src/main/webapp/advisor/courseplan.html | 58 ++++++--- src/main/webapp/advisor/index.html | 4 +- src/main/webapp/css/timeline.css | 77 ++++++++++-- src/main/webapp/js/antidote.js | 151 +++++++++++++++++++++++- 4 files changed, 255 insertions(+), 35 deletions(-) diff --git a/src/main/webapp/advisor/courseplan.html b/src/main/webapp/advisor/courseplan.html index 2ce4bd5..e1aeaa7 100644 --- a/src/main/webapp/advisor/courseplan.html +++ b/src/main/webapp/advisor/courseplan.html @@ -10,6 +10,7 @@ + @@ -39,15 +40,29 @@ // urlRoot = window.location.href.split('/').slice(0, 3).join('/'); urlRoot = "http://127.0.0.1:8086" + // TODO(mierdin): throw error if no lesson ID provided + if (urlRoot.substring(0, 11) == "https://ptr") { appendPTRBanner(); } + $('#strengthsFinder').modal({ backdrop: 'static', keyboard: false }) + getLessonCategories() getPrereqs() - $('#strengthsFinder').modal({ backdrop: 'static', keyboard: false }) + var lessonSlug = LESSONS[getLessonId()].Slug + var planTitle = document.getElementById('plantitle') + planTitle.innerText = "Your Journey to " + lessonSlug + + $('#planNamebox').on('input', function (e) { + if (e.target.value == "") { + planTitle.innerText = "Your Journey to " + lessonSlug + } else { + planTitle.innerText = e.target.value + "'s Journey to " + lessonSlug + } + }); }); @@ -149,35 +164,40 @@
-
-
-
-

2017

-

Lorem ipsum..

-
-
-
-
-

2016

-

Lorem ipsum..

-
+ +
+

Your Custom Learning Plan.

+

Based on the suggested prerequisites for the lesson you selected, and your existing + strengths, + we've curated a custom learning path for you to get where you need to be. +

+
+

Scroll down for the lesson plan, or use the options below to print or send this plan.

+

+ + +

+
+
+
+

Your Journey to Greatness

+ https://labs.networkreliability.engineering
-
@@ -160,41 +116,47 @@

NRE Labs Advisor

-
+
-

Search for what you want to learn above, or browse Lessons Below:

+

Search for what you want to learn above, or browse lessons below:

-
+

Fundamentals

-
-
Every Journey Has a Beginning.
-
These lessons are aimed at teaching the fundamental +
+
These lessons are aimed at + teaching the fundamental technologies used needed by automation tools and NRE processes
+
+

Tools

-
-
Building Blocks of Automation.
-
These lessons will introduce you to the specific tools +
+
These lessons will introduce you + to the specific tools you're likely to use in your NRE journey.
+
+

Workflows

-
-
Skills, applied.
-
These lessons will bring it all together and apply +
+
These lessons will bring it all + together and apply fundamental skills and tools experience to accomplish a specific outcome.
+
+
diff --git a/src/main/webapp/css/timeline.css b/src/main/webapp/css/timeline.css index 3b7a7f5..8b367cc 100644 --- a/src/main/webapp/css/timeline.css +++ b/src/main/webapp/css/timeline.css @@ -25,8 +25,10 @@ body { border-radius: 5px; box-shadow: 5px 5px 10px rgba(0,0,0,0.5); border-style: solid; - max-height: 900px; - overflow: auto; + padding-bottom: 10px; + /* max-height: 900px; */ + /* overflow: auto; */ + overflow: hidden; margin-bottom: 2rem; } @@ -36,11 +38,12 @@ body { position: absolute; width: 6px; background-color: rgb(4, 214, 109); - top: 0; + top: 100px; bottom: 0; - left: 50%; + left: 10%; margin-left: -3px; z-index: 1; + height: 90%; } /* Container around content */ @@ -48,7 +51,8 @@ body { padding: 10px 40px; position: relative; background-color: inherit; - width: 50%; + width: 70%; + top: 10px; } /* The circles on the timeline */ @@ -72,7 +76,7 @@ body { /* Place the container to the right */ .timeline-right { - left: 50%; + left: 10%; } /* Add arrows to the left container (pointing right) */ @@ -112,25 +116,36 @@ body { .timeline-content-low { padding: 20px 30px; position: relative; - border-color: rgb(255, 158, 158); + border-color: rgb(249, 99, 99); + background-color: rgb(255, 215, 215); border-radius: 20px; border-style: solid; + border-width: 7px; + height: 100%; } .timeline-content-mid { padding: 20px 30px; position: relative; - border-color: rgb(255, 217, 147); + border-color: rgb(255, 180, 21); + background-color: rgb(252, 255, 147); border-radius: 20px; border-style: solid; + border-width: 7px; } .timeline-content-high { padding: 20px 30px; position: relative; - border-color: rgb(154, 255, 154); + border-color: rgb(44, 208, 0); + background-color: rgb(154, 255, 154); border-radius: 20px; border-style: solid; + border-width: 7px; +} + +.timeline-content a { + font-size: 40px; } .timeline-header { diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index 483b321..667109b 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -8,6 +8,7 @@ + + + + + + + + @@ -60,52 +94,9 @@
From e63dbe6427c85edfc51dfbf87b595554d26c628b Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 4 Feb 2019 13:16:55 -0800 Subject: [PATCH 4/8] Fix border style issues on advisor page Signed-off-by: Matt Oswalt --- src/main/webapp/advisor/courseplan.html | 2 +- src/main/webapp/advisor/index.html | 1 + .../webapp/css/{timeline.css => advisor.css} | 16 ++++++++++++++++ src/main/webapp/index.html | 2 +- 4 files changed, 19 insertions(+), 2 deletions(-) rename src/main/webapp/css/{timeline.css => advisor.css} (91%) diff --git a/src/main/webapp/advisor/courseplan.html b/src/main/webapp/advisor/courseplan.html index 4327207..b5cfbb9 100644 --- a/src/main/webapp/advisor/courseplan.html +++ b/src/main/webapp/advisor/courseplan.html @@ -8,7 +8,7 @@ - + diff --git a/src/main/webapp/advisor/index.html b/src/main/webapp/advisor/index.html index bcbe400..20d02fc 100644 --- a/src/main/webapp/advisor/index.html +++ b/src/main/webapp/advisor/index.html @@ -8,6 +8,7 @@ + @@ -108,24 +119,29 @@
-

NRE Labs Advisor

-

- Get a customized learning path. -

+
+

NRE Labs Advisor

+

+ Get a customized learning path. +

+ +
+

Use the box above to say what you want to learn, and we'll work with you to build a relevant learning path. Try "Python" or "StackStorm"!

+
-
- -
-

Search for what you want to learn above, or browse lessons below:

-
+
+ +
-
+

Lesson Catalog

+

Already know what lesson you're looking for? That's awesome - all lessons are linked directly below.

+

Fundamentals

diff --git a/src/main/webapp/css/advisor.css b/src/main/webapp/css/advisor.css index 4fd195b..3979a23 100644 --- a/src/main/webapp/css/advisor.css +++ b/src/main/webapp/css/advisor.css @@ -12,6 +12,14 @@ body { height: 70px; } +#searchResults { + position:absolute; + max-width:600px; + max-height: 900px; + overflow-y: auto; + z-index: 4; +} + .list-group-flush:first-child .list-group-item:first-child { border-top-width: 1px; border-top-style: solid; diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index fefab8f..0ba2d9f 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -28,6 +28,24 @@ + diff --git a/src/main/webapp/js/antidote.js b/src/main/webapp/js/antidote.js index 0d864fe..82e0b82 100644 --- a/src/main/webapp/js/antidote.js +++ b/src/main/webapp/js/antidote.js @@ -720,22 +720,19 @@ function searchBox(e) { ] }; + // Handle keyboard events like this, so folks can arrow down to the one they want? + // if (e.code == "ArrowDown") {} + //TODO(mierdin): Need to figure out how to search on description and tags // http://fusejs.io/ var fuse = new Fuse(LESSONS_ARRAY, options); var results = fuse.search(e.target.value); - - console.log(LESSONS_ARRAY) - console.log(results) - while (searchResults.firstChild) { searchResults.removeChild(searchResults.firstChild); } - - // TODO (mierdin): Limit this to like 3 or 4 for (var result in results) { var resultItem = results[result].item @@ -757,19 +754,18 @@ function searchBox(e) { var categoryBadge = document.createElement('span'); categoryBadge.classList.add('badge'); - // categoryBadge.classList.add('badge-pill'); categoryBadge.classList.add('badge-primary'); categoryBadge.style = "margin-right: 10px;" categoryBadge.innerText = "Category: " + resultItem.Category badgesDiv.appendChild(categoryBadge); - var collectionBadge = document.createElement('span'); - collectionBadge.classList.add('badge'); - // collectionBadge.classList.add('badge-pill'); - collectionBadge.classList.add('badge-info'); - collectionBadge.style = "margin-right: 10px;" - collectionBadge.innerText = "Collection: " + resultItem.Collection - badgesDiv.appendChild(collectionBadge); + // FUTURE + // var collectionBadge = document.createElement('span'); + // collectionBadge.classList.add('badge'); + // collectionBadge.classList.add('badge-info'); + // collectionBadge.style = "margin-right: 10px;" + // collectionBadge.innerText = "Collection: " + resultItem.Collection + // badgesDiv.appendChild(collectionBadge); searchResultItem.appendChild(badgesDiv); @@ -779,58 +775,6 @@ function searchBox(e) { } } -// getLessonDeps is designed to take a lesson ID as a parameter and return a list of lesson IDs -// that this lesson depends on. The returned list will include the provided lesson ID as well. -function getLessonDeps(lessonId) { - - for (var lesson in alldefs) { - - } - -} - - - - - -// // Run all this once the DOM is fully rendered so we can get a handle on the DIVs -// document.addEventListener('DOMContentLoaded', function () { - -// // urlRoot = window.location.href.split('/').slice(0, 3).join('/'); -// urlRoot = "http://127.0.0.1:8086" - -// renderLessonCategories() - -// if (getLessonId() != 0) { -// if (isMobile() == true) { -// alert("WARNING - NRE Labs doesn't yet support mobile. You can continue, but it likely won't work. Mobile support coming soon!") -// } - -// provisionLesson(); -// } - -// $('#videoModal').on('show.bs.modal', function () { -// $("#videoModal iframe").attr("src", "https://www.youtube.com/embed/YhbWBX71yGQ?autoplay=1&rel=0"); -// }); - -// $("#videoModal").on('hidden.bs.modal', function (e) { -// $("#videoModal iframe").attr("src", null); -// }); - -// $("#lessonVideoModal").on('hidden.bs.modal', function (e) { -// // Just reset the `src` attribute, which will "un-play" the video -// $("#lessonVideoModal iframe").attr("src", document.getElementById("lessonVideoIframe")); -// }); - -// if (urlRoot.substring(0,11) == "https://ptr") { -// appendPTRBanner(); -// } - -// $('#searchBox').on('input',function(e){ -// searchBox(e) -// }); -// }); - var PREREQS = []; function getPrereqs(lessonId) { @@ -842,7 +786,11 @@ function getPrereqs(lessonId) { reqLessonPrereqs.open("GET", urlRoot + "/syringe/exp/lessondef/" + getLessonId() + "/prereqs", false); reqLessonPrereqs.setRequestHeader('Content-type', 'application/json; charset=utf-8'); reqLessonPrereqs.send(); + var prereqs = JSON.parse(reqLessonPrereqs.responseText).prereqs; + if (prereqs == null){ + prereqs = [] + } if (reqLessonPrereqs.status != 200) { var errorMessage = document.getElementById("error-modal-body"); @@ -852,15 +800,16 @@ function getPrereqs(lessonId) { return 0; } - console.log("GOT PREREQS - " + prereqs) - - PREREQS = prereqs; + // No need to show strength sliders, this is a leson without prereqs. + if (prereqs.length == 0) { + buildLessonPlan({}) + $('#strengthsFinder').modal("hide") + return 0; + } - // function updateStrengthLabels() { - // for (var i = 0; i < prereqs.length; i++) { + $('#strengthsFinder').modal({ backdrop: 'static', keyboard: false }) - // } - // } + PREREQS = prereqs; for (var i = 0; i < prereqs.length; i++) { var strengthDiv = document.createElement('div'); From de96f060e9fe6b574bf14accde1288cc8ce8da18 Mon Sep 17 00:00:00 2001 From: Matt Oswalt Date: Mon, 4 Feb 2019 23:13:59 -0800 Subject: [PATCH 7/8] Last-minute cleanup Signed-off-by: Matt Oswalt --- CHANGELOG.md | 1 + src/main/webapp/advisor/courseplan.html | 3 - src/main/webapp/advisor/index.html | 1 - src/main/webapp/minty.html.original | 1526 ----------------------- src/main/webapp/oldadvisor.html | 137 -- src/main/webapp/oldcourses.html | 250 ---- 6 files changed, 1 insertion(+), 1917 deletions(-) delete mode 100644 src/main/webapp/minty.html.original delete mode 100644 src/main/webapp/oldadvisor.html delete mode 100644 src/main/webapp/oldcourses.html diff --git a/CHANGELOG.md b/CHANGELOG.md index 7907e48..f548acf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## In development - Finally implement clipboard functionality; formally establish toolbar [31](https://github.com/nre-learning/antidote-web/pull/31) +- Implement Advisor functionality [#30](https://github.com/nre-learning/antidote-web/pull/30) ## 0.2.0 - January 24, 2019 diff --git a/src/main/webapp/advisor/courseplan.html b/src/main/webapp/advisor/courseplan.html index 2be7289..80f244c 100644 --- a/src/main/webapp/advisor/courseplan.html +++ b/src/main/webapp/advisor/courseplan.html @@ -39,7 +39,6 @@ document.addEventListener('DOMContentLoaded', function () { urlRoot = window.location.href.split('/').slice(0, 3).join('/'); - // urlRoot = "http://127.0.0.1:8086" // TODO(mierdin): throw error if no lesson ID provided @@ -148,13 +147,11 @@

Your Journey to Greatness

- -