-
Notifications
You must be signed in to change notification settings - Fork 8
/
think.html
452 lines (416 loc) · 19.8 KB
/
think.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
<!DOCTYPE html>
<html>
<head>
<link rel="canonical" href="https://gun.eco/think.html" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="./dep/jquery.js"></script>
<script src="dep/rsc/analytics.js"></script>
<link href='https://fonts.googleapis.com/css?family=Anonymous+Pro:700,400' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Oxygen' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="styles/typography.css">
</head>
<body>
<div class="header">
<div class="title">
<h1 class="oxygen top">To-Do app tutorial</h1>
<a href="../../index.html"><img src="img/gun_logo_white.svg"></a>
</div>
<div class="panelbg blue"></div>
</div>
<div class="wrapper oxygen">
<br/>
<div style="position: relative; padding-bottom: 56.25%;"><iframe allowfullscreen="true" src="https://scrimba.com/cast/cW2Vsa.embed" style="border: 0px; position: absolute; width: 100%; height: 100%;"></iframe></div>
<p class="article blue_first">Before we can start building anything interesting, we should have a way to jot down our thoughts. Therefore the first thing we will build is a tool to keep track of what needs to be done. The infamous To-Do app, allowing us to keep temporary notes.</p>
<p>So what are the requirements? The ability to add a note, read our notes, and to clear them off. We will also need a space to keep these notes in, and a web page to access them through. Let's start with the page! You can edit the code below, which will update the live preview.</p>
</div>
<div class="edit oxygen">... loading editor and preview ...
<textarea style="height: 13em" class="anonymous"><html>
<body>
<h1>Title</h1>
<form>
<input><button>Add</button>
</form>
<ul></ul>
</body>
</html></textarea>
</div>
<script>
var code = $('.edit').load('./editor.html', function(){setTimeout(function(){
var editor = $('.CodeMirror')[0].CodeMirror;
editor.live(function(frame){
$('body', frame).on('submit', 'form', function(e){e.preventDefault()});
});
editor.setValue(code);
},1)}).find('textarea').text();
</script>
<style>
.none { display: none }
.step-at { display: block }
textarea { border: none; width: 100%; }
</style>
<div id="step-1" class="wrapper step oxygen">
<p>What does this do? HTML is how we code the layout of a web page.</p>
<ul>
<li>We first must wrap all our code in an open and closing <u>html</u> tag so our computer knows it is a web page.</li>
<li>The <u>body</u> tag tells it to display the contents enclosed within.</li>
<li><u>h1</u> is one of many semantic tags for declaring a title, others include <u>h2</u>, <u>h3</u> and so on of different sizes.</li>
<li>A <u>form</u> is a container for getting information from a user.</li>
<li>Forms have <u>input</u>s which let the user type data in, it is a self-closing tag.</li>
<li>The <u>button</u> can be pressed, causing an action that we code to happen.</li>
<li><u>ul</u> is an unordered list which we will display our thoughts inside of.</li>
</ul>
<p>Now, try changing the <u>h1</u> text in the editor from "Title" to the name of our app, "Thoughts".</p>
<button class="none">I've done it!</button>
<textarea id="code-1" class="none oxygen" style="height: 14em"><html>
<body>
<h2>Thoughts</h2>
<form>
<input><button>Add</button>
</form>
<ul></ul>
<!-- Replace this Comment Line with the Code in the Step below! -->
</body>
</html></textarea>
</div>
<div id="step-2" class="wrapper step oxygen">
<p>HTML controls the layout, but how do we control what happens when a user presses the 'add' button? This is done with javascript. But using raw javascript quickly becomes verbose, so to keep things concise we will use a popular tool called jQuery (there are other examples for tools, like React/Angular, as well). We also need a tool to store data, so we will include GUN as well.</p>
<p>Insert the following between the ending <u>ul</u> tag and the ending <u>body</u> tag, replacing the comment line:</p>
<textarea style="height: 6em;" class="anonymous">
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
<script>
alert("Good job! You'll replace this line in the next step!");
</script></textarea>
<ul>
<li>The <u>script</u> tag tells the browser to use some javascript code, and <u>src</u> is where to load it from.</li>
<li>We can then test to see if our code worked with an <u>alert</u> message, which pops up and forces you to press ok.</li>
<li>In javascript, we denote text by wrapping it inside quotation marks, double <u>""</u> or single <u>''</u>.</li>
<li>We instruct the computer to notify us with that text by calling the <u>alert</u> function using parenthesis <u>()</u>.</li>
<li>A function is just a fancy word for a reusable piece of code that does something when we call its name, such as <u>alert</u>.</u>
<li>A semicolon <u>;</u> marks the end of a javascript sentence in the same way a period marks the end of a sentence.</li>
</ul>
<button class="none">It worked.</button>
<textarea id="code-2" class="none oxygen" style="height: 14em"><html>
<body>
<h2>Thoughts</h2>
<form>
<input><button>Add</button>
</form>
<ul></ul>
</script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
</script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
</script>
alert("Good job! You'll replace this line in the next step!");
</script>
</body>
</html></textarea>
</div>
<div id="step-3" class="wrapper step oxygen">
<p>Wonderful! You should have gotten the alert message, this means writing code works! Let's replace the alert line entirely with code that responds to user input.</p>
<textarea style="height: 5em;" class="anonymous">
$('form').on('submit', function(event){
event.preventDefault();
alert("We got your thought! " + $('input').val());
});</textarea>
<p>What's going on here?</p>
<ul>
<li>jQuery is a function like <u>alert</u>, its name is <u>$</u> which can be called with parenthesis <u>()</u>.</li>
<li>Calling <u>$</u> with <u>'form'</u> as the input gives us a reference to the corresponding HTML form tag.</li>
<li>We then call <u>on</u> with two inputs. First the text name of an <u>event</u> we want to react to, and then a <u>function</u> we create.
<ul>
<li>Events are predefined ways we can interact with a user, such as <u>'mousemove'</u> or a <u>'keypress'</u>.</li>
<li>We use <u>'submit'</u> because it responds to both a button <u>'click'</u> and hitting enter on a form.</li>
<li>Our <u>function</u> will get called with the <u>event</u> every time the user does that action, allowing us to react to their input.</li>
</ul>
<li>The default behavior of a form is to cause the browser to change pages which is annoying, we prevent that by calling <u>preventDefault</u> on the <u>event</u>.</li>
<li>Finally, calling <u>$</u> with <u>'input'</u> will reference the HTML input tag which we then call <u>val</u> on, giving us the text the user typed in.</li>
</ul>
<button class="none">Got it.</button>
<textarea id="code-3" class="none anonymous" style="height: 14em"><html>
<body>
<h2>Thoughts</h2>
<form>
<input><button>Add</button>
</form>
<ul></ul>
</script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
</script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
</script>
$('form').on('submit', function(event){
event.preventDefault();
alert("We got your thought! " + $('input').val());
});
</script>
</body>
</html></textarea>
</div>
<div id="step-4" class="wrapper step oxygen">
<p>Now that users can jot down their thoughts, we need a place to save them. Let's start using GUN for just that.</p>
<textarea style="height: 1em;" class="anonymous">
var gun = Gun().get('thoughts');</textarea>
<ul>
<li>The <u>var</u>iable keyword tells javascript that we want to create a reference named <u>gun</u> that we can reuse.</li>
<li>We call <u>Gun</u> to start the database, which only needs to be done once per page load.</li>
<li>Now we want to open up a reference to some data, so we call <u>get</u> with the name of the data we want.</li>
<li>However, no data has been saved to <u>'thoughts'</u> yet! Let's fix that in the next step by using <u>gun</u>.</li>
</ul>
<button class="none">Let's do it!</button>
<textarea id="code-4" class="none anonymous" style="height: 14em"><html>
<body>
<h2>Thoughts</h2>
<form>
<input><button>Add</button>
</form>
<ul></ul>
</script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
</script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
</script>
var gun = Gun().get('thoughts').set();
$('form').on('submit', function(event){
event.preventDefault();
alert("We got your thought! " + $('input').val());
});
</script>
</body>
</html></textarea>
</div>
<div id="step-5" class="wrapper step oxygen">
<p>Replace the alert line in the submit function with the following:</p>
<textarea style="height: 2.5em;">
gun.set($('input').val());
$('input').val("");</textarea>
<ul>
<li>We're telling gun to add the value of the input as an item in a <u>set</u> of thoughts.</li>
<li>Then we also want the input's <u>val</u>ue to become empty text, so we can add new thoughts later.</li>
</ul>
<button class="none">Show me the data!</button>
<textarea id="code-5" class="none anonymous" style="height: 14em"><html>
<body>
<h2>Thoughts</h2>
<form>
<input><button>Add</button>
</form>
<ul></ul>
</script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
</script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
</script>
var gun = Gun().get('thoughts').set();
$('form').on('submit', function(event){
event.preventDefault();
gun.set($('input').val());
$('input').val("");
});
</script>
</body>
</html></textarea>
</div>
<div id="step-6" class="wrapper step oxygen">
<p>Fantastic! Now that we can successfully store data, we want show the data! Replace the comment line in the editor with the following:</p>
<textarea style="height: 9em;" class="anonymous">
gun.map().on(function(thought, id){
var li = $('#' + id).get(0) || $('<li>').attr('id', id).appendTo('ul');
if(thought){
$(li).text(thought);
} else {
$(li).hide();
}
});</textarea>
<ul>
<li><u>map</u> tells gun to get each item in the set, one at a time, to do something with it.</li>
<li>What we do is the same as $'s <u>on</u>, which reacts to events. So does gun, it responds to any update on 'thoughts'.</li>
<li>We get the <u>thought</u> value itself and a unique <u>id</u>entifier for the item in the set.</li>
<li>This next line looks scary, but read it like this, "make <u>var</u>iable <u>li</u> equal to X or Y".
<ul>
<li>The X part asks <u>$</u> to find the <u>id</u> in the HTML and <u>get</u> it.</li>
<li>In javascript, <u>||</u> means 'or', such that javascript will use X if it exist or it will use Y.</li>
<li>The Y part asks <u>$</u> to create a new <u><li></u> HTML tag, set its <u>id</u> <u>attr</u>ibute to our id and <u>append</u> it to the end of the HTML <u>ul</u> list.</li>
</ul></li>
<li>Finally, the javascript <u>if</u> statement either asks <u>$</u> to make <u>thought</u> be the text of the <u>li</u> if thought exists, <u>else</u> hide the <u>li</u> from being displayed.</li>
<li>Altogether it says "Create or reuse the HTML list item and make sure it is in the HTML list, then update the text or hide the item if there is no text".</li>
</ul>
<button class="none">I've tried adding a thought!</button>
<textarea id="code-6" class="none anonymous" style="height: 14em"><html>
<body>
<h2>Thoughts</h2>
<form>
<input><button>Add</button>
</form>
<ul></ul>
</script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
</script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
</script>
var gun = Gun().get('thoughts').set();
$('form').on('submit', function(event){
event.preventDefault();
gun.set($('input').val());
$('input').val("");
});
gun.map().on(function(thought, id){
var li = $('#' + id).get(0) || $('<li>').attr('id', id).appendTo('ul');
if(thought){
$(li).text(thought);
} else {
$(li).hide();
}
});
</script>
</body>
</html></textarea>
</div>
<div id="step-7" class="wrapper step oxygen">
<p>Finally we want to be able to clear off our thoughts when we are done with them. The interface for this could be done in many different ways, but for simplicity we will use a double tap as the gesture to clear it off. Replace the comment line in the editor with this code.</p>
<textarea style="height: 4em;" class="anonymous">
$('body').on('dblclick', 'li', function(event){
gun.get(this.id).put(null);
});</textarea>
<ul>
<li>In order to react to any <u>'dblclick'</u> event rather than a specific one, we call <u>on</u> on the page's <u>'body'</u> as a whole.</li>
<li>But we want to filter the events to ones that happened only on any <u>'li'</u> tag. Fortunately, we can call <u>on</u> with an optional second input of <u>'li'</u> which does just that.</li>
<li>Inside a <u>function</u> we get a special <u>this</u> keyword in javascript, which <u>$</u> uses as a reference to the original HTML tag that caused the event.</li>
<li>Calling <u>get</u> tells <u>gun</u> to filter its data to just the <u>id</u> of the thought we want to clear off.</li>
<li>Then calling <u>put</u> on that tells gun to update that thought to <u>null</u>, so we no longer have the thought.</li>
<li>And whenever an update happens, <u>gun</u>'s <u>on</u> function from the previous step gets called again, which then hides the corresponding HTML list item.</u>
</ul>
<button class="none">Done!</button>
<textarea id="code-7" class="none anonymous" style="height: 14em"><html>
<body>
<h2>Thoughts</h2>
<form>
<input><button>Add</button>
</form>
<ul></ul>
</script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
</script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
</script>
var gun = Gun().get('thoughts').set();
$('form').on('submit', function(event){
event.preventDefault();
gun.set($('input').val());
$('input').val("");
});
gun.map().on(function(thought, id){
var li = $('#' + id).get(0) || $('<li>').attr('id', id).appendTo('ul');
if(thought){
$(li).text(thought);
} else {
$(li).hide();
}
});
$('body').on('dblclick', 'li', function(event){
$(this).remove();
gun.get(this.id).put(null);
});
</script>
</body>
</html></textarea>
</div>
<div id="step-8" class="wrapper step oxygen">
<p>Congratulations! You are all done, you have built your first GUN app!</p>
<p>In the next tutorial we will use GUN to synchronize data in realtime across multiple devices. We'll start by copying the app we made here and modifying it to become a chat app.</p>
<a href="./converse.html">Next Tutorial: Converse</a>
</div>
<!-- FOOTER -->
<div id="footer">
<div class="container" class="page">
<div class="footer_social">
<div class="grid">
<a href="http://www.github.com/amark/gun" target="_blank"><img src="img/icons/github.svg"></a>
<a href="https://gitter.im/amark/gun" target="_blank"><img src="img/icons/gitter.svg"></a>
<a href="https://twitter.com/databasegun" target="_blank"><img src="img/icons/twitter.svg"></a>
<a href="https://www.linkedin.com/company/gun-inc" target="_blank"><img src="img/icons/linkedin.svg"></a>
</div>
</div>
</div>
<nav>
<a href="https://gun.eco/">Get Started</a>
<a href="docs">Documentation</a>
<a href="https://gun.eco/think.html">Tutorials</a>
<a href="https://github.com/amark/gun">Download</a>
<a href="http://gun.js.org/enterprise/team.html" target="_blank">Our Team</a>
</nav>
</div>
<footer>
<div class="container">
<p>Designed with ♥ by Mark Nadal, the gun team, and many very awesome contributors. Liberally licensed under <a href="https://github.com/amark/gun/wiki/package.json"> Zlib or MIT or Apache 2.0.</a></p>
</div>
</footer>
<!-- END FOOTER -->
<script>
$('.step').addClass('none').first().addClass('step-at');
$('button').on('click', next).removeClass('none');
function next(){
$('.step-at').removeClass('step-at');
$('#step-' + (++next.at)).addClass('step-at');
(next.step[next.at] || function(){})();
}
next.at = 1;
next.comment = /\s*\/\*(.*?)\*\/\s*/i;
next.unwrap = /\s*<\/?(html|body)>\s*\s*/ig;
next.wrap = function(c){ return '<html>\n\t<body>\n\t\t' + c + '\n\t</body>\n</html>' }
next.code = function(){ return $('<div>' + next.mir.getValue().replace(next.wrap, '') + '</div>') }
next.step = [null, null, function(){
next.mir = $('.CodeMirror')[0].CodeMirror;
var code = next.code();
if(code.find('h1').text().toLowerCase() == 'title'){
code.find('h1').text('Thoughts');
}
next.mir.setValue(next.wrap($.trim(code.html()) + "\n<!-- Replace this Comment Line with the Code in the Step below! -->"));
}, function(){
var code = next.code();
var ctx = {$: true, gun: true, alert: true};
code.find('script').each(function(){
var src = ($(this).attr('src') || '').toLowerCase();
if(src.indexOf('jquery')){ ctx.$ = false }
if(src.indexOf('gun')){ ctx.gun = false }
if(($(this).text()||'').indexOf('alert')){ ctx.alert = false }
});
code.contents().filter(function(){ return this.nodeType == 8 }).remove();
var ul = code.find('ul');
if(ctx.alert){ ul.after($('<script>').text('\n\t\t\talert("Good job! Our code will go in this script tag!");\n\t\t')) }
if(ctx.gun){ ul.after($('<script>').attr('src', 'https://cdn.jsdelivr.net/npm/gun/gun.js')) }
if(ctx.$){ ul.after($('<script>').attr('src','https://code.jquery.com/jquery-1.11.3.min.js')) }
if(ctx.alert || ctx.gun || ctx.$){ next.mir.setValue(next.wrap($.trim(code.html().replace(/<script/ig, '\n\t\t<script')))) }
}, function(){
var code = next.code();
var script = code.find('script').last();
if(0 > script.text().indexOf('.on')){
script.text('\n' + script.text().replace(/\s*alert.*\n/i, $('#step-3').find('textarea').first().text() + '\n'));
}
script.text('\n/* Replace this Comment Line with the Code in the Step below! */' + script.text());
next.mir.setValue(next.wrap($.trim(code.html())));
}, function(){
var code = next.code();
var script = code.find('script').last();
script.text(script.text().replace(next.comment, ''));
if(0 > script.text().indexOf('Gun')){
script.text('\n' + $('#step-4').find('textarea').first().text() + "\n\t\t\t" + script.text());
}
next.mir.setValue(next.wrap($.trim(code.html())));
}, function(){
var code = next.code();
var script = code.find('script').last();
if(0 < script.text().indexOf('alert')){
script.text(script.text().replace(/\s*alert.*\n/i, '\n' + $('#step-5').find('textarea').first().text() + '\n'));
}
script.text(script.text() + '\n/* Replace this Comment Line with the Code in the Step below! */\n\t\t');
next.mir.setValue(next.wrap($.trim(code.html())));
}, function(){
var code = next.code();
var script = code.find('script').last();
if(0 > script.text().indexOf('.map')){
script.text(script.text().replace(/\s*.*replace.*\n/i, '\n' + $('#step-6').find('textarea').first().text()));
}
script.text(script.text() + '\n/* Replace this Comment Line with the Code in the Step below! */\n\t\t');
next.mir.setValue(next.wrap($.trim(code.html())));
}, function(){
var code = next.code();
var script = code.find('script').last();
if(0 > script.text().indexOf('gun.get')){
script.text(script.text().replace(/\s*.*replace.*\n/i, '\n' + $('#step-7').find('textarea').first().text() + '\n'));
}
next.mir.setValue(next.wrap($.trim(code.html())));
}]
</script>
</body>
</html>