diff --git a/css/80_app.css b/css/80_app.css
index 801158597..c2a9b5213 100644
--- a/css/80_app.css
+++ b/css/80_app.css
@@ -3703,6 +3703,35 @@ img.tile-removing {
margin-right: -20px;
}
+.curtain-tooltip.intro-mouse {
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.curtain-tooltip.intro-mouse .counter {
+ position: absolute;
+ display: block;
+ top: 50px;
+ width: 100%;
+ text-align: center;
+ font-weight: bold;
+ font-size: 14px;
+ z-index: 1003;
+}
+
+.curtain-tooltip.intro-mouse .tooltip-illustration use {
+ fill: rgba(112, 146, 255, 0);
+ color: rgba(112, 146, 255, 0);
+}
+.curtain-tooltip.intro-mouse.leftclick .tooltip-illustration use {
+ fill: rgba(112, 146, 255, 1);
+}
+.curtain-tooltip.intro-mouse.rightclick .tooltip-illustration use {
+ color: rgba(112, 146, 255, 1);
+}
+
.huge-modal-button {
width: 100%;
height: auto;
diff --git a/data/core.yaml b/data/core.yaml
index 37a8aa9d8..e454fe58a 100644
--- a/data/core.yaml
+++ b/data/core.yaml
@@ -897,7 +897,10 @@ en:
welcome: "Welcome! This walkthrough will teach you the basics of editing on OpenStreetMap."
practice: "All of the data in this walkthrough is just for practicing, and any edits that you make in the walkthrough will not be saved."
words: "This walkthrough will introduce some new words and concepts. When we introduce a new word, we'll use *italics*."
- chapters: "You can use the buttons below to skip chapters at any time or to restart a chapter if you get stuck. Let's begin! **Click '{next}' to continue.**"
+ mouse: "You can use any input device to edit the map, but this walkthrough assumes you have a mouse with left and right buttons. **If you want to attach a mouse, do so now, then click OK.**"
+ leftclick: "When this tutorial asks you to click or double-click, we mean with the left button. On a trackpad it might be a single-click or single-finger tap. **Left-click {num} times.**"
+ rightclick: "Sometimes we'll also ask you to right-click. This might be the same as control-click, or two-finger tap on a trackpad. Your keyboard might even have a 'menu' key that works like right-click. **Right-click {num} times.**"
+ chapters: "So far, so good! You can use the buttons below to skip chapters at any time or to restart a chapter if you get stuck. Let's begin! **Click '{next}' to continue.**"
navigation:
title: "Navigation"
drag: "The main map area shows OpenStreetMap data on top of a background. You can navigate by dragging and scrolling, just like any web map. **Drag the map!**"
diff --git a/dist/locales/en.json b/dist/locales/en.json
index 90e7adcb5..32d8a50ee 100644
--- a/dist/locales/en.json
+++ b/dist/locales/en.json
@@ -756,7 +756,10 @@
"welcome": "Welcome! This walkthrough will teach you the basics of editing on OpenStreetMap.",
"practice": "All of the data in this walkthrough is just for practicing, and any edits that you make in the walkthrough will not be saved.",
"words": "This walkthrough will introduce some new words and concepts. When we introduce a new word, we'll use *italics*.",
- "chapters": "You can use the buttons below to skip chapters at any time or to restart a chapter if you get stuck. Let's begin! **Click '{next}' to continue.**"
+ "mouse": "You can use any input device to edit the map, but this walkthrough assumes you have a mouse with left and right buttons. **If you want to attach a mouse, do so now, then click OK.**",
+ "leftclick": "When this tutorial asks you to click or double-click, we mean with the left button. On a trackpad it might be a single-click or single-finger tap. **Left-click {num} times.**",
+ "rightclick": "Sometimes we'll also ask you to right-click. This might be the same as control-click, or two-finger tap on a trackpad. Your keyboard might even have a 'menu' key that works like right-click. **Right-click {num} times.**",
+ "chapters": "So far, so good! You can use the buttons below to skip chapters at any time or to restart a chapter if you get stuck. Let's begin! **Click '{next}' to continue.**"
},
"navigation": {
"title": "Navigation",
diff --git a/modules/ui/curtain.js b/modules/ui/curtain.js
index 4d947334e..267021994 100644
--- a/modules/ui/curtain.js
+++ b/modules/ui/curtain.js
@@ -126,6 +126,13 @@ export function uiCurtain() {
tooltipArrow = 5,
side, pos;
+
+ // hack: this will have bottom placement,
+ // so need to reserve extra space for the tooltip illustration.
+ if (options.tooltipClass === 'intro-mouse') {
+ tip.height += 80;
+ }
+
// trim box dimensions to just the portion that fits in the window..
if (tooltipBox.top + tooltipBox.height > h) {
tooltipBox.height -= (tooltipBox.top + tooltipBox.height - h);
diff --git a/modules/ui/intro/welcome.js b/modules/ui/intro/welcome.js
index 8af0a59d8..155d2555c 100644
--- a/modules/ui/intro/welcome.js
+++ b/modules/ui/intro/welcome.js
@@ -4,8 +4,8 @@ import { utilRebind } from '../../util/rebind';
export function uiIntroWelcome(context, reveal) {
- var dispatch = d3.dispatch('done');
-
+ var dispatch = d3.dispatch('done'),
+ listener = clickListener();
var chapter = {
title: 'intro.welcome.title'
@@ -30,10 +30,101 @@ export function uiIntroWelcome(context, reveal) {
function words() {
reveal('.intro-nav-wrap .chapter-welcome',
t('intro.welcome.words'),
- { buttonText: t('intro.ok'), buttonCallback: chapters }
+ { buttonText: t('intro.ok'), buttonCallback: mouse }
);
}
+
+ function mouse() {
+ reveal('.intro-nav-wrap .chapter-welcome',
+ t('intro.welcome.mouse'),
+ { buttonText: t('intro.ok'), buttonCallback: leftClick }
+ );
+ }
+
+
+ function leftClick() {
+ var counter = 0,
+ times = 5;
+
+ var tooltip = reveal('.intro-nav-wrap .chapter-welcome',
+ t('intro.welcome.leftclick', { num: times }),
+ { tooltipClass: 'intro-mouse' }
+ );
+
+ tooltip.selectAll('.tooltip-inner')
+ .insert('svg', 'span')
+ .attr('class', 'tooltip-illustration')
+ .append('use')
+ .attr('xlink:href', '#walkthrough-mouse');
+
+ tooltip
+ .append('div')
+ .attr('class', 'counter');
+
+ tooltip.call(listener);
+
+ listener.on('click', function(which) {
+ if (which === 'left') {
+ d3.select('.curtain-tooltip.intro-mouse .counter')
+ .text(String(++counter));
+
+ if (counter === times) {
+ window.setTimeout(function() { continueTo(rightClick); }, 1000);
+ }
+ }
+ });
+
+ function continueTo(nextStep) {
+ listener.on('click', null);
+ tooltip.call(listener.off);
+ tooltip.select('.counter').remove();
+ nextStep();
+ }
+ }
+
+
+ function rightClick() {
+ var counter = 0,
+ times = 5;
+
+ var tooltip = reveal('.intro-nav-wrap .chapter-welcome',
+ t('intro.welcome.rightclick', { num: times }),
+ { tooltipClass: 'intro-mouse' }
+ );
+
+ tooltip.selectAll('.tooltip-inner')
+ .insert('svg', 'span')
+ .attr('class', 'tooltip-illustration')
+ .append('use')
+ .attr('xlink:href', '#walkthrough-mouse');
+
+ tooltip
+ .append('div')
+ .attr('class', 'counter');
+
+ tooltip.call(listener);
+
+ listener.on('click', function(which) {
+ if (which === 'right') {
+ d3.select('.curtain-tooltip.intro-mouse .counter')
+ .text(String(++counter));
+
+ if (counter === times) {
+ window.setTimeout(function() { continueTo(chapters); }, 1000);
+ }
+ }
+ });
+
+ function continueTo(nextStep) {
+ listener.on('click', null);
+ tooltip.call(listener.off);
+ tooltip.select('.counter').remove();
+ nextStep();
+ }
+ }
+
+
function chapters() {
dispatch.call('done');
reveal('.intro-nav-wrap .chapter-navigation',
@@ -48,6 +139,7 @@ export function uiIntroWelcome(context, reveal) {
chapter.exit = function() {
+ listener.off();
};
@@ -59,3 +151,104 @@ export function uiIntroWelcome(context, reveal) {
return utilRebind(chapter, dispatch, 'on');
}
+
+
+
+function clickListener() {
+ var dispatch = d3.dispatch('click'),
+ minTime = 120,
+ tooltip = d3.select(null),
+ down = {};
+
+
+ function keydown() {
+ if (d3.event.keyCode === 93) { //context menu
+ d3.event.preventDefault();
+ d3.event.stopPropagation();
+ down.menu = d3.event.timeStamp;
+ tooltip.classed('rightclick', true);
+ }
+ }
+
+
+ function keyup() {
+ if (d3.event.keyCode === 93) { //context menu
+ d3.event.preventDefault();
+ d3.event.stopPropagation();
+ var endTime = d3.event.timeStamp,
+ startTime = down.menu || endTime,
+ delay = (endTime - startTime < minTime) ? minTime : 0;
+
+ window.setTimeout(function() { tooltip.classed('rightclick', false); }, delay);
+ dispatch.call('click', this, 'right');
+ down.menu = undefined;
+ }
+ }
+
+
+ function mousedown() {
+ if (d3.event.button === 0 && !d3.event.ctrlKey) {
+ tooltip.classed('leftclick', true);
+ } else if (d3.event.button === 2) {
+ tooltip.classed('rightclick', true);
+ }
+ down[d3.event.button] = d3.event.timeStamp;
+ }
+
+
+ function mouseup() {
+ var endTime = d3.event.timeStamp,
+ startTime = down[d3.event.button] || endTime,
+ delay = (endTime - startTime < minTime) ? minTime : 0;
+
+ if (d3.event.button === 0 && !d3.event.ctrlKey) {
+ window.setTimeout(function() { tooltip.classed('leftclick', false); }, delay);
+ dispatch.call('click', this, 'left');
+ } else if (d3.event.button === 2) {
+ window.setTimeout(function() { tooltip.classed('rightclick', false); }, delay);
+ dispatch.call('click', this, 'right');
+ }
+ down[d3.event.button] = undefined;
+ }
+
+
+ function contextmenu() {
+ d3.event.preventDefault();
+ d3.event.stopPropagation();
+ if (!down[2] && !down.menu) {
+ tooltip.classed('rightclick', true);
+ window.setTimeout(function() { tooltip.classed('rightclick', false); }, minTime);
+ dispatch.call('click', this, 'right');
+ }
+ }
+
+
+ var behavior = function(selection) {
+ tooltip = selection;
+ down = {};
+
+ d3.select(window)
+ .on('keydown.intro', keydown)
+ .on('keyup.intro', keyup)
+ .on('mousedown.intro', mousedown)
+ .on('mouseup.intro', mouseup)
+ .on('contextmenu.intro', contextmenu);
+ };
+
+
+ behavior.off = function() {
+ d3.select(window)
+ .on('keydown.intro', null)
+ .on('keyup.intro', null)
+ .on('mousedown.intro', null)
+ .on('mouseup.intro', null)
+ .on('contextmenu.intro', null);
+
+ tooltip
+ .classed('leftclick', false)
+ .classed('rightclick', false);
+ };
+
+ return utilRebind(behavior, dispatch, 'on');
+}
+
diff --git a/svg/iD-sprite.json b/svg/iD-sprite.json
index 41699554c..3047f98d2 100644
--- a/svg/iD-sprite.json
+++ b/svg/iD-sprite.json
@@ -402,5 +402,11 @@
"poi-images": { "viewBox": "0 320 200 80" },
"landuse-images": { "viewBox": "0 400 200 80" },
"feature-images": { "viewBox": "0 480 200 80" },
- "building-images": { "viewBox": "700 480 200 80" }
+ "building-images": { "viewBox": "700 480 200 80" },
+
+ "walkthrough-mouse": { "viewBox": "400 411 25 43" },
+
+ "walkthrough-mouse-shape": { "fill": "#000000" },
+ "walkthrough-mouse-left": { "fill": "inherit" },
+ "walkthrough-mouse-right": { "fill": "currentColor" }
}
diff --git a/svg/iD-sprite.src.idraw b/svg/iD-sprite.src.idraw
index 42ef70b4a..7b9ebd908 100644
Binary files a/svg/iD-sprite.src.idraw and b/svg/iD-sprite.src.idraw differ
diff --git a/svg/iD-sprite.src.svg b/svg/iD-sprite.src.svg
index 76802fef0..6021c0325 100644
--- a/svg/iD-sprite.src.svg
+++ b/svg/iD-sprite.src.svg
@@ -194,6 +194,11 @@
+
+
+
+
+