Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make a tutorial that acts as an editor #742

Merged
merged 3 commits into from
Oct 13, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 1 addition & 36 deletions examples/common/examples.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,4 @@
var exampleUtils = {
/* Decode query components into a dictionary of values.
*
* @returns {object}: the query parameters as a dictionary.
*/
getQuery: function () {
var query = document.location.search.replace(/(^\?)/, '').split(
'&').map(function (n) {
n = n.split('=');
if (n[0]) {
this[decodeURIComponent(n[0].replace(/\+/g, '%20'))] = decodeURIComponent(n[1].replace(/\+/g, '%20'));
}
return this;
}.bind({}))[0];
return query;
},

/* Encode a dictionary of parameters to the query string, setting the window
* location and history. This will also remove undefined values from the
* set properites of params.
*
* @param {object} params: the query parameters as a dictionary.
*/
setQuery: function (params) {
$.each(params, function (key, value) {
if (value === undefined) {
delete params[key];
}
});
var newurl = window.location.protocol + '//' + window.location.host +
window.location.pathname + '?' + $.param(params);
window.history.replaceState(params, '', newurl);
}
};

window.utils = exampleUtils;
window.utils = require('./utils');

/* Add a function to take a screenshot. Show the screenshot so that a user can
* click on it to save it or right-click to copy it. */
Expand Down
36 changes: 36 additions & 0 deletions examples/common/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
var exampleUtils = {
/* Decode query components into a dictionary of values.
*
* @returns {object}: the query parameters as a dictionary.
*/
getQuery: function () {
var query = document.location.search.replace(/(^\?)/, '').split(
'&').map(function (n) {
n = n.split('=');
if (n[0]) {
this[decodeURIComponent(n[0].replace(/\+/g, '%20'))] = decodeURIComponent(n[1].replace(/\+/g, '%20'));
}
return this;
}.bind({}))[0];
return query;
},

/* Encode a dictionary of parameters to the query string, setting the window
* location and history. This will also remove undefined values from the
* set properites of params.
*
* @param {object} params: the query parameters as a dictionary.
*/
setQuery: function (params) {
$.each(params, function (key, value) {
if (value === undefined) {
delete params[key];
}
});
var newurl = window.location.protocol + '//' + window.location.host +
window.location.pathname + '?' + $.param(params);
window.history.replaceState(params, '', newurl);
}
};

module.exports = exampleUtils;
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"mousetrap": "^1.6.0",
"nib": "^1.1.2",
"node-resemble": "^2.0.1",
"pako": "^1.0.6",
"phantomjs-prebuilt": "^2.1.5",
"proj4": "2.3.16",
"pug": "2.0.0-rc.2",
Expand Down
1 change: 1 addition & 0 deletions tutorials/basic/index.pug
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ block mainTutorial
height: 100%;
padding: 0;
margin: 0;
overflow: hidden;
}

:markdown-it
Expand Down
4 changes: 4 additions & 0 deletions tutorials/common/tutorials.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ html, body {
width: 100%;
height: 100%;
overflow: hidden;
display: flex;
}
.navbar #maincontent {
height: calc(100% - 60px);
Expand All @@ -28,9 +29,12 @@ html, body {
overflow-y: auto;
padding: 0.5em;
background: ivory;
resize: horizontal;
min-width: 10em;
}
#workframe {
overflow: hidden;
flex-grow: 100;
}
.codeblock .codeblock_entry {
width: calc(100% - 6em);
Expand Down
119 changes: 95 additions & 24 deletions tutorials/common/tutorials.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
var $ = require('jquery');
var pako = require('pako');
var utils = require('../../examples/common/utils');

/* Track the last selector used for generating a code block and also a timer so
* we can avoid rendering the same codeblock if it is actively being edited. */
Expand Down Expand Up @@ -26,6 +28,7 @@ var processBlockInfo = {
' height: 100%;\n' +
' padding: 0;\n' +
' margin: 0;\n' +
' overflow: hidden;\n' +
'}'
};

Expand Down Expand Up @@ -156,6 +159,7 @@ function process_block(selector) {
window.tutorial = targetelem[0].contentWindow;
window.tutorials = window.tutorials || {};
window.tutorials[target] = targetelem[0].contentWindow;
elem.trigger('geojs-tutorial-run');
}
}

Expand Down Expand Up @@ -187,13 +191,101 @@ function process_block_debounce(selector, debounce) {
}, 2000);
}

/**
* Run any default code blocks, then start listening for changes in code
* blocks.
*/
function run_tutorial() {
/* If any of the codeblocks is marked 'default', run them. Do this in a
* timeout so that other start up scripts can run */
$('.codeblock[initial="true"]').each(function (idx, elem) {
run_block(elem);
});
/* Whenever a code block changes, run it with its parents */
$('.codeblock textarea').bind('input propertychange', function (evt) {
run_block(evt.target, true, true);
});
$('.codeblock .CodeMirror').each(function () {
$(this)[0].CodeMirror.on('change', function (elem) {
run_block(elem.getWrapperElement(), true, true);
});
});
/* Bind run and reset buttons */
$('.codeblock_reset').click(function (evt) {
var elem = $('textarea', $(evt.target).closest('.codeblock'));
elem.val(elem.attr('defaultvalue'));
$('.CodeMirror', $(evt.target).closest('.codeblock'))[0].CodeMirror.setValue(elem.attr('defaultvalue'));
run_block(evt.target, true);
});
$('.codeblock_run').click(function (evt) {
run_block(evt.target, undefined, undefined, evt.shiftKey);
});
}

