diff --git a/AppSchema.json b/AppSchema.json index d71cf8078..aeefe678d 100644 --- a/AppSchema.json +++ b/AppSchema.json @@ -14151,7 +14151,7 @@ "config": true }, "initialValues": { - "config": "{ \n\"title\": \"\",\n \"summary\": \"\",\n \"subTitle\": \"\",\n \"image\": \"\",\n \"definition\": \"\",\n \"bulletListIntro\": \"\",\n \"bulletArray\": \n[\n [\"\", \"\"], \n[\"\", \"\"], \n[\"\", \"\"] \n],\n \"paragraph1\": \"\",\n \"callOut\": \"\",\n \"externalLink\": [\"\", \"\"],\n \"paragraph2\": \"\",\n \"note\": \"\",\n \"tip\": \"\",\n \"important\": \"\",\n \"warning\": \"\", \n \"position\": \"\",\n \"documentationURL\": \"\" \n}" + "config": "{ \n\"title\": \"\",\n \"summary\": \"\",\n \"subTitle\": \"\",\n \"gif\": \"\",\n \"image\": \"\",\n \"definition\": \"\",\n \"bulletListIntro\": \"\",\n \"bulletArray\": \n[\n [\"\", \"\"], \n[\"\", \"\"], \n[\"\", \"\"] \n],\n \"paragraph1\": \"\",\n \"callOut\": \"\",\n \"externalLink\": [\"\", \"\"],\n \"paragraph2\": \"\",\n \"note\": \"\",\n \"tip\": \"\",\n \"important\": \"\",\n \"warning\": \"\", \n \"position\": \"\",\n \"slider\": \"toTop\",\n \"documentationURL\": \"\" \n}" }, "isTitleAllwaysVisible": true, "isHierarchyHead": true, @@ -14222,10 +14222,11 @@ "config": true }, "initialValues": { - "config": "{ \n\"title\": \"\",\n \"summary\": \"\",\n \"image\": \"\",\n \"definition\": \"\",\n \"bulletListIntro\": \"\",\n \"bulletArray\": \n[\n [\"\", \"\"], \n[\"\", \"\"], \n[\"\", \"\"] \n],\n \"paragraph1\": \"\",\n \"callOut\": \"\",\n \"externalLink\": [\"\", \"\"],\n \"paragraph2\": \"\",\n \"note\": \"\",\n \"tip\": \"\",\n \"important\": \"\",\n \"warning\": \"\", \n \"position\": \"\",\n \"documentationURL\": \"\" \n}" + "config": "{ \n\"title\": \"\",\n \"summary\": \"\",\n \"gif\": \"\",\n \"image\": \"\",\n \"definition\": \"\",\n \"bulletListIntro\": \"\",\n \"bulletArray\": \n[\n [\"\", \"\"], \n[\"\", \"\"], \n[\"\", \"\"] \n],\n \"paragraph1\": \"\",\n \"callOut\": \"\",\n \"externalLink\": [\"\", \"\"],\n \"paragraph2\": \"\",\n \"note\": \"\",\n \"tip\": \"\",\n \"important\": \"\",\n \"warning\": \"\", \n \"position\": \"\",\n \"slider\": \"toTop\",\n \"documentationURL\": \"\" \n}" }, "referenceAttachesTo": { - "compatibleTypes": "->*Any Node*->" + "compatibleTypes": "->*Any Node*->", + "incompatibleTypes": "->Tutorial->Tutorial Topic->Tutorial Step->" }, "chainAttachesTo": { "compatibleTypes": "->Tutorial->" @@ -14285,10 +14286,11 @@ "config": true }, "initialValues": { - "config": "{ \n \"subTitle\": \"\",\n \"image\": \"\",\n \"definition\": \"\",\n \"bulletListIntro\": \"\",\n \"bulletArray\": \n[\n [\"\", \"\"], \n[\"\", \"\"], \n[\"\", \"\"] \n],\n \"paragraph1\": \"\",\n \"callOut\": \"\",\n \"externalLink\": [\"\", \"\"],\n \"paragraph2\": \"\",\n \"note\": \"\",\n \"tip\": \"\",\n \"important\": \"\",\n \"warning\": \"\", \n \"position\": \"\",\n \"documentationURL\": \"\" \n}" + "config": "{ \n \"subTitle\": \"\",\n \"gif\": \"\",\n \"image\": \"\",\n \"definition\": \"\",\n \"bulletListIntro\": \"\",\n \"bulletArray\": \n[\n [\"\", \"\"], \n[\"\", \"\"], \n[\"\", \"\"] \n],\n \"paragraph1\": \"\",\n \"callOut\": \"\",\n \"externalLink\": [\"\", \"\"],\n \"paragraph2\": \"\",\n \"note\": \"\",\n \"tip\": \"\",\n \"important\": \"\",\n \"warning\": \"\", \n \"position\": \"\",\n \"slider\": \"toTop\",\n \"documentationURL\": \"\" \n}" }, "referenceAttachesTo": { - "compatibleTypes": "->*Any Node*->" + "compatibleTypes": "->*Any Node*->", + "incompatibleTypes": "->Tutorial->Tutorial Topic->Tutorial Step->" }, "chainAttachesTo": { "compatibleTypes": "->Tutorial->Tutorial Topic->" diff --git a/BackendServers/WebServer/AppLoader.js b/BackendServers/WebServer/AppLoader.js index d34a37a99..f1d8b2627 100644 --- a/BackendServers/WebServer/AppLoader.js +++ b/BackendServers/WebServer/AppLoader.js @@ -65,6 +65,7 @@ function newAppLoader() { 'Utilities/Clipboard.js', 'Utilities/DrawPrint.js', 'Utilities/LoadSaveFrame.js', + 'Utilities/LoadSaveTutorial.js', 'Utilities/NodeConfig.js', 'Utilities/HiriarchyMap.js', 'Utilities/Folders.js', diff --git a/BackendServers/WebServer/Fonts/segoeui.woff b/BackendServers/WebServer/Fonts/segoeui.woff new file mode 100644 index 000000000..afe528bc7 Binary files /dev/null and b/BackendServers/WebServer/Fonts/segoeui.woff differ diff --git a/BackendServers/WebServer/Images/Tutorial/Gifs/example.gif b/BackendServers/WebServer/Images/Tutorial/Gifs/example.gif new file mode 100644 index 000000000..766cf4c48 Binary files /dev/null and b/BackendServers/WebServer/Images/Tutorial/Gifs/example.gif differ diff --git a/BackendServers/WebServer/index.html b/BackendServers/WebServer/index.html index 30027805f..88e9d558b 100644 --- a/BackendServers/WebServer/index.html +++ b/BackendServers/WebServer/index.html @@ -54,13 +54,13 @@
- + - + - +
diff --git a/BackendServers/WebServer/main.css b/BackendServers/WebServer/main.css index fb1e63fe0..c96697ada 100644 --- a/BackendServers/WebServer/main.css +++ b/BackendServers/WebServer/main.css @@ -15,6 +15,13 @@ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } +@font-face { + font-family: segoeui; + src: url(Fonts/segoeui.woff) format('woff'); + font-weight: normal; + font-style: normal; +} + .tutorial-step { padding: 15px; margin-bottom: 20px; @@ -139,38 +146,42 @@ -moz-osx-font-smoothing:grayscale } -.tutorial-saira-small { +.tutorial-font-small { - font:normal normal normal 14px/1 "Saira"; + font:normal normal normal 14px/1 "segoeui"; font-size:inherit; text-rendering:auto; -webkit-font-smoothing:antialiased; - -moz-osx-font-smoothing:grayscale + -moz-osx-font-smoothing:grayscale; + line-height: 150% } -.tutorial-saira-medium { +.tutorial-font-medium { - font:normal normal normal 30px/1 "Saira"; + font:normal normal normal 30px/1 "segoeui"; text-rendering:auto; -webkit-font-smoothing:antialiased; - -moz-osx-font-smoothing:grayscale + -moz-osx-font-smoothing:grayscale; + line-height: 150% } -.tutorial-saira-large { +.tutorial-font-large { - font:bold normal normal 40px/1 "Saira"; + font:bold normal normal 40px/1 "segoeui"; text-rendering:auto; -webkit-font-smoothing:antialiased; - -moz-osx-font-smoothing:grayscale + -moz-osx-font-smoothing:grayscale; + line-height: 150% } -.tutorial-saira-bold-small { +.tutorial-font-bold-small { - font:bold normal normal 14px/1 "Saira"; + font:bold normal normal 14px/1 "segoeui"; font-size:inherit; text-rendering:auto; -webkit-font-smoothing:antialiased; - -moz-osx-font-smoothing:grayscale + -moz-osx-font-smoothing:grayscale; + line-height: 150% } .tutorial-stop-button { diff --git a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ProtocolNode.js b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ProtocolNode.js index d339a10e8..2e5803fa6 100644 --- a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ProtocolNode.js +++ b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/ProtocolNode.js @@ -179,6 +179,12 @@ function newProtocolNode() { } } + if (node.payload.tutorial !== undefined) { + savedPayload.tutorial = { + status: node.payload.tutorial.status + } + } + if (includeReferencesInSavedPayload) { /* Next for the ones that have a reference parent, we include it */ if (node.payload.referenceParent !== undefined) { diff --git a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/UiObjectsFromNodes.js b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/UiObjectsFromNodes.js index f76a584d9..cc7b25ab2 100644 --- a/CanvasApp/DesignSpace/Workspace/FunctionLibraries/UiObjectsFromNodes.js +++ b/CanvasApp/DesignSpace/Workspace/FunctionLibraries/UiObjectsFromNodes.js @@ -576,6 +576,12 @@ function newUiObjectsFromNodes() { frame.radius = node.savedPayload.frame.radius saveFrame(payload, frame) } + if (node.savedPayload.tutorial !== undefined) { + let tutorial = { + status: node.savedPayload.tutorial.status + } + saveTutorial(payload, tutorial) + } } /* Now we mount the floating object where the UIOBject will be laying on top of */ diff --git a/CanvasApp/FloatingSpace/UiObject.js b/CanvasApp/FloatingSpace/UiObject.js index 96f49cb53..0304da0a9 100644 --- a/CanvasApp/FloatingSpace/UiObject.js +++ b/CanvasApp/FloatingSpace/UiObject.js @@ -618,6 +618,11 @@ function newUiObject() { let floatingObject = nearby[1] let nearbyNode = floatingObject.payload.node if (compatibleTypes.indexOf('->' + nearbyNode.type + '->') >= 0 || compatibleTypes === "->*Any Node*->" ) { + if (nodeDefinition.referenceAttachesTo.incompatibleTypes !== undefined) { + if (nodeDefinition.referenceAttachesTo.incompatibleTypes.indexOf('->' + nearbyNode.type + '->') >= 0) { + continue + } + } if (foundCompatible === false) { if (distance < thisObject.payload.floatingObject.container.frame.radius * 1.5 + floatingObject.container.frame.radius * 1.5) { nearbyNode.payload.uiObject.getReadyToReferenceAttach() diff --git a/CanvasApp/Spaces/TutorialSpace.js b/CanvasApp/Spaces/TutorialSpace.js index a87e4b96a..e21a2eebd 100644 --- a/CanvasApp/Spaces/TutorialSpace.js +++ b/CanvasApp/Spaces/TutorialSpace.js @@ -10,8 +10,6 @@ function newTutorialSpace() { resumeTutorialTopic: resumeTutorialTopic, playTutorialStep: playTutorialStep, resumeTutorialStep: resumeTutorialStep, - tutorialTopicDone: tutorialTopicDone, - tutorialStepDone: tutorialStepDone, resetTutorialProgress: resetTutorialProgress, container: undefined, physics: physics, @@ -41,6 +39,9 @@ function newTutorialSpace() { let tutorialDiv = document.getElementById('tutorialDiv') let tutorialFormDiv = document.getElementById('tutorialFormDiv') + let htmlGif = document.createElement("IMG") + let currentGifName = 'Never Set' + let newGifName = 'Never Set' let htmlImage = document.createElement("IMG") let currentImageName = 'Never Set' let newImageName = 'Never Set' @@ -48,6 +49,7 @@ function newTutorialSpace() { let newConfig let newDocumentationURL let currentDocumentationURL + let resumeModeActivated // In this mode, we skip all the node which status is 'Done' return thisObject @@ -65,6 +67,7 @@ function newTutorialSpace() { tutorialDiv = undefined tutorialFormDiv = undefined image = undefined + gif = undefined } function resize() { @@ -105,11 +108,26 @@ function newTutorialSpace() { } } + checkGif() checkImage() checkDocumentation() + checkSlider() checkReference() return + function checkGif() { + let tutorialGifDiv = document.getElementById('tutorialGifDiv') + if (tutorialGifDiv !== null && tutorialGifDiv !== undefined) { + tutorialGifDiv.appendChild(htmlGif) + } + + if (currentGifName === newGifName) { return } + currentGifName = newGifName + htmlGif.src = 'Images/Tutorial/Gifs/' + currentGifName + '.gif' + htmlGif.width = "400" + //htmlGif.height = "100" + } + function checkImage() { let tutorialImageDiv = document.getElementById('tutorialImageDiv') if (tutorialImageDiv !== null && tutorialImageDiv !== undefined) { @@ -124,18 +142,63 @@ function newTutorialSpace() { } function checkDocumentation() { - if (currentNode === undefined) {return} + if (currentNode === undefined) { return } let config = JSON.parse(currentNode.config) let newDocumentationURL = config.documentationURL - if (newDocumentationURL === undefined || newDocumentationURL === "") { + if (newDocumentationURL === undefined) { + /* + The doc panel will remain as it is, and the user will be free to open or close it at will. + */ + return + } + if (newDocumentationURL === "") { + /* + This forces the tutorial to close the documentation panel and to keep it closed. + */ canvas.docSpace.sidePanelTab.close() + return } if (newDocumentationURL === currentDocumentationURL) { return } - + currentDocumentationURL = newDocumentationURL canvas.docSpace.navigateTo(currentDocumentationURL) } + function checkSlider() { + if (currentNode === undefined) { return } + let config = JSON.parse(currentNode.config) + if (config.slider === undefined) { + /* + The cockpit space will remain where it is. The user is free to move it. + */ + return + } + switch (config.slider) { + case "toTop": { + /* + This forces the tutorial to close the charting space and to keep it closed. + */ + canvas.cockpitSpace.toTop() + return + } + case "toMiddle": { + /* + This forces the tutorial to share the screen half with the designer and half + with the charting space and force it in that way. + */ + canvas.cockpitSpace.toMiddle() + return + } + case "toBottom": { + /* + This forces the tutorial to fully open the charting space and to keep it open. + */ + canvas.cockpitSpace.toBottom() + return + } + } + } + function checkReference() { /* If there is a reference parent defined, we will highlight it @@ -240,10 +303,15 @@ function newTutorialSpace() { tutorialRootNode = node currentNode = node currentStatus = 'Playing Tutorial' + resumeModeActivated = false } function resumeTutorial(node) { - + tutorialRootNode = node + currentNode = node + currentStatus = 'Playing Tutorial' + resumeModeActivated = true + advance() } function stop() { @@ -252,11 +320,18 @@ function newTutorialSpace() { } function skip() { - + let tutorial = { + status: 'Skipped' + } + saveTutorial(currentNode.payload, tutorial) + advance() } function done() { - /* If we are standing at the Tutorial Node, we do this. */ + let tutorial = { + status: 'Done' + } + saveTutorial(currentNode.payload, tutorial) advance() } @@ -264,34 +339,56 @@ function newTutorialSpace() { currentTopicNode = node currentNode = node currentStatus = 'Playing Topic' + resumeModeActivated = false findTutorialNode(node) } function resumeTutorialTopic(node) { - + currentTopicNode = node + currentNode = node + currentStatus = 'Playing Topic' + resumeModeActivated = true + findTutorialNode(node) } function playTutorialStep(node) { currentStepNode = node currentNode = node currentStatus = 'Playing Step' + resumeModeActivated = false findTutorialNode(node) } function resumeTutorialStep(node) { - + currentStepNode = node + currentNode = node + currentStatus = 'Playing Step' + resumeModeActivated = true + findTutorialNode(node) } - function tutorialTopicDone() { + function resetTutorialProgress(node) { + resetNextNode(node) - } + function resetNextNode(node) { - function tutorialStepDone() { - - } - - function resetTutorialProgress() { + for (let i = 0; i < node.tutorialSteps.length; i++) { + let tutorialStep = node.tutorialSteps[i] + let tutorial = { + status: 'Reset' + } + saveTutorial(tutorialStep.payload, tutorial) + } + for (let i = 0; i < node.tutorialTopics.length; i++) { + let tutorialTopic = node.tutorialTopics[i] + let tutorial = { + status: 'Reset' + } + saveTutorial(tutorialTopic.payload, tutorial) + resetNextNode(tutorialTopic) + } + } } function findTutorialNode(node) { @@ -354,10 +451,23 @@ function newTutorialSpace() { let tutorialStep = node.tutorialSteps[i] if (found === true) { - currentNode = tutorialStep - currentStatus = 'Playing Step' - advanced = true - return + if (resumeModeActivated !== true) { + currentNode = tutorialStep + currentStatus = 'Playing Step' + advanced = true + return + } else { + let tutorial = { + status: undefined + } + loadTutorial(tutorialStep.payload, tutorial) + if (tutorial.status !== 'Done') { + currentNode = tutorialStep + currentStatus = 'Playing Step' + advanced = true + return + } + } } if (tutorialStep.id === currentNode.id) { found = true @@ -372,10 +482,23 @@ function newTutorialSpace() { let tutorialTopic = node.tutorialTopics[i] if (found === true) { - currentNode = tutorialTopic - currentStatus = 'Playing Topic' - advanced = true - return + if (resumeModeActivated !== true) { + currentNode = tutorialTopic + currentStatus = 'Playing Topic' + advanced = true + return + } else { + let tutorial = { + status: undefined + } + loadTutorial(tutorialTopic.payload, tutorial) + if (tutorial.status !== 'Done') { + currentNode = tutorialTopic + currentStatus = 'Playing Topic' + advanced = true + return + } + } } if (tutorialTopic.id === currentNode.id) { found = true @@ -395,14 +518,18 @@ function newTutorialSpace() { let nodeConfig = JSON.parse(currentNode.config) let html = '' if (nodeConfig.title !== undefined && nodeConfig.title !== '') { - html = html + '

' + nodeConfig.title + '

' + html = html + '

' + nodeConfig.title + '

' } html = html + '
' if (nodeConfig.summary !== undefined && nodeConfig.summary !== '') { - html = html + '
' + nodeConfig.summary + '
' + html = html + '
' + nodeConfig.summary + '
' } if (nodeConfig.subTitle !== undefined && nodeConfig.subTitle !== '') { - html = html + '

' + nodeConfig.subTitle + '

' + html = html + '

' + nodeConfig.subTitle + '

' + } + if (nodeConfig.gif !== undefined && nodeConfig.gif !== '') { + html = html + '
' + newGifName = nodeConfig.gif } if (nodeConfig.definition !== undefined && nodeConfig.definition !== '') { html = html + '' @@ -414,51 +541,51 @@ function newTutorialSpace() { } html = html + '' html = html + '' html = html + '' html = html + '
' - html = html + '' + nodeConfig.definition + '' + html = html + '' + nodeConfig.definition + '' html = html + '
' } if (nodeConfig.bulletListIntro !== undefined && nodeConfig.bulletListIntro !== '') { - html = html + '

' + nodeConfig.bulletListIntro + '

' + html = html + '

' + nodeConfig.bulletListIntro + '

' } if (nodeConfig.bulletArray !== undefined) { html = html + '' } if (nodeConfig.paragraph1 !== undefined && nodeConfig.paragraph1 !== '') { - html = html + '

' + nodeConfig.paragraph1 + '

' + html = html + '

' + nodeConfig.paragraph1 + '

' } if (nodeConfig.callOut !== undefined && nodeConfig.callOut !== '') { - html = html + '
' + nodeConfig.callOut + '' + html = html + '
' + nodeConfig.callOut + '' if (nodeConfig.externalLink !== undefined) { html = html + '' + nodeConfig.externalLink[0] + '' } html = html + '
' } if (nodeConfig.paragraph2 !== undefined && nodeConfig.paragraph2 !== '') { - html = html + '

' + nodeConfig.paragraph2 + '

' + html = html + '

' + nodeConfig.paragraph2 + '

' } if (nodeConfig.paragraph2 !== undefined && nodeConfig.paragraph2 !== '') { - html = html + '

' + nodeConfig.paragraph2 + '

' + html = html + '

' + nodeConfig.paragraph2 + '

' } if (nodeConfig.note !== undefined && nodeConfig.note !== '') { - html = html + '' + html = html + '' } if (nodeConfig.tip !== undefined && nodeConfig.tip !== '') { - html = html + '' + html = html + '' } if (nodeConfig.important !== undefined && nodeConfig.important !== '') { - html = html + '' + html = html + '' } if (nodeConfig.warning !== undefined && nodeConfig.warning !== '') { - html = html + '' + html = html + '' } html = html + '
' diff --git a/CanvasApp/Utilities/LoadSaveTutorial.js b/CanvasApp/Utilities/LoadSaveTutorial.js new file mode 100644 index 000000000..402a3ddf0 --- /dev/null +++ b/CanvasApp/Utilities/LoadSaveTutorial.js @@ -0,0 +1,18 @@ +function saveTutorial(payload, tutorial) { + payload.tutorial = { + status: tutorial.status + } +} + +function loadTutorial(payload, tutorial) { + if (payload.tutorial !== undefined) { + tutorial.status = payload.tutorial.status + return + } + if (payload.node.savedPayload !== undefined) { + if (payload.node.savedPayload.tutorial !== undefined) { + tutorial.status = payload.node.savedPayload.tutorial.status + payload.node.savedPayload.tutorial = undefined + } + } +}