├── .bookignore ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── Building User Interfaces.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Choose Your Own Emoji.md ├── Deployment and Next Steps.md ├── Emojification.md ├── Getting Started.md ├── Introduction.md ├── Making It Dynamic.md ├── Our First Full Feature.md ├── PUBLISHING.md ├── README.md ├── SUMMARY.md ├── Special Installation Notes.md ├── The Basic Building Blocks of Elm.md ├── The Elm Architecture.md ├── Why Elm.md ├── book.json ├── docs ├── Building User Interfaces.html ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Choose Your Own Emoji.html ├── Deployment and Next Steps.html ├── Emojification.html ├── Getting Started.html ├── Making It Dynamic.html ├── Our First Full Feature.html ├── README.md ├── Special Installation Notes.html ├── The Basic Building Blocks of Elm.html ├── The Elm Architecture.html ├── Why Elm.html ├── gitbook │ ├── fonts │ │ └── fontawesome │ │ │ ├── FontAwesome.otf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ └── fontawesome-webfont.woff2 │ ├── gitbook-plugin-fontsettings │ │ ├── fontsettings.js │ │ └── website.css │ ├── gitbook-plugin-highlight │ │ ├── ebook.css │ │ └── website.css │ ├── gitbook-plugin-lunr │ │ ├── lunr.min.js │ │ └── search-lunr.js │ ├── gitbook-plugin-search │ │ ├── lunr.min.js │ │ ├── search-engine.js │ │ ├── search.css │ │ └── search.js │ ├── gitbook-plugin-sharing │ │ └── buttons.js │ ├── gitbook.js │ ├── images │ │ ├── apple-touch-icon-precomposed-152.png │ │ └── favicon.ico │ ├── style.css │ └── theme.js ├── images │ ├── elm-architecture-1.jpeg │ ├── elm-architecture-2.jpeg │ ├── elm-architecture-3.jpeg │ ├── elm-architecture-4.jpeg │ ├── final-release.png │ ├── hello-world.gif │ ├── intro-to-elm-initial.png │ ├── release-0.png │ ├── release-1.gif │ ├── release-2.png │ ├── release-3-part-2.gif │ └── release-4-part-3.gif ├── index.html ├── search_index.json └── styles │ └── website.css ├── images ├── elm-architecture-1.jpeg ├── elm-architecture-2.jpeg ├── elm-architecture-3.jpeg ├── elm-architecture-4.jpeg ├── final-release.png ├── hello-world.gif ├── intro-to-elm-initial.png ├── release-0.png ├── release-1.gif ├── release-2.png ├── release-3-part-2.gif └── release-4-part-3.gif ├── package-lock.json ├── package.json └── styles └── website.css /.bookignore: -------------------------------------------------------------------------------- 1 | /.travis.yml 2 | /.github 3 | /.bookignore 4 | /.gitignore 5 | /PUBLISHING.md 6 | /package.json 7 | /package-lock.json 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Context: 2 | (Describe the situation that led you to discover the issue.) 3 | 4 | ## Problem: 5 | (What is the pain point and who does it affect?) 6 | 7 | ## Possible solution or next step: 8 | (If you have an idea for how to resolve the problem, please let us know! If not, no worries!) 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _book/ 2 | /node_modules/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: node_js 4 | node_js: "10" 5 | 6 | script: 7 | - npm run-script build 8 | -------------------------------------------------------------------------------- /Building User Interfaces.md: -------------------------------------------------------------------------------- 1 | # Building User Interfaces 2 | 3 | Congratulations on completing the last lesson! If this was your first experience with a functional programming language, it may have been difficult. The jump from object oriented programming to functional programming can be scary, and Elm's syntax can be confusing for recent transplants. **This initial struggle is totally normal**, and will eventually dissipate as you continue to work with Elm. 4 | 5 | As we discussed earlier, Elm is distinctive in its enforcement of **purity**, **immutability** and **static typing**. That's all well and good for the toy problems that you just solved, but you may be wondering how Elm can model a complex and interactive UI given these constraints. If nothing can change in Elm, how can Elm applications model systems that change? 6 | 7 | As we will see, Elm has a series of abstractions built into the language that are helpful in modeling user interfaces. Together, these abstractions form **The Elm Architecture**, which is a detailed template for constructing an application in Elm. Similar to how the strictness of the language pushes you to write code in a particular way, The Elm Architecture pushes you to structure applications in a way that prioritizes maintainability. 8 | 9 | The rest of this tutorial is meant to teach you what this means in practice. Piece by piece, you'll be building and extending an interactive user interface in Elm. By the end of the day, you should have a better sense of what it feels like to develop in Elm and what it's like to work with a functional programming language. 10 | 11 | Off we go! 12 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at saviinirs@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Thanks! 2 | 3 | Thanks for taking the time to contribute to ElmBridge. We sincerely appreciate it. 4 | 5 | Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms. 6 | 7 | We ask that contributions be made as issues or pull requests via GitHub. If those words 8 | are totally foreign to you, 9 | [see here](#its-my-first-time-on-github-ever-what-do-i-do). 10 | 11 | # Creating an Issue 12 | 13 | If you see a bug or something that can be improved, please let us know! 14 | 15 | You can make an issue on the ElmBridge Curriculum [here](https://github.com/elmbridge/curriculum/issues/new). 16 | 17 | # Submitting a Pull Request 18 | 19 | *Here's a couple of tricks to grease the wheels and make it easy for the 20 | maintainers to love you. :heart:* 21 | 22 | ## Before You Start! 23 | 24 | - If you have an existing fork, please make sure it's up to date. 25 | It just makes your life easier! If not, make sure you fork *before* cloning, 26 | otherwise you'll need to spend some time juggling remotes. 27 | Look at the section "Keep your fork synced" in GitHub's 28 | [Fork A Repo](https://help.github.com/articles/fork-a-repo) article. 29 | 30 | - Create a local topic branch before you start working. This branch is going to 31 | be named for what you plan to change. `fix-typo-in-slides`, `move-resources`, 32 | and `mountain-lion-support` are all good names for topic branches. If you've 33 | never created a local branch before, you can use `git checkout -b 34 | new-branch-name`. 35 | 36 | ## Before Submitting 37 | 38 | - Push to a branch on GitHub. Just like you developed in a local branch, you 39 | should push to a branch of your repo on GitHub as well. The `master` branch is 40 | best used as a clean copy of the upstream docs repo in case you need to make 41 | some unrelated changes. To push to a branch, 42 | if your branch is named "fix-typo-in-slides", 43 | use `git push origin fix-typo-in-slides`. 44 | 45 | ## Submitting a Pull Request 46 | 47 | - Read the GitHub Guide on [Forking](https://guides.github.com/activities/forking/), especially the part about 48 | [Pull Requests](https://guides.github.com/activities/forking/#making-a-pull-request). 49 | 50 | - Remember, pull requests are submitted *from* your repo, but show up on the 51 | *upstream* repo. 52 | 53 | ## Discussion and Waiting On a Merge 54 | 55 | - Every pull request will receive a response from the team. 56 | - Not every pull request will be merged as is. 57 | - Not every pull request will be merged at all. 58 | - If a pull request falls significantly behind master, we may ask that you close 59 | it, rebase your changes off of master, and submit a new pull request. 60 | - Feel free to "ping" the team by adding a short comment to your pull request 61 | if it's been more than a week with no reply 62 | 63 | ## After your merge has been accepted 64 | 65 | - go back to your fork and keep it up to date, e.g. 66 | 67 | git checkout master 68 | git pull upstream master 69 | git push origin master 70 | 71 | - you can also delete your topic branch if you like 72 | 73 | git branch -dr fix-typo-in-slides 74 | 75 | # It's My First Time on GitHub Ever What Do I Do? 76 | 77 | Relax, you came to the right place. In order to contribute you'll need to be 78 | able to familiarize yourself with some concepts from git and GitHub. It's going 79 | to be a lot of information, but you're :sparkles:awesome:sparkles:! So you'll 80 | be fine. 81 | 82 | First, you'll need a GitHub account, which is totally free. You can sign up 83 | [here](https://github.com/join). 84 | 85 | Next, browse the [GitHub Help site](https://help.github.com) and the 86 | [GitHub Guides](https://guides.github.com/). The Help Site is more technical, and the 87 | Guides are very easy to understand tutorials. 88 | 89 | You'll want to read about 90 | [forking](https://help.github.com/articles/fork-a-repo) and then make your own 91 | fork of [elmbridge/curriculum](https://github.com/elmbridge/curriculum). Once you've 92 | done so, you can clone it and get started by reading up on [what to do when 93 | submitting a pull request](#when-submitting-a-pull-request), and read up on 94 | [pull requests](https://help.github.com/articles/using-pull-requests) 95 | themselves. 96 | 97 | # Closing 98 | 99 | If you haven't taken the time yet, go through the [Git Immersion lab](http://gitimmersion.com) 100 | at . Do it. It's worth it no matter how much git-fu you have. 101 | 102 | Also, [Pro Git](http://git-scm.com/book) is a great (and free!) book about Git. 103 | 104 | We apologize for how long this document is! Hopefully it addressed 105 | most of your concerns about git, contributing, and GitHub. And we're open to any suggestions about improvements, 106 | including to this document. 107 | 108 | _This contributor's guide has been adapted from [RailsBridge](https://github.com/railsbridge/docs)._ 109 | -------------------------------------------------------------------------------- /Choose Your Own Emoji.md: -------------------------------------------------------------------------------- 1 | # Choose Your Own Emoji 2 | 3 | For this lesson, we'll allow our users to choose which emoji to use when encoding their messages. That way, they can send secret messages to each other that can only be decoded if they have the right key! It will eventually look like this: 4 | 5 | ![Final Release](images/final-release.png) 6 | 7 | There's a lot to do! Let's do this! 8 | 9 | Note: Your code should currently look like the code in `Part5.elm`. You can either carry your code over from the last lesson, or start fresh with `Part5.elm`. 10 | 11 | 12 | ## Goals 13 | 14 | - Use transformation functions to render dynamic HTML. 15 | - Use complex `Html` functions to render conditional classes. 16 | - Model a domain using tagged values. 17 | - Implement a full feature from start to finish. 18 | 19 | ## Steps 20 | 21 | ### Adding New UI Elements 22 | 23 | As we did with our last lesson, let's start first by updating our `view` with our new UI elements. The eventual markup should look like this: 24 | 25 | ```html 26 | 27 |
28 |
29 |

Select Your Key

