-
+
- width - the width of the canvas in pixels
- height - the height of the canvas in pixels
- children - (optional) - paths are written as child records; while optional, without any children, the canvas will be blank
-
+
|
// creates a canvas tagged #my-canvas that is 160 pixels wide by 90 pixel tall
```
@@ -838,7 +843,7 @@ commit
-
+
- fillStyle - (optional) - sets the color, gradient, or pattern used to fill the drawing; if undefined, the default is black
- strokeStyle - (optional) - sets the color, gradient, or pattern used for the strokes; if undefined, the path will default to a black fill style
- lineWidth - (optional) - sets the width of strokes in pixels; if undefined, the default is 1px
@@ -857,7 +862,7 @@ commit
children - (optional) - each individual operation in the path is written as a child record; while optional, without any children, the canvas will be blank
-
+
|
// adds a #canvas/path to #my-canvas with a black stroke, a line width of 2 pixels and beveled corners
```
@@ -884,10 +889,10 @@ commit
-
+
- x - the horizontal coordinate to move to, in pixels
- y - the vertical coordinate to move to, in pixels
-
+
|
// creates a 100x100 canvas and moves the path 20 pixels right and 15 pixels down from the top left corner of the canvas without drawing a line
```
@@ -909,10 +914,10 @@ commit
-
+
- x - the horizontal coordinate to move to, in pixels
- y - the vertical coordinate to move to, in pixels
-
+
|
// draws a black line from the top left corner of a 100x100 canvas to the center
```
@@ -934,14 +939,14 @@ commit
-
+
- cp1x - the x coordinate of the first control point
- cp1y - the y coordinate of the first control point
- cp2x - the x coordinate of the second control point
- cp2y - the y coordinate of the second control point
- x - the x coordinate of the end point
- y - the y coordinate of the end point
-
+
|
// draws a red Bézier curve starting at (20, 25) and ending at (40, 50)
```
@@ -964,12 +969,12 @@ commit
-
+
- cpx - the x coordinate of the control point
- cpy - the y coordinate of the control point
- y - the x coordinate of the end point
- y - the y coordinate of the end point
-
+
|
|
@@ -985,14 +990,14 @@ commit
-
+
- y - the x coordinate of the center of the curve
- y - the y coordinate of the center of the curve
- radius - the radius of the curve in pixels
- startAngle - the starting angle of the curve in radians
- endAngle - the ending angle of the curve in radians
- anticlockwise - (optional) - values can be true or false; true draws the arc counterclockwise, false draws the arc clockwise
-
+
|
// draws an arc that circumscribes ¾ of a 40 pixel-wide circle in the middle of the canvas
```
@@ -1014,13 +1019,13 @@ commit
-
+
- x1 - the x coordinate of the starting point of the curve
- y1 - the y coordinate of the starting point of the curve
- x2 - the x coordinate of the ending point of the curve
- y2 - the y coordinate of the ending point of the curve
- radius - the radius of the curve in pixels
-
+
|
// draws an arc that connects two perpendicular lines with a circular curve
```
@@ -1044,7 +1049,7 @@ commit
-
+
- y - the x coordinate of the center of the curve
- y - the y coordinate of the center of the curve
- radiusX - the horizontal radius of the curve
@@ -1053,7 +1058,7 @@ commit
- startAngle - the starting angle of the curve in radians
- endAngle - the ending angle of the curve in radians
- anticlockwise - (optional) - values can be true or false; true draws the arc counterclockwise, false draws the arc clockwise
-
+
|
// draws a green oval in the center of the canvas that is 20 pixels wide and 30 pixels high
```
@@ -1075,12 +1080,12 @@ commit
-
+
- y - the x coordinate of the upper left corner of the rectangle
- y - the y coordinate of the upper left corner of the rectangle
- width - the width of the rectangle in pixels
- height - the height of the rectangle in pixels
-
+
|
// draws a blue square with black borders in the middle of the canvas
```
@@ -1102,9 +1107,9 @@ commit
-
+
+
|
// returns the path to (10, 10) from (20, 40) to create a triangle
```
@@ -1135,7 +1140,7 @@ The UI library provides a shorthand for adding standard HTML elements, as well a
-
+
- the
#ui tag supports the following html elements:
#ui/row
@@ -1162,7 +1167,7 @@ The UI library provides a shorthand for adding standard HTML elements, as well a
- # - (optional) - any other tags on the record will be applied to the element as classes
- class - (optional) defines one or more classes for the element
- Other attributes - (optional) - other attribute-value pairs will be applied directly to the element for integration with existing JS libraries or debugging
-
+
|
// commits a div in the browser with the text “Hello world!”
```
@@ -1182,14 +1187,14 @@ commit
-
+
- children - (optional) - the children contained within the column or row
- style - (optional) - specific CSS styles can be defined, but must be entered as a subrecord
- text - (optional) - defines text within the column or row
- # - (optional) - any other tags on the record will be applied to the column or row as classes
- class - (optional) defines one or more classes for the element
- Other attributes - (optional) - other attribute-value pairs will be applied directly to the column or row for integration with existing JS libraries or debugging
-
+
|
// creates 3 very wise divs in the browser stacked on top of one another
```
@@ -1212,7 +1217,7 @@ commit
-
+
- - (optional) - preset options for class are:
- “inset” - an inset button style
@@ -1225,7 +1230,7 @@ commit
- text - (optional) - defines text in the button
- # - (optional) - any other tags on the record will be applied to the button as classes
- Other attributes - (optional) - other attribute-value pairs will be applied directly to the button for integration with existing JS libraries or debugging
-
+
|
|
@@ -1241,7 +1246,7 @@ commit
-
+
- completion - the list of possible responses, given as a subrecord with the attribute “text” whose value is the list; requires the use of a pipe in the #ui record if matching against multiple search records for one autocomplete field
- Note: Once an autocomplete option has been selected, #ui/autocomplete automatically gains a selected attribute whose value is the completion record; in the case of the example shown here, the selected attribute would be whichever
#state record was chosen
- placeholder - (optional) - the placeholder text for the input field
@@ -1249,7 +1254,7 @@ commit
- # - (optional) - any other tags on the record will be applied to the autocomplete as classes
- class - (optional) defines one or more classes for the autocomplete
- Other attributes - (optional) - other attribute-value pairs will be applied directly to the autocomplete for integration with existing JS libraries or debugging
-
+
|
// creates an autocomplete form with the class “birth-state” and the placeholder “Which state were you born in?”, where the autocomplete options are the names of any #state records found
```
@@ -1277,9 +1282,9 @@ commit
-
+
- autocomplete - the autocomplete element to clear
-
+
|
// clears an autocomplete field if it loses focus
```
@@ -1303,9 +1308,9 @@ commit
-
+
- autocomplete - the autocomplete being opened
-
+
|
// changes the font color of the autocomplete input to red when the list is opened; the color will not revert once the menu is closed unless another block is specifically written to do so
```
@@ -1330,9 +1335,9 @@ commit
-
+
- autocomplete - the autocomplete being closed
-
+
|
// changes the font color of the autocomplete input to black when the list is closed; reverts the change in the example for #ui/event/open
```
@@ -1357,9 +1362,9 @@ commit
-
+
- autocomplete - the autocomplete being closed
-
+
|
// as a follow-up to the #ui/autocomplete example, this waits for the user to pick a birth state, then creates a new autocomplete tagged #birth-county to ask which county within that particular state the user was born in
```
@@ -1384,9 +1389,9 @@ commit
-
+
- autocomplete - the autocomplete whose input field changed
-
+
|
// when an autocomplete asking what the magic word is changes to the correct answer, adds the tag #magic-word to the autocomplete
```
@@ -1400,3 +1405,223 @@ bind
|
+
+## System
+
+The system library provides various system-level utilities for Eve.
+
+
+
+
+ #system/timer - starts a timer at the specified resolution
+ |
+
+
+
+
+
+ - resolution - the frequency in milliseconds of the timer.
+ - year - (optional) - the current year
+ - month - (optional) - the current month (1 - 12)
+ - day - (optional) - the current day of the month (1 - 31)
+ - weekday - (optional) - the current day of the week (1 - 7, where 1 is Sunday)
+ - hour - (optional) - the current hour (0 - 23)
+ - minute - (optional) - the current minute (0 - 59)
+ - second - (optional) - the current second (0 - 59)
+ - millisecond - (optional) - the current millisecond (0 - 999)
+ - timestamp - (optional) - the current time represented as the number of milliseconds elapsed since 1 January 1970 00:00:00 UTC
+ - tick - (optional) - the number of ticks of the timer since it was created
+
+ |
+
+
+Commits a timer that ticks every 1000 milliseconds
+```
+commit
+ [#system/timer resolution: 1000]
+```
+
+Displays the current time
+```
+search
+ [#system/timer hour minute second]
+bind
+ [#ui/text text: "{{hour}}:{{minute}}:{{second}}"]
+```
+
+ |
+
+
+
+## File
+
+A library for accessing the filesystem. This library only works when Eve is run in headless mode.
+
+
+
+
+ #file/read - read the specified file
+ |
+
+
+
+
+
+ - path - The path of the file to be read.
+ - encoding - (optional) - The encoding of the file. The default is utf-8.
+ - error - (optional) - If an error is encountered when attempting to read the file, it will be stored here as a #file/error.
+ - contents - The contents of the file. This attribute will have a value once the entire contents of the file are read.
+
+ |
+
+
+Read a file
+```
+commit
+ [#file/read #my-file path: "test-file.txt"]
+```
+
+Display the contents of the file
+```
+search
+ [#my-file contents]
+commit
+ [#console/log text: contents]
+```
+
+ |
+
+
+
+
+
+
+ #file/read - read the specified file
+ |
+
+
+
+
+
+ - path - The path of the file to be written.
+ - encoding - (optional) - The encoding of the file. The default is utf-8.
+ - contents - The string that will be written to the file
+ - error - (optional) - If an error is encountered when attempting to read the file, it will be stored here as a #file/error.
+ - #file/complete - When the contents are written successfully, the record will be tagged #file/complete.
+
+ |
+
+
+```
+commit
+ [#file/write path: "test-file.txt" contents: "This will be in the file"]
+```
+
+ |
+
+
+
+
+
+
+ #file/error - stores information relating to file access errors
+ |
+
+
+
+
+
+ - code - The error code
+ - message - The error message
+
+ |
+
+
+```
+search
+ [#file/read path error: [code: "ENOENT"]]
+commit
+ [#console/error text: "Could not file file {{path}}"]
+```
+
+ |
+
+
+
+## Console
+
+Write text to the console
+
+
+
+
+ #console/log - write info to the console
+ |
+
+
+
+
+
+ - text - The text to write to the console. Text will also be written to stdout.
+
+ |
+
+
+```
+commit
+ [#console/log text: "Hello world!"]
+```
+
+ |
+
+
+
+
+
+
+ #console/warn - write a warning to the console
+ |
+
+
+
+
+
+ - text - The text to write to the console.
+
+ |
+
+
+```
+commit
+ [#console/warn text: "Memory is running low."]
+```
+
+ |
+
+
+
+
+
+
+ #console/error - write an error to the console.
+ |
+
+
+
+
+
+ - text - The text to write to the console. Text will also be written to stderr.
+
+ |
+
+
+```
+commit
+ [#console/error text: "Access is Denied"]
+```
+
+ |
+
+
+
+{% endraw %}
diff --git a/v0.3/tutorials/advanced-counter.eve b/v0.3/tutorials/advanced-counter.eve
new file mode 100644
index 0000000..382f665
--- /dev/null
+++ b/v0.3/tutorials/advanced-counter.eve
@@ -0,0 +1,42 @@
+---
+layout: default
+title: Quickstart + Improved Counter
+---
+
+{% raw %}
+
+# Quickstart + Improved Counter
+
+This guide expands on the quickstart `Counter` page and shows you how to display the counter message on the button itself. The following code is meant to replace all the code from the original counter.
+
+## The self-contained counter
+
+```
+search
+ [#app page: "Counter"]
+ view = [#qs-contents]
+ how-many = if n = gather/count[for: [#clicked]] then n else 0
+
+bind
+ view.children += [#ui/button #qs-increment text: "The button has been clicked {{how-many}} times" sort: 1]
+
+end
+```
+
+Sometimes the way forward is to delete code, and this follows suit - we actually get a more elegant solution here by condensing two blocks before down into one. We can't just smash together the two blocks entirely though because we want the original message to read "The button has been clicked 0 times" before any clicks happen. Recall that in the original counter example, before any clicks occurred, the counter message wouldn't show up because the search was trying to `gather/count` a non-existent record and causing the whole block to fail. If we tried that here, the counter button wouldn't show up. To account for this, an `if` statement sets `how-many` to the number of clicks once they happen, and `else` handles the edge case of starting at 0.
+
+## Remembering clicks
+
+Just like last time, we have to remember any clicks in order to count them.
+
+```
+search
+ event = [#html/event/click element: [#qs-increment]]
+
+commit
+ [#clicked event]
+
+end
+```
+
+That's it!
diff --git a/v0.3/tutorials/advanced-stopwatch.eve b/v0.3/tutorials/advanced-stopwatch.eve
new file mode 100644
index 0000000..2ee6d56
--- /dev/null
+++ b/v0.3/tutorials/advanced-stopwatch.eve
@@ -0,0 +1,253 @@
+---
+layout: default
+title: Quickstart + Two Button Stopwatch
+---
+
+{% raw %}
+
+# Quickstart + Two Button Stopwatch
+
+This guide expands on the quickstart `Clock` page and shows you how to turn the clock into a two button stopwatch. The following code is meant to replace all the code from the [original clock](./qs-clock/) or the [one button stopwatch](./basic-stopwatch/).
+
+## Implement system time
+
+```
+commit
+ [#time #system/timer resolution: 10]
+ [#stopwatch #stopped #reset]
+
+end
+```
+
+Nothing's changed here from the one button stopwatch.
+
+## Draw clock face
+
+```
+search
+ [#app page: "Clock"]
+ view = [#qs-contents]
+ [#stopwatch minute second millisecond]
+
+bind
+ view.children += [#svg/root sort: 1 viewBox: "0 0 100 100", width: "300px", children:
+ [#svg/circle #second-face sort: 1, cx: 50, cy: 50, r: 48, fill: "#fff", stroke: "#000", stroke-width: 0.5]
+ [#svg/circle #minute-face sort: 2, cx: 50, cy: 26, r: 12, fill: "#fff", stroke: "#000", stroke-width: 0.25]
+ [#minute-hand sort: 3, degrees: 6 * minute, length: 11, stroke: "#000", stroke-width: 0.5]
+ [#second-hand sort: 4, degrees: 6 * second + 0.006 * millisecond, length: 46, stroke: "#ff0000", stroke-width: 0.5]]
+
+end
+```
+
+Once more identical to the one button stopwatch.
+
+## Drawing the buttons
+
+This time there are two buttons instead of one.
+
+```
+search
+ [#app page: "Clock"]
+ view = [#qs-contents]
+
+bind
+ view.children += [#ui/div sort: 2 children:
+ [#ui/div #qs-stopwatch-btn #start-stop-btn text: "Start/Stop"]
+ [#ui/div #qs-stopwatch-btn #reset-btn text: "Reset"]]
+
+end
+```
+
+Now we have two buttons - one for start/stop and another for reset.
+
+## Pressing the buttons
+
+This is where things get a little bit tricky. We're going to keep the same three pieces of button logic from last time, although additional blocks will be required to give us two button functionality.
+
+### Starting from a reset
+
+```
+search
+ [#time timestamp]
+ stopwatch = [#stopwatch #stopped #reset]
+ [#html/event/click element: [#start-stop-btn]]
+
+commit
+ [#start-time timestamp]
+ stopwatch -= #stopped
+ stopwatch -= #reset
+ stopwatch += #running
+
+end
+```
+
+This is exactly what the start button did in the last example.
+
+### Stopping the first time
+
+```
+search
+ [#time timestamp]
+ not([#stop-time])
+ stopwatch = [#stopwatch #running]
+ [#html/event/click element: [#start-stop-btn]]
+
+commit
+ [#stop-time timestamp]
+ stopwatch += #stopped
+ stopwatch -= #running
+
+end
+```
+
+This is close to what the stop button did in the last example, except it checks to make sure there's no existing `#stop-time`. This means it's the first time the stop button has been pressed since a reset, and `#stop-time` needs to be created.
+
+### Pressing Reset
+
+```
+search
+ race = [#stopwatch #stopped]
+ start = [#start-time]
+ stop = [#stop-time]
+ [#html/event/click element: [#reset-btn]]
+
+commit
+ race := none
+ start := none
+ stop := none
+ [#stopwatch #stopped #reset]
+
+end
+```
+
+The reset button's only difference from last time is that it's discrete from the start/stop button, and as such this blocks looks specifically for the `#reset-btn` to be pushed.
+
+### Resuming from a stop
+
+```
+search
+ [#time timestamp]
+ start = [#start-time]
+ stop = [#stop-time]
+ stopwatch = [#stopwatch #stopped not(#reset)]
+ [#html/event/click element: [#start-stop-btn]]
+
+commit
+ start.timestamp := start.timestamp + (timestamp - stop.timestamp)
+ stopwatch -= #stopped
+ stopwatch += #running
+
+end
+```
+
+This is where things get a little tricky. This new block checks for the start button to be pressed while the stopwatch is stopped and checks for existing `#start-time` and `#stop-time` records, which indicate that we're resuming from a stop and not a reset. It then changes the `#stopwatch` from `#stopped` back to `#running` and gives `#start-time` a new timestamp, which is the sum of its original value and the amount of time the stopwatch was stopped (the difference between the timestamp upon resuming and the timestamp when it was stopped). This lets the hands pick up exactly where they left off.
+
+### Subsequent stops
+
+```
+search
+ [#time timestamp]
+ stop = [#stop-time]
+ stopwatch = [#stopwatch #running]
+ [#html/event/click element: [#start-stop-btn]]
+
+commit
+ stop.timestamp := timestamp
+ stopwatch += #stopped
+ stopwatch -= #running
+
+end
+```
+
+Whereas the first time you hit stop, `#stop-time` is created, subsequent stops don't need to create the record. Instead, they just need to update the timestamp on that stop.
+
+## Adding time values to the race timer
+
+### Time values while reset
+
+The three blocks that handle time values on the `#stopwatch` are identical to the one button stopwatch.
+
+### Time values while reset
+
+```
+search
+ stopwatch = [#stopwatch #stopped #reset]
+
+bind
+ stopwatch.minute += 0
+ stopwatch.second += 0
+ stopwatch.millisecond += 0
+
+end
+```
+
+### Time values while running
+
+```
+search
+ stopwatch = [#stopwatch #running]
+ start-time = [#start-time timestamp: start]
+ system-time = [#time timestamp: now]
+ milliseconds = math/mod[value: (now - start), by: 1000]
+ seconds = math/floor[value: (now - start) / 1000]
+ minutes = math/floor[value: seconds / 60]
+
+bind
+ stopwatch.minute += minutes
+ stopwatch.second += seconds
+ stopwatch.millisecond += milliseconds
+
+end
+```
+
+### Time values while stopped
+
+```
+search
+ stopwatch = [#stopwatch #stopped not(#reset)]
+ start-time = [#start-time timestamp: start]
+ stop-time = [#stop-time timestamp: stop]
+ milliseconds = math/mod[value: (stop - start), by: 1000]
+ seconds = math/floor[value: (stop - start) / 1000]
+ minutes = math/floor[value: seconds / 60]
+
+bind
+ stopwatch.minute += minutes
+ stopwatch.second += seconds
+ stopwatch.millisecond += milliseconds
+
+end
+```
+
+## Drawing the hands
+
+These blocks are also identical to the two button example.
+
+### Drawing the second hand
+
+```
+search
+ second-hand = [#second-hand degrees length]
+ x2 = 50 + (length * math/sin[degrees])
+ y2 = 50 - (length * math/cos[degrees])
+
+bind
+ second-hand <- [#svg/line, x1: 50, y1: 50, x2, y2]
+
+end
+```
+
+### Drawing the minute hand
+
+```
+search
+ minute-hand = [#minute-hand degrees length]
+ x2 = 50 + (length * math/sin[degrees])
+ y2 = 26 - (length * math/cos[degrees])
+
+bind
+ minute-hand <- [#svg/line, x1: 50, y1: 26, x2, y2]
+
+end
+```
+
diff --git a/v0.3/tutorials/basic-stopwatch.eve b/v0.3/tutorials/basic-stopwatch.eve
new file mode 100644
index 0000000..4ea4df9
--- /dev/null
+++ b/v0.3/tutorials/basic-stopwatch.eve
@@ -0,0 +1,218 @@
+---
+layout: default
+title: Quickstart + One Button Stopwatch
+---
+
+{% raw %}
+
+# Quickstart + One Button Stopwatch
+
+This guide expands on the quickstart `Clock` page and shows you how to turn the clock into a one button stopwatch. The following code is meant to replace all the code from the [original clock](./qs-clock/).
+
+## Implement system time and a race timer
+
+```
+commit
+ [#time #system/timer resolution: 10]
+ [#stopwatch #stopped #reset]
+```
+
+Stopwatches tend to measure seconds to two decimal points, so our resolution changes to 10ms (or one centisecond) to account for that. The other thing added here is a `#stopwatch` record that lets us keep track of when the stopwatch is stopped, running, or reset.
+
+
+## Draw clock face
+
+```
+search
+ [#app page: "Clock"]
+ view = [#qs-contents]
+ [#stopwatch minute second millisecond]
+
+bind
+ view.children += [#svg/root sort: 1 viewBox: "0 0 100 100", width: "300px", children:
+ [#svg/circle #second-face sort: 1, cx: 50, cy: 50, r: 48, fill: "#fff", stroke: "#000", stroke-width: 0.5]
+ [#svg/circle #minute-face sort: 2, cx: 50, cy: 26, r: 12, fill: "#fff", stroke: "#000", stroke-width: 0.25]
+ [#minute-hand sort: 3, degrees: 6 * minute, length: 11, stroke: "#000", stroke-width: 0.5]
+ [#second-hand sort: 4, degrees: 6 * second + 0.006 * millisecond, length: 46, stroke: "#ff0000", stroke-width: 0.5]]
+
+end
+```
+
+This block closely resembles its predecessor, but uses the record `#stopwatch` to determine the position of the hands instead of the time of day.
+
+## One Button
+
+### Draw the button
+
+We'll add one button to cycle between start, stop, and reset.
+
+```
+search
+ [#app page: "Clock"]
+ view = [#qs-contents]
+
+bind
+ view.children += [#ui/div sort: 2 children:
+ [#ui/div #qs-stopwatch-btn text: "Start/Stop/Reset"]]
+
+end
+```
+
+### Pressing Start
+
+If the button is pressed while the stopwatch is stopped and reset (thus at zero), this records the `#start-time` and changes the `#stopwatch` from `#stopped` to `#running` and removes the `#reset` tag.
+
+```
+search
+ [#time timestamp]
+ stopwatch = [#stopwatch #stopped #reset]
+ [#html/event/click element: [#qs-stopwatch-btn]]
+
+commit
+ [#start-time timestamp]
+ stopwatch -= #stopped
+ stopwatch -= #reset
+ stopwatch += #running
+
+end
+```
+
+### Pressing Stop
+
+```
+search
+ [#time timestamp]
+ stopwatch = [#stopwatch #running]
+ [#html/event/click element: [#qs-stopwatch-btn]]
+
+commit
+ [#stop-time timestamp]
+ stopwatch += #stopped
+ stopwatch -= #running
+
+end
+```
+
+If the button is pressed with the stopwatch is running, this records the `#stop-time` and changes the `#stopwatch` from `#running` to `#stopped`.
+
+### Pressing Reset
+
+```
+search
+ race = [#stopwatch #stopped not(#reset)]
+ start = [#start-time]
+ stop = [#stop-time]
+ [#html/event/click element: [#qs-quickstart-btn]]
+
+commit
+ race := none
+ start := none
+ stop := none
+ [#stopwatch #stopped #reset]
+
+end
+```
+
+When the timer is stopped but hasn't been reset yet, and the button gets hit, this erases the `#stopwatch`, `#start-time`, and `#stop-time` records to clear the stopwatch of the previous time's data, and recommits a new `#stopwatch` without any time values on it.
+
+## Adding time values to the race timer
+
+There's three conditions where the `#stopwatch` needs values assigned to it.
+
+### Time values while reset
+
+```
+search
+ stopwatch = [#stopwatch #stopped #reset]
+
+bind
+ stopwatch.minute += 0
+ stopwatch.second += 0
+ stopwatch.millisecond += 0
+
+end
+```
+
+The stopwatch has no values before the start button is pressed, so we add default values for the minute, second, and millisecond attributes. This way, when the page is loaded or the stopwatch is reset, the hands are zeroed out.
+
+### Time values while running
+
+```
+search
+ stopwatch = [#stopwatch #running]
+ start-time = [#start-time timestamp: start]
+ system-time = [#time timestamp: now]
+ milliseconds = math/mod[value: (now - start), by: 1000]
+ seconds = math/floor[value: (now - start) / 1000]
+ minutes = math/floor[value: seconds / 60]
+
+bind
+ stopwatch.minute += minutes
+ stopwatch.second += seconds
+ stopwatch.millisecond += milliseconds
+
+end
+```
+
+Hooray for math functions! While the stopwatch is running, we simply take the difference between the current timestamp and the starting timestamp. From there, `math/mod` will let you extract just milliseconds, and `math/floor` yields whole seconds and minutes, which once again get added as attributes to the `#stopwatch`.
+
+### Time values while stopped
+
+```
+search
+ stopwatch = [#stopwatch #stopped not(#reset)]
+ start-time = [#start-time timestamp: start]
+ stop-time = [#stop-time timestamp: stop]
+ milliseconds = math/mod[value: (stop - start), by: 1000]
+ seconds = math/floor[value: (stop - start) / 1000]
+ minutes = math/floor[value: seconds / 60]
+
+bind
+ stopwatch.minute += minutes
+ stopwatch.second += seconds
+ stopwatch.millisecond += milliseconds
+
+end
+```
+
+This block is almost completely identical to the last block, except `#time` was swapped out for `#stop-time`. The system timestamp is always changing, whereas the stopped timestamp is static, leaving the stopwatch hands paused in place.
+
+## Drawing the hands
+
+Last time, all 3 hands could be easily handled by the same block. Technically the two following blocks could be combined with a small amount of effort, but since the second hand and the minute hand no longer share the same y values on the canvas, things stay clearer by having them separated.
+
+### Drawing the second hand
+
+Good like last time.
+
+```
+search
+ second-hand = [#second-hand degrees length]
+ x2 = 50 + (length * math/sin[degrees])
+ y2 = 50 - (length * math/cos[degrees])
+
+bind
+ second-hand <- [#svg/line, x1: 50, y1: 50, x2, y2]
+
+end
+```
+
+### Drawing the minute hand
+
+The only change here is that the minute hand is on a smaller dial on the watch face, shifting the `y1` and `y2` values a little higher.
+
+```
+search
+ minute-hand = [#minute-hand degrees length]
+ x2 = 50 + (length * math/sin[degrees])
+ y2 = 26 - (length * math/cos[degrees])
+
+bind
+ minute-hand <- [#svg/line, x1: 50, y1: 26, x2, y2]
+
+end
+```
+
+## Next steps
+
+A one button stopwatch is the classic, but isn't quite as useful as a two button stopwatch that has a start/stop button and a reset button. In the case of two button stopwatch, one is a stop/start button and the other is a reset button. The stop/start button will pause the stopwatch and resume counting if you hit it again without hitting reset, which adds a few more states and calculations that you need to account for. Try building it yourself or [follow along with our solution](./advanced-stopwatch/)!
diff --git a/v0.3/tutorials/qs-clock.eve b/v0.3/tutorials/qs-clock.eve
new file mode 100644
index 0000000..baeafd9
--- /dev/null
+++ b/v0.3/tutorials/qs-clock.eve
@@ -0,0 +1,65 @@
+---
+layout: default
+title: Quickstart + Counter
+---
+
+{% raw %}
+
+# Quickstart + Counter
+
+This guide expands on the quickstart by adding a live clock to the `Clock` page.
+
+## Quickstart code
+
+This is made to add onto the web app you made in the quickstart. Make sure you have [all the code from that](./quickstart/) in your Eve program before you start adding the code found here to begin working on the clock!
+
+## Building the clock
+
+The first thing to do is to create a system timer that will tick once per second (or 1000 milliseconds, which is the unit of measurement that resolution uses).
+
+```
+commit
+ [#time #system/timer resolution: 1000]
+
+end
+```
+
+### Drawing the clock face
+
+Next, we can draw the clock face on the `Clock` page.
+
+```
+search
+ [#app page: "Clock"]
+ view = [#qs-contents]
+ [#time hour minute second]
+
+bind
+ view.children += [#svg/root viewBox: "0 0 100 100", width: "300px", children:
+ [#svg/circle sort: 1, cx: 50, cy: 50, r: 45, fill: "#0B79CE"]
+ [#clock-hand #hour-hand sort: 2, degrees: 30 * hour + 0.5 * minute, length: 25, stroke: "#023963"]
+ [#clock-hand #minute-hand sort: 3, degrees: 6 * minute, length: 38, stroke: "#023963"]
+ [#clock-hand #second-hand sort: 4, degrees: 6 * second, length: 40, stroke: "#ce0b46"]]
+
+end
+```
+
+### Drawing the hands
+
+By adding an svg path to the `#hour-hand`, `#minute-hand`, and `#second-hand`, we can draw the hands of the clock.
+
+```
+search
+ hand = [#clock-hand degrees length]
+ x2 = 50 + (length * math/sin[degrees])
+ y2 = 50 - (length * math/cos[degrees])
+
+bind
+ hand <- [#svg/line, x1: 50, y1: 50, x2, y2]
+
+end
+```
+
+## Next steps
+
+What would it take to move from a watch to a stopwatch? The geometry for drawing the hands is very similar, so most of what you'd need to add is a way to track starting and stopping. Try starting with a one button stopwatch, where the button cycles between start, stop, and reset. Give it a try on your own or [follow along with our solution](./basic-stopwatch/)!
diff --git a/v0.3/tutorials/qs-counter.eve b/v0.3/tutorials/qs-counter.eve
new file mode 100644
index 0000000..9328516
--- /dev/null
+++ b/v0.3/tutorials/qs-counter.eve
@@ -0,0 +1,68 @@
+---
+layout: default
+title: Quickstart
+---
+
+{% raw %}
+
+# Quickstart + Counter
+
+This guide expands on the quickstart by adding a simple incrementing counter to the `Counter` page.
+
+## Quickstart code
+
+This is made to add onto the web app you made in the quickstart. Make sure you have [all the code from that](./quickstart/) in your Eve program before you start adding the code found here to begin working on the counter!
+
+## Building the counter
+
+The first thing to do is to create some basic content on our `Counter` page.
+
+```
+search
+ [#app page: "Counter"]
+ view = [#qs-contents]
+
+bind
+ view.children += [#ui/button #qs-increment text: "+1" sort: 1]
+
+end
+```
+
+
+## Remembering clicks
+
+To be able to count the clicks, we have to first remember them.
+
+```
+search
+ event = [#html/event/click element: [#qs-increment]]
+
+commit
+ [#clicked event]
+
+end
+```
+
+Click events only last for an instant in Eve before the `#html/event/click` record goes away, but we want to create a permanent record of each click so we can search for them later.
+
+## Counting clicks
+
+Now that there's a permanent `#clicked` record for each click, we can count them and display that on the `Counter` page.
+
+```
+search
+ how-many = gather/count[for: [#clicked]]
+ [#app page: "Counter"]
+ view = [#qs-contents]
+
+bind
+ view.children += [#ui/text text: "The button has been clicked {{how-many}} times." sort: 2]
+
+end
+```
+
+You may notice that nothing appears until you click the button at least once. When there are zero clicks, `how-many` has nothing to count, the search will fail, and so no message will be created. Once there's a click, the search passes, and the count message will show up.
+
+## Next steps
+
+The way this is written, the button has the "+1" text on it and a message appears underneath saying how many times the button has been clicked. What if you wanted to combine that so the button reads "This button has been clicked X times"? You can try it out yourself or just jump to (the solution)[./advanced-counter/) to follow along with it!
diff --git a/v0.3/tutorials/quickstart.eve b/v0.3/tutorials/quickstart.eve
index 0e9c911..c0aa97e 100644
--- a/v0.3/tutorials/quickstart.eve
+++ b/v0.3/tutorials/quickstart.eve
@@ -7,117 +7,120 @@ title: Quickstart
# Quickstart
-This guide is a 5 - 10 minute introduction to the essential concepts in Eve. If you've never used Eve before, you're in the right place! Before you start with this tutorial, please follow the [installation and usage](/v0.3/install/) instructions, which will get you running Eve programs on your machine. In the eve-starter/programs directory, create an empty document called "quickstart.eve". Use your favorite editor to edit the program, and run it with the command:
+This guide will give you a quick introduction to Eve by making a simple web app in just a few minutes.
+
+## Getting Eve Running
+
+Before you start with this tutorial, please follow the [installation and usage instructions](/v0.3/install/), which will get you running Eve programs on your machine. In the eve-starter directory, you can launch the Program Switcher:
+
+```
+npm start
```
-npm start -- eve-starter/programs/quickstart.eve
-```
-Eve documents are written in Markdown. Eve code is written in blocks, delineated by Markdown code fences (matching pairs of ``` or ~~~ surrounding a section of code). These blocks of code search for and create records, which are key value pairs attached to a unique ID.
+You'll see a list of Eve programs in your browser; select quickstart.eve in the program switcher, and use your favorite editor to open quickstart.eve so we can edit the program.
-## Adding records to Eve
-Let's start with a block that adds a record to Eve (we'll show the code fence in this block, but elide them in later blocks):
+## The Basics
+Let's start with the most basic example of Eve. Put this whole block, with the set of backticks (\`\`\`) included, into the quickstart.eve file, then refresh your browser page where it's running.
-``````
-~~~
+```
commit
- [#greeting message: "hello world"]
-~~~
-``````
-
-The record committed by this block is tagged `#greeting`, and has an attribute "message" with the value "hello world". Tags are attributes like any other, their special syntax notwithstanding. We encourage you to use tags to classify groups of related records.
+ [#html/div text: "My Quickstart App"]
-## Finding records in Eve
+end
+```
-Blocks search for records, and then bind or commit new records. Let's search for the `#greeting` we just committed, and then display it on the screen:
+`#html/div` is a tag with a very specific behavior - it will make a div in the browser - but not all tags do that. Tags are useful ways to organize similar records or to give a record a tag that can be searched for later. To demonstrate, check out the next block. We want our web app to be laid out nicely with a logo, a navigation bar, and a content area. To do that, add this whole block into the quickstart file right after the other block you already wrote, and refresh the browser page again.
-~~~eve
-search
- [#greeting message]
+```
+commit
+ [#html/div #qs-wrapper children:
+ [#html/div #qs-logo]
+ [#html/div #qs-nav-bar]
+ [#html/div #qs-contents]]
-bind
- [#ui/text text: message]
-~~~
+end
+```
-Searches find records that matches the supplied pattern, in this case records tagged greeting with a message attribute. You use these results to create new records. In this block, we create a `#ui/text` record for every message that is found (the UI library looks for records tagged `#ui/text` and draws them as DOM elements in the browser). If no records match the search, the block has no effect. Try adding another `#greeting` record in the first block to see what happens when more than one record matches the search.
+These records are all tagged `#html/div`, but now they each have a unique tag on them as well. Those names are totally arbitary and have no prebuilt behavior, but from now on, we can search for any of those tags to do stuff to that particular div. To make CSS a little easier as well, Eve adds all the other tags on a record as classes to the divs. The Eve logo is already set in the CSS for the class `qs-logo`, but next we need to add some pages to our nav bar and give each page some content.
-Variables with the same name are equivalent within a block; because they have the same name, the `message` in `[#greeting message]` and `[#ui/text text: message]` are equivalent.
+As a side note, if you're curious about which tags do have prebuilt behavior in Eve, you can read all about them in our [standard library](/v0.3/library/). The quick and easy rule of thumb though is this: if there's a slash in the tag, like `#html/div`, it's a prebuilt function; if there's no slash, the tag doesn't do anything on its own.
-## Records update as data changes
-Blocks in Eve react automatically to changes in data. When a record changes, any bound records are automatically updated. We can see this in action by outputting the current time. First we create a timer that ticks once every second:
+## Pages for the Web App
+We can easily add a list of the pages we want for our app. `#page` is again an arbitrary name, but here we've got it tagged on multiple records to help us remember what they are. These records alone don't add anything to the browser, they just create a list that's easy to change later.
-~~~eve
+```
commit
- [#time #system/timer resolution: 1000]
-~~~
+ [#page name: "Home" sort: 1]
+ [#page name: "Counter" sort: 2]
+ [#page name: "Clock" sort: 3]
-Now we can search for the current time, and display it on the screen:
+end
+```
-~~~eve
-search
- [#time seconds]
+## Adding buttons to the Nav Bar
+To actually make the list of pages appear somewhere, we need to write a block that first looks for our pages, then adds them into the browser.
-bind
- [#ui/text text: seconds]
-~~~
+```
+search
+ page = [#page name sort]
+ nav-bar = [#html/div #qs-nav-bar]
-As the time changes, the output updates to reflect the current state of the `#time` record. Records can be committed instead of bound, but the behavior is a little different -- committed records persist until they are removed explicitly.
+commit
+ nav-bar.children += [#html/div #qs-nav-btn page text: name sort]
-## Reacting to events
+end
+```
-Let's draw a button on the screen:
+By making a button for each page from the list, it's easy to add more pages later. For now though, we're going to focus on the Home page. The first step is to make a special record to keep track of which page we're looking at.
-~~~eve
-commit
- [#ui/button #increment text: "+1"]
-~~~
-When you click anywhere on the screen, Eve creates an `#html/event/click` record representing the click. You can react to clicks on the `#increment` button by searching for the `#html/event/click` record, where the element attribute is the button:
+## Starting Page
-~~~eve
-search
- event = [#html/event/click element: [#increment]]
+We want Home to be the default starting location, which we'll start to accomplish here by creating a record called `#app`.
+```
commit
- [#clicked event]
-~~~
+ [#app page: "Home"]
-Clicks only last for an instant, but we want to create a permanent record of each click so we can search for them later. This block commits a `#clicked` record that will persist until it's explicitly removed. Much like the `#greeting` text we bound to the `#ui`, variables with the same name are equivalent, so the variable `event` in the `#clicked` record is a reference to the `#html/event/click` on the `#increment` button.
-
-The identity of a record is determined by its attribute/value pairs. Two records with the same attributes and values are the same record in Eve. We included the `event` attribute in the `#clicked` record to differentiate each record. Without this differentiation, we could only ever create a single `#clicked` record.
+end
+```
-## Count the number of clicks
+This alone doesn't do anything. We've given it an attribute `page` with the value `"Home"`, which will eventually be used to keep track of which page we're on, but it doesn't mean anything on its own. It will, however, give us something to match patterns against in the next block.
-Now let's count the number of times the button has been clicked. Make sure `event` is back in `#clicked`, and then we can count those records directly:
+## Home Page
+To make the home page show up, we want to check for two things: the `#app` record keeping track of which page were on should say `page: "Home"`; and there should be a record tagged `#qs-content` (which we've named `contents` for this block and know to be a div in the browser). This is what we search for, then inject a couple more divs into it to make a home page.
-~~~eve
+```
search
- how-many = gather/count[for: [#clicked]]
+ [#app page: "Home"]
+ contents = [#qs-contents]
bind
- [#ui/text text: "The button has been clicked {{how-many}} times"]
-~~~
+ contents.children += ([#html/div text: "This is the Eve Quickstart app! Choose a link above to see some of the things Eve can do."],
+ [#html/img #qs-flappy src:"http://i.imgur.com/sp68LtM.gif"])
-This block searches for every unique `#clicked`, counts them, and returns that value in `how-many`. Then we display this value in a text container using the operator `{{ ... }}`, which inserts the value of the contained variable into the string. An important thing to remember here is that this block will only run when the button has been clicked at least once. Before then, this block will not run because there are no `#clicked` records to count.
+end
+```
-## Summary
+## Navigation
+The last thing to do is make the nav bar links switch which page the app is showing.
-That's it for the 5 minute introduction to Eve. To summarize:
+```
+search
+ click = [#html/event/click element:[#html/div #qs-nav-btn page]]
+ view = [#app]
-- Eve programs are made up of blocks.
-- Data are represented by records, key value pairs associated to a unique ID.
-- There are two sections of a block: one where you search for records, and one where you bind or commit records.
-- Blocks update records automatically to reflect changes in data.
-- Bound records are replaced when data changes, while committed records must be removed manually.
-- Records are unique, uniqueness is determined by a record's attributes and their values.
+commit
+ view.page := page.name
+
+end
+```
-This will get you started with Eve, but there's still more to learn. From here, you can:
+When you click on a navigation button, it changes the `page` attribute on the `#app` record to whichever page you clicked on the nav bar. Clicking Counter or Clock will make the home page go away because its search will no longer succeed, and thus it won't inject any thing into the browser. Right now, clicking on Counter or Clock will leave you with blank pages because there aren't any blocks yet to give them content like Home, but if you click on the Home button, you'll see that content again.
-- Advance to Level 2 of the introductory tutorial (coming soon).
-- View the [syntax reference](/v0.3/syntaxreference/) or the [library reference](/v0.3/handbook/libraries/stdlib/).
-- Explore already made [examples](https://github.com/witheve/eve-examples).
-- Or dive right in to the editor and try out the concepts you've just learned (coming soon).
+## Next steps
-{% endraw %}
\ No newline at end of file
+The next step is to add content to Counter or Clock! You can play around with them on your own, or can keep following the tutorials and see how we filled those pages out. Check out how to [build a counter](./qs-counter/) or [draw a clock](./qs-clock/). Have fun!
| | | | | | | | | | | | | | | | |