/**
* Get query parameters for each step and update them as we start. Monitor the
* code blocks and update the url when they change if appropriate.
*
* @param {boolean} alwaysKeep If `true`, update the url even if the `keep` url
* parameter is not specified.
*/
function start_keeper(alwaysKeep) {
var query = utils.getQuery(),
keep = query.keep || (alwaysKeep === true);

$('.codeblock').each(function () {
var block = $(this),
key = 'src' + (block.attr('step') !== '1' ? block.attr('step') : '');
if (query[key]) {
try {
/* Strip out white space and pluses, then convert ., -, and _ to /, +,
* =. By removing whitespace, the url is more robust against email
* handling. The others keep things short. */
var src = atob(query[key].replace(/(\s|\+)/g, '').replace(/\./g, '/').replace(/-/g, '+').replace(/_/g, '='));
src = pako.inflate(src, {to: 'string', raw: true});
if ($('.CodeMirror', block).length) {
$('.CodeMirror', block)[0].CodeMirror.setValue(src);
} else {
$('textarea', block).val(src);
}
} catch (err) { }
}
});
if (keep) {
$(document).on('geojs-tutorial-run', '.codeblock', function () {
var newQuery = {};
if (query.keep && alwaysKeep !== true) {
newQuery.keep = query.keep;
}
$('.codeblock').each(function () {
var block = $(this),
defaultSrc = $('textarea', block).attr('defaultvalue'),
key = 'src' + (block.attr('step') !== '1' ? block.attr('step') : '');

var src = $('.CodeMirror', block).length ? $('.CodeMirror', block)[0].CodeMirror.getValue() : $('textarea', block).val().trim();
if (src !== defaultSrc) {
var comp = btoa(pako.deflate(src, {to: 'string', level: 9, raw: true}));
/* instead of using regular base64, convert /, +, and = to ., -, and _
* so that they don't need to be escaped on the url. This reduces the
* average length of the url by 6 percent. */
comp = comp.replace(/\//g, '.').replace(/\+/g, '-').replace(/=/g, '_');
newQuery[key] = comp;
}
});
utils.setQuery(newQuery);
});
}
}

/**
* Process code blocks to remove unwanted white space and store default values,
* set up event handling, and run any initial code blocks.
*
* @param {boolean} useCodeMirror If explicitly false, don't use CodeMirror.
* @param {boolean} alwaysKeep If `true`, update the url even if the `keep` url
* parameter is not specified.
*/
function start_tutorial(useCodeMirror) {
function start_tutorial(useCodeMirror, alwaysKeep) {
/* clean up whitespace and store a default value for each code block */
$('.codeblock').each(function () {
var elem = $('textarea', this),
Expand Down Expand Up @@ -225,29 +317,8 @@ function start_tutorial(useCodeMirror) {
}
/* Check if iframe srcdoc support is present */
processBlockInfo.srcdocSupport = !!('srcdoc' in document.createElement('iframe'));
/* If any of the codeblocks is marked 'default', run them */
$('.codeblock[initial="true"]').each(function (idx, elem) {
run_block(elem);
});
/* Whenever a code block changes, run it with its parents */
$('.codeblock textarea').bind('input propertychange', function (evt) {
run_block(evt.target, true, true);
});
$('.codeblock .CodeMirror').each(function () {
$(this)[0].CodeMirror.on('change', function (elem) {
run_block(elem.getWrapperElement(), true, true);
});
});
/* Bind run and reset buttons */
$('.codeblock_reset').click(function (evt) {
var elem = $('textarea', $(evt.target).closest('.codeblock'));
elem.val(elem.attr('defaultvalue'));
$('.CodeMirror', $(evt.target).closest('.codeblock'))[0].CodeMirror.setValue(elem.attr('defaultvalue'));
run_block(evt.target, true);
});
$('.codeblock_run').click(function (evt) {
run_block(evt.target, undefined, undefined, evt.shiftKey);
});
start_keeper(alwaysKeep);
run_tutorial();
}

module.exports = start_tutorial;
7 changes: 7 additions & 0 deletions tutorials/editor/editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* global start_tutorial */

$(function () {
/* start_tutorial has to be called explicitly when we are ready since we need
* to ask it to always keep url changes. */
start_tutorial(undefined, true);
});
21 changes: 21 additions & 0 deletions tutorials/editor/index.pug
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
extends ../common/index.pug

block mainTutorial
:markdown-it
# Editor
Any changes made will be stored in the URL whenever the code is run. This can be sent as a link, bookmarked, or otherwise shared.

You can interact with the code through the javascript console by accessing the top-level variables in the `tutorial` global parameter.

+codeblock('javascript', 1, undefined, true).
var map = geo.map({
node: "#map",
center: {x: 4.90, y: 52.37},
zoom: 14
});
var layer = map.createLayer('osm');
+codeblock_test('map has one osm layer from openstreetmap', [
'map.layers().length === 1',
'map.layers()[0] instanceof geo.osmLayer',
'layer.url().match(/openstreetmap/)'
])
Binary file added tutorials/editor/thumb.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions tutorials/editor/tutorial.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"title": "Editor",
"hideNavbar": true,
"level": 10,
"tutorialJs": ["editor.js"],
"about": {
"text": "Edit and save work in URL."
}
}