30 |
31 |
32 |
😁
33 |
34 |
35 |
😅
36 |
37 | 38 |
39 |
40 | ``` 41 | 42 | Take a crack at converting this to Elm. Some of this is straightforward — we already know how to render static elements with attributes and children using the `Html` module. We also already have a list of all supported emojis in `EmojiConverter.supportedEmojis`. But how will we render all those emojis without a lot of copying and pasting? 43 | 44 | Elm shines in situations like this. Since `view` is simply Elm code, and `Html.Html` is just another type in Elm, we can use normal transformations to produce HTML. We can use `EmojiConverter.supportedEmojis` in combination with the `List` module to convert that list of emojis into a list of HTML elements! 45 | 46 | The code in `view` can use a helper function to render all emoji keys, like this: 47 | 48 | ```elm 49 | -- ... 50 | [ Html.h4 51 | [ Html.Attributes.class "center" ] 52 | [ Html.text "Select Your Key" ] 53 | , renderKeys 54 | ] 55 | ``` 56 | 57 | And we can write `renderKeys` to iterate over the list of supported emojis and call another helper function, `renderKey`. 58 | 59 | ```elm 60 | renderKeys = 61 | Html.div 62 | [ Html.Attributes.class "row" ] 63 | (List.map (\emoji -> renderKey emoji) EmojiConverter.supportedEmojis) 64 | ``` 65 | 66 | It's worth taking a moment to explain what `List.map` is doing. Much like `Array.prototype.map` in JavaScript, `List.map` takes a function and a list, calling the function on each element on the list and returning a list of the produced values. The `(\... -> ...)` syntax is how you define an **anonymous function** in Elm. 67 | 68 | If you want to learn more, the `List` module is thoroughly documented on [package.elm-lang.org](https://package.elm-lang.org/packages/elm/core/latest/List). The site is a great resource if you want to learn more about the public API for available Elm packages and core modules. 69 | 70 | Now, it's your turn! Add the new markup to `view`, with the `renderKeys` and `renderKey` helper functions. 71 | 72 | ### Displaying the Currently Selected Key 73 | 74 | There's one more part of the UI that we have to render — we have to display the currently selected key as selected. If we give the specified emoji `.key-selector` a class of `.is-selected`, it will display with a blue background. For now, we'll 75 | hard code the selected key to `defaultKey`. 76 | 77 | When you have to conditionally attach classes to an HTML element in Elm, the `Html.Attributes.classList` function is helpful in simplifying the code. The function consumes a list of tuples, with each tuple containing a string (representing a class) and a boolean (describing whether the class should be attached to the element). The code for rendering the `.key-selector` element might look something like this: 78 | 79 | ```elm 80 | Html.div 81 | [ Html.Attributes.classList 82 | [ ( "key-selector", True ) 83 | , ( "is-selected", emoji == defaultKey ) 84 | ] 85 | ] 86 | [ Html.text emoji ] 87 | ``` 88 | 89 | Once you've added that code to your `renderKey` function, you should have a working, if static, UI! 90 | 91 | ### Adding User Actions 92 | 93 | Note: Your code should currently look like the code in `Part6.elm`. 94 | 95 | Now that we've updated the UI, we have to map our new user action to a message for our application to consume. Add the following line of code to your `renderKey` function, as an attribute to the element with a class of `.key-selector`: 96 | 97 | ```elm 98 | Html.Events.onClick (SetSelectedKey emoji) 99 | ``` 100 | 101 | When this element is clicked, a `Msg` of `SetSelectedKey String` will be triggered, which will eventually be consumed by our application. 102 | 103 | Like `SetCurrentText String`, `SetSelectedKey String` is a **tagged value**. In effect, we are saying that `SetCurrentText` and `SetSelectedKey` are only valid values of the `Msg` custom type if they are accompanied by a string. 104 | 105 | Tagged values are helpful for modeling systems with uneven information requirements. For instance, if we were modeling a coffee shop, we might structure our data like this: 106 | 107 | 108 | ```elm 109 | type BrewingStrategy = PourOver | FrenchPress 110 | 111 | type Milk = Cow | Soy | Almond 112 | 113 | type alias NumberOfEspressoShots = Integer 114 | 115 | type DrinkOrder 116 | = BlackCoffee BrewingStrategy 117 | | Latte NumberOfEspressoShots Milk 118 | | HotChocolate Milk 119 | | ColdBrew 120 | ``` 121 | 122 | Apparently, there's a lot of complexity bundled up in our drink orders! In some cases, we need to know what kind of milk the customer wants. In other cases, we need to know information specific to only one kind of drink. The above data model accounts for that complexity by using tagged values — a `HotChocolate` is only a valid `DrinkOrder` if it is accompanied by a `Milk` selection, and a `BlackCoffee` relies on another custom type to determine the customer's preferred brewing strategy. 123 | 124 | We could have also modeled this problem with a record, but the data would have likely been harder to follow. What would the `.milk` field on that record mean, if the customer wanted black coffee? Why would we need a `.numberOfEspressoShots` in order to make a hot chocolate? 125 | 126 | Tagged values are also helpful in preventing bad data from propagating through our system. For instance, a drink order of "single shot latte" isn't enough in the real world — if we tried to make it, we wouldn't know what kind of milk our customer wanted. Tagged values protect us here — if we tried to use `Latte 1` in a function that was expecting something of the type `DrinkOrder`, our code would fail to compile. 127 | 128 | So let's wire up our new tagged value to `update`! In order: 129 | 130 | - Add the aforementioned `onClick` attribute to the `.key-selector` element. 131 | - Add the new `SetSelectedKey String` value to `Msg`, and ensure that `update` handles the new case. 132 | - Find a way to store the key information on the model, perhaps in a new field called `selectedKey`. 133 | - Ensure that you the model initialization logic still works. We should start off the application with a `model.selectedKey` value of `defaultKey`. 134 | - Implement the `SetSelectedKey String` case of `update`, so that it updates the model with the correct `selectedKey`. 135 | 136 | That's a lot! Work slowly, and lean on your compiler for help. If you get stuck, you can see a completed version of this step in `Part7.elm`. 137 | 138 | ### Displaying Model Values 139 | 140 | Now that `update` consumes our new message, we need to reflect changes to the model onto the UI. First off, we need to use our new `model.selectedKey` field to display to the user which key is currently selected. As of now, our `renderKeys` function does not consume `model`, so it has no idea about the current state of the application. Let's change that! `renderKeys` should look like this: 141 | 142 | 143 | ```elm 144 | renderKeys model = 145 | Html.div 146 | [ Html.Attributes.class "row" ] 147 | (List.map (\emoji -> renderKey model emoji) EmojiConverter.supportedEmojis) 148 | ``` 149 | 150 | You will have to change the call site of `renderKeys` in `view` to match the new signature of the function, and you will have to change `renderKey` to consume a model. Once that's done, you can use `model.selectedKey` in place of `defaultKey` when checking whether a key is selected. The UI should now show you which key is selected, as you click on them! 151 | 152 | ### Changing our Cipher 153 | 154 | And now, for the final step — we need to change our translation key based on the value of `model.selectedKey`! When you're done, your application should work like this: 155 | 156 | ![Release 4 part 3](images/release-4-part-3.gif) 157 | 158 | You definitely know enough Elm to figure this out for yourself. However, if you get stuck, check out the working solution in `Part8.elm`. 159 | 160 | And with that, you've completed your first Elm application! 161 | -------------------------------------------------------------------------------- /Deployment and Next Steps.md: -------------------------------------------------------------------------------- 1 | # Deployment and Next Steps 2 | 3 | Since Elm code compiles to JavaScript, it can be very simple to host and deploy! There are lots of tools out there for hosting static websites composed of HTML, CSS, and JavaScript. For this tutorial, we'll use [Forge](https://getforge.com/), which is free for your first site! 4 | 5 | ## Goals 6 | 7 | - Deploy your Elm application for the world to see! 8 | - Learn about what's *not* covered in this tutorial, and how you can learn it 9 | 10 | ## Steps 11 | 12 | ### Deployment 13 | 14 | Deploying a static site on Forge is relatively simple. Just follow these steps: 15 | 16 | 1. **Create an account at https://getforge.com/.** This should be absolutely free! Once you have an account, follow instructions to create a new, blank site. 17 | 18 | 2. **Compile your Elm application.** This is important! Since Forge only understands compiled JavaScript, any changes you've made to your app after your last compilation will not be reflected. We have been using `elm reactor` for local development, but to create the final compiled application, we will need to use `elm make`. 19 | 20 | Run the following in your terminal: 21 | 22 | ```sh 23 | elm make Part1.elm 24 | ``` 25 | 26 | If you got stuck along the way and want to deploy the finished product, feel free to use the code from `Part8.elm` 27 | 28 | ```sh 29 | elm make Part8.elm 30 | ``` 31 | 32 | If you see a `Success` message, then everything is ready to publish! 33 | Elm has generated an `index.html` file from your elm file that contains the HTML and Javascript needed to render your app in a browser. 34 | 35 | 3. **Compress your project's folder into a .zip file.** The exact directions depend on your operating system, but it should be relatively straightforward on all platforms. You can skip all the elm files if you want. What you need is the `index.html` file and the stylesheets folder containing all the CSS. 36 | 37 | 4. **Upload your new .zip file to Forge**. In a few seconds, your website should be ready. 38 | 39 | You've just deployed your Elm application! 40 | 41 | ### Next Steps 42 | 43 | Thank you for taking the journey with us! Hopefully, you now have an understanding of what it's like to be an Elm developer, and what it feels like to work with a functional, compiled language. 44 | 45 | Before we finish, we should at least mention the things that this tutorial *didn't* cover, which will come up as you work more in Elm. Here's a non-exhaustive list: 46 | 47 | - **Types and Type Signatures.** Elm allows developers to constrain and document their code using these tools. They are highly encouraged — a well-factored type system makes code more descriptive and more fault-tolerant. Once you've learned more about types, some of the more esoteric Elm compiler messages will begin to make more sense as well. 48 | - **Currying and Piping**. Elm, like many functional languages, allows for functions to be partially applied. For example, if a function takes three arguments, you can give it one argument instead — and it will return a new function that takes two arguments! Elm developers use currying to reuse logic across their applications, and make their code more readable. In combination with the `|>` symbol (a.k.a. the pipe operator), currying can be a powerful tool. 49 | - **Decoders, Commands, and Subscriptions**. Sometimes, it is necessary for an Elm application to talk to the outside world. You may need to parse JSON sent from your server, make an HTTP request to an API, use a third-party JavaScript library, or manipulate a part of the UI not rendered by your `view` function. In those cases, you will often have to tell Elm how to parse data it is receiving by using the `Json.Decode` module. You may also need to listen for changes to the outside world with `Subscriptions` or to trigger events outside of Elm using `Commands`. 50 | 51 | There's so much to learn! Here are some resources to get you started: 52 | 53 | - [The official Elm guide](https://guide.elm-lang.org/) is a great resource, and gives a great rundown of some of the more complicated parts of the language. 54 | - [The elm-tutorial project](https://www.gitbook.com/book/sporto/elm-tutorial/details) is an incredibly in-depth tutorial on building a single-page app using Elm. 55 | - [The Elm Slack group](http://elmlang.herokuapp.com/) is a great way to get in touch with other developers learning and using Elm. We even have our own #elmbridge channel for you to connect with volunteers and fellow students. You can also check out http://elm-lang.org/community for more ways to get involved in the Elm community! 56 | 57 | But more than anything, the best way to learn Elm is to write more Elm! You now know more than enough to write complex and rich front-end applications in the language, and you'll pick up nuances of the language as you go. Good luck! 58 | -------------------------------------------------------------------------------- /Emojification.md: -------------------------------------------------------------------------------- 1 | # Emojification 2 | 3 | Up until now, this project has had a severe lack of emojis. In this lesson, we'll learn how to use an external module to translate text into emojis. Once we're done, it should look something like this: 4 | 5 | ![Release 2](images/release-2.png) 6 | 7 | 8 | Your code should currently look like the code in `Part2.elm`. You can either carry your code over from the last lesson, or start with the `Part2.elm` code. 9 | 10 | 11 | ## Goals 12 | 13 | - Access and utilize code in other modules 14 | - Be able to write helper functions 15 | - Understand where to place domain-specific code 16 | 17 | ## Steps 18 | 19 | ### Emoji Converter 20 | 21 | Spoiler alert: Emojis are complicated! And creating a [decoder-ring-style][secret decoder rings] translator between plain text and emojis is no small feat. Thankfully, one of your teammates has already written an emoji converter library for you to use, so you don't have to deal with that complexity! The code is already in your skeleton, in `EmojiConverter.elm`. 22 | 23 | [secret decoder rings]: https://en.wikipedia.org/wiki/Secret_decoder_ring 24 | 25 | (In the style of [secret decoder rings], `EmojiConverter` implements a simple [substitution cipher] that works by replacing all the letters in a message with the corresponding letter in a shifted alphabet. A shared, secret value (called the "key") determines how much shifting occurs, and the original message can later be recovered if the key is known.) 26 | 27 | [substitution cipher]: https://en.wikipedia.org/wiki/Substitution_cipher 28 | 29 | We'll need to use that code to convert the display text from plain text to emojis. But how do we pull that code in to our application code? 30 | 31 | That's where the Elm **module** system comes into play. Modules are simply collections of related functions. They are helpful for encapsulating behavior, and for providing clear boundaries between libraries. Let's take a look at the first few lines of `EmojiConverter.elm`: 32 | 33 | ```elm 34 | module EmojiConverter exposing (textToEmoji, emojiToText, supportedEmojis) 35 | ``` 36 | 37 | Like all Elm files, `EmojiConverter.elm` starts off by telling us what module it defines. In other files, we'll be able to call its functions by using the `EmojiConverter` namespace, in the same way we use the `Html` or `List` namespaces. 38 | 39 | This line also tells us the public API for the `EmojiConverter` module — regardless of what else is defined in this file, other files can, at most, access the three functions defined above. Of the three publicly accessible functions, the `textToEmoji` function seems like exactly what we are looking for. 40 | 41 | Let's import the module into our file, by adding this to the list of imports: 42 | 43 | ```elm 44 | import EmojiConverter 45 | ``` 46 | 47 | Now let's try using the `textToEmoji` function in our output text section. 48 | 49 | 50 | ```elm 51 | Html.p 52 | [ Html.Attributes.class "center output-text emoji-size" ] 53 | [ Html.text (EmojiConverter.textToEmoji model.currentText) ] 54 | ``` 55 | 56 | Let's try to compile this, and...uh oh! You should be getting the following error: 57 | 58 | ``` 59 | -- TYPE MISMATCH -------------------------------------------------- ././Part2.elm 60 | 61 | The argument to function `text` is causing a mismatch. 62 | 63 | 42| Html.text (EmojiConverter.textToEmoji model.currentText) 64 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 65 | Function `text` is expecting the argument to be: 66 | 67 | String 68 | 69 | But it is: 70 | 71 | String -> String 72 | ``` 73 | 74 | This error might seem like gibberish at first, but stay strong — Elm error messages are very good at telling you exactly what you need to know. In this case, it seems that, instead of passing `Html.text` a string to render, we are passing it a function that takes a string and returns a string. In Elm-land, that often means that you passed too few arguments into a function — if a function takes two arguments, and you only provided it one, it will return a **partially-applied function** that still needs one more argument! 75 | 76 | As you may have guessed, it seems like we're using `EmojiConverter.textToEmoji` incorrectly. Let's take a look at its definition in `EmojiConverter.elm` to figure out how to use it: 77 | 78 | ```elm 79 | type alias Key = 80 | String 81 | 82 | 83 | textToEmoji : Key -> String -> String 84 | textToEmoji key text = 85 | ... 86 | ``` 87 | 88 | Apparently, `textToEmoji` takes two arguments — a `Key` and a `String`, and returns a converted `String`. If we look a few lines above in the file, we see that `Key` is simply an alias for `String`. 89 | 90 | Of course! Like any good decoder ring, the `EmojiConverter` library uses a key to determine how to encode and decode messages. In this case, the key can be one of the emojis supported by the library. In order to turn text into emojis, we need to give it a key, as well as the text we hope to translate. 91 | 92 | Let's go back to our code, and hard-code an emoji as the translation key. 93 | 94 | 95 | ```elm 96 | Html.p 97 | [ Html.Attributes.class "center output-text emoji-size" ] 98 | [ Html.text (EmojiConverter.textToEmoji "😅" model.currentText) ] 99 | ``` 100 | 101 | Recompile the code, refresh your browser, and you should be in business! 102 | 103 | ### Our First Refactor 104 | 105 | While this solution works, I'd argue that the code has become harder to follow. Let's refactor! 106 | 107 | First off, let's extract a helper function for translating text. We can add a function that consumes a model and returns emojis: 108 | 109 | ```elm 110 | translateText model = 111 | EmojiConverter.textToEmoji "😅" model.currentText 112 | ``` 113 | 114 | Note: Type signatures are always optional in Elm, but they are highly encouraged — type signatures can be a good form of documentation, and they help the compiler make educated guesses about what went wrong when your code fails to recompile. Feel free to add your own to `translateText`! 115 | 116 | We can now use this function in our `view` function. 117 | 118 | ```elm 119 | Html.text (translateText model) 120 | ``` 121 | 122 | Recompile your code and make sure everything still works! 123 | 124 | ### Emoji Key 125 | 126 | Finally, let's pull out the hard-coded emoji key into something more readable. Let's create a `defaultKey` that returns the "😅" emoji. Switch out the reference to the "😅" emoji key with references to the new `defaultKey` function. 127 | 128 | Once you think you've got it, recompile to make sure it worked. If you get stuck, check out `Part3.elm` to see how we've implemented it, or flag down a TA or instructor. 129 | -------------------------------------------------------------------------------- /Getting Started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## Goals 4 | 5 | - Learn how to use the Elm REPL 6 | - Learn how to manipulate strings and numbers in Elm 7 | - Learn how to call functions 8 | - Learn where to find documentation about the Elm core library 9 | 10 | ## Steps 11 | 12 | ### REPL 13 | 14 | Launch the Elm REPL again by running the following command: 15 | 16 | ```sh 17 | elm repl 18 | ``` 19 | 20 | Now that you are in the REPL, you can write your first Elm code! Code you should try out in the REPL is written on lines starting with `>`. 21 | 22 | You can enter values, and Elm will tell you the type of the values. Try typing 23 | strings, numbers, and simple mathematical expressions: 24 | 25 | ``` 26 | > "Hello" 27 | "Hello" : String 28 | > 100 29 | 100 : number 30 | > 100.5 31 | 100.5 : Float 32 | > 50 / 7 33 | 7.142857142857143 : Float 34 | > 4 + 3 * (6 - 2) 35 | 16 : number 36 | ``` 37 | 38 | ### Variables 39 | 40 | You can define and refer to variables: 41 | 42 | ``` 43 | > x = 5 44 | 5 : number 45 | > y = 3 46 | 3 : number 47 | > x + y 48 | 8 : number 49 | ``` 50 | 51 | ### Strings 52 | 53 | You can concatenate strings using the `++` operator: 54 | 55 | 56 | ``` 57 | > "Hello" ++ ", World" 58 | "Hello, World" : String 59 | > name = "Anna" 60 | "Anna" : String 61 | > "Hello, " ++ name ++ "!" 62 | "Hello, Anna!" : String 63 | ``` 64 | 65 | ### Error Messages 66 | 67 | If you try to perform operations that don't make sense, Elm will try to tell you 68 | what's wrong: 69 | 70 | ``` 71 | > 10 + "Betsy" 72 | -- TYPE MISMATCH ----------------------------------------------------------- elm 73 | 74 | I cannot do addition with String values like this one: 75 | 76 | 7| 10 + "Betsy" 77 | ^^^^^^^ 78 | The (+) operator only works with Int and Float values. 79 | 80 | Hint: Switch to the (++) operator to append strings! 81 | ``` 82 | 83 | ### Functions 84 | 85 | To call a function in Elm, you simply type the name of the function and any parameters you want to pass, separated by spaces. No parentheses or commas are necessary. 86 | 87 | Here are some of the functions that are available by default in Elm: `max`, `min`, `sqrt`, `round`, `floor`. These are defined in the `Basics` module, which is always imported for you. You can read more about these and other functions in [its documentation](https://package.elm-lang.org/packages/elm/core/latest/Basics). 88 | 89 | ``` 90 | > max 9 1 91 | 9 : number 92 | > min 9 1 93 | 1 : number 94 | ``` 95 | 96 | To disambiguate order of operations, use parentheses. 97 | 98 | ``` 99 | > round (96 / 7) 100 | 14 : Int 101 | ``` 102 | 103 | ### The String Module 104 | 105 | Let's use some functions from the [`String` module](https://package.elm-lang.org/packages/elm/core/latest/String), which is one of the modules imported by default. 106 | 107 | Any function we want to use is namespaced under `String`: 108 | 109 | ``` 110 | > String.fromInt 10 ++ "!" 111 | "10!" : String 112 | > String.toUpper "Carey" 113 | "CAREY" : String 114 | > String.split "," "Apple,Apricot,Avocado,Banana,Blackberry" 115 | ["Apple","Apricot","Avocado","Banana","Blackberry"] : List String 116 | > String.join " -- " (String.split "," "Apple,Apricot,Avocado") 117 | "Apple -- Apricot -- Avocado" : String 118 | ``` 119 | 120 | ### Importing Modules 121 | 122 | If we want to use functions in a module that isn't available by default, we need to first import the module. 123 | 124 | ``` 125 | > import Bitwise 126 | ``` 127 | 128 | Functions in the `Bitwise` module are namespaced under `Bitwise`: 129 | 130 | ``` 131 | > Bitwise.and 7 2 132 | 2 : Int 133 | ``` 134 | 135 | ### Defining our own Functions 136 | 137 | Now let's define and use our own functions. 138 | 139 | We saw types before when we entered numbers and Strings into the console; functions have types too! 140 | 141 | When Elm shows the type of a function, it uses arrows `->` and colons `:`. We've seen the colons already, when Elm told us the types of values (e.g., `14 : Int`). 142 | 143 | The arrows point to the return value. Generally, the rightmost type is the return value, and the other types are the parameters to the function. For example, `Int -> String -> Float` is the type of a function that takes two parameters: the first is an Int, the second is a String, and the function returns a Float. 144 | 145 | ``` 146 | > multiplyByThree x = x * 3 147 | : number -> number 148 | > multiplyByThree 10 149 | 30 : number 150 | > add a b = a + b 151 | : number -> number -> number 152 | > add 10 4 153 | 14 : number 154 | > sayHello name = "Hello, " ++ name 155 | : String -> String 156 | > sayHello "Janice" 157 | "Hello, Janice" : String 158 | ``` 159 | 160 | > **Note:** If you get a `SHADOWING` error in the Elm REPL from previously defining `x`, you can type `:reset` to clear all previous imports and definitions. 161 | 162 | ### All done! 163 | 164 | Exit the Elm REPL by typing `:exit`, or `CTRL-D`. 165 | 166 | 167 | ### **Bonus!** 168 | 169 | You may have noticed that all the types began with a capital letter (`String`, `Float`, `Int`) except for one (`number`). In general, you can remember that all types are capital but variables are lowercase. `number` in these cases is a special variable type saying the value can act as an `Int` or a `Float`. An example: 170 | 171 | ``` 172 | > 1 + 1 173 | 1 : number 174 | > 1 + 1.0 175 | 2 : Float 176 | > floor 1.3 177 | 1 : Int 178 | > (floor 1.3) + 1.0 179 | -- TYPE MISMATCH ---------------------------------------------------------- REPL 180 | 181 | I need both sides of (+) to be the exact same type. Both Int or both Float. 182 | 183 | 4| (floor 1.3) + 1.0 184 | ^^^^^^^^^^^^^^^^^ 185 | But I see an Int on the left and a Float on the right. 186 | 187 | Use toFloat on the left (or round on the right) to make both sides match! 188 | 189 | Note: Read to learn why Elm does 190 | not implicitly convert Ints to Floats. 191 | ``` 192 | -------------------------------------------------------------------------------- /Introduction.md: -------------------------------------------------------------------------------- 1 | # Welcome to ElmBridge! 2 | 3 | Today, you're going to be learning Elm! So exciting! 4 | 5 | This tutorial is meant to be completed over the course of 5 hours, broken up into two chunks: 6 | - 2.5 hours on learning the basics of the language. 7 | - 3 hours building an Elm application from scratch — specifically, an emoji translator! 8 | 9 | By the end of this tutorial, you can expect to: 10 | 11 | - Know what Elm is, and why people use it. 12 | - Be familiar with the core data structures of the language. 13 | - Understand the architecture of Elm applications. 14 | - Be able to build interactive front-end applications in Elm. 15 | 16 | Thanks for coming! Let's get started. 17 | 18 | ## Setup 19 | 20 | We're going to be working with: 21 | 22 | - The latest version of the Elm language platform. 23 | - The terminal. 24 | - The text editor of your choice. 25 | - The web browser of your choice. 26 | 27 | At various points today, you'll also be asked to download code from GitHub, so internet access is also required. 28 | 29 | ### Installing Elm 30 | 31 | If you haven't done so already, follow [the official Elm guide](https://guide.elm-lang.org/install/elm.html) for installing the language platform. **Note for Windows users:** You might want to check our [handy Windows installation checklist](Special Installation Notes.md) to guide you through installation. 32 | 33 | You can verify everything is working by launching the Elm REPL ([What is a REPL?](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)) by running the following command in your terminal: 34 | 35 | ```sh 36 | elm repl 37 | ``` 38 | 39 | If you see the following message, you are ready to go! 40 | 41 | ``` 42 | ---- Elm 0.19.1 ---------------------------------------------------------------- 43 | Say :help for help and :exit to exit! More at 44 | -------------------------------------------------------------------------------- 45 | > 46 | ``` 47 | 48 | When you're done exploring, you can type `:exit` to get out. 49 | -------------------------------------------------------------------------------- /Making It Dynamic.md: -------------------------------------------------------------------------------- 1 | # Making it Dynamic 2 | 3 | In this lesson, we'll start making our emoji translator application! Eventually, the user will be able to "encrypt" messages using an emoji key, so that users can pass secret messages to each other. The final product will look something like this: 4 | 5 | 6 | ![Final Release](images/final-release.png) 7 | 8 | 9 | We need to walk before we run, though! First, we'll make an application that simply consumes text input provided by the user and displays it back to the page. 10 | 11 | ## Goals 12 | 13 | - Download and run the Skeleton Elmoji app. 14 | - Understand how to use Elm to make things change on the page. 15 | 16 | ## Steps 17 | 18 | ### Skeleton Setup 19 | 20 | Download the Skeleton app here: [https://github.com/elmbridge/elmoji-translator/releases/tag/release-8](https://github.com/elmbridge/elmoji-translator/releases/tag/release-8) and navigate to the downloaded folder. Start `elm reactor` in your terminal: 21 | 22 | ```sh 23 | elm reactor 24 | ``` 25 | 26 | And then go to [http://localhost:8000/Part1.elm](http://localhost:8000/Part1.elm) in your web browser. You should now have a fully functional Elm application running in your browser! It should look like this: 27 | 28 | ![Release 0](images/release-0.png) 29 | 30 | ### Updating the Model 31 | 32 | As you may have already noticed, however, this Elm application doesn't *do* anything. While you can enter text on the page, the application seems to ignore it. 33 | 34 | Our goal is to make the text that the user enters display back to them, like this: 35 | 36 | ![Release 1 in GIF form](images/release-1.gif) 37 | 38 | As we learned in [the last lesson](The Elm Architecture.html), every Elm application is built upon the `model`, `view`, and `update` triad. This app is no different – in order to implement this feature, we may need to change all three. 39 | 40 | First, let's take a look at the model: 41 | 42 | 43 | ```elm 44 | type alias Model = 45 | { currentText : String } 46 | ``` 47 | 48 | For this application, the model is a record that contains a `currentText` field that must be a string. That's perfect for our feature — as a user inputs text into the application, we'll update the `currentText` field, and reflect its new value to the UI. 49 | 50 | Let's verify that user text input is mapped to a `message`. First, let's look at our `view`. Let's zoom in on the code for input field. 51 | 52 | ```elm 53 | Html.input 54 | [ Html.Attributes.type_ "text" 55 | , Html.Attributes.class "center" 56 | , Html.Attributes.placeholder "Let's Translate!" 57 | , Html.Events.onInput SetCurrentText 58 | ] 59 | [] 60 | ``` 61 | 62 | It's worth taking a moment to discuss the API for the `Html` module. Almost all functions in this module have the same structure — they consume a list of HTML attributes (like `class`, `id`, `display`, etc.) as well as a list of child elements that are nested inside them. 63 | 64 | While the API is somewhat verbose, much of its contents are probably familiar to you. In this case, the code is generating an `input` element with a type of `text` and a class of `center` and some placeholder text. The resulting HTML looks like this: 65 | 66 | ```HTML 67 | 68 | ``` 69 | 70 | The final attribute comes from the `Html.Events` module, which describes which `update` message is sent when the element hears an `input` event. In this case, a `message` called `SetCurrentText` is sent, along with the element's current text. (`SetCurrentText String` is actually a tagged value — don't worry, we'll cover that in a future lesson!) 71 | 72 | This is great! When this `message` is sent, we can update our model with the new value. To do that, let's check out the `update` function: 73 | 74 | ```elm 75 | update msg model = 76 | case msg of 77 | SetCurrentText newText -> 78 | -- currently, this does nothing! 79 | model 80 | ``` 81 | 82 | Whenever an update message is sent, the `update` function consumes the sent message, the current model, and returns a new model to render. Currently, the `SetCurrentText` message is handled by this function, but the function simply returns the old model. 83 | 84 | Let's change that. Instead, we should have the update function update the model's `currentText` value whenever the message `SetCurrentText` is sent. Let's use record update syntax to do so: 85 | 86 | ```elm 87 | update msg model = 88 | case msg of 89 | SetCurrentText newText -> 90 | { model | currentText = newText } 91 | ``` 92 | 93 | Once you've made the change, go back to your web browser (to [http://localhost:8000/Part1.elm](http://localhost:8000/Part1.elm)), refresh the page, and `elm reactor` will recompile your application. 94 | 95 | If there are any errors from the compiler, `elm reactor` will show them to you. If there aren't any errors, you will see the updated application! 96 | 97 | ### Adding to the View 98 | 99 | Great, so we're now updating the model every time the user inputs text into our application. However, that's only half the battle — we still need to display the application's `currentText` back to the user! 100 | 101 | Thankfully, the skeleton already includes some styling for us. All we need to do is add code to our `view` function to display a paragraph tag right after our input tag, with the model's `currentText` as its value. The resulting HTML should look like this: 102 | 103 | 104 | ```HTML 105 |

