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 + '' + nodeConfig.definition + ''
+ html = html + '' + nodeConfig.definition + ''
html = html + ' | '
html = html + ''
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 + '
'
}
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 + '
Note: ' + nodeConfig.note + '
'
+ html = html + '
Note: ' + nodeConfig.note + '
'
}
if (nodeConfig.tip !== undefined && nodeConfig.tip !== '') {
- html = html + '
Tip: ' + nodeConfig.tip + '
'
+ html = html + '
Tip: ' + nodeConfig.tip + '
'
}
if (nodeConfig.important !== undefined && nodeConfig.important !== '') {
- html = html + '
Important: ' + nodeConfig.important + '
'
+ html = html + '
Important: ' + nodeConfig.important + '
'
}
if (nodeConfig.warning !== undefined && nodeConfig.warning !== '') {
- html = html + '
Important: ' + nodeConfig.warning + '
'
+ html = html + '
Important: ' + nodeConfig.warning + '
'
}
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
+ }
+ }
+}