-
Notifications
You must be signed in to change notification settings - Fork 8
Creating Tutorials
Within the context of netnet.stuio, a "tutorial" is a directory which consists of a metadata.json
file, as well as a JavaScript file with the tutorial's content (the "steps" and any necessary widgets) as well as any other accompanying assets (images, videos, etc).
netnet's TutorialManager.js (instantiated globally in main.js as NNT
) is responsible for much of the internal tutorial logic.
It can load tutorial data by passing a tutorial name (or URL) into it's .load()
method, for example:
// loading an internal tutorial
NNT.load('example')
// loading an externally hosted tutorial
const url = 'https://raw.githubusercontent.com/netizenorg/netnet.studio/master/www/tutorials/example/'
NNT.load(url)
It can also launch a tutorial on page load by passing netnet a tutorial
URL parameter, ex: http://netnet.studio/?tutorial=name-or-url
.
In the root directory of a tutorial, we need to define all the meta data in a json file named metadata.json
which should look like this:
{
"title": "New Media Art",
"sub-title": "understanding digital art as a metamedia",
"author": "Nick Briz",
"preferred-name": "Nick",
"main": "main.js",
"checkpoints": {
"defining new media": "intro",
"new media as a metamedium": "metamedia",
"taxonomies && properties": "tax-and-props",
"new media as a cultural movement": "movement",
"new media as an ecology": "ecology"
},
"endnotes": [
{
"note": "'Murray, Janet H.\"Inventing the Medium\" The New Media Reader. Ed. Noah Wardrip-Fruin & Nick Montfort. MIT Press, 2003.",
"url": "http://www.newmediareader.com/book_samples/nmr-intro-murray-excerpt.pdf"
},
{
"note": "Kay, Alan & Adele Goldberg. \"Personal Dynamic Computer\" Computer 10(3):31-41. March 1977",
"url": "http://www.newmediareader.com/book_samples/nmr-26-kay.pdf"
}
]
}
main should be a path to the js file containing our tutorial data.
checkpoints is an optional object netnet uses to create the "table of contents" or "list of chapters", the values for whic should be id names from the associated steps (ie. where those "chapters" begin).
endnotes sources for any external references or sources for claims made throughout the tutorial.
The main js file should include a single global variable called TUTORIAL
which should be an object with at least a steps
array (collection of objects representing the data for each "step" in the tutorial). If the tutorial makes use of any custom widgets, those can be defined in the optional widgets
property (itself an object).
window.TUTORIAL = {
steps: [/* array of "step" objects */],
widgets: {} // optional widgets created/used during tutorial
}
A "step", is the object representing the data for that part of the tutorial (stored in the steps
array). It needs to have at the very least a content
property containing the info we want showing up in netnet's speech bubble at that step:
{ content: 'content to display in text bubble' }
It can, however, contain much more data than that, here's a verbose example:
{
id: 'defining new media', // id of step for non-linear tutorials
content: 'content to display in text bubble',
options: {/* custom buttons to appear in text bubble */}
code: '<h1>code to be injected into netitor at this step</h1>',
edit: false, // should netitor be editable during this step
highlight: { // highlight code in the editor for this step
startLine: 10, // the line to start highlighting on
startCol: 16, // the column on that line to start on
endLine: 10, // the line to end highlighting on
endCol: 22, // the column on that line to end on
color: 'rgba(0, 255, 0, 0.5)' // highlight color
},
layout: 'dock-left', // change layout at this step
opacity: 0.5, // change opacity at this step
}
The highlight property could also just be a number if/when we want to highlight the entire line with a default color, ex: highlight: 10
Her's an example of a simple (but totally functional) main.js file
window.TUTORIAL = {
steps: [
{ content: 'HTML isn\'t the only type of markup language.' },
{ content: 'Others include MathML and even SVG.' },
{ content: 'Artists like the Graffiti Research Lab have even made their own.' },
{ content: 'It\'s called GML or Graffiti Markup Language.'}
]
}
The previous example and the following are examples of "linear tutorials". If none of the steps have an id
property the TutorialManager will assume they should be displayed linearly. Additionally, if a step doesn't include an options
property it takes the liberty of creating "next" and "previous" buttons to navigate each step, as well as an "ok" button at the last step to complete the tutorial. We can of course overide these default buttons at any step by creating our own options
property. In a linear tutorial an example options object could look like:
//...
options: {
'go on to the next step': (e) => { e.next() },
'wait, go back a step': (e) => { e.prev() }
}
//...
the .next()
and .prev()
methods are simply shorter alias of the STORE.dispatch('TUTORIAL_NEXT_STEP')
and STORE.dispatch('TUTORIAL_PREV_STEP')
(we could also just as simply call these). Similarly there's a .end()
alias for STORE.dispatch('TUTORIAL_FINISHED')
and .hide()
for STORE.dispatch('HIDE_TUTORIAL_TEXT')
Here's an example of a functioning linear steps array with custom options:
[
{
content: 'HTML isn\'t the only type of markup language.',
options: {
'oh no? what others are there?': (e) => e.next()
}
},
{
content: 'There\'s <a href="https://developer.mozilla.org/en-US/docs/Web/MathML" target="_blank">MathML</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/SVG" target="_blank">SVG</a>.',
options: {
'you don\'t say! Are there more?': (e) => e.next(),
'wait a sec, go back.': (e) => e.prev()
}
},
{
content: 'Oh yes! Artists like the Graffiti Research Lab have even made their own.',
options: {
'really? What\'s it called?': (e) => e.next(),
'wait a sec, go back.': (e) => e.prev()
}
},
{
content: 'It\'s called GML or <a href="https://en.wikipedia.org/wiki/Graffiti_Markup_Language" target="_blank">Graffiti Markup Language</a>.',
options: {
'wait a sec, go back.': (e) => e.prev(),
'cool thnx for the info!': (e) => e.end()
}
}
]
We can of course add other buttons to trigger other behavior, like opening up a custom widget, this can be done using .open('widget name')
an alias for STORE.dispatch('OPEN_WIDGET', 'name')
. There's also .close('widget name')
an alias for STORE.dispatch('CLOSE_WIDGET', 'name')
Here's another simple (but totally functional) example:
window.TUTORIAL = {
widgets: {
'color wheel': new Widget({
title: 'color wheel',
innerHTML: `<img src="https://cdn.sparkfun.com/assets/learn_tutorials/7/1/0/TertiaryColorWheel_Chart.png" alt="color wheel" style="width: 400px">`
})
},
steps: [
{
content: 'Take a look at this line of code, notice anything?',
highlight: 10
},
{
content: 'We\'ve changed the way we\'re defining colors.'
},
{
content: 'There are even more ways to specify colors in CSS.',
options: {
'launch color widget': () => e.open('color wheel'),
'cool, i\'m done': (e) => e.fin() // an alias for e.end() (^___^)
}
}
]
}
What if we want to have a non-linear conversation with netnet? Where the user's choice determines which step they see next? In these instances we need to give our step objects an additional id
property, which can be any string (but need to be unique to other id's in the array). We can then use the TutorialManager's internal .goTo('id')
method (an alias for STORE.dispatch('TUTORIAL_GOTO', 'id')
) in the option's callback functions by passing in the id of the step we want netnet to switch to when the user picks that option.
[
{
id: 'begin',
content: 'Would you like to experiment a bit more?',
options: {
'yes please': (e) => e.goTo('more-experimenting'),
'naw, i\'m good': (e) => e.goTo('last')
}
},
{
id: 'more-experimenting',
content: 'Ok great, dont\'t forget about the helpful widgets!',
options: {
'open color widget': (e) => e.open('color picker'),
'ok, i\'m done': (e) => e.goTo('last')
}
},
{
id: 'last',
content: 'Great! hope you had a good time!',
options: {
'i did, thnx': (e) => e.end()
}
},
]
TODO: add hyper video tutorial functionality