It's happening!!!

106 | ``` 107 | 108 | In Elm, the code to generate that HTML would look like this: 109 | 110 | ```elm 111 | Html.p 112 | [ Html.Attributes.class "center output-text emoji-size" ] 113 | [ Html.text "It's happening!" ] 114 | ``` 115 | 116 | Note: `Html.text` is a special kind of `Html` function, that produces a plain-text node. In this case, we've built a paragraph element with a nested child element that is simply plain text. 117 | 118 | Insert the above code into the `view` function, as a list element after the div with a class of `.input-field`. Once you think you have it, recompile your code to see if it worked. 119 | 120 | ### Displaying Model Values 121 | 122 | We don't want the text to always be `It's happening!`, though. Instead, the text of the paragraph node should reflect the `currentText` of the current model. As the model changes, this paragraph node should change as well. 123 | 124 | You'll notice that the `view` function consumes a model. This model represents the current state of the application. So if we use record getter syntax, we can pull the `currentText` value out of the model and use it, like this: 125 | 126 | ```elm 127 | Html.p 128 | [ Html.Attributes.class "center output-text emoji-size" ] 129 | [ Html.text model.currentText ] 130 | ``` 131 | 132 | Recompile your code, and you should have a fully functional, dynamic application! 133 | -------------------------------------------------------------------------------- /Our First Full Feature.md: -------------------------------------------------------------------------------- 1 | # Our First Full Feature 2 | 3 | While emojis are great, a one-way translator from text to emojis is not particularly useful. We need to be able translate in both directions, like this: 4 | 5 | ![Release 3 part 2](images/release-3-part-2.gif) 6 | 7 | This is a complex feature — to implement it, we'll have to change every part of our application. Let's get started! 8 | 9 | Note: Your code should currently look like the code in `Part3.elm`. 10 | 11 | 12 | ## Goals 13 | 14 | - Write Elm view code using HTML as a template. 15 | - Add a new message for your application to consume. 16 | - Extend a type alias and fix subsequent compiler errors. 17 | - Use custom types to store information in more literate ways. 18 | 19 | ## Steps 20 | 21 | ### Adding User Interfaces 22 | 23 | First, let's start in our `view` function, where we'll map user actions in the UI to messages for our application to consume. 24 | 25 | ![Elm Architecture](images/elm-architecture-2.jpeg) 26 | 27 | In our case, we want to build a switch that will allow users to toggle between "Translate Text" and "Translate Emoji." We can use the [materialize.css](http://materializecss.com/) lever component to do the job. Between the text input and paragraph tag that our `view` function produces, we want the following markup to appear: 28 | 29 | ```html 30 |
31 | 37 |
38 | ``` 39 | 40 | Take a shot at translating the above HTML to Elm and update the `view` function! Some tips if you get stuck: 41 | - Most `Html` functions take two arguments: a list of attributes (produced by the `Html.Attributes` and the `Html.Events` modules), followed by a list of child elements. In this case, our `div` has one child (the `label` element), and the `label` element has four children! 42 | - To render plain text elements, use `Html.text`. It simply takes a string as its argument. 43 | - Compile early and often! The Elm compiler will nudge you in the right direction if your syntax is off. 44 | 45 | If you get stuck, flag down a TA or instructor to help you through it! 46 | 47 | ### Wiring in User Actions 48 | 49 | 50 | Now that we have a working lever, we need to wire it up to our `update` function! Let's see what messages our `update` function can currently consume. 51 | 52 | ```elm 53 | type Msg 54 | = SetCurrentText String 55 | ``` 56 | 57 | This is a **custom type declaration**. It defines a new type that we can use in our program, along with all its possible **values**. Specifically, the above code defines the `Msg` type, and declares that it has exactly one possible value – `SetCurrentText String`, which is triggered whenever the user inputs text into the translation box. 58 | 59 | Even though custom types and values look similar, they operate in different parts of our program. A custom type is just another type, like `String` or `List`, which we can use in our type signatures. Values, on the other hand, can only be used in our implementation code. For instance: 60 | 61 | We can use the `case pet of` syntax to **Pattern Match** to branch our code based on which **value** of the **type** we receive. 62 | 63 | 64 | ```elm 65 | type Pet = Cat | Dog | Rabbit | Turtle 66 | -- this defines the custom type `Pet` with four possible values 67 | 68 | makeSound : Pet -> String 69 | -- custom types, like `Pet`, can only be used in type signatures. 70 | makeSound pet = 71 | -- values like `Cat` and `Dog` can't be used in type signatures, 72 | -- but they can be used inside functions. 73 | if pet == Cat then 74 | "meow" 75 | else if pet == Dog then 76 | "woof" 77 | else 78 | "..silence.." 79 | ``` 80 | 81 | You may be wondering what the `String` means after `SetCurrentText`. This is the syntax for declaring a **tagged value**, which we will learn more about in a future lesson. For now, know that a use of `SetCurrentText` must also include a string to be considered a valid message: 82 | 83 | ```elm 84 | -- valid value for `Msg` 85 | SetCurrentText "some awesome text" 86 | 87 | -- invalid values for `Msg` 88 | SetCurrentText 12345 89 | SetCurrentText "string one" "string two" 90 | ``` 91 | 92 | Back to the task at hand: in order for the user to toggle between translation modes, we need a new message to our system. Let's add another possible value to our `Msg` custom type declaration: 93 | 94 | ```elm 95 | type Msg 96 | = SetCurrentText String 97 | | ToggleDirection 98 | ``` 99 | 100 | We'll make sure our lever triggers the new `ToggleDirection` `Msg` whenever the user clicks it! 101 | 102 | If you try to compile now, you'll notice that the compiler realizes something is wrong: 103 | 104 | ``` 105 | -- MISSING PATTERNS --------------------------------------------- ././Part3.elm 106 | 107 | This `case` does not have branches for all possibilities. 108 | 109 | 13|> case msg of 110 | 14|> SetCurrentText newText -> 111 | 15|> { model | currentText = newText } 112 | 113 | You need to account for the following values: 114 | 115 | ToggleDirection 116 | 117 | Add a branch to cover this pattern! 118 | ``` 119 | 120 | Helpfully, the Elm compiler enforces **case exhaustiveness** – since our `update` function doesn't handle our new `ToggleDirection` value, the compiler realizes that triggering that `Msg` will break our program. Let's fix it by adding another clause to our case expression, this time matching on the pattern `ToggleDirection`. 121 | 122 | ```elm 123 | ToggleDirection -> 124 | -- currently, this does nothing! 125 | model 126 | ``` 127 | 128 | Finally, let's make our view actually trigger our new `Msg` value. Add the following attribute to the lever's HTML in our `view`: 129 | 130 | ```elm 131 | Html.Events.onClick ToggleDirection 132 | ``` 133 | 134 | Compile, and make sure nothing is broken. 135 | 136 | ### Expanding the Model 137 | 138 | Note: Your code should currently look like the code in `Part4.elm`. 139 | 140 | When our `update` function receives a `ToggleDirection` message and the current `model`, it should return a new, changed `model`: 141 | 142 | ![Elm Architecture](images/elm-architecture-3.jpeg) 143 | 144 | Eventually, our `view` function will reflect those changes to the user. 145 | 146 | First things first — we need to find a way to describe the current direction in our `Model`. Let's take a look at our description of a `model`: 147 | 148 | ```elm 149 | type alias Model = 150 | { currentText : String } 151 | ``` 152 | 153 | The code above declares that `Model` is a **type alias** for a specific record structure. A type alias works a lot like a custom type declaration — it also produces a new type that we can use in our program. In this case, the `Model` type is just another name for "a record with a single field called `currentText`." 154 | 155 | Type aliases are helpful for making type signatures simple and readable. For example, you can see that `Model` is used in the type signature for `init`: 156 | 157 | ```elm 158 | init : Model 159 | init = 160 | { currentText = "" } 161 | ``` 162 | 163 | Without this type alias, we would've had to write: 164 | 165 | ```elm 166 | init : { currentText : String } 167 | init = 168 | { currentText = "" } 169 | ``` 170 | 171 | Now back to business: we'll need to extend this type alias declaration, by adding another field to the record: 172 | 173 | ```elm 174 | type alias Model = 175 | { currentText : String 176 | , direction: -- ???? 177 | } 178 | ``` 179 | 180 | But wait, what *is* direction? In other languages, we might describe the current direction as a string, with possible values "emoji-to-text" and "text-to-emoji". We could also model it as a boolean value, perhaps renaming it `translatingTextToEmoji`. 181 | 182 | In Elm, we have a more powerful tool at our disposal: custom types! We can create a custom type that enumerates all possible values for a direction, which keeps the code readable and fault tolerant. 183 | 184 | Let's create a custom `Direction` type with two possible values: 185 | 186 | ```elm 187 | type Direction 188 | = TextToEmoji 189 | | EmojiToText 190 | ``` 191 | 192 | Now, we can use that new type in our `Model` type alias: 193 | 194 | ```elm 195 | type alias Model = 196 | { currentText : String 197 | , direction : Direction 198 | } 199 | ``` 200 | 201 | If you try to compile right now, you should get some errors: Our code wasn't written to handle a `Model` with a `direction` field! See if you can figure out what's wrong and fix it. 202 | 203 | Now that `Model` can describe the current translation direction, we need to change the `update` function to correctly set the `direction` field when it consumes the `ToggleDirection` message. 204 | 205 | Inside `update`, we know two things – (1) the current `Msg` we received and (2) the current state of the `model`, which includes its current `direction`. That's all the information we need – if the current `model.direction` is `EmojiToText`, then `update` should return a `model` with a `direction` value of `TextToEmoji`, and vice versa. 206 | 207 | We can use `case ___ of` syntax to **Pattern Match** on the different values of the type `Direction`. This allows us to branch our code to handle the different conditions. 208 | 209 | ```elm 210 | ToggleDirection -> 211 | case model.direction of 212 | TextToEmoji -> 213 | -- return a model with a direction value of `EmojiToText` 214 | 215 | EmojiToText -> 216 | -- return a model with a direction value of `TextToEmoji` 217 | ``` 218 | 219 | Implement the toggle direction functionality in your `update`. Make sure your code compiles! 220 | 221 | ### Displaying Model Values 222 | 223 | Now to the final part of the feature: We need to reflect changes to the model in the UI! 224 | 225 | ![Elm Architecture](images/elm-architecture-4.jpeg) 226 | 227 | In our case, we need to change our translation logic depending on the current `model.direction`. Let's take a look at our `translateText` function: 228 | 229 | ```elm 230 | translateText model = 231 | EmojiConverter.textToEmoji defaultKey model.currentText 232 | ``` 233 | 234 | Here's what we know: 235 | 236 | - The `EmojiConverter` exposes a `emojiToText` function that we can use if the current direction is `EmojiToText`. 237 | - The `translateText` function also has access to the current `model`, which stores the current `Direction`. 238 | - In a similar manner to `update` function, we can use a case expression to choose which `EmojiConverter` function to use. 239 | 240 | That should be enough to get you started! If you are unsure, try to compile, and follow compiler errors until everything is fixed. If you get stuck, you can see the completed version of this feature in `Part4.elm`, or flag down a TA or instructor. 241 | 242 | Congratulations, you've completed your first full feature in Elm! 243 | -------------------------------------------------------------------------------- /PUBLISHING.md: -------------------------------------------------------------------------------- 1 | ## How to publish this gitbook 2 | 3 | You will need NodeJS 10 or later. 4 | 5 | Make sure you have the project dependencies installed: 6 | 7 | ```sh 8 | npm install 9 | ``` 10 | 11 | Rebuild the gitbook, commit, and push: 12 | 13 | ```sh 14 | npm run-script build 15 | git add docs 16 | git commit 17 | git push 18 | ``` 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ElmBridge Curriculum 2 | 3 | Curriculum for the ElmBridge workshop. Hosted here: https://elmbridge.github.io/curriculum/ 4 | 5 | ## Development Setup 6 | 7 | This curriculum is written using [GitBook](https://github.com/GitbookIO/gitbook). To run locally, run: 8 | 9 | ```sh 10 | npm install gitbook-cli -g 11 | gitbook serve 12 | ``` 13 | 14 | ## Contributing 15 | 16 | Check out [CONTRIBUTING.md](CONTRIBUTING.md) to see how to join our [list of contributors](https://github.com/elmbridge/curriculum/contributors)! 17 | 18 | Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. 19 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | ## Learning the Basics 4 | 5 | * [Introduction](Introduction.md) 6 | * [Intermission: Why Elm?](Why Elm.md) 7 | * [Getting Started](Getting Started.md) 8 | * [The Basic Building Blocks of Elm](The Basic Building Blocks of Elm.md) 9 | * [Intermission: Building User Interfaces](Building User Interfaces.md) 10 | * [Special Installation Notes](Special Installation Notes.md) 11 | 12 | ## Making an Emoji Translator 13 | 14 | * [The Elm Architecture](The Elm Architecture.md) 15 | * [Making It Dynamic](Making It Dynamic.md) 16 | * [Emojification!!!](Emojification.md) 17 | * [Our First Full Feature](Our First Full Feature.md) 18 | * [Choose Your Own Emoji](Choose Your Own Emoji.md) 19 | * [Deployment and Next Steps](Deployment and Next Steps.md) 20 | -------------------------------------------------------------------------------- /Special Installation Notes.md: -------------------------------------------------------------------------------- 1 | # Installation Notes for Windows Users 2 | 3 | If you are running Windows on your machine, you might find this checklist of activities helpful: 4 | 5 | * Before running the Windows installer, shut down your command prompt (`cmd.exe`) and/or Cygwin applications. 6 | * After running the installer, you will also need to install `nodejs`, from the [Node JS](https://nodejs.org/) website. You can choose the option that says "Recommended For Most Users." 7 | * After completing the NodeJS installation, open a new command prompt (or Cygwin) window and type in `elm repl` at the command line. If you _don't_ see this message, you are good to go! If not, consult an instructor or TA: 8 | 9 | ``` 10 | $ elm repl 11 | The REPL relies on node.js to execute JavaScript code outside the browser. 12 | I could not find executable 'node' or 'nodejs' on your computer though! 13 | 14 | You can install node.js from . If it is already installed 15 | but has a different name, use the --interpreter flag. 16 | ---- Elm 0.19.1 ---------------------------------------------------------------- 17 | Say :help for help and :exit to exit! More at 18 | -------------------------------------------------------------------------------- 19 | ``` 20 | -------------------------------------------------------------------------------- /The Basic Building Blocks of Elm.md: -------------------------------------------------------------------------------- 1 | # The Basic Building Blocks of Elm 2 | 3 | In this lesson, you'll learn how to work with the basic data structures of Elm. You'll be working with a [custom application that we've written](https://github.com/elmbridge/elm-basics) where you'll be asked to implement some basic transformations. 4 | 5 | ## Goals 6 | 7 | - Practice defining and using functions in Elm 8 | - Learn how to use strings, lists, and records. 9 | 10 | ## Steps 11 | 12 | ### Elm Basics Exercises 13 | 14 | Get a copy of the [elm-basics](https://github.com/elmbridge/elm-basics) code: 15 | 16 | ```bash 17 | git clone https://github.com/elmbridge/elm-basics.git 18 | cd elm-basics 19 | ``` 20 | 21 | Start Elm Reactor to build the project: 22 | 23 | ```bash 24 | elm reactor 25 | ``` 26 | 27 | Now open [http://localhost:8000/](http://localhost:8000/) in your web browser, and go to `Main.elm`. 28 | 29 | You should see something like this: 30 | 31 | ![Intro to Elm initial](images/intro-to-elm-initial.png) 32 | 33 | This application contains a series of unimplemented functions designed to teach you about the basic data structures of Elm. All the functions that you need to implement live in `Main.elm`. By default, you'll see some work-in-progress emojis, and when you successfully implement a function, you'll see a heart. But if you'd like to customize the emojis or the colors, you can do so in `Style.elm`. When you're done poking around, go back to `Main.elm` to start the exercises. 34 | 35 | ### Working with Strings 36 | 37 | Like many languages, Elm has a **String** data type to store text. In this step, you should complete all the unimplemented functions for the string section of the application. Once you're ready to get started, navigate to the `sayHello` function. 38 | 39 | ```elm 40 | sayHello friendsName = 41 | "TODO: implement me" 42 | ``` 43 | 44 | You'll notice that this assertion is currently failing in your browser. Your job is to make it pass! Once you have a guess, change and save the code in `Main.elm` — refresh your browser and you'll learn if you implemented it correctly. If you've made a syntax error, the page will display an error message that will tell you what to fix. 45 | 46 | You should have enough to get started! A few tips: 47 | 48 | - Remember, the `++` operator is used to add strings together. 49 | - If you want to transform a string in some way, you should use [the `String` module](https://package.elm-lang.org/packages/elm/core/latest/String) to do so. For instance, if you wanted to get the last letter of a string, you might use the `String.right` function like this: 50 | 51 | ```elm 52 | String.right 1 someStringVariable 53 | ``` 54 | 55 | If you get stuck, flag down an instructor! They are here to help. 56 | 57 | ### Conditional Logic 58 | 59 | Elm has all the traditional structures for conditional logic, including `if`, `else`, `case`, and the `==`, `<`, `>`, `&&`, and `||` operators. (Note: the "not equal" operator is a non-standard `/=`.) Here's what that looks like in practice: 60 | 61 | ```elm 62 | numberToWords num = 63 | if num == 0 then 64 | "zero" 65 | else if num == 1 then 66 | "one" 67 | else 68 | "a number larger than one" 69 | 70 | --here's the same function, implemented with a case expression 71 | numberToWords num = 72 | case num of 73 | 0 -> "zero" 74 | 1 -> "one" 75 | _ -> "a number larger than one" 76 | ``` 77 | 78 | Elm enforces that all branches of an `if` or `case` expression must return the same type of value — so all `if` expressions must include a corresponding `else` clause, and all possible values must be accounted for in a `case` expression. 79 | 80 | Take a shot at completing the assertions for `if` expressions! 81 | 82 | ### Lists 83 | 84 | In Elm, you can use a **List** to store a collection of elements. Unlike dynamic languages, though, Elm lists are **typed** — every element in a list must be the same kind of thing. You can't have a list that stores both strings and numbers, for example. 85 | 86 | For this step, you should complete all the `List` assertions. Some tips: 87 | 88 | - In another language, you might solve these assertions by creating an empty list, and incrementally adding things to it as you iterate over the passed-in list. That's not possible in Elm — once you've declared a variable in Elm, you can never change it. You will have to use more complicated `List` functions, like `List.map`. 89 | - Most common functions that operate on lists live in [the `List` module](https://package.elm-lang.org/packages/elm/core/latest/List). The order of arguments to these functions may be disorienting — in functional programming languages, the convention is to have the data you are operating on be the *last* argument to a function, not the first. 90 | - Many of these functions consume a function. To use them, you can use an anonymous function, like this: 91 | 92 | ```elm 93 | List.map (\list_element -> list_element * 2) [1,2,3] 94 | ``` 95 | 96 | Note: the value after the `\` is the input to the function, while whatever is after the `->` sign is the output. 97 | 98 | Good luck! 99 | 100 | ### Records 101 | 102 | Just like JavaScript has objects to store name-value pairs, Elm has **records**. Unlike JavaScript's objects, though, Elm treats the structure of a record as immutable. Once a record has been defined, you cannot add or remove a field, or change the type of a field. 103 | 104 | Unlike strings and lists, records are a special data type that has a special syntax for getting and setting values. You can create a record like this: 105 | 106 | ```elm 107 | aNewRecordAppears = 108 | { fieldOne = "value", anotherField = 1111 } 109 | ``` 110 | 111 | To get the value out of a record, you can use `.` syntax: 112 | 113 | ```elm 114 | aNewRecordAppears.fieldOne 115 | ``` 116 | 117 | And to update a specific field, you use `|` syntax: 118 | 119 | ```elm 120 | { aNewRecordAppears | fieldOne = "a new value for the specified field" } 121 | ``` 122 | 123 | There is no `Record` module — all record functions are implemented through special syntax. You can learn more about the type in [the official Elm guide on records](http://elm-lang.org/docs/records). 124 | 125 | Once you've finished this step, feel free to complete some of the bonus assertions, or look at some of the implementation code in the `main` function. Next up, we'll talk about how to build applications in Elm. 126 | -------------------------------------------------------------------------------- /The Elm Architecture.md: -------------------------------------------------------------------------------- 1 | # The Elm Architecture 2 | 3 | ![The Elm Architecture](images/elm-architecture-1.jpeg) 4 | 5 | In this lesson, we'll run a simple Elm application and learn how it all fits together! 6 | 7 | ## Goals 8 | 9 | - Run your first Elm application. 10 | - Learn how every part of an Elm application works. 11 | 12 | ## Steps 13 | 14 | ### The Development Environment 15 | 16 | Download the skeleton app here: [https://github.com/elmbridge/elmoji-translator/releases/tag/release-8](https://github.com/elmbridge/elmoji-translator/releases/tag/release-8), navigate to the downloaded folder in your terminal, and run the following command: 17 | 18 | ```sh 19 | elm make HelloWorld.elm 20 | ``` 21 | 22 | `elm make` **compiles** your application — it turns your Elm code into JavaScript code that your browser can understand. The above command uses the `main` function in `HelloWorld.elm` as the **entry point** to your application. It compiles that file, along with any files it references, and creates `index.html`, which is a simple HTML page that includes all the compiled JavaScript for your application. (It's also possible to compile into a JavaScript file if you want to use your own HTML file.) 23 | 24 | Elm comes with another tool that makes it easy to quickly develop your application: `elm reactor` starts a local development server that will automatically compile your Elm code (and, if there are any errors, give you helpful error messages on what you need to fix). Let's start it up: 25 | 26 | ```sh 27 | elm reactor 28 | ``` 29 | 30 | Now that `elm reactor` is running, you can go to [http://localhost:8000/HelloWorld.elm](http://localhost:8000/HelloWorld.elm) in your web browser to see your compiled application. Whenever you refresh the page, `elm reactor` will recompile your code and show you the result. 31 | 32 | You should now have a fully functional Elm application, that looks like this: 33 | 34 | ![Hello World](images/hello-world.gif) 35 | 36 | ### Elm Wiring 37 | 38 | So how does any of this work? Let's take a look at `HelloWorld.elm`. 39 | 40 | 41 | ```elm 42 | main = 43 | Browser.sandbox 44 | { init = init 45 | , view = view 46 | , update = update 47 | } 48 | ``` 49 | 50 | The `main` function describes the initialization logic for your Elm application. Every entry point file requires a `main` function. It requires a record with three basic pieces of every Elm application: 51 | 52 | - **The initial `model`**, which describes the state of our application. Our application will create new `model` values as the user interacts with it. 53 | - **The `view` function**, which is responsible for converting a `model` into HTML for Elm to render to the UI. It also maps all possible user actions to the appropriate `messages`. 54 | - **The `update` function**, which is responsible for updating the application's state based on triggered `messages`. It consumes the current application state (the `model`) and a single `message`, and returns a new `model` that describes the application's new state. 55 | 56 | But wait, didn't we say that a value can never change in Elm? What's exactly changing here? Good question — let's come back to it when we finish looking at the rest of the pieces. 57 | 58 | ![The Elm Architecture](images/elm-architecture-4.jpeg) 59 | 60 | The initial `model` (passed to sandbox via the `init` argument), with the `view`, and `update` functions, together form a **triad** that is required in every Elm application. For more complicated programs, a few other pieces are required — however, for this session, we'll focus on the core triad. 61 | 62 | ### On Initializing the Application 63 | 64 | Let's investigate each of these pieces one by one. First, let's check out the `init` function to see what the initial state of our application is. 65 | 66 | ```elm 67 | init = 68 | { buttonLabel = "hello world!" } 69 | ``` 70 | 71 | As you can see, `init` takes no arguments and returns a **record**, which is a name-value map with predefined structure. This particular record has a single field called `buttonLabel`, with a value of `"hello world!"`. This record represents the state of our application — it is the only thing in our entire application that can change over time. As the `model` changes, the UI should change to reflect its new value. 72 | 73 | It is convention to write a **type alias** to describe your application's state, and call it `Model`, like this: 74 | 75 | ```elm 76 | type alias Model = 77 | { buttonLabel : String } 78 | ``` 79 | 80 | We'll learn more about type aliases later in this tutorial, but for now, it's worth noting that it is simply used for convenience. It's an easy way for other Elm developers to tell exactly what information is stored in your `model`. 81 | 82 | ### On the Application View 83 | 84 | Now, let's take a look at our `view`, which is responsible for converting our `model` into HTML. Any changes between the produced HTML and the last rendered HTML will be rendered to the UI by Elm. 85 | 86 | ```elm 87 | view model = 88 | Html.div 89 | [ Html.Attributes.class "skeleton-elm-project" ] 90 | [ Html.node "link" 91 | [ Html.Attributes.rel "stylesheet" 92 | , Html.Attributes.href "stylesheets/main.css" 93 | ] 94 | [] 95 | , Html.div 96 | [ Html.Attributes.class "waves-effect waves-light btn-large" 97 | , Html.Events.onClick ChangeText 98 | ] 99 | [ Html.text model.buttonLabel ] 100 | ] 101 | ``` 102 | 103 | If you have worked with Angular, React, Ember, or another front-end framework, this part may be somewhat familiar to you. Every front-end framework has its own syntax for describing HTML, and Elm is no different. The resulting HTML looks like this: 104 | 105 | 106 | ```html 107 |
108 | 109 |
110 | hello world! 111 |
112 |
113 | ``` 114 | 115 | The `view` function uses the `Html` module to render HTML nodes. `Html.div` is a function that consumes two lists — a list of attributes, and a list of children. Our first div has one attribute (a `class` attribute that provides a class for styling) and it has two children: a link tag and another div. 116 | 117 | The link tag has two attributes (`rel`, and `href`) and no children. 118 | 119 | The child div has two attributes (another `class` attribute and an `onClick` handler). It also has one child: a plain-text HTML node, rendered using the `Html.text` function. 120 | 121 | What does `model.buttonLabel` mean in the context of our `view` function? This function is responsible for rendering the application's current state as HTML — and that state is stored in the `model` variable. Every time the application's state changes, this function will be called with a new `model` value — if `model.buttonLabel` is different than the last time this function was called, the UI will update with new text! 122 | 123 | You may wonder how this function can be performant — every time the `model` changes, even slightly, our `view` function has to recalculate every HTML element in your application. If you have hundreds of elements, and dozens of potential changes, how does Elm not collapse under the load? 124 | 125 | Good news: Elm is optimized to be performant under pressure. Using a virtual DOM, Elm ensures that the entire page doesn't have to re-render every time the application's state changes. React and Ember use similar strategies, although Elm has more tricks up its sleeve because of its functional nature. The exact details are out of scope for this tutorial, however — just know that you generally don't have to worry about HTML rendering performance when writing Elm. 126 | 127 | Before we move on, let's take another look at that `onClick` handler: 128 | 129 | ```elm 130 | Html.Events.onClick ChangeText 131 | ``` 132 | 133 | This line of code is key to our application — it maps a potential user action to a `message` that can cause our application to change. In this case, clicking on our div causes the `ChangeText` message to be sent. 134 | 135 | There are lots of potential user actions defined in the [`Html.Events`](https://package.elm-lang.org/packages/elm/html/latest/Html-Events) module — as with JavaScript, you can track when a user clicks an element, presses a key, or submits a form. If you don't map these user actions to a `message`, however, they will be ignored by your application. 136 | 137 | 138 | ### On the Application Model 139 | 140 | Let's return to the question of change we saved for later when we were looking at `init`: What does it mean when we say `model` changes? Let's take another look at our diagram: 141 | 142 | ![The Elm Architecture](images/elm-architecture-4.jpeg) 143 | 144 | Even though we said `model`, `view`, and `update` together form a triad, you can see that `model` is not a first-class circle in this diagram. In fact, `model` is just passed around as an argument to `view` and `update` functions. 145 | 146 | This `model` argument may be different each time those functions are called. So you see, changing the `model` doesn't mean modifying its value, but pointing the `model` argument at a different value. 147 | 148 | The `update` function is responsible for this transformation. When a `message` is triggered by the UI, `update` consumes the specified `message` and the current `model`, and returns a new `model` for the `view` function to render. 149 | 150 | ```elm 151 | update msg model = 152 | case msg of 153 | ChangeText -> 154 | if model.buttonLabel == "hello world!" then 155 | { model | buttonLabel = "goodbye world!" } 156 | else 157 | { model | buttonLabel = "hello world!" } 158 | ``` 159 | 160 | We just saw that the `ChangeText` message can be triggered when the user clicks our button. When a message is sent, this function applies some conditional logic: 161 | 162 | - If the current application's state (stored in the `model` variable) has a `.buttonLabel` value of `"hello world!"`, the function returns a new `model` with a value of `"goodbye world!"`. 163 | - If not, then `model.buttonLabel` must be *already* equal to `"goodbye world!"`, so the function returns a `model` with a `buttonLabel` value of `"hello world!"`. 164 | 165 | Either way, the new `model` will be converted to HTML, and any changes will be rendered to the UI by our `view` function. 166 | 167 | Wait, there's only one possible action tracked in our UI — why have a case expression at all? This is convention in Elm-land. As you build an application, there will be more and more kinds of `messages` to which your `update` function will have to react. Consequently, this case expression will get longer and longer to accommodate the new messages. 168 | 169 | It's worth taking a look at the `Msg` type: 170 | 171 | ```elm 172 | type Msg 173 | = ChangeText 174 | ``` 175 | 176 | and later on in the `update` function's type signature: 177 | 178 | ```elm 179 | update : Msg -> Model -> Model 180 | ``` 181 | 182 | This is an example of a **custom type** declaration in Elm. We'll go further into the details in a future lesson, but for right now, you can think of this as a way to model `messages` in our application. The above code declares two things (reading from that type signature first): 183 | 184 | * the `update` function takes two arguments, the first is a `Msg`, and the second is a `Model`. 185 | * any value that is of `Msg` type will be exactly one of the values specified in the `type` definition (in this case, there is only one acceptable value which is `ChangeText`.) If we wanted to add another possible value for `message`, we would have to change this declaration of the custom type `Msg` to include the new possibility. 186 | 187 | Elm doesn't force us to use a custom type called `Msg` for `messages` — we could just as easily model `messages` as strings, numbers, or records. It is convention, however, to model data using custom types whenever appropriate. Don't worry if you are somewhat lost — custom types are one of the more difficult concepts to learn as a beginner to Elm, and it's fine for now if they are simply magic words you know you can recite. 188 | 189 | To review one more time, a `Browser.sandbox` needs: 190 | * An initial version of our model (`init : Model`) 191 | * A description of how the model is turned into html (`view : Model -> Html`) 192 | * A function that given a Message and an old model tells us what the new model should look like (`update : Msg -> Model -> Model`) 193 | 194 | Indeed, if we look at the type signature of `Browser.sandbox`, we see that it takes this type of record and returns a program! 195 | 196 | ```elm 197 | sandbox : 198 | { init: model 199 | , view : model -> Html msg 200 | , update : msg -> model -> model 201 | } -> Program () model msg 202 | ``` 203 | 204 | Now that we're done touring the basics of the Elm triad, let's get building! 205 | -------------------------------------------------------------------------------- /Why Elm.md: -------------------------------------------------------------------------------- 1 | # Why Elm? 2 | 3 | In this section, we'll talk about the advantages — and disadvantages! — of using Elm to make front-end applications. 4 | 5 | 6 | ## First off, what is Elm? 7 | 8 | **Elm is a language for building complex web interfaces.** Much like CoffeeScript or JSX, Elm compiles to JavaScript; the Elm Platform takes your Elm code and turns it into JavaScript that any web browser can understand. In many ways, it operates similarly to front-end JavaScript frameworks like React, Angular, and Ember. 9 | 10 | Elm is more powerful than a frontend framework though: **it is a distinct programming language that can strictly enforce its opinions.** 11 | 12 | You may have heard words like **functional** and **statically typed** used to describe Elm. **Functional** means that a function always returns the same value for a given input. **Statically typed** means that the compiler checks that the code makes sense before you can use it in production: it won't let you ask for the length of an integer or try to multiply a string and a float. If your code compiles, that means that you're doing operations that make sense. If you give a function the wrong *type* of value (a `String` rather than an `Int`) the Elm compiler will figure it out immediately and give you a hint. 13 | 14 | 15 | ## So why Elm? 16 | 17 | One compelling reason is that **entire categories of bugs are impossible in Elm**, like: 18 | - "I expected this function to return a number, but it returned nil instead." 19 | - "Sometimes when I call this function it fails, even though I gave it the exact same input." 20 | - "Something is changing the value of my variable, and I'm not sure where it's happening." 21 | 22 | These errors cannot happen in Elm because of the language's strictness. Indeed, the rules mean that **Elm applications can never accidentally crash.** There are no runtime exceptions — no `undefined is not a function` or `undefined method for nil:NilClass`. Consequently, Elm developers spend much less time in the Developer Tools console than JavaScript developers, and less time in the browser in general. 23 | 24 | Because Elm requires all programs to follow its rules, **the Elm compiler will tell you exactly what's wrong with your program if you make a mistake**. It will tell you if you made an `if/else` expression return two different kinds of values, if you are calling a function with too many arguments, or if you are referencing a variable that does not exist. 25 | 26 | Developing with Elm often feels like an ongoing conversation with the compiler, gently pushing you toward writing better code. Often, you don't even need to look at your application in the browser while implementing a feature — you can trust the compiler to tell you if you are going in the right direction. 27 | 28 | 29 | ## Why not Elm? 30 | 31 | If you are coming from an object-oriented, dynamically typed language like Ruby or JavaScript, Elm can feel like a straightjacket. There are no `each` or `for` loops in Elm — instead, you use functions like `map`, `filter`, and `fold`. If you need to change a value deep inside a nested data structure, you can't simply read the value and mutate it, as you could in other languages. 32 | 33 | Working with widget libraries can be hard due to conflicting paradigms. If you need to use [D3.js](https://d3js.org/) or [Three.js](https://threejs.org/), you may have difficulties using Elm. 34 | 35 | ## Learning Elm 36 | 37 | You may be asking yourself why you should choose to learn a language that's so different from other tools in the ecosystem--especially if you aren't looking to work with it professionally yet. **We'd argue that the experience of working in an alien environment, even if it's just for a little while, can be fruitful.** You are probably not used to dealing with the constraints of purity, immutability, and static typing, which together make up the basic tenets of **functional programming**. These constraints will push you to solve problems in creative ways, and hopefully you will learn strategies that you can employ in other languages. 38 | 39 | We make no claim that functional programming is *better* than other types of programming, but it definitely is *different*. We strongly believe that learning a new way to think about programming will make you a better developer, in the same way that learning a foreign language makes you appreciate the architecture and beauty of the languages you already know. 40 | 41 | So let's get started! 42 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "structure": { 3 | "readme": "Introduction.md" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /docs/Building User Interfaces.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Intermission: Building User Interfaces · GitBook 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 | 78 | 79 | 80 | 273 | 274 | 275 |
276 | 277 |
278 | 279 |
280 | 281 | 282 | 283 | 292 | 293 | 294 | 295 | 296 |
297 |
298 | 299 |
300 |
301 | 302 |
303 | 304 |

Building User Interfaces

305 |

Congratulations on completing the last lesson! If this was your first experience with a functional programming language, it may have been difficult. The jump from object oriented programming to functional programming can be scary, and Elm's syntax can be confusing for recent transplants. This initial struggle is totally normal, and will eventually dissipate as you continue to work with Elm.

306 |

As we discussed earlier, Elm is distinctive in its enforcement of purity, immutability and static typing. That's all well and good for the toy problems that you just solved, but you may be wondering how Elm can model a complex and interactive UI given these constraints. If nothing can change in Elm, how can Elm applications model systems that change?

307 |

As we will see, Elm has a series of abstractions built into the language that are helpful in modeling user interfaces. Together, these abstractions form The Elm Architecture, which is a detailed template for constructing an application in Elm. Similar to how the strictness of the language pushes you to write code in a particular way, The Elm Architecture pushes you to structure applications in a way that prioritizes maintainability.

308 |

The rest of this tutorial is meant to teach you what this means in practice. Piece by piece, you'll be building and extending an interactive user interface in Elm. By the end of the day, you should have a better sense of what it feels like to develop in Elm and what it's like to work with a functional programming language.

309 |

Off we go!

310 | 311 | 312 |
313 | 314 |
315 |
316 |
317 | 318 |

results matching ""

319 |
    320 | 321 |
    322 |
    323 | 324 |

    No results matching ""

    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 | 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 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at saviinirs@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Thanks! 2 | 3 | Thanks for taking the time to contribute to ElmBridge. We sincerely appreciate it. 4 | 5 | Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms. 6 | 7 | We ask that contributions be made as issues or pull requests via GitHub. If those words 8 | are totally foreign to you, 9 | [see here](#its-my-first-time-on-github-ever-what-do-i-do). 10 | 11 | # Creating an Issue 12 | 13 | If you see a bug or something that can be improved, please let us know! 14 | 15 | You can make an issue on the ElmBridge Curriculum [here](https://github.com/elmbridge/curriculum/issues/new). 16 | 17 | # Submitting a Pull Request 18 | 19 | *Here's a couple of tricks to grease the wheels and make it easy for the 20 | maintainers to love you. :heart:* 21 | 22 | ## Before You Start! 23 | 24 | - If you have an existing fork, please make sure it's up to date. 25 | It just makes your life easier! If not, make sure you fork *before* cloning, 26 | otherwise you'll need to spend some time juggling remotes. 27 | Look at the section "Keep your fork synced" in GitHub's 28 | [Fork A Repo](https://help.github.com/articles/fork-a-repo) article. 29 | 30 | - Create a local topic branch before you start working. This branch is going to 31 | be named for what you plan to change. `fix-typo-in-slides`, `move-resources`, 32 | and `mountain-lion-support` are all good names for topic branches. If you've 33 | never created a local branch before, you can use `git checkout -b 34 | new-branch-name`. 35 | 36 | ## Before Submitting 37 | 38 | - Push to a branch on GitHub. Just like you developed in a local branch, you 39 | should push to a branch of your repo on GitHub as well. The `master` branch is 40 | best used as a clean copy of the upstream docs repo in case you need to make 41 | some unrelated changes. To push to a branch, 42 | if your branch is named "fix-typo-in-slides", 43 | use `git push origin fix-typo-in-slides`. 44 | 45 | ## Submitting a Pull Request 46 | 47 | - Read the GitHub Guide on [Forking](https://guides.github.com/activities/forking/), especially the part about 48 | [Pull Requests](https://guides.github.com/activities/forking/#making-a-pull-request). 49 | 50 | - Remember, pull requests are submitted *from* your repo, but show up on the 51 | *upstream* repo. 52 | 53 | ## Discussion and Waiting On a Merge 54 | 55 | - Every pull request will receive a response from the team. 56 | - Not every pull request will be merged as is. 57 | - Not every pull request will be merged at all. 58 | - If a pull request falls significantly behind master, we may ask that you close 59 | it, rebase your changes off of master, and submit a new pull request. 60 | - Feel free to "ping" the team by adding a short comment to your pull request 61 | if it's been more than a week with no reply 62 | 63 | ## After your merge has been accepted 64 | 65 | - go back to your fork and keep it up to date, e.g. 66 | 67 | git checkout master 68 | git pull upstream master 69 | git push origin master 70 | 71 | - you can also delete your topic branch if you like 72 | 73 | git branch -dr fix-typo-in-slides 74 | 75 | # It's My First Time on GitHub Ever What Do I Do? 76 | 77 | Relax, you came to the right place. In order to contribute you'll need to be 78 | able to familiarize yourself with some concepts from git and GitHub. It's going 79 | to be a lot of information, but you're :sparkles:awesome:sparkles:! So you'll 80 | be fine. 81 | 82 | First, you'll need a GitHub account, which is totally free. You can sign up 83 | [here](https://github.com/join). 84 | 85 | Next, browse the [GitHub Help site](https://help.github.com) and the 86 | [GitHub Guides](https://guides.github.com/). The Help Site is more technical, and the 87 | Guides are very easy to understand tutorials. 88 | 89 | You'll want to read about 90 | [forking](https://help.github.com/articles/fork-a-repo) and then make your own 91 | fork of [elmbridge/curriculum](https://github.com/elmbridge/curriculum). Once you've 92 | done so, you can clone it and get started by reading up on [what to do when 93 | submitting a pull request](#when-submitting-a-pull-request), and read up on 94 | [pull requests](https://help.github.com/articles/using-pull-requests) 95 | themselves. 96 | 97 | # Closing 98 | 99 | If you haven't taken the time yet, go through the [Git Immersion lab](http://gitimmersion.com) 100 | at . Do it. It's worth it no matter how much git-fu you have. 101 | 102 | Also, [Pro Git](http://git-scm.com/book) is a great (and free!) book about Git. 103 | 104 | We apologize for how long this document is! Hopefully it addressed 105 | most of your concerns about git, contributing, and GitHub. And we're open to any suggestions about improvements, 106 | including to this document. 107 | 108 | _This contributor's guide has been adapted from [RailsBridge](https://github.com/railsbridge/docs)._ 109 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # ElmBridge Curriculum 2 | 3 | Curriculum for the ElmBridge workshop. Hosted here: https://elmbridge.github.io/curriculum/ 4 | 5 | ## Development Setup 6 | 7 | This curriculum is written using [GitBook](https://github.com/GitbookIO/gitbook). To run locally, run: 8 | 9 | ```sh 10 | npm install gitbook-cli -g 11 | gitbook serve 12 | ``` 13 | 14 | ## Contributing 15 | 16 | Check out [CONTRIBUTING.md](CONTRIBUTING.md) to see how to join our [list of contributors](https://github.com/elmbridge/curriculum/contributors)! 17 | 18 | Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. 19 | -------------------------------------------------------------------------------- /docs/Special Installation Notes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Special Installation Notes · GitBook 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 | 78 | 79 | 80 | 273 | 274 | 275 |
    276 | 277 |
    278 | 279 |
    280 | 281 | 282 | 283 | 292 | 293 | 294 | 295 | 296 |
    297 |
    298 | 299 |
    300 |
    301 | 302 |
    303 | 304 |

    Installation Notes for Windows Users

    305 |

    If you are running Windows on your machine, you might find this checklist of activities helpful:

    306 |
      307 |
    • Before running the Windows installer, shut down your command prompt (cmd.exe) and/or Cygwin applications.
    • 308 |
    • After running the installer, you will also need to install nodejs, from the Node JS website. You can choose the option that says "Recommended For Most Users."
    • 309 |
    • After completing the NodeJS installation, open a new command prompt (or Cygwin) window and type in elm repl at the command line. If you don't see this message, you are good to go! If not, consult an instructor or TA:
    • 310 |
    311 |
    $ elm repl
    312 | The REPL relies on node.js to execute JavaScript code outside the browser.
    313 | I could not find executable 'node' or 'nodejs' on your computer though!
    314 | 
    315 | You can install node.js from <http://nodejs.org/>. If it is already installed
    316 | but has a different name, use the --interpreter flag.
    317 | ---- Elm 0.19.1 ----------------------------------------------------------------
    318 | Say :help for help and :exit to exit! More at <https://elm-lang.org/0.19.1/repl>
    319 | --------------------------------------------------------------------------------
    320 | 
    321 | 322 |
    323 | 324 |
    325 |
    326 |
    327 | 328 |

    results matching ""

    329 |
      330 | 331 |
      332 |
      333 | 334 |

      No results matching ""

      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 | 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 | -------------------------------------------------------------------------------- /docs/Why Elm.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Intermission: Why Elm? · GitBook 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 | 78 | 79 | 80 | 273 | 274 | 275 |
      276 | 277 |
      278 | 279 |
      280 | 281 | 282 | 283 | 292 | 293 | 294 | 295 | 296 |
      297 |
      298 | 299 |
      300 |
      301 | 302 |
      303 | 304 |

      Why Elm?

      305 |

      In this section, we'll talk about the advantages — and disadvantages! — of using Elm to make front-end applications.

      306 |

      First off, what is Elm?

      307 |

      Elm is a language for building complex web interfaces. Much like CoffeeScript or JSX, Elm compiles to JavaScript; the Elm Platform takes your Elm code and turns it into JavaScript that any web browser can understand. In many ways, it operates similarly to front-end JavaScript frameworks like React, Angular, and Ember.

      308 |

      Elm is more powerful than a frontend framework though: it is a distinct programming language that can strictly enforce its opinions.

      309 |

      You may have heard words like functional and statically typed used to describe Elm. Functional means that a function always returns the same value for a given input. Statically typed means that the compiler checks that the code makes sense before you can use it in production: it won't let you ask for the length of an integer or try to multiply a string and a float. If your code compiles, that means that you're doing operations that make sense. If you give a function the wrong type of value (a String rather than an Int) the Elm compiler will figure it out immediately and give you a hint.

      310 |

      So why Elm?

      311 |

      One compelling reason is that entire categories of bugs are impossible in Elm, like:

      312 |
        313 |
      • "I expected this function to return a number, but it returned nil instead."
      • 314 |
      • "Sometimes when I call this function it fails, even though I gave it the exact same input."
      • 315 |
      • "Something is changing the value of my variable, and I'm not sure where it's happening."
      • 316 |
      317 |

      These errors cannot happen in Elm because of the language's strictness. Indeed, the rules mean that Elm applications can never accidentally crash. There are no runtime exceptions — no undefined is not a function or undefined method for nil:NilClass. Consequently, Elm developers spend much less time in the Developer Tools console than JavaScript developers, and less time in the browser in general.

      318 |

      Because Elm requires all programs to follow its rules, the Elm compiler will tell you exactly what's wrong with your program if you make a mistake. It will tell you if you made an if/else expression return two different kinds of values, if you are calling a function with too many arguments, or if you are referencing a variable that does not exist.

      319 |

      Developing with Elm often feels like an ongoing conversation with the compiler, gently pushing you toward writing better code. Often, you don't even need to look at your application in the browser while implementing a feature — you can trust the compiler to tell you if you are going in the right direction.

      320 |

      Why not Elm?

      321 |

      If you are coming from an object-oriented, dynamically typed language like Ruby or JavaScript, Elm can feel like a straightjacket. There are no each or for loops in Elm — instead, you use functions like map, filter, and fold. If you need to change a value deep inside a nested data structure, you can't simply read the value and mutate it, as you could in other languages.

      322 |

      Working with widget libraries can be hard due to conflicting paradigms. If you need to use D3.js or Three.js, you may have difficulties using Elm.

      323 |

      Learning Elm

      324 |

      You may be asking yourself why you should choose to learn a language that's so different from other tools in the ecosystem--especially if you aren't looking to work with it professionally yet. We'd argue that the experience of working in an alien environment, even if it's just for a little while, can be fruitful. You are probably not used to dealing with the constraints of purity, immutability, and static typing, which together make up the basic tenets of functional programming. These constraints will push you to solve problems in creative ways, and hopefully you will learn strategies that you can employ in other languages.

      325 |

      We make no claim that functional programming is better than other types of programming, but it definitely is different. We strongly believe that learning a new way to think about programming will make you a better developer, in the same way that learning a foreign language makes you appreciate the architecture and beauty of the languages you already know.

      326 |

      So let's get started!

      327 | 328 | 329 |
      330 | 331 |
      332 |
      333 |
      334 | 335 |

      results matching ""

      336 |
        337 | 338 |
        339 |
        340 | 341 |

        No results matching ""

        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 | 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 | -------------------------------------------------------------------------------- /docs/gitbook/fonts/fontawesome/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/gitbook/fonts/fontawesome/FontAwesome.otf -------------------------------------------------------------------------------- /docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /docs/gitbook/gitbook-plugin-fontsettings/fontsettings.js: -------------------------------------------------------------------------------- 1 | require(['gitbook', 'jquery'], function(gitbook, $) { 2 | // Configuration 3 | var MAX_SIZE = 4, 4 | MIN_SIZE = 0, 5 | BUTTON_ID; 6 | 7 | // Current fontsettings state 8 | var fontState; 9 | 10 | // Default themes 11 | var THEMES = [ 12 | { 13 | config: 'white', 14 | text: 'White', 15 | id: 0 16 | }, 17 | { 18 | config: 'sepia', 19 | text: 'Sepia', 20 | id: 1 21 | }, 22 | { 23 | config: 'night', 24 | text: 'Night', 25 | id: 2 26 | } 27 | ]; 28 | 29 | // Default font families 30 | var FAMILIES = [ 31 | { 32 | config: 'serif', 33 | text: 'Serif', 34 | id: 0 35 | }, 36 | { 37 | config: 'sans', 38 | text: 'Sans', 39 | id: 1 40 | } 41 | ]; 42 | 43 | // Return configured themes 44 | function getThemes() { 45 | return THEMES; 46 | } 47 | 48 | // Modify configured themes 49 | function setThemes(themes) { 50 | THEMES = themes; 51 | updateButtons(); 52 | } 53 | 54 | // Return configured font families 55 | function getFamilies() { 56 | return FAMILIES; 57 | } 58 | 59 | // Modify configured font families 60 | function setFamilies(families) { 61 | FAMILIES = families; 62 | updateButtons(); 63 | } 64 | 65 | // Save current font settings 66 | function saveFontSettings() { 67 | gitbook.storage.set('fontState', fontState); 68 | update(); 69 | } 70 | 71 | // Increase font size 72 | function enlargeFontSize(e) { 73 | e.preventDefault(); 74 | if (fontState.size >= MAX_SIZE) return; 75 | 76 | fontState.size++; 77 | saveFontSettings(); 78 | } 79 | 80 | // Decrease font size 81 | function reduceFontSize(e) { 82 | e.preventDefault(); 83 | if (fontState.size <= MIN_SIZE) return; 84 | 85 | fontState.size--; 86 | saveFontSettings(); 87 | } 88 | 89 | // Change font family 90 | function changeFontFamily(configName, e) { 91 | if (e && e instanceof Event) { 92 | e.preventDefault(); 93 | } 94 | 95 | var familyId = getFontFamilyId(configName); 96 | fontState.family = familyId; 97 | saveFontSettings(); 98 | } 99 | 100 | // Change type of color theme 101 | function changeColorTheme(configName, e) { 102 | if (e && e instanceof Event) { 103 | e.preventDefault(); 104 | } 105 | 106 | var $book = gitbook.state.$book; 107 | 108 | // Remove currently applied color theme 109 | if (fontState.theme !== 0) 110 | $book.removeClass('color-theme-'+fontState.theme); 111 | 112 | // Set new color theme 113 | var themeId = getThemeId(configName); 114 | fontState.theme = themeId; 115 | if (fontState.theme !== 0) 116 | $book.addClass('color-theme-'+fontState.theme); 117 | 118 | saveFontSettings(); 119 | } 120 | 121 | // Return the correct id for a font-family config key 122 | // Default to first font-family 123 | function getFontFamilyId(configName) { 124 | // Search for plugin configured font family 125 | var configFamily = $.grep(FAMILIES, function(family) { 126 | return family.config == configName; 127 | })[0]; 128 | // Fallback to default font family 129 | return (!!configFamily)? configFamily.id : 0; 130 | } 131 | 132 | // Return the correct id for a theme config key 133 | // Default to first theme 134 | function getThemeId(configName) { 135 | // Search for plugin configured theme 136 | var configTheme = $.grep(THEMES, function(theme) { 137 | return theme.config == configName; 138 | })[0]; 139 | // Fallback to default theme 140 | return (!!configTheme)? configTheme.id : 0; 141 | } 142 | 143 | function update() { 144 | var $book = gitbook.state.$book; 145 | 146 | $('.font-settings .font-family-list li').removeClass('active'); 147 | $('.font-settings .font-family-list li:nth-child('+(fontState.family+1)+')').addClass('active'); 148 | 149 | $book[0].className = $book[0].className.replace(/\bfont-\S+/g, ''); 150 | $book.addClass('font-size-'+fontState.size); 151 | $book.addClass('font-family-'+fontState.family); 152 | 153 | if(fontState.theme !== 0) { 154 | $book[0].className = $book[0].className.replace(/\bcolor-theme-\S+/g, ''); 155 | $book.addClass('color-theme-'+fontState.theme); 156 | } 157 | } 158 | 159 | function init(config) { 160 | // Search for plugin configured font family 161 | var configFamily = getFontFamilyId(config.family), 162 | configTheme = getThemeId(config.theme); 163 | 164 | // Instantiate font state object 165 | fontState = gitbook.storage.get('fontState', { 166 | size: config.size || 2, 167 | family: configFamily, 168 | theme: configTheme 169 | }); 170 | 171 | update(); 172 | } 173 | 174 | function updateButtons() { 175 | // Remove existing fontsettings buttons 176 | if (!!BUTTON_ID) { 177 | gitbook.toolbar.removeButton(BUTTON_ID); 178 | } 179 | 180 | // Create buttons in toolbar 181 | BUTTON_ID = gitbook.toolbar.createButton({ 182 | icon: 'fa fa-font', 183 | label: 'Font Settings', 184 | className: 'font-settings', 185 | dropdown: [ 186 | [ 187 | { 188 | text: 'A', 189 | className: 'font-reduce', 190 | onClick: reduceFontSize 191 | }, 192 | { 193 | text: 'A', 194 | className: 'font-enlarge', 195 | onClick: enlargeFontSize 196 | } 197 | ], 198 | $.map(FAMILIES, function(family) { 199 | family.onClick = function(e) { 200 | return changeFontFamily(family.config, e); 201 | }; 202 | 203 | return family; 204 | }), 205 | $.map(THEMES, function(theme) { 206 | theme.onClick = function(e) { 207 | return changeColorTheme(theme.config, e); 208 | }; 209 | 210 | return theme; 211 | }) 212 | ] 213 | }); 214 | } 215 | 216 | // Init configuration at start 217 | gitbook.events.bind('start', function(e, config) { 218 | var opts = config.fontsettings; 219 | 220 | // Generate buttons at start 221 | updateButtons(); 222 | 223 | // Init current settings 224 | init(opts); 225 | }); 226 | 227 | // Expose API 228 | gitbook.fontsettings = { 229 | enlargeFontSize: enlargeFontSize, 230 | reduceFontSize: reduceFontSize, 231 | setTheme: changeColorTheme, 232 | setFamily: changeFontFamily, 233 | getThemes: getThemes, 234 | setThemes: setThemes, 235 | getFamilies: getFamilies, 236 | setFamilies: setFamilies 237 | }; 238 | }); 239 | 240 | 241 | -------------------------------------------------------------------------------- /docs/gitbook/gitbook-plugin-fontsettings/website.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Theme 1 3 | */ 4 | .color-theme-1 .dropdown-menu { 5 | background-color: #111111; 6 | border-color: #7e888b; 7 | } 8 | .color-theme-1 .dropdown-menu .dropdown-caret .caret-inner { 9 | border-bottom: 9px solid #111111; 10 | } 11 | .color-theme-1 .dropdown-menu .buttons { 12 | border-color: #7e888b; 13 | } 14 | .color-theme-1 .dropdown-menu .button { 15 | color: #afa790; 16 | } 17 | .color-theme-1 .dropdown-menu .button:hover { 18 | color: #73553c; 19 | } 20 | /* 21 | * Theme 2 22 | */ 23 | .color-theme-2 .dropdown-menu { 24 | background-color: #2d3143; 25 | border-color: #272a3a; 26 | } 27 | .color-theme-2 .dropdown-menu .dropdown-caret .caret-inner { 28 | border-bottom: 9px solid #2d3143; 29 | } 30 | .color-theme-2 .dropdown-menu .buttons { 31 | border-color: #272a3a; 32 | } 33 | .color-theme-2 .dropdown-menu .button { 34 | color: #62677f; 35 | } 36 | .color-theme-2 .dropdown-menu .button:hover { 37 | color: #f4f4f5; 38 | } 39 | .book .book-header .font-settings .font-enlarge { 40 | line-height: 30px; 41 | font-size: 1.4em; 42 | } 43 | .book .book-header .font-settings .font-reduce { 44 | line-height: 30px; 45 | font-size: 1em; 46 | } 47 | .book.color-theme-1 .book-body { 48 | color: #704214; 49 | background: #f3eacb; 50 | } 51 | .book.color-theme-1 .book-body .page-wrapper .page-inner section { 52 | background: #f3eacb; 53 | } 54 | .book.color-theme-2 .book-body { 55 | color: #bdcadb; 56 | background: #1c1f2b; 57 | } 58 | .book.color-theme-2 .book-body .page-wrapper .page-inner section { 59 | background: #1c1f2b; 60 | } 61 | .book.font-size-0 .book-body .page-inner section { 62 | font-size: 1.2rem; 63 | } 64 | .book.font-size-1 .book-body .page-inner section { 65 | font-size: 1.4rem; 66 | } 67 | .book.font-size-2 .book-body .page-inner section { 68 | font-size: 1.6rem; 69 | } 70 | .book.font-size-3 .book-body .page-inner section { 71 | font-size: 2.2rem; 72 | } 73 | .book.font-size-4 .book-body .page-inner section { 74 | font-size: 4rem; 75 | } 76 | .book.font-family-0 { 77 | font-family: Georgia, serif; 78 | } 79 | .book.font-family-1 { 80 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 81 | } 82 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal { 83 | color: #704214; 84 | } 85 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal a { 86 | color: inherit; 87 | } 88 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, 89 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2, 90 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h3, 91 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h4, 92 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h5, 93 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { 94 | color: inherit; 95 | } 96 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, 97 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2 { 98 | border-color: inherit; 99 | } 100 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { 101 | color: inherit; 102 | } 103 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal hr { 104 | background-color: inherit; 105 | } 106 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal blockquote { 107 | border-color: inherit; 108 | } 109 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre, 110 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code { 111 | background: #fdf6e3; 112 | color: #657b83; 113 | border-color: #f8df9c; 114 | } 115 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal .highlight { 116 | background-color: inherit; 117 | } 118 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table th, 119 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table td { 120 | border-color: #f5d06c; 121 | } 122 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr { 123 | color: inherit; 124 | background-color: #fdf6e3; 125 | border-color: #444444; 126 | } 127 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { 128 | background-color: #fbeecb; 129 | } 130 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal { 131 | color: #bdcadb; 132 | } 133 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal a { 134 | color: #3eb1d0; 135 | } 136 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, 137 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2, 138 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h3, 139 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h4, 140 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h5, 141 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { 142 | color: #fffffa; 143 | } 144 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, 145 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2 { 146 | border-color: #373b4e; 147 | } 148 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { 149 | color: #373b4e; 150 | } 151 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal hr { 152 | background-color: #373b4e; 153 | } 154 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal blockquote { 155 | border-color: #373b4e; 156 | } 157 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre, 158 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code { 159 | color: #9dbed8; 160 | background: #2d3143; 161 | border-color: #2d3143; 162 | } 163 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal .highlight { 164 | background-color: #282a39; 165 | } 166 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table th, 167 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table td { 168 | border-color: #3b3f54; 169 | } 170 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr { 171 | color: #b6c2d2; 172 | background-color: #2d3143; 173 | border-color: #3b3f54; 174 | } 175 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { 176 | background-color: #35394b; 177 | } 178 | .book.color-theme-1 .book-header { 179 | color: #afa790; 180 | background: transparent; 181 | } 182 | .book.color-theme-1 .book-header .btn { 183 | color: #afa790; 184 | } 185 | .book.color-theme-1 .book-header .btn:hover { 186 | color: #73553c; 187 | background: none; 188 | } 189 | .book.color-theme-1 .book-header h1 { 190 | color: #704214; 191 | } 192 | .book.color-theme-2 .book-header { 193 | color: #7e888b; 194 | background: transparent; 195 | } 196 | .book.color-theme-2 .book-header .btn { 197 | color: #3b3f54; 198 | } 199 | .book.color-theme-2 .book-header .btn:hover { 200 | color: #fffff5; 201 | background: none; 202 | } 203 | .book.color-theme-2 .book-header h1 { 204 | color: #bdcadb; 205 | } 206 | .book.color-theme-1 .book-body .navigation { 207 | color: #afa790; 208 | } 209 | .book.color-theme-1 .book-body .navigation:hover { 210 | color: #73553c; 211 | } 212 | .book.color-theme-2 .book-body .navigation { 213 | color: #383f52; 214 | } 215 | .book.color-theme-2 .book-body .navigation:hover { 216 | color: #fffff5; 217 | } 218 | /* 219 | * Theme 1 220 | */ 221 | .book.color-theme-1 .book-summary { 222 | color: #afa790; 223 | background: #111111; 224 | border-right: 1px solid rgba(0, 0, 0, 0.07); 225 | } 226 | .book.color-theme-1 .book-summary .book-search { 227 | background: transparent; 228 | } 229 | .book.color-theme-1 .book-summary .book-search input, 230 | .book.color-theme-1 .book-summary .book-search input:focus { 231 | border: 1px solid transparent; 232 | } 233 | .book.color-theme-1 .book-summary ul.summary li.divider { 234 | background: #7e888b; 235 | box-shadow: none; 236 | } 237 | .book.color-theme-1 .book-summary ul.summary li i.fa-check { 238 | color: #33cc33; 239 | } 240 | .book.color-theme-1 .book-summary ul.summary li.done > a { 241 | color: #877f6a; 242 | } 243 | .book.color-theme-1 .book-summary ul.summary li a, 244 | .book.color-theme-1 .book-summary ul.summary li span { 245 | color: #877f6a; 246 | background: transparent; 247 | font-weight: normal; 248 | } 249 | .book.color-theme-1 .book-summary ul.summary li.active > a, 250 | .book.color-theme-1 .book-summary ul.summary li a:hover { 251 | color: #704214; 252 | background: transparent; 253 | font-weight: normal; 254 | } 255 | /* 256 | * Theme 2 257 | */ 258 | .book.color-theme-2 .book-summary { 259 | color: #bcc1d2; 260 | background: #2d3143; 261 | border-right: none; 262 | } 263 | .book.color-theme-2 .book-summary .book-search { 264 | background: transparent; 265 | } 266 | .book.color-theme-2 .book-summary .book-search input, 267 | .book.color-theme-2 .book-summary .book-search input:focus { 268 | border: 1px solid transparent; 269 | } 270 | .book.color-theme-2 .book-summary ul.summary li.divider { 271 | background: #272a3a; 272 | box-shadow: none; 273 | } 274 | .book.color-theme-2 .book-summary ul.summary li i.fa-check { 275 | color: #33cc33; 276 | } 277 | .book.color-theme-2 .book-summary ul.summary li.done > a { 278 | color: #62687f; 279 | } 280 | .book.color-theme-2 .book-summary ul.summary li a, 281 | .book.color-theme-2 .book-summary ul.summary li span { 282 | color: #c1c6d7; 283 | background: transparent; 284 | font-weight: 600; 285 | } 286 | .book.color-theme-2 .book-summary ul.summary li.active > a, 287 | .book.color-theme-2 .book-summary ul.summary li a:hover { 288 | color: #f4f4f5; 289 | background: #252737; 290 | font-weight: 600; 291 | } 292 | -------------------------------------------------------------------------------- /docs/gitbook/gitbook-plugin-highlight/ebook.css: -------------------------------------------------------------------------------- 1 | pre, 2 | code { 3 | /* http://jmblog.github.io/color-themes-for-highlightjs */ 4 | /* Tomorrow Comment */ 5 | /* Tomorrow Red */ 6 | /* Tomorrow Orange */ 7 | /* Tomorrow Yellow */ 8 | /* Tomorrow Green */ 9 | /* Tomorrow Aqua */ 10 | /* Tomorrow Blue */ 11 | /* Tomorrow Purple */ 12 | } 13 | pre .hljs-comment, 14 | code .hljs-comment, 15 | pre .hljs-title, 16 | code .hljs-title { 17 | color: #8e908c; 18 | } 19 | pre .hljs-variable, 20 | code .hljs-variable, 21 | pre .hljs-attribute, 22 | code .hljs-attribute, 23 | pre .hljs-tag, 24 | code .hljs-tag, 25 | pre .hljs-regexp, 26 | code .hljs-regexp, 27 | pre .hljs-deletion, 28 | code .hljs-deletion, 29 | pre .ruby .hljs-constant, 30 | code .ruby .hljs-constant, 31 | pre .xml .hljs-tag .hljs-title, 32 | code .xml .hljs-tag .hljs-title, 33 | pre .xml .hljs-pi, 34 | code .xml .hljs-pi, 35 | pre .xml .hljs-doctype, 36 | code .xml .hljs-doctype, 37 | pre .html .hljs-doctype, 38 | code .html .hljs-doctype, 39 | pre .css .hljs-id, 40 | code .css .hljs-id, 41 | pre .css .hljs-class, 42 | code .css .hljs-class, 43 | pre .css .hljs-pseudo, 44 | code .css .hljs-pseudo { 45 | color: #c82829; 46 | } 47 | pre .hljs-number, 48 | code .hljs-number, 49 | pre .hljs-preprocessor, 50 | code .hljs-preprocessor, 51 | pre .hljs-pragma, 52 | code .hljs-pragma, 53 | pre .hljs-built_in, 54 | code .hljs-built_in, 55 | pre .hljs-literal, 56 | code .hljs-literal, 57 | pre .hljs-params, 58 | code .hljs-params, 59 | pre .hljs-constant, 60 | code .hljs-constant { 61 | color: #f5871f; 62 | } 63 | pre .ruby .hljs-class .hljs-title, 64 | code .ruby .hljs-class .hljs-title, 65 | pre .css .hljs-rules .hljs-attribute, 66 | code .css .hljs-rules .hljs-attribute { 67 | color: #eab700; 68 | } 69 | pre .hljs-string, 70 | code .hljs-string, 71 | pre .hljs-value, 72 | code .hljs-value, 73 | pre .hljs-inheritance, 74 | code .hljs-inheritance, 75 | pre .hljs-header, 76 | code .hljs-header, 77 | pre .hljs-addition, 78 | code .hljs-addition, 79 | pre .ruby .hljs-symbol, 80 | code .ruby .hljs-symbol, 81 | pre .xml .hljs-cdata, 82 | code .xml .hljs-cdata { 83 | color: #718c00; 84 | } 85 | pre .css .hljs-hexcolor, 86 | code .css .hljs-hexcolor { 87 | color: #3e999f; 88 | } 89 | pre .hljs-function, 90 | code .hljs-function, 91 | pre .python .hljs-decorator, 92 | code .python .hljs-decorator, 93 | pre .python .hljs-title, 94 | code .python .hljs-title, 95 | pre .ruby .hljs-function .hljs-title, 96 | code .ruby .hljs-function .hljs-title, 97 | pre .ruby .hljs-title .hljs-keyword, 98 | code .ruby .hljs-title .hljs-keyword, 99 | pre .perl .hljs-sub, 100 | code .perl .hljs-sub, 101 | pre .javascript .hljs-title, 102 | code .javascript .hljs-title, 103 | pre .coffeescript .hljs-title, 104 | code .coffeescript .hljs-title { 105 | color: #4271ae; 106 | } 107 | pre .hljs-keyword, 108 | code .hljs-keyword, 109 | pre .javascript .hljs-function, 110 | code .javascript .hljs-function { 111 | color: #8959a8; 112 | } 113 | pre .hljs, 114 | code .hljs { 115 | display: block; 116 | background: white; 117 | color: #4d4d4c; 118 | padding: 0.5em; 119 | } 120 | pre .coffeescript .javascript, 121 | code .coffeescript .javascript, 122 | pre .javascript .xml, 123 | code .javascript .xml, 124 | pre .tex .hljs-formula, 125 | code .tex .hljs-formula, 126 | pre .xml .javascript, 127 | code .xml .javascript, 128 | pre .xml .vbscript, 129 | code .xml .vbscript, 130 | pre .xml .css, 131 | code .xml .css, 132 | pre .xml .hljs-cdata, 133 | code .xml .hljs-cdata { 134 | opacity: 0.5; 135 | } 136 | -------------------------------------------------------------------------------- /docs/gitbook/gitbook-plugin-lunr/lunr.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.5.12 3 | * Copyright (C) 2015 Oliver Nightingale 4 | * MIT Licensed 5 | * @license 6 | */ 7 | !function(){var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.5.12",t.utils={},t.utils.warn=function(t){return function(e){t.console&&console.warn&&console.warn(e)}}(this),t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var t=Array.prototype.slice.call(arguments),e=t.pop(),n=t;if("function"!=typeof e)throw new TypeError("last argument must be a function");n.forEach(function(t){this.hasHandler(t)||(this.events[t]=[]),this.events[t].push(e)},this)},t.EventEmitter.prototype.removeListener=function(t,e){if(this.hasHandler(t)){var n=this.events[t].indexOf(e);this.events[t].splice(n,1),this.events[t].length||delete this.events[t]}},t.EventEmitter.prototype.emit=function(t){if(this.hasHandler(t)){var e=Array.prototype.slice.call(arguments,1);this.events[t].forEach(function(t){t.apply(void 0,e)})}},t.EventEmitter.prototype.hasHandler=function(t){return t in this.events},t.tokenizer=function(t){return arguments.length&&null!=t&&void 0!=t?Array.isArray(t)?t.map(function(t){return t.toLowerCase()}):t.toString().trim().toLowerCase().split(/[\s\-]+/):[]},t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.registeredFunctions[e];if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");this._stack.splice(i,0,n)},t.Pipeline.prototype.remove=function(t){var e=this._stack.indexOf(t);-1!=e&&this._stack.splice(e,1)},t.Pipeline.prototype.run=function(t){for(var e=[],n=t.length,i=this._stack.length,o=0;n>o;o++){for(var r=t[o],s=0;i>s&&(r=this._stack[s](r,o,t),void 0!==r);s++);void 0!==r&&e.push(r)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(r===t)return o;t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o]}return r===t?o:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,o=e+Math.floor(i/2),r=this.elements[o];i>1;)t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o];return r>t?o:t>r?o+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,o=0,r=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>r-1||o>s-1)break;a[i]!==h[o]?a[i]h[o]&&o++:(n.add(a[i]),i++,o++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;return this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone(),i.add.apply(i,n.toArray()),i},t.SortedSet.prototype.toJSON=function(){return this.toArray()},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.Store,this.tokenStore=new t.TokenStore,this.corpusTokens=new t.SortedSet,this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var t=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,t)},t.Index.prototype.off=function(t,e){return this.eventEmitter.removeListener(t,e)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;return n._fields=e.fields,n._ref=e.ref,n.documentStore=t.Store.load(e.documentStore),n.tokenStore=t.TokenStore.load(e.tokenStore),n.corpusTokens=t.SortedSet.load(e.corpusTokens),n.pipeline=t.Pipeline.load(e.pipeline),n},t.Index.prototype.field=function(t,e){var e=e||{},n={name:t,boost:e.boost||1};return this._fields.push(n),this},t.Index.prototype.ref=function(t){return this._ref=t,this},t.Index.prototype.add=function(e,n){var i={},o=new t.SortedSet,r=e[this._ref],n=void 0===n?!0:n;this._fields.forEach(function(n){var r=this.pipeline.run(t.tokenizer(e[n.name]));i[n.name]=r,t.SortedSet.prototype.add.apply(o,r)},this),this.documentStore.set(r,o),t.SortedSet.prototype.add.apply(this.corpusTokens,o.toArray());for(var s=0;s0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(t.tokenizer(e)),i=new t.Vector,o=[],r=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*r,h=this,l=this.tokenStore.expand(e).reduce(function(n,o){var r=h.corpusTokens.indexOf(o),s=h.idf(o),l=1,u=new t.SortedSet;if(o!==e){var c=Math.max(3,o.length-e.length);l=1/Math.log(c)}return r>-1&&i.insert(r,a*s*l),Object.keys(h.tokenStore.get(o)).forEach(function(t){u.add(t)}),n.union(u)},new t.SortedSet);o.push(l)},this);var a=o.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,o=new t.Vector,r=0;i>r;r++){var s=n.elements[r],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);o.insert(this.corpusTokens.indexOf(s),a*h)}return o},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",o=n+"[^aeiouy]*",r=i+"[aeiou]*",s="^("+o+")?"+r+o,a="^("+o+")?"+r+o+"("+r+")?$",h="^("+o+")?"+r+o+r+o,l="^("+o+")?"+i,u=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(l),p=/^(.+?)(ss|i)es$/,m=/^(.+?)([^s])s$/,v=/^(.+?)eed$/,y=/^(.+?)(ed|ing)$/,g=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),x=new RegExp("^"+o+i+"[^aeiouwxy]$"),k=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,_=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,F=/^(.+?)(s|t)(ion)$/,O=/^(.+?)e$/,P=/ll$/,N=new RegExp("^"+o+i+"[^aeiouwxy]$"),T=function(n){var i,o,r,s,a,h,l;if(n.length<3)return n;if(r=n.substr(0,1),"y"==r&&(n=r.toUpperCase()+n.substr(1)),s=p,a=m,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=v,a=y,s.test(n)){var T=s.exec(n);s=u,s.test(T[1])&&(s=g,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,l=x,a.test(n)?n+="e":h.test(n)?(s=g,n=n.replace(s,"")):l.test(n)&&(n+="e"))}if(s=k,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+t[o])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+e[o])}if(s=_,a=F,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=O,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=N,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=P,a=c,s.test(n)&&a.test(n)&&(s=g,n=n.replace(s,"")),"y"==r&&(n=r.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.stopWordFilter=function(e){return e&&t.stopWordFilter.stopWords[e]!==e?e:void 0},t.stopWordFilter.stopWords={a:"a",able:"able",about:"about",across:"across",after:"after",all:"all",almost:"almost",also:"also",am:"am",among:"among",an:"an",and:"and",any:"any",are:"are",as:"as",at:"at",be:"be",because:"because",been:"been",but:"but",by:"by",can:"can",cannot:"cannot",could:"could",dear:"dear",did:"did","do":"do",does:"does",either:"either","else":"else",ever:"ever",every:"every","for":"for",from:"from",get:"get",got:"got",had:"had",has:"has",have:"have",he:"he",her:"her",hers:"hers",him:"him",his:"his",how:"how",however:"however",i:"i","if":"if","in":"in",into:"into",is:"is",it:"it",its:"its",just:"just",least:"least",let:"let",like:"like",likely:"likely",may:"may",me:"me",might:"might",most:"most",must:"must",my:"my",neither:"neither",no:"no",nor:"nor",not:"not",of:"of",off:"off",often:"often",on:"on",only:"only",or:"or",other:"other",our:"our",own:"own",rather:"rather",said:"said",say:"say",says:"says",she:"she",should:"should",since:"since",so:"so",some:"some",than:"than",that:"that",the:"the",their:"their",them:"them",then:"then",there:"there",these:"these",they:"they","this":"this",tis:"tis",to:"to",too:"too",twas:"twas",us:"us",wants:"wants",was:"was",we:"we",were:"were",what:"what",when:"when",where:"where",which:"which","while":"while",who:"who",whom:"whom",why:"why",will:"will","with":"with",would:"would",yet:"yet",you:"you",your:"your"},t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){var e=t.replace(/^\W+/,"").replace(/\W+$/,"");return""===e?void 0:e},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t[0],o=t.slice(1);return i in n||(n[i]={docs:{}}),0===o.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(o,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;no;o++){for(var r=t[o],s=0;i>s&&(r=this._stack[s](r,o,t),void 0!==r);s++);void 0!==r&&e.push(r)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(r===t)return o;t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o]}return r===t?o:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,o=e+Math.floor(i/2),r=this.elements[o];i>1;)t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o];return r>t?o:t>r?o+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,o=0,r=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>r-1||o>s-1)break;a[i]!==h[o]?a[i]h[o]&&o++:(n.add(a[i]),i++,o++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;return this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone(),i.add.apply(i,n.toArray()),i},t.SortedSet.prototype.toJSON=function(){return this.toArray()},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.Store,this.tokenStore=new t.TokenStore,this.corpusTokens=new t.SortedSet,this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var t=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,t)},t.Index.prototype.off=function(t,e){return this.eventEmitter.removeListener(t,e)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;return n._fields=e.fields,n._ref=e.ref,n.documentStore=t.Store.load(e.documentStore),n.tokenStore=t.TokenStore.load(e.tokenStore),n.corpusTokens=t.SortedSet.load(e.corpusTokens),n.pipeline=t.Pipeline.load(e.pipeline),n},t.Index.prototype.field=function(t,e){var e=e||{},n={name:t,boost:e.boost||1};return this._fields.push(n),this},t.Index.prototype.ref=function(t){return this._ref=t,this},t.Index.prototype.add=function(e,n){var i={},o=new t.SortedSet,r=e[this._ref],n=void 0===n?!0:n;this._fields.forEach(function(n){var r=this.pipeline.run(t.tokenizer(e[n.name]));i[n.name]=r,t.SortedSet.prototype.add.apply(o,r)},this),this.documentStore.set(r,o),t.SortedSet.prototype.add.apply(this.corpusTokens,o.toArray());for(var s=0;s0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(t.tokenizer(e)),i=new t.Vector,o=[],r=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*r,h=this,l=this.tokenStore.expand(e).reduce(function(n,o){var r=h.corpusTokens.indexOf(o),s=h.idf(o),l=1,u=new t.SortedSet;if(o!==e){var c=Math.max(3,o.length-e.length);l=1/Math.log(c)}return r>-1&&i.insert(r,a*s*l),Object.keys(h.tokenStore.get(o)).forEach(function(t){u.add(t)}),n.union(u)},new t.SortedSet);o.push(l)},this);var a=o.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,o=new t.Vector,r=0;i>r;r++){var s=n.elements[r],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);o.insert(this.corpusTokens.indexOf(s),a*h)}return o},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",o=n+"[^aeiouy]*",r=i+"[aeiou]*",s="^("+o+")?"+r+o,a="^("+o+")?"+r+o+"("+r+")?$",h="^("+o+")?"+r+o+r+o,l="^("+o+")?"+i,u=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(l),p=/^(.+?)(ss|i)es$/,m=/^(.+?)([^s])s$/,v=/^(.+?)eed$/,y=/^(.+?)(ed|ing)$/,g=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),x=new RegExp("^"+o+i+"[^aeiouwxy]$"),k=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,_=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,F=/^(.+?)(s|t)(ion)$/,O=/^(.+?)e$/,P=/ll$/,N=new RegExp("^"+o+i+"[^aeiouwxy]$"),T=function(n){var i,o,r,s,a,h,l;if(n.length<3)return n;if(r=n.substr(0,1),"y"==r&&(n=r.toUpperCase()+n.substr(1)),s=p,a=m,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=v,a=y,s.test(n)){var T=s.exec(n);s=u,s.test(T[1])&&(s=g,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,l=x,a.test(n)?n+="e":h.test(n)?(s=g,n=n.replace(s,"")):l.test(n)&&(n+="e"))}if(s=k,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+t[o])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+e[o])}if(s=_,a=F,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=O,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=N,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=P,a=c,s.test(n)&&a.test(n)&&(s=g,n=n.replace(s,"")),"y"==r&&(n=r.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.stopWordFilter=function(e){return e&&t.stopWordFilter.stopWords[e]!==e?e:void 0},t.stopWordFilter.stopWords={a:"a",able:"able",about:"about",across:"across",after:"after",all:"all",almost:"almost",also:"also",am:"am",among:"among",an:"an",and:"and",any:"any",are:"are",as:"as",at:"at",be:"be",because:"because",been:"been",but:"but",by:"by",can:"can",cannot:"cannot",could:"could",dear:"dear",did:"did","do":"do",does:"does",either:"either","else":"else",ever:"ever",every:"every","for":"for",from:"from",get:"get",got:"got",had:"had",has:"has",have:"have",he:"he",her:"her",hers:"hers",him:"him",his:"his",how:"how",however:"however",i:"i","if":"if","in":"in",into:"into",is:"is",it:"it",its:"its",just:"just",least:"least",let:"let",like:"like",likely:"likely",may:"may",me:"me",might:"might",most:"most",must:"must",my:"my",neither:"neither",no:"no",nor:"nor",not:"not",of:"of",off:"off",often:"often",on:"on",only:"only",or:"or",other:"other",our:"our",own:"own",rather:"rather",said:"said",say:"say",says:"says",she:"she",should:"should",since:"since",so:"so",some:"some",than:"than",that:"that",the:"the",their:"their",them:"them",then:"then",there:"there",these:"these",they:"they","this":"this",tis:"tis",to:"to",too:"too",twas:"twas",us:"us",wants:"wants",was:"was",we:"we",were:"were",what:"what",when:"when",where:"where",which:"which","while":"while",who:"who",whom:"whom",why:"why",will:"will","with":"with",would:"would",yet:"yet",you:"you",your:"your"},t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){var e=t.replace(/^\W+/,"").replace(/\W+$/,"");return""===e?void 0:e},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t[0],o=t.slice(1);return i in n||(n[i]={docs:{}}),0===o.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(o,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;n element for each result 48 | res.results.forEach(function(res) { 49 | var $li = $('
      • ', { 50 | 'class': 'search-results-item' 51 | }); 52 | 53 | var $title = $('

        '); 54 | 55 | var $link = $('', { 56 | 'href': gitbook.state.basePath + '/' + res.url, 57 | 'text': res.title 58 | }); 59 | 60 | var content = res.body.trim(); 61 | if (content.length > MAX_DESCRIPTION_SIZE) { 62 | content = content.slice(0, MAX_DESCRIPTION_SIZE).trim()+'...'; 63 | } 64 | var $content = $('

        ').html(content); 65 | 66 | $link.appendTo($title); 67 | $title.appendTo($li); 68 | $content.appendTo($li); 69 | $li.appendTo($searchList); 70 | }); 71 | } 72 | 73 | function launchSearch(q) { 74 | // Add class for loading 75 | $body.addClass('with-search'); 76 | $body.addClass('search-loading'); 77 | 78 | // Launch search query 79 | throttle(gitbook.search.query(q, 0, MAX_RESULTS) 80 | .then(function(results) { 81 | displayResults(results); 82 | }) 83 | .always(function() { 84 | $body.removeClass('search-loading'); 85 | }), 1000); 86 | } 87 | 88 | function closeSearch() { 89 | $body.removeClass('with-search'); 90 | $bookSearchResults.removeClass('open'); 91 | } 92 | 93 | function launchSearchFromQueryString() { 94 | var q = getParameterByName('q'); 95 | if (q && q.length > 0) { 96 | // Update search input 97 | $searchInput.val(q); 98 | 99 | // Launch search 100 | launchSearch(q); 101 | } 102 | } 103 | 104 | function bindSearch() { 105 | // Bind DOM 106 | $searchInput = $('#book-search-input input'); 107 | $bookSearchResults = $('#book-search-results'); 108 | $searchList = $bookSearchResults.find('.search-results-list'); 109 | $searchTitle = $bookSearchResults.find('.search-results-title'); 110 | $searchResultsCount = $searchTitle.find('.search-results-count'); 111 | $searchQuery = $searchTitle.find('.search-query'); 112 | 113 | // Launch query based on input content 114 | function handleUpdate() { 115 | var q = $searchInput.val(); 116 | 117 | if (q.length == 0) { 118 | closeSearch(); 119 | } 120 | else { 121 | launchSearch(q); 122 | } 123 | } 124 | 125 | // Detect true content change in search input 126 | // Workaround for IE < 9 127 | var propertyChangeUnbound = false; 128 | $searchInput.on('propertychange', function(e) { 129 | if (e.originalEvent.propertyName == 'value') { 130 | handleUpdate(); 131 | } 132 | }); 133 | 134 | // HTML5 (IE9 & others) 135 | $searchInput.on('input', function(e) { 136 | // Unbind propertychange event for IE9+ 137 | if (!propertyChangeUnbound) { 138 | $(this).unbind('propertychange'); 139 | propertyChangeUnbound = true; 140 | } 141 | 142 | handleUpdate(); 143 | }); 144 | 145 | // Push to history on blur 146 | $searchInput.on('blur', function(e) { 147 | // Update history state 148 | if (usePushState) { 149 | var uri = updateQueryString('q', $(this).val()); 150 | history.pushState({ path: uri }, null, uri); 151 | } 152 | }); 153 | } 154 | 155 | gitbook.events.on('page.change', function() { 156 | bindSearch(); 157 | closeSearch(); 158 | 159 | // Launch search based on query parameter 160 | if (gitbook.search.isInitialized()) { 161 | launchSearchFromQueryString(); 162 | } 163 | }); 164 | 165 | gitbook.events.on('search.ready', function() { 166 | bindSearch(); 167 | 168 | // Launch search from query param at start 169 | launchSearchFromQueryString(); 170 | }); 171 | 172 | function getParameterByName(name) { 173 | var url = window.location.href; 174 | name = name.replace(/[\[\]]/g, '\\$&'); 175 | var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)', 'i'), 176 | results = regex.exec(url); 177 | if (!results) return null; 178 | if (!results[2]) return ''; 179 | return decodeURIComponent(results[2].replace(/\+/g, ' ')); 180 | } 181 | 182 | function updateQueryString(key, value) { 183 | value = encodeURIComponent(value); 184 | 185 | var url = window.location.href; 186 | var re = new RegExp('([?&])' + key + '=.*?(&|#|$)(.*)', 'gi'), 187 | hash; 188 | 189 | if (re.test(url)) { 190 | if (typeof value !== 'undefined' && value !== null) 191 | return url.replace(re, '$1' + key + '=' + value + '$2$3'); 192 | else { 193 | hash = url.split('#'); 194 | url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, ''); 195 | if (typeof hash[1] !== 'undefined' && hash[1] !== null) 196 | url += '#' + hash[1]; 197 | return url; 198 | } 199 | } 200 | else { 201 | if (typeof value !== 'undefined' && value !== null) { 202 | var separator = url.indexOf('?') !== -1 ? '&' : '?'; 203 | hash = url.split('#'); 204 | url = hash[0] + separator + key + '=' + value; 205 | if (typeof hash[1] !== 'undefined' && hash[1] !== null) 206 | url += '#' + hash[1]; 207 | return url; 208 | } 209 | else 210 | return url; 211 | } 212 | } 213 | }); 214 | -------------------------------------------------------------------------------- /docs/gitbook/gitbook-plugin-sharing/buttons.js: -------------------------------------------------------------------------------- 1 | require(['gitbook', 'jquery'], function(gitbook, $) { 2 | var SITES = { 3 | 'facebook': { 4 | 'label': 'Facebook', 5 | 'icon': 'fa fa-facebook', 6 | 'onClick': function(e) { 7 | e.preventDefault(); 8 | window.open('http://www.facebook.com/sharer/sharer.php?s=100&p[url]='+encodeURIComponent(location.href)); 9 | } 10 | }, 11 | 'twitter': { 12 | 'label': 'Twitter', 13 | 'icon': 'fa fa-twitter', 14 | 'onClick': function(e) { 15 | e.preventDefault(); 16 | window.open('http://twitter.com/home?status='+encodeURIComponent(document.title+' '+location.href)); 17 | } 18 | }, 19 | 'google': { 20 | 'label': 'Google+', 21 | 'icon': 'fa fa-google-plus', 22 | 'onClick': function(e) { 23 | e.preventDefault(); 24 | window.open('https://plus.google.com/share?url='+encodeURIComponent(location.href)); 25 | } 26 | }, 27 | 'weibo': { 28 | 'label': 'Weibo', 29 | 'icon': 'fa fa-weibo', 30 | 'onClick': function(e) { 31 | e.preventDefault(); 32 | window.open('http://service.weibo.com/share/share.php?content=utf-8&url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)); 33 | } 34 | }, 35 | 'instapaper': { 36 | 'label': 'Instapaper', 37 | 'icon': 'fa fa-instapaper', 38 | 'onClick': function(e) { 39 | e.preventDefault(); 40 | window.open('http://www.instapaper.com/text?u='+encodeURIComponent(location.href)); 41 | } 42 | }, 43 | 'vk': { 44 | 'label': 'VK', 45 | 'icon': 'fa fa-vk', 46 | 'onClick': function(e) { 47 | e.preventDefault(); 48 | window.open('http://vkontakte.ru/share.php?url='+encodeURIComponent(location.href)); 49 | } 50 | } 51 | }; 52 | 53 | 54 | 55 | gitbook.events.bind('start', function(e, config) { 56 | var opts = config.sharing; 57 | 58 | // Create dropdown menu 59 | var menu = $.map(opts.all, function(id) { 60 | var site = SITES[id]; 61 | 62 | return { 63 | text: site.label, 64 | onClick: site.onClick 65 | }; 66 | }); 67 | 68 | // Create main button with dropdown 69 | if (menu.length > 0) { 70 | gitbook.toolbar.createButton({ 71 | icon: 'fa fa-share-alt', 72 | label: 'Share', 73 | position: 'right', 74 | dropdown: [menu] 75 | }); 76 | } 77 | 78 | // Direct actions to share 79 | $.each(SITES, function(sideId, site) { 80 | if (!opts[sideId]) return; 81 | 82 | gitbook.toolbar.createButton({ 83 | icon: site.icon, 84 | label: site.text, 85 | position: 'right', 86 | onClick: site.onClick 87 | }); 88 | }); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /docs/gitbook/images/apple-touch-icon-precomposed-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/gitbook/images/apple-touch-icon-precomposed-152.png -------------------------------------------------------------------------------- /docs/gitbook/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/gitbook/images/favicon.ico -------------------------------------------------------------------------------- /docs/images/elm-architecture-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/images/elm-architecture-1.jpeg -------------------------------------------------------------------------------- /docs/images/elm-architecture-2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/images/elm-architecture-2.jpeg -------------------------------------------------------------------------------- /docs/images/elm-architecture-3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/images/elm-architecture-3.jpeg -------------------------------------------------------------------------------- /docs/images/elm-architecture-4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/images/elm-architecture-4.jpeg -------------------------------------------------------------------------------- /docs/images/final-release.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/images/final-release.png -------------------------------------------------------------------------------- /docs/images/hello-world.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/images/hello-world.gif -------------------------------------------------------------------------------- /docs/images/intro-to-elm-initial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/images/intro-to-elm-initial.png -------------------------------------------------------------------------------- /docs/images/release-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/images/release-0.png -------------------------------------------------------------------------------- /docs/images/release-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/images/release-1.gif -------------------------------------------------------------------------------- /docs/images/release-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/images/release-2.png -------------------------------------------------------------------------------- /docs/images/release-3-part-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/images/release-3-part-2.gif -------------------------------------------------------------------------------- /docs/images/release-4-part-3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/docs/images/release-4-part-3.gif -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Introduction · GitBook 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 | 76 | 77 | 78 | 271 | 272 | 273 |
        274 | 275 |
        276 | 277 |
        278 | 279 | 280 | 281 | 290 | 291 | 292 | 293 | 294 |
        295 |
        296 | 297 |
        298 |
        299 | 300 |
        301 | 302 |

        Welcome to ElmBridge!

        303 |

        Today, you're going to be learning Elm! So exciting!

        304 |

        This tutorial is meant to be completed over the course of 5 hours, broken up into two chunks:

        305 |
          306 |
        • 2.5 hours on learning the basics of the language.
        • 307 |
        • 3 hours building an Elm application from scratch — specifically, an emoji translator!
        • 308 |
        309 |

        By the end of this tutorial, you can expect to:

        310 |
          311 |
        • Know what Elm is, and why people use it.
        • 312 |
        • Be familiar with the core data structures of the language.
        • 313 |
        • Understand the architecture of Elm applications.
        • 314 |
        • Be able to build interactive front-end applications in Elm.
        • 315 |
        316 |

        Thanks for coming! Let's get started.

        317 |

        Setup

        318 |

        We're going to be working with:

        319 |
          320 |
        • The latest version of the Elm language platform.
        • 321 |
        • The terminal.
        • 322 |
        • The text editor of your choice.
        • 323 |
        • The web browser of your choice.
        • 324 |
        325 |

        At various points today, you'll also be asked to download code from GitHub, so internet access is also required.

        326 |

        Installing Elm

        327 |

        If you haven't done so already, follow the official Elm guide for installing the language platform. Note for Windows users: You might want to check our handy Windows installation checklist to guide you through installation.

        328 |

        You can verify everything is working by launching the Elm REPL (What is a REPL?) by running the following command in your terminal:

        329 |
        elm repl
        330 | 
        331 |

        If you see the following message, you are ready to go!

        332 |
        ---- Elm 0.19.1 ----------------------------------------------------------------
        333 | Say :help for help and :exit to exit! More at <https://elm-lang.org/0.19.1/repl>
        334 | --------------------------------------------------------------------------------
        335 | >
        336 | 

        When you're done exploring, you can type :exit to get out.

        337 | 338 | 339 |
        340 | 341 |
        342 |
        343 |
        344 | 345 |

        results matching ""

        346 |
          347 | 348 |
          349 |
          350 | 351 |

          No results matching ""

          352 | 353 |
          354 |
          355 |
          356 | 357 |
          358 |
          359 | 360 |
          361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 |
          372 | 373 | 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 | -------------------------------------------------------------------------------- /docs/styles/website.css: -------------------------------------------------------------------------------- 1 | .markdown-section img { 2 | padding: 20px; 3 | background-color: #f7f7f7; 4 | } 5 | 6 | .hljs-title { 7 | color: #4271ae !important; 8 | } 9 | -------------------------------------------------------------------------------- /images/elm-architecture-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/images/elm-architecture-1.jpeg -------------------------------------------------------------------------------- /images/elm-architecture-2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/images/elm-architecture-2.jpeg -------------------------------------------------------------------------------- /images/elm-architecture-3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/images/elm-architecture-3.jpeg -------------------------------------------------------------------------------- /images/elm-architecture-4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/images/elm-architecture-4.jpeg -------------------------------------------------------------------------------- /images/final-release.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/images/final-release.png -------------------------------------------------------------------------------- /images/hello-world.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/images/hello-world.gif -------------------------------------------------------------------------------- /images/intro-to-elm-initial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/images/intro-to-elm-initial.png -------------------------------------------------------------------------------- /images/release-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/images/release-0.png -------------------------------------------------------------------------------- /images/release-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/images/release-1.gif -------------------------------------------------------------------------------- /images/release-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/images/release-2.png -------------------------------------------------------------------------------- /images/release-3-part-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/images/release-3-part-2.gif -------------------------------------------------------------------------------- /images/release-4-part-3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elmbridge/curriculum/3e407f94a61b64e91ebb3e20c7a0a03ef62ef729/images/release-4-part-3.gif -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "curriculum", 3 | "version": "1.0.0", 4 | "description": "Curriculum for the ElmBridge workshop. Hosted here: https://elmbridge.github.io/curriculum/", 5 | "dependencies": {}, 6 | "devDependencies": { 7 | "gitbook-cli": "^2.3.2" 8 | }, 9 | "scripts": { 10 | "build": "gitbook build --gitbook=3.2.3 . docs", 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/elmbridge/curriculum.git" 16 | }, 17 | "keywords": [], 18 | "author": "", 19 | "license": "ISC", 20 | "bugs": { 21 | "url": "https://github.com/elmbridge/curriculum/issues" 22 | }, 23 | "homepage": "https://github.com/elmbridge/curriculum#readme" 24 | } 25 | -------------------------------------------------------------------------------- /styles/website.css: -------------------------------------------------------------------------------- 1 | .markdown-section img { 2 | padding: 20px; 3 | background-color: #f7f7f7; 4 | } 5 | 6 | .hljs-title { 7 | color: #4271ae !important; 8 | } 9 | --------------------------------------------------------------------------------