/**
* @class
* @classdesc The tutorial object, containing data and methods for running the tutorial.
*/
Syntree.Tutorial = {
/**
* Any data that might need stored for the Tutorial.
* For example, the number of key presses when typing a node name.
*
* @type {object}
*/
data: {
node_naming_1: 0,
},
/**
* Standard time interval between messages, in milliseconds.
*
* type {number}
*/
standard_message_interval: 2500,
/**
* Data for each frame of the tutorial.
*
* @type {object}
* @property {string} message - the text to display on screen for this frame
* @property {number|object} gateway - either milliseconds to stay on screen for, or an object containing event data that describes an event on which to continue to the next frame
*/
frames: [
{
message: 'Hello! Welcome to Syntree',
},
{
message: 'Right now there is one node, called S',
},
{
message: 'You can tell it is selected because it is highlighted in gray',
},
{
message: 'Press the down arrow key to create a child node of S',
gateway: {
event_type: 'keydown',
condition: function(e) {
return e.keyCode === 40;
},
},
},
{
message: 'Good job!',
},
{
message: 'Now give this new node a name (just type!)',
gateway: {
event_type: 'keypress',
condition: function(e) {
if (e.key.length === 1) {
Syntree.Tutorial.data['node_naming_1'] += 1;
}
if (Syntree.Tutorial.data['node_naming_1'] > 2) {
return true;
}
return true;
}
},
},
{
message: 'And press Enter to make the new node permanent!',
gateway: {
event_type: 'keydown',
condition: function(e) {
return e.keyCode === 13;
}
},
},
{
message: 'Well done!',
},
{
message: 'Press left or right to create a sibling node',
gateway: {
event_type: 'keydown',
condition: function(e) {
return e.keyCode === 37 || e.keyCode === 39;
},
},
},
{
message: 'Name it and press Enter',
gateway: {
event_type: 'keydown',
condition: function(e) {
return e.keyCode === 13;
},
},
},
{
message: 'Woohoo! You\'ve got a tiny tree!',
},
{
message: 'The arrow keys can also be used to navigate to existing nodes',
},
{
message: 'Navigate back to the root node',
gateway: {
event_type: 'keydown',
condition: function() {
return Syntree.Workspace.page.tree.root.getState('selected');
},
},
},
{
message: 'Ok! You can press Enter, or double click, to edit the node',
gateway: {
event_type: [
'keydown',
'dblclick',
],
condition: function() {
return Syntree.Workspace.page.tree.getRoot().getState('editing');
}
},
},
{
message: 'You can edit it like regular text, and press Enter to save',
gateway: {
event_type: 'keydown',
condition: function(e) {
return e.keyCode === 13;
},
}
},
{
message: 'Remember, if you navigate away from a node without saving, your change will be lost!',
gateway: 5000,
},
{
message: 'Similarly, if you navigate away from a new node that hasn\'t been saved, it will disappear.',
gateway: 5000,
},
{
message: 'This makes it easy to fix accidental node creation -- if you press down and make a node you didn\'t want, just press up!',
gateway: 5000,
},
{
message: 'Ok. Moving on.',
},
{
message: 'You can delete subtrees by selecting a Node and either pressing DEL or clicking the small (x) button',
},
{
message: 'Give it a try! Delete a subtree.',
gateway: {
event_type: [
'keydown',
'click',
],
condition: function() {
return Syntree.History.getAll()[1].type === 'delete';
},
},
},
{
message: 'You can press CTRL + Z to undo most actions',
},
{
message: 'Try it now -- undo your actions until you\'re back to where you started!',
gateway: {
event_type: 'keydown',
condition: function() {
return Syntree.Workspace.page.tree.getRoot().getChildren().length === 0;
},
}
},
{
message: 'Great!'
},
{
message: 'That\'s all for now. For more help, click \'Help\' in the upper lefthand corner.'
},
],
/**
* Index of the current frame.
*
* @type {number}
*/
index: -1,
/**
* Is the tutorial currently running?
*
* @type {boolean}
*/
running: false,
/**
* Continue to the next frame.
*/
continue: function() {
this.index += 1;
$(document).off('.syntree_tutorial');
clearTimeout(this.timer);
if (this.index < this.frames.length && this.running) {
frame = this.frames[this.index];
this.frame(frame);
} else {
$('.tutorial_instruction').fadeOut(2000, function(){
this.quit();
});
}
},
/**
* Start the tutorial.
*/
start: function() {
if (this.running) {
this.quit();
this.start();
} else {
this.index = -1;
modal_close('app');
this.running = true;
this.continue();
}
},
/**
* Quit the tutorial.
*/
quit: function() {
this.index = Infinity;
this.running = false;
$(document).off('.syntree_tutorial');
clearTimeout(this.timer);
this.timer = undefined;
this.data.node_naming_1 = 0;
$('.tutorial_instruction').remove();
},
/**
* Run a single frame of the tutorial.
*
* @param {object} frame - one of Syntree.Tutorial.frames
*/
frame: function(frame) {
var message = Syntree.Lib.checkArg(frame.message, 'string');
var gateway = Syntree.Lib.checkArg(frame.gateway, ['object', 'number'], this.standard_message_interval);
this.instruction(message);
if (Syntree.Lib.checkType(gateway, 'number')) {
this.timer = setTimeout(
(function() {
this.continue()
}).bind(this), gateway);
} else if (Syntree.Lib.checkType(gateway, 'object')) {
var event_string = String(gateway.event_type).replace(',', '.syntree_tutorial ') + '.syntree_tutorial';
$(document).on(event_string, (function(e) {
if (gateway.condition(e)) {
$(document).off(event_string);
this.continue();
}
}).bind(this))
}
},
/**
* Display a new message to the screen.
*
* @param {string} text - the text to display.
*/
instruction: function(text) {
if ($('.tutorial_instruction:not(.primary)').length > 0) {
$('.tutorial_instruction:not(.primary)').fadeOut(1000, function() {
$(this).remove();
});
}
if ($('.tutorial_instruction.primary').length > 0) {
$('.tutorial_instruction.primary').removeClass('primary');
}
$('#workspace_container').append('<p class="tutorial_instruction primary">' + text + '</p>');
$('.tutorial_instruction.primary').fadeIn(1500);
},
}