[
  {
    "path": ".editorconfig",
    "content": "[*]\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\n\n[*.js]\nindent_style = tab\nindent_size = 2\n\n[*.elm]\nindent_style = space\nindent_size = 4\n\n[Makefile]\nindent_style = tab\nindent_size = 2\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nelm-stuff\nnode_modules\nelm.js\ndocumentation.json\n.vscode/tasks.json\nnpm-debug.log\n.envrc\ntests.js\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "// Place your settings in this file to overwrite default and user settings.\n{\n  \"search.exclude\": {\n\t\t\"**/node_modules\": true,\n\t\t\"**/bower_components\": true,\n\t\t\"**/*.elmo\": true,\n\t\t\"documentation.json\": true\n\t}\n}\n"
  },
  {
    "path": "Makefile",
    "content": "# Start basic application example locally\nbasic-up:\n\tcd ./examples/basic && elm reactor\n\nfull-up:\n\tcd ./examples/full && npm run dev\n\n# Generate documentation for preview\ndocs:\n\telm make --docs=documentation.json\n\n# Run unit tests locally\ntest-unit:\n\tnpm test\n\ntest-unit-ci:\n\tnpm install -g elm\n\tnpm install -g elm-test\n\telm-package install -y\n\tcd tests && elm-package install -y && cd ..\n\telm-test\n\nbuild-basic:\n\tcd examples/basic/ && elm make --yes Main.elm\n\nbuild-full:\n\tcd examples/full/ && elm make --yes src/Main.elm\n\ntest-ci:\n\tmake test-unit-ci\n\tmake build-basic\n\tmake build-full\n\n.PHONY: docs test\n"
  },
  {
    "path": "docs/building-routes.md",
    "content": "# Building routes\n\nAs of version 6 Hop doesn't provide matchers anymore, instead you can use [__UrlParser__](http://package.elm-lang.org/packages/evancz/url-parser).\n\nYou build your routes by using union types:\n\n```elm\ntype Route\n  = HomeRoute\n  | UserRoute Int\n  | UserStatusRoute Int\n  | NotFoundRoute\n```\n\nThen you need to create matchers for these routes:\n\n```elm\nimport UrlParser exposing ((</>), format, oneOf, int, s)\n\nmatchers =\n  oneOf [\n    UrlParser.format HomeRoute (s \"\")\n  , UrlParser.format UserRoute (s \"users\" </> int)\n  , UrlParser.format UserStatusRoute (s \"users\" </> int </> s \"status\")\n  ]\n```\n\nThese matchers will match:\n\n- \"/\"\n- \"users/1\"\n- \"users/1/status\"\n\n## Order matters\n\nThe order of the matchers makes a big difference. See these examples.\n\nGiven you have some routes and matchers:\n\n```elm\nimport UrlParser exposing (format, s, parse, int, oneOf, (</>))\n\ntype Route  = UserRoute Int | UserEditRoute Int\n\n-- match 'users/1'\nuserMatcher = format UserRoute (s \"users\" </> int)\n\n-- match '/uses/1/edit'\nuserEditMatcher = format UserEditRoute (s \"users\" </> int </> s \"edit\")\n```\n\n### Incorrect order\n\n```elm\nmatchers = \n    oneOf [userMatcher, userEditMatcher]\n\nparse identity matchers \"users/1\"\n== Ok (UserRoute 1) : Result.Result String Repl.Route\n\nparse identity matchers \"users/1/edit\"\n== Err \"The parser worked, but /edit was left over.\"\n```\n\nThe `userEditMatcher` doesn't even run in this case. The `userMatcher` fails and stops the flow.\n\n## Correct order\n\n```elm\nmatchers = \n    oneOf [userEditMatcher, userMatcher]\n\nparse identity matchers \"users/1\"\n== Ok (UserRoute 1) : Result.Result String Repl.Route\n\nparse identity matchers \"users/1/edit\"\n== Ok (UserEditRoute 1) : Result.Result String Repl.Route\n```\n\nThis works as expected, so is important to put the more specific matchers first.\n"
  },
  {
    "path": "docs/changelog.md",
    "content": "# Changelog\n\n### 6.0.0\n\n- Remove matchers (Use UrlParser instead)\n- Rename input and output functions\n- Encode and decode path segments\n\n### 5.0.1\n\n- Encode query string when converting location to URL.\n\n## 5.0.0\n\n- Update for Elm 0.17\n\n### 4.0.3\n\n- Fix issue where root path wouldn't match when using hash routing.\n\n### 4.0.2\n\n- Futher fix for navigating to root path, use / instead of .\n\n### 4.0.1\n\n- Fix https://github.com/sporto/hop/issues/20\n\n## 4.0.0\n\n- Support setState (No hashes)\n- Most functions take a config record\n\n## 3.0.0\n\n- Typed values in routes\n- Nested routes\n- Reverse routing\n\n### 2.1.1\n\n- Remove unnecessary dependency to `elm-test`\n\n### 2.1.0\n\n- Expose `Query` and `Url` types\n\n## 2.0.0\n\n- Remove dependency on `Erl`.\n- Change order of arguments on `addQuery`, `clearQuery`, `removeQuery` and `setQuery`\n\n### 1.2.1\n\n- Url is normalized before navigation i.e append `#/` if necessary\n\n### 1.2.0 \n\n- Added `addQuery`, changed behaviour of `setQuery`.\n\n### 1.1.1\n\n- Fixed issue where query string won't be set when no hash wash present\n"
  },
  {
    "path": "docs/matching-routes.md",
    "content": "# Matching routes\n\nCreate a parser using `Navigation.makeParser` combined with `Hop.makeResolver`.\nThere are serveral strategies you can use.\n\n## Given you have some configuration\n\n```\nroutes =\n  oneOf [...]\n\nhopConfig = \n  { ... }\n```\n\n## A parser that returns `(Route, Address)`\n\n```\nurlParserRouteAddress : Navigation.Parser ( MainRoute, Address )\nurlParserRouteAddress =\n    let\n        parse path =\n            path\n                |> UrlParser.parse identity routes\n                |> Result.withDefault NotFoundRoute\n\n        solver =\n            Hop.makeResolver configWithHash parse\n    in\n        Navigation.makeParser (.href >> solver)\n```\n\nThis parser:\n\n- Takes the `.href` from the `Location` record given by `Navigation`.\n- Converts that to a normalised path (done inside `makeResolver`).\n- Passes the normalised path to your `parse` function, which returns a matched route or `NotFoundRoute`.\n- When run returns a tuple `(Route, Address)`.\n\n## A parser that returns only the matched route\n\n```\nurlParserOnlyRoute : Navigation.Parser MainRoute\nurlParserOnlyRoute =\n    let\n        parse path =\n            path\n                |> UrlParser.parse identity routes\n                |> Result.withDefault NotFoundRoute\n\n        solver =\n            Hop.makeResolver configWithHash parse\n    in\n        Navigation.makeParser (.href >> solver >> fst)\n```\n\nThis parser only returns the matched route. The `address` record is discarded. \nHowever you probably need the address record for doing things with the query later.\n\n## A parser that returns the parser result + Address\n\n```\nurlParserResultAddress : Navigation.Parser (Result String MainRoute, Address)\nurlParserResultAddress =\n    let\n        parse path =\n            path\n                |> UrlParser.parse identity routes\n\n        solver =\n            Hop.makeResolver configWithHash parse\n    in\n        Navigation.makeParser (.href >> solver)\n```\n\nThis parser returns the result from `parse` e.g. `Result String MainRoute` and the address record.\n"
  },
  {
    "path": "docs/navigating.md",
    "content": "# Navigating\n\n## Changing the location\n\nUse `Hop.outputFromPath` for changing the browser location.\n\nAdd a message:\n\n```elm\ntype Msg\n  ...\n  | NavigateTo String\n```\n\nTrigger this message from you view:\n\n```elm\nbutton [ onClick (NavigateTo \"/users\") ] [ text \"Users\" ]\n```\n\nReact to this message in update:\n\n```elm\nNavigateTo path ->\n  let\n    command =\n      Hop.outputFromPath routerConfig path\n        |> Navigation.newUrl\n  in\n    ( model, command )\n```\n\n## Changing the query string\n\nAdd actions for changing the query string:\n\n```elm\ntype Msg\n  = ...\n  | AddQuery (Dict.Dict String String)\n  | SetQuery (Dict.Dict String String)\n  | ClearQuery\n```\n\nChange update to respond to these actions:\n\n```elm\nimport Hop exposing(addQuery, setQuery, clearQuery)\n\nupdate msg model =\n  case msg of\n    ...\n\n    AddQuery query ->\n      let\n        command =\n          model.address\n            |> Hop.addQuery query\n            |> Hop.output routerConfig\n            |> Navigation.newUrl\n      in\n        (model, command)\n```\n\nYou need to pass the current `address` record to these functions. \nThen you use that `address` record to generate a url using `output`.\n\nTrigger these messages from your views:\n\n```elm\nbutton [ onClick (AddQuery (Dict.singleton \"color\" \"red\")) ] [ text \"Set query\" ]\n```\n\nSee details of available functions at <http://package.elm-lang.org/packages/sporto/hop/latest/Hop>\n"
  },
  {
    "path": "docs/nesting-routes.md",
    "content": "# Nesting routes\n\nUrlParser supports nested routes:\n\n```elm\ntype UserRoute\n    = UsersRoute\n    | UserRoute UserId\n\ntype MainRoute\n    = HomeRoute\n    | AboutRoute\n    | UsersRoutes UserRoute\n    | NotFoundRoute\n\nusersMatchers =\n    [ UrlParser.format UserRoute (int)\n    , UrlParser.format UsersRoute (s \"\")\n    ]\n\nmainMatchers =\n    [ UrlParser.format HomeRoute (s \"\")\n    , UrlParser.format AboutRoute (s \"about\")\n    , UrlParser.format UsersRoutes (s \"users\" </> (oneOf usersMatchers))\n    ]\n\nmatchers =\n  oneOf mainMatchers \n```\n\nWith a setup like this UrlParser will be able to match routes like:\n\n- \"\" -> HomeRoute\n- \"/about\" -> AboutRoute\n- \"/users\" -> UsersRoutes UsersRoute\n- \"/users/2\" -> UsersRoutes (UserRoute 2)\n"
  },
  {
    "path": "docs/reverse-routing.md",
    "content": "# Reverse routing\n\nReverse routing means converting a route tag back to an url e.g.\n\n```\nUserRoute 1 --> \"/users/1\"\n```\n\nIn the current version Hop doesn't have any helpers for reverse routing. You can do this manually:\n\n```elm\nreverse : Route -> String\nreverse route =\n    case route of\n        HomeRoute ->\n            \"\"\n\n        AboutRoute ->\n            \"about\"\n\n        UserRoute id ->\n            \"users/\" ++ id \n\n        NotFoundRoute ->\n            \"\"\n```\n"
  },
  {
    "path": "docs/testing.md",
    "content": "# Testing Hop\n\n## Unit tests\n\n```bash\ncd tests\nelm package install -y\n\ncd ..\nnpm i\nnpm test\n```\n"
  },
  {
    "path": "docs/upgrade-2-to-3.md",
    "content": "# Upgrading from 2 to 3\n\nHop has changed in many ways in version 3. Please review the [Getting started guide](https://github.com/sporto/hop/blob/master/docs/getting-started.md). Following is an overview of the major changes.\n\n## Routes\n\nNow routes can take values, instead of:\n\n```elm\ntype Route\n  = Home\n  | Users\n  | User\n  | Token\n```\n\nYou can have values attached:\n\n```elm\ntype Route\n  = Home\n  | Users\n  | User Int\n  | Token String\n```\n\nRoutes are now defined using matchers. So instead of\n\n```elm\nroutes =\n  [ (\"/users/:int\", User) ]\n```\n\nYou do:\n\n```elm\nimport Hop.Matchers exposing(match2)\n\nuserMatcher =\n  match2 User \"/users/\" int\n\nmatchers =\n  [userMatcher]\n```\n\nThis is so we can have stronger types e.g. `User Int`.\n\n## Actions\n\nHop.signal now returns a tuple with `(Route, Location)`. Your application needs to map this to an action. e.g.\n\n```elm\n\ntype Action\n  = HopAction ()\n  | ApplyRoute ( Route, Location )\n\ntaggedRouterSignal : Signal Action\ntaggedRouterSignal =\n  Signal.map ApplyRoute router.signal\n```\n\nThis is so routes (`Route`) are different type than the application actions.\n\n## Payload\n\nBefore Hop returned a `payload` with a dictionary with matched parameters.\n\nNow it returns the matched route with the values e.g. `User 1` and a `Location` record in the form of a tuple:\n\n```elm\n(User 1, location)\n```\n\n`location` has the information about the current path and the query:\n\n```elm\n{\n  path = [\"users\", \"1\"],\n  query = <Dict String String>\n}\n```\n\n## Views\n\nIn your views you don't need to 'search' for the correct parameter in the payload anymore. The parameters are now in the route e.g. `User 1`.\n\nSo instead of doing:\n\n```elm\nuserId =\n  model.routerPayload.params\n    |> Dict.get \"userId\"\n    |> Maybe.withDefault \"\"\n```\n\nYou simply get the id from the route:\n\n```elm\ncase User userId ->\n  ...\n```\n\nThe query is still a dictionary. The query is now in the `location` record as shown above.\n"
  },
  {
    "path": "docs/upgrade-3-to-4.md",
    "content": "# Upgrading from 3 to 4\n\nVersion 4 introduces push state. There are two major changes:\n\n## Config\n\nConfig now includes `hash` and `basePath`.\n\n```elm\nrouterConfig : Config Route\nrouterConfig =\n  { hash = True\n  , basePath = \"\"\n  , matchers = matchers\n  , notFound = NotFoundRoute\n  }\n```\n\n## Functions require the router config\n\nMost functions in Hop now require your router config. For example instead of:\n\n```elm\nnavigateTo path\naddQuery query location\n```\n\nIt is now:\n\n```elm\nnavigateTo config path\naddQuery config query location\n```\n\nThis is because Hop needs to know if you are using hash or path routing.\n"
  },
  {
    "path": "docs/upgrade-4-to-5.md",
    "content": "# Upgrading from 4 to 5\n\nVersion 5 works with Navigation and Elm 0.17\n\n## Matchers\n\nAll matchers stay the same as in version 4.\n\n## Navigation\n\nNavigation is now handled by the Navigation module. See example app at `examples/basic/Main.elm`.\nHop doesn't return effects / commands anymore, this should be done by passing a path to `Navigation.modifyUrl`.\n"
  },
  {
    "path": "docs/upgrade-5-to-6.md",
    "content": "# Upgrading from 5 to 6\n\nVersion 6 removes Hop matchers. Use UrlParser instead. See Building Routes document for examples.\n"
  },
  {
    "path": "elm-package.json",
    "content": "{\n    \"version\": \"6.0.1\",\n    \"summary\": \"Routing and Navigation helpers for SPAs in Elm\",\n    \"repository\": \"https://github.com/sporto/hop.git\",\n    \"license\": \"MIT\",\n    \"source-directories\": [\n        \"examples\",\n        \"src\"\n    ],\n    \"exposed-modules\": [\n        \"Hop\",\n        \"Hop.Types\"\n    ],\n    \"dependencies\": {\n        \"elm-lang/core\": \"4.0.0 <= v < 5.0.0\",\n        \"evancz/elm-http\": \"3.0.1 <= v < 4.0.0\"\n    },\n    \"elm-version\": \"0.16.0 <= v < 0.18.0\"\n}\n"
  },
  {
    "path": "examples/basic/.dockerignore",
    "content": "Dockefile\nelm-stuff\n"
  },
  {
    "path": "examples/basic/.gitignore",
    "content": "index.html\n"
  },
  {
    "path": "examples/basic/Main.elm",
    "content": "module Main exposing (..)\n\n{-|\nYou will need Navigation, UrlParser and Hop.\n\n```\nelm package install elm-lang/navigation\nelm package install evancz/url-parser\nelm package install sporto/hop\n```\n-}\n\nimport Html exposing (..)\nimport Html.Attributes exposing (class)\nimport Html.Events exposing (onClick)\nimport Dict\nimport Navigation\nimport UrlParser exposing ((</>))\nimport Hop\nimport Hop.Types exposing (Config, Address, Query)\n\n\n-- ROUTES\n\n\n{-|\nDefine your routes as union types.\nYou need to provide a route for when the current URL doesn't match any known route i.e. NotFoundRoute.\n-}\ntype Route\n    = AboutRoute\n    | MainRoute\n    | NotFoundRoute\n\n\n{-|\nDefine route matchers.\nSee `docs/building-routes.md` for more examples.\n-}\nroutes : UrlParser.Parser (Route -> a) a\nroutes =\n    UrlParser.oneOf\n        [ UrlParser.format MainRoute (UrlParser.s \"\")\n        , UrlParser.format AboutRoute (UrlParser.s \"about\")\n        ]\n\n\n{-|\nDefine your router configuration.\n\nUse `hash = True` for hash routing e.g. `#/users/1`.\nUse `hash = False` for push state e.g. `/users/1`.\n\nThe `basePath` is only used for path routing.\nThis is useful if you application is not located at the root of a url e.g. `/app/v1/users/1` where `/app/v1` is the base path.\n-}\nhopConfig : Config\nhopConfig =\n    { hash = True\n    , basePath = \"\"\n    }\n\n\n\n-- MESSAGES\n\n\n{-|\nAdd messages for navigation and changing the query.\n-}\ntype Msg\n    = NavigateTo String\n    | SetQuery Query\n\n\n\n-- MODEL\n\n\n{-|\nAdd the current route and address to your model.\n\n- `Route` is your Route union type defined above.\n- `Hop.Address` is record to aid with changing the query string.\n\n`route` will be used for determining the current route in the views.\n\n`address` is needed because:\n\n- Some navigation functions in Hop need this information to rebuild the current address.\n- Your views might need information about the current query string.\n\n-}\ntype alias Model =\n    { address : Address\n    , route : Route\n    }\n\n\n{-|\nRespond to navigation messages in update i.e. NavigateTo and SetQuery\n-}\nupdate : Msg -> Model -> ( Model, Cmd Msg )\nupdate msg model =\n    case (Debug.log \"msg\" msg) of\n        NavigateTo path ->\n            let\n                command =\n                    -- First generate the URL using your config (`outputFromPath`).\n                    -- Then generate a command using Navigation.newUrl.\n                    Hop.outputFromPath hopConfig path\n                        |> Navigation.newUrl\n            in\n                ( model, command )\n\n        SetQuery query ->\n            let\n                command =\n                    -- First modify the current stored address record (setting the query)\n                    -- Then generate a URL using Hop.output\n                    -- Finally, create a command using Navigation.newUrl\n                    model.address\n                        |> Hop.setQuery query\n                        |> Hop.output hopConfig\n                        |> Navigation.newUrl\n            in\n                ( model, command )\n\n\n{-|\nCreate a URL Parser for Navigation\n-}\nurlParser : Navigation.Parser ( Route, Address )\nurlParser =\n    let\n        -- A parse function takes the normalised path from Hop after taking\n        -- in consideration the basePath and the hash.\n        -- This function then returns a result.\n        parse path =\n            -- First we parse using UrlParser.parse.\n            -- Then we return the parsed route or NotFoundRoute if the parsed failed.\n            -- You can choose to return the parse return directly.\n            path\n                |> UrlParser.parse identity routes\n                |> Result.withDefault NotFoundRoute\n\n        resolver =\n            -- Create a function that parses and formats the URL\n            -- This function takes 2 arguments: The Hop Config and the parse function.\n            Hop.makeResolver hopConfig parse\n    in\n        -- Create a Navigation URL parser\n        Navigation.makeParser (.href >> resolver)\n\n\n{-|\nNavigation will call urlUpdate when the address changes.\nThis function gets the result from `urlParser`, which is a tuple with (Route, Hop.Types.Address)\n\nAddress is a record that has:\n\n```elm\n{\n  path: List String,\n  query: Hop.Types.Query\n}\n```\n\n- `path` is an array of strings that has the current path e.g. `[\"users\", \"1\"]` for `\"/users/1\"`\n- `query` Is dictionary of String String. You can access this information in your views to show the relevant content.\n\nWe store these two things in our model. We keep the address because it is needed for matching a query string.\n\n-}\nurlUpdate : ( Route, Address ) -> Model -> ( Model, Cmd Msg )\nurlUpdate ( route, address ) model =\n    ( { model | route = route, address = address }, Cmd.none )\n\n\n\n-- VIEWS\n\n\nview : Model -> Html Msg\nview model =\n    div []\n        [ menu model\n        , pageView model\n        ]\n\n\nmenu : Model -> Html Msg\nmenu model =\n    div []\n        [ div []\n            [ button\n                [ class \"btnMain\"\n                , onClick (NavigateTo \"\")\n                ]\n                [ text \"Main\" ]\n            , button\n                [ class \"btnAbout\"\n                , onClick (NavigateTo \"about\")\n                ]\n                [ text \"About\" ]\n            , button\n                [ class \"btnQuery\"\n                , onClick (SetQuery (Dict.singleton \"keyword\" \"el/m\"))\n                ]\n                [ text \"Set query string\" ]\n            , currentQuery model\n            ]\n        ]\n\n\ncurrentQuery : Model -> Html msg\ncurrentQuery model =\n    let\n        query =\n            toString model.address.query\n    in\n        span [ class \"labelQuery\" ]\n            [ text query ]\n\n\n{-|\nViews can decide what to show using `model.route`.\n\n-}\npageView : Model -> Html msg\npageView model =\n    case model.route of\n        MainRoute ->\n            div [] [ h2 [ class \"title\" ] [ text \"Main\" ] ]\n\n        AboutRoute ->\n            div [] [ h2 [ class \"title\" ] [ text \"About\" ] ]\n\n        NotFoundRoute ->\n            div [] [ h2 [ class \"title\" ] [ text \"Not found\" ] ]\n\n\n\n-- APP\n\n\n{-|\nYour init function will receive an initial payload from Navigation, this payload is the initial matched location.\nHere we store the `route` and `address` in our model.\n-}\ninit : ( Route, Address ) -> ( Model, Cmd Msg )\ninit ( route, address ) =\n    ( Model address route, Cmd.none )\n\n\n{-|\nWire everything using Navigation.\n-}\nmain : Program Never\nmain =\n    Navigation.program urlParser\n        { init = init\n        , view = view\n        , update = update\n        , urlUpdate = urlUpdate\n        , subscriptions = (always Sub.none)\n        }\n"
  },
  {
    "path": "examples/basic/elm-package.json",
    "content": "{\n    \"version\": \"1.0.0\",\n    \"summary\": \"helpful summary of your project, less than 80 characters\",\n    \"repository\": \"https://github.com/user/project.git\",\n    \"license\": \"BSD3\",\n    \"source-directories\": [\n        \"../../src/\"\n    ],\n    \"exposed-modules\": [],\n    \"dependencies\": {\n        \"elm-lang/core\": \"4.0.0 <= v < 5.0.0\",\n        \"elm-lang/html\": \"1.0.0 <= v < 2.0.0\",\n        \"elm-lang/navigation\": \"1.0.0 <= v < 2.0.0\",\n        \"evancz/elm-http\": \"3.0.1 <= v < 4.0.0\",\n        \"evancz/url-parser\": \"1.0.0 <= v < 2.0.0\"\n    },\n    \"elm-version\": \"0.16.0 <= v < 0.18.0\"\n}\n"
  },
  {
    "path": "examples/basic/install-packages.sh",
    "content": "#!/bin/bash\n\nn=0\nuntil [ $n -ge 5 ]\ndo\n  elm package install -y && break\n  n=$[$n+1]\n  sleep 15\ndone\n"
  },
  {
    "path": "examples/basic/readme.md",
    "content": "# Basic Hop Example\n\n- Install packages `elm package install -y`\n- Run `elm reactor`\n- Open `http://localhost:8000/Main.elm`\n"
  },
  {
    "path": "examples/full/.gitignore",
    "content": "index.html\n"
  },
  {
    "path": "examples/full/dev_server.js",
    "content": "var path    = require('path');\nvar express = require('express');\nvar http    = require('http');\nvar webpack = require('webpack');\nvar config  = require('./webpack.config');\n\nvar app      = express();\nvar compiler = webpack(config);\nvar host     = 'localhost';\nvar port     = 3000;\n\napp.use(require('webpack-dev-middleware')(compiler, {\n  // contentBase: 'src',\n  noInfo: true,\n  publicPath: config.output.publicPath,\n  inline: true,\n  stats: { colors: true },\n}))\n\napp.get('/app', function(req, res) {\n  res.sendFile(path.join(__dirname, 'public/index.html'));\n});\n\napp.get('/app/*', function(req, res) {\n  res.sendFile(path.join(__dirname, 'public/index.html'));\n});\n\n// When hitting / redirect to app\napp.get('/', function(req, res) {\n  res.redirect('/app');\n});\n\n// Server images\napp.use(express.static('public'));\n\nvar server = http.createServer(app);\n\nserver.listen(port, function(err) {\n  if (err) {\n    console.log(err);\n    return;\n  }\n\n  var addr = server.address();\n\n  console.log('Listening at http://%s:%d', addr.address, addr.port);\n})\n"
  },
  {
    "path": "examples/full/elm-package.json",
    "content": "{\n    \"version\": \"1.0.0\",\n    \"summary\": \"helpful summary of your project, less than 80 characters\",\n    \"repository\": \"https://github.com/user/project.git\",\n    \"license\": \"BSD3\",\n    \"source-directories\": [\n        \"./src\",\n        \"../../src/\"\n    ],\n    \"exposed-modules\": [],\n    \"dependencies\": {\n        \"elm-lang/core\": \"4.0.0 <= v < 5.0.0\",\n        \"elm-lang/html\": \"1.0.0 <= v < 2.0.0\",\n        \"elm-lang/navigation\": \"1.0.0 <= v < 2.0.0\",\n        \"evancz/elm-http\": \"3.0.1 <= v < 4.0.0\",\n        \"evancz/url-parser\": \"1.0.0 <= v < 2.0.0\"\n    },\n    \"elm-version\": \"0.16.0 <= v < 0.18.0\"\n}\n"
  },
  {
    "path": "examples/full/install-packages.sh",
    "content": "#!/bin/bash\n\nn=0\nuntil [ $n -ge 5 ]\ndo\n  elm package install -y && break\n  n=$[$n+1]\n  sleep 15\ndone\n"
  },
  {
    "path": "examples/full/package.json",
    "content": "{\n  \"name\": \"full\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"elm.js\",\n  \"scripts\": {\n    \"dev\": \"node dev_server.js\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"ace-css\": \"^1.0.1\",\n    \"css-loader\": \"^0.23.1\",\n    \"elm-webpack-loader\": \"^3.0.1\",\n    \"express\": \"^4.13.4\",\n    \"file-loader\": \"^0.8.5\",\n    \"style-loader\": \"^0.13.1\",\n    \"url-loader\": \"^0.5.7\",\n    \"webpack\": \"^1.12.14\",\n    \"webpack-dev-middleware\": \"^1.5.1\"\n  }\n}\n"
  },
  {
    "path": "examples/full/readme.md",
    "content": "# Full Hop Example\n\nThis example uses push state.\n\nTo run:\n\n```\nelm package install -y\nnpm i\nnpm run dev\n```\n\nOpen http://localhost:3000\n\n## Webpack config\n\nThe Webpack config included in this example is not ready for production. Please refer to the Webpack site.\n"
  },
  {
    "path": "examples/full/src/Languages/Edit.elm",
    "content": "module Languages.Edit exposing (..)\n\nimport Html exposing (..)\nimport Html.Events exposing (on, targetValue)\nimport Html.Attributes exposing (href, style, src, value, name)\nimport Json.Decode as Json\nimport Languages.Models exposing (..)\nimport Languages.Messages exposing (..)\n\n\nstyles : Html.Attribute a\nstyles =\n    style\n        [ ( \"float\", \"left\" )\n        ]\n\n\nview : Language -> Html Msg\nview language =\n    div [ styles ]\n        [ h2 [] [ text language.name ]\n        , form []\n            [ input\n                [ value language.name\n                , name \"name\"\n                , on \"input\" (Json.map (Update language.id \"name\") targetValue)\n                ]\n                []\n            ]\n        ]\n"
  },
  {
    "path": "examples/full/src/Languages/Filter.elm",
    "content": "module Languages.Filter exposing (..)\n\nimport Html exposing (..)\nimport Html.Events exposing (onClick)\nimport Html.Attributes exposing (id, class, href, style)\nimport Dict\nimport Languages.Messages exposing (..)\n\n\ntype alias ViewModel =\n    {}\n\n\nstyles : Html.Attribute a\nstyles =\n    style\n        [ ( \"float\", \"left\" )\n        , ( \"margin-left\", \"2rem\" )\n        , ( \"margin-right\", \"2rem\" )\n        ]\n\n\nview : ViewModel -> Html Msg\nview model =\n    div [ styles ]\n        [ h2 [] [ text \"Filter\" ]\n        , btn \"btnSetQuery\" \"SetQuery\" (SetQuery (Dict.singleton \"latests\" \"true\"))\n        , div []\n            [ h3 [] [ text \"Kind\" ]\n            , div []\n                [ btn \"btnAll\" \"All\" (AddQuery (Dict.singleton \"typed\" \"\"))\n                , btn \"btnDynamic\" \"Dynamic\" (AddQuery (Dict.singleton \"typed\" \"dynamic\"))\n                , btn \"btnStatic\" \"Static\" (AddQuery (Dict.singleton \"typed\" \"static\"))\n                ]\n            ]\n        ]\n\n\nbtn : String -> String -> Msg -> Html Msg\nbtn viewId label action =\n    button [ id viewId, class \"btn btn-primary btn-small inline-block mr1\", onClick action ]\n        [ text label ]\n"
  },
  {
    "path": "examples/full/src/Languages/List.elm",
    "content": "module Languages.List exposing (..)\n\nimport Html exposing (..)\nimport Html.Events exposing (onClick)\nimport Html.Attributes exposing (class, href, style)\nimport Hop.Types exposing (Address)\nimport Dict\nimport Languages.Models exposing (..)\nimport Languages.Messages exposing (..)\n\n\ntype alias ViewModel =\n    { languages : List Language\n    , address : Address\n    }\n\n\nstyles : Html.Attribute a\nstyles =\n    style\n        [ ( \"float\", \"left\" )\n        , ( \"margin-right\", \"2rem\" )\n        ]\n\n\nhasTag : String -> Language -> Bool\nhasTag tag language =\n    List.any (\\t -> t == tag) language.tags\n\n\nfilteredLanguages : ViewModel -> List Language\nfilteredLanguages model =\n    let\n        typed =\n            model.address.query\n                |> Dict.get \"typed\"\n                |> Maybe.withDefault \"\"\n    in\n        case typed of\n            \"\" ->\n                model.languages\n\n            _ ->\n                List.filter (hasTag typed) model.languages\n\n\nview : ViewModel -> Html Msg\nview model =\n    div [ styles ]\n        [ h2 [] [ text \"Languages\" ]\n        , table []\n            [ tbody [] (tableRows (filteredLanguages model)) ]\n        ]\n\n\ntableRows : List Language -> List (Html Msg)\ntableRows collection =\n    List.map rowView collection\n\n\nrowView : Language -> Html Msg\nrowView language =\n    tr []\n        [ td [] [ text (toString language.id) ]\n        , td []\n            [ text language.name\n            ]\n        , td []\n            [ actionBtn (Show language.id) \"Show\"\n            , actionBtn (Edit language.id) \"Edit\"\n            ]\n        ]\n\n\nactionBtn : Msg -> String -> Html Msg\nactionBtn action label =\n    button [ class \"regular btn btn-small\", onClick action ]\n        [ text label ]\n"
  },
  {
    "path": "examples/full/src/Languages/Messages.elm",
    "content": "module Languages.Messages exposing (..)\n\nimport Dict\nimport Languages.Models exposing (..)\n\n\ntype alias Prop =\n    String\n\n\ntype alias Value =\n    String\n\n\ntype Msg\n    = Show LanguageId\n    | Edit LanguageId\n    | Update LanguageId Prop Value\n    | AddQuery (Dict.Dict String String)\n    | SetQuery (Dict.Dict String String)\n"
  },
  {
    "path": "examples/full/src/Languages/Models.elm",
    "content": "module Languages.Models exposing (..)\n\n\ntype alias LanguageId =\n    Int\n\n\ntype alias Language =\n    { id : LanguageId\n    , name : String\n    , image : String\n    , tags : List String\n    }\n\n\n\n-- ROUTING\n\n\ntype Route\n    = LanguagesRoute\n    | LanguageRoute LanguageId\n    | LanguageEditRoute LanguageId\n\n\nlanguages : List Language\nlanguages =\n    [ { id = 1\n      , name = \"Elm\"\n      , image = \"elm\"\n      , tags = [ \"functional\", \"browser\", \"static\" ]\n      }\n    , { id = 2\n      , name = \"JavaScript\"\n      , image = \"js\"\n      , tags = [ \"functional\", \"oo\", \"browser\", \"dynamic\", \"prototypical\" ]\n      }\n    , { id = 3\n      , name = \"Go\"\n      , image = \"go\"\n      , tags = [ \"oo\", \"google\", \"static\" ]\n      }\n    , { id = 4\n      , name = \"Rust\"\n      , image = \"rust\"\n      , tags = [ \"functional\", \"mozilla\", \"static\" ]\n      }\n    , { id = 5\n      , name = \"Elixir\"\n      , image = \"elixir\"\n      , tags = [ \"functional\", \"erlang\", \"dynamic\" ]\n      }\n    , { id = 6\n      , name = \"Ruby\"\n      , image = \"ruby\"\n      , tags = [ \"oo\", \"japan\", \"1996\", \"dynamic\", \"classical\" ]\n      }\n    , { id = 7\n      , name = \"Python\"\n      , image = \"python\"\n      , tags = [ \"oo\", \"dynamic\", \"classical\" ]\n      }\n    , { id = 8\n      , name = \"Swift\"\n      , image = \"swift\"\n      , tags = [ \"functional\", \"apple\", \"static\", \"classical\" ]\n      }\n    , { id = 9\n      , name = \"Haskell\"\n      , image = \"haskell\"\n      , tags = [ \"functional\", \"static\" ]\n      }\n    , { id = 10\n      , name = \"Java\"\n      , image = \"java\"\n      , tags = [ \"oo\", \"static\", \"classical\" ]\n      }\n    , { id = 11\n      , name = \"C#\"\n      , image = \"csharp\"\n      , tags = [ \"oo\", \"microsoft\", \"static\", \"classical\" ]\n      }\n    , { id = 12\n      , name = \"PHP\"\n      , image = \"php\"\n      , tags = [ \"oo\", \"server\", \"1994\", \"dynamic\", \"classical\" ]\n      }\n    ]\n"
  },
  {
    "path": "examples/full/src/Languages/Routing.elm",
    "content": "module Languages.Routing exposing (..)\n\nimport Languages.Models exposing (..)\nimport UrlParser exposing ((</>), int, s, string)\n\n\nmatchers : List (UrlParser.Parser (Route -> a) a)\nmatchers =\n    [ UrlParser.format LanguageEditRoute (int </> s \"edit\")\n    , UrlParser.format LanguageRoute (int)\n    , UrlParser.format LanguagesRoute (s \"\")\n    ]\n\n\ntoS : a -> String\ntoS =\n    toString\n\n\nreverseWithPrefix : Route -> String\nreverseWithPrefix route =\n    \"/languages\" ++ (reverse route)\n\n\nreverse : Route -> String\nreverse route =\n    case route of\n        LanguagesRoute ->\n            \"/\"\n\n        LanguageRoute id ->\n            \"/\" ++ (toS id)\n\n        LanguageEditRoute id ->\n            \"/\" ++ (toS id) ++ \"/edit\"\n"
  },
  {
    "path": "examples/full/src/Languages/Show.elm",
    "content": "module Languages.Show exposing (..)\n\nimport Html exposing (..)\nimport Html.Attributes exposing (id, href, style, src)\nimport Languages.Models exposing (..)\nimport Languages.Messages exposing (..)\n\n\nstyles : Html.Attribute a\nstyles =\n    style\n        [ ( \"float\", \"left\" )\n        ]\n\n\nview : Language -> Html Msg\nview language =\n    div [ styles ]\n        [ h2 [ id \"titleLanguage\" ] [ text language.name ]\n        , img [ src (\"/images/\" ++ language.image ++ \".png\") ] []\n        , tags language\n        ]\n\n\ntags : Language -> Html Msg\ntags language =\n    div [] (List.map tag language.tags)\n\n\ntag : String -> Html Msg\ntag tagName =\n    span []\n        [ text (tagName ++ \", \")\n        ]\n"
  },
  {
    "path": "examples/full/src/Languages/Update.elm",
    "content": "module Languages.Update exposing (..)\n\nimport Debug\nimport Navigation\nimport Hop exposing (output, outputFromPath, addQuery, setQuery)\nimport Hop.Types exposing (Config, Address)\nimport Routing\nimport Languages.Models exposing (..)\nimport Languages.Messages exposing (Msg(..))\nimport Languages.Routing\n\n\ntype alias UpdateModel =\n    { languages : List Language\n    , address : Address\n    }\n\n\nrouterConfig : Config\nrouterConfig =\n    Routing.config\n\n\nnavigationCmd : String -> Cmd a\nnavigationCmd path =\n    path\n        |> outputFromPath routerConfig\n        |> Navigation.modifyUrl\n\n\nupdate : Msg -> UpdateModel -> ( UpdateModel, Cmd Msg )\nupdate message model =\n    case Debug.log \"message\" message of\n        Show id ->\n            let\n                path =\n                    Languages.Routing.reverseWithPrefix (Languages.Models.LanguageRoute id)\n            in\n                ( model, navigationCmd path )\n\n        Edit id ->\n            let\n                path =\n                    Languages.Routing.reverseWithPrefix (Languages.Models.LanguageEditRoute id)\n            in\n                ( model, navigationCmd path )\n\n        Update id prop value ->\n            let\n                udpatedLanguages =\n                    List.map (updateWithId id prop value) model.languages\n            in\n                ( { model | languages = udpatedLanguages }, Cmd.none )\n\n        AddQuery query ->\n            let\n                command =\n                    model.address\n                        |> addQuery query\n                        |> output routerConfig\n                        |> Navigation.modifyUrl\n            in\n                ( model, command )\n\n        SetQuery query ->\n            let\n                command =\n                    model.address\n                        |> setQuery query\n                        |> output routerConfig\n                        |> Navigation.modifyUrl\n            in\n                ( model, command )\n\n\nupdateWithId : LanguageId -> String -> String -> Language -> Language\nupdateWithId id prop value language =\n    if id == language.id then\n        case prop of\n            \"name\" ->\n                { language | name = value }\n\n            _ ->\n                language\n    else\n        language\n"
  },
  {
    "path": "examples/full/src/Languages/View.elm",
    "content": "module Languages.View exposing (..)\n\nimport Html exposing (..)\nimport Html.Attributes exposing (href, style)\nimport Hop.Types exposing (Address)\nimport Languages.Models exposing (LanguageId, Language, Route, Route(..))\nimport Languages.Messages exposing (..)\nimport Languages.Filter\nimport Languages.List\nimport Languages.Show\nimport Languages.Edit\n\n\ntype alias ViewModel =\n    { languages : List Language\n    , address : Address\n    , route : Route\n    }\n\n\ncontainerStyle : Html.Attribute a\ncontainerStyle =\n    style\n        [ ( \"margin-bottom\", \"5rem\" )\n        , ( \"overflow\", \"auto\" )\n        ]\n\n\nview : ViewModel -> Html Msg\nview model =\n    div [ containerStyle ]\n        [ Languages.Filter.view {}\n        , Languages.List.view { languages = model.languages, address = model.address }\n        , subView model\n        ]\n\n\nsubView : ViewModel -> Html Msg\nsubView model =\n    case model.route of\n        LanguageRoute languageId ->\n            let\n                maybeLanguage =\n                    getLanguage model.languages languageId\n            in\n                case maybeLanguage of\n                    Just language ->\n                        Languages.Show.view language\n\n                    Nothing ->\n                        notFoundView model\n\n        LanguageEditRoute languageId ->\n            let\n                maybeLanguage =\n                    getLanguage model.languages languageId\n            in\n                case maybeLanguage of\n                    Just language ->\n                        Languages.Edit.view language\n\n                    _ ->\n                        notFoundView model\n\n        LanguagesRoute ->\n            emptyView\n\n\nemptyView : Html msg\nemptyView =\n    span [] []\n\n\nnotFoundView : ViewModel -> Html msg\nnotFoundView model =\n    div []\n        [ text \"Not Found\"\n        ]\n\n\ngetLanguage : List Language -> LanguageId -> Maybe Language\ngetLanguage languages id =\n    languages\n        |> List.filter (\\lang -> lang.id == id)\n        |> List.head\n"
  },
  {
    "path": "examples/full/src/Main.elm",
    "content": "module Main exposing (..)\n\nimport Navigation\nimport Hop\nimport Hop.Types exposing (Address)\nimport Messages exposing (..)\nimport Models exposing (..)\nimport Update exposing (..)\nimport View exposing (..)\nimport Routing\nimport String\nimport UrlParser\n\n\nurlParser : Navigation.Parser ( Route, Address )\nurlParser =\n    let\n        parse path =\n            path\n                |> UrlParser.parse identity Routing.routes\n                |> Result.withDefault NotFoundRoute\n\n        matcher =\n            Hop.makeResolver Routing.config parse\n    in\n        Navigation.makeParser (.href >> matcher)\n\n\nurlUpdate : ( Route, Address ) -> AppModel -> ( AppModel, Cmd Msg )\nurlUpdate ( route, address ) model =\n    let\n        _ =\n            Debug.log \"urlUpdate address\" address\n    in\n        ( { model | route = route, address = address }, Cmd.none )\n\n\ninit : ( Route, Address ) -> ( AppModel, Cmd Msg )\ninit ( route, address ) =\n    ( newAppModel route address, Cmd.none )\n\n\nmain : Program Never\nmain =\n    Navigation.program urlParser\n        { init = init\n        , view = view\n        , update = update\n        , urlUpdate = urlUpdate\n        , subscriptions = (always Sub.none)\n        }\n"
  },
  {
    "path": "examples/full/src/Messages.elm",
    "content": "module Messages exposing (..)\n\nimport Hop.Types exposing (Query)\nimport Languages.Messages\n\n\ntype Msg\n    = SetQuery Query\n    | LanguagesMsg Languages.Messages.Msg\n    | ShowHome\n    | ShowLanguages\n    | ShowAbout\n"
  },
  {
    "path": "examples/full/src/Models.elm",
    "content": "module Models exposing (..)\n\nimport Hop.Types exposing (Address, newAddress)\nimport Languages.Models exposing (Language, languages)\n\n\ntype Route\n    = HomeRoute\n    | AboutRoute\n    | LanguagesRoutes Languages.Models.Route\n    | NotFoundRoute\n\n\ntype alias AppModel =\n    { languages : List Language\n    , address : Address\n    , route : Route\n    , selectedLanguage : Maybe Language\n    }\n\n\nnewAppModel : Route -> Address -> AppModel\nnewAppModel route address =\n    { languages = languages\n    , address = address\n    , route = route\n    , selectedLanguage = Maybe.Nothing\n    }\n"
  },
  {
    "path": "examples/full/src/Routing.elm",
    "content": "module Routing exposing (..)\n\nimport Models exposing (..)\nimport Hop.Types exposing (Config)\nimport Languages.Routing\nimport UrlParser exposing ((</>), oneOf, int, s)\nimport Languages.Routing\n\n\nmatchers : List (UrlParser.Parser (Route -> a) a)\nmatchers =\n    [ UrlParser.format HomeRoute (s \"\")\n    , UrlParser.format AboutRoute (s \"about\")\n    , UrlParser.format LanguagesRoutes (s \"languages\" </> (oneOf Languages.Routing.matchers))\n    ]\n\n\nroutes : UrlParser.Parser (Route -> a) a\nroutes =\n    oneOf matchers\n\n\nconfig : Config\nconfig =\n    { basePath = \"/app\"\n    , hash = False\n    }\n\nreverse : Route -> String\nreverse route =\n    case route of\n        HomeRoute ->\n            \"\"\n\n        AboutRoute ->\n            \"about\"\n\n        LanguagesRoutes subRoute ->\n            \"languages\" ++ Languages.Routing.reverse subRoute\n\n        NotFoundRoute ->\n            \"\"\n"
  },
  {
    "path": "examples/full/src/Update.elm",
    "content": "module Update exposing (..)\n\nimport Debug\nimport Navigation\nimport Hop exposing (output, outputFromPath, setQuery)\nimport Hop.Types exposing (Config)\nimport Messages exposing (..)\nimport Models exposing (..)\nimport Routing\nimport Languages.Update\nimport Languages.Models\n\n\nnavigationCmd : String -> Cmd a\nnavigationCmd path =\n    path\n        |> outputFromPath Routing.config\n        |> Navigation.newUrl\n\n\nrouterConfig : Config\nrouterConfig =\n    Routing.config\n\n\nupdate : Msg -> AppModel -> ( AppModel, Cmd Msg )\nupdate message model =\n    case Debug.log \"message\" message of\n        LanguagesMsg subMessage ->\n            let\n                updateModel =\n                    { languages = model.languages\n                    , address = model.address\n                    }\n\n                ( updatedModel, cmd ) =\n                    Languages.Update.update subMessage updateModel\n            in\n                ( { model | languages = updatedModel.languages }, Cmd.map LanguagesMsg cmd )\n\n        SetQuery query ->\n            let\n                command =\n                    model.address\n                        |> setQuery query\n                        |> output routerConfig\n                        |> Navigation.newUrl\n            in\n                ( model, command )\n\n        ShowHome ->\n            let\n                path =\n                    Routing.reverse HomeRoute\n            in\n                ( model, navigationCmd path )\n\n        ShowLanguages ->\n            let\n                path =\n                    Routing.reverse (LanguagesRoutes Languages.Models.LanguagesRoute)\n            in\n                ( model, navigationCmd path )\n\n        ShowAbout ->\n            let\n                path =\n                    Routing.reverse AboutRoute\n            in\n                ( model, navigationCmd path )\n"
  },
  {
    "path": "examples/full/src/View.elm",
    "content": "module View exposing (..)\n\nimport Html exposing (..)\nimport Html.App\nimport Html.Events exposing (onClick)\nimport Html.Attributes exposing (id, class, href, style)\nimport Models exposing (..)\nimport Messages exposing (..)\nimport Languages.View\n\n\nview : AppModel -> Html Msg\nview model =\n    div []\n        [ menu model\n        , pageView model\n        ]\n\n\nmenu : AppModel -> Html Msg\nmenu model =\n    div [ class \"p2 white bg-black\" ]\n        [ div []\n            [ menuLink ShowHome \"btnHome\" \"Home\"\n            , text \"|\"\n            , menuLink ShowLanguages \"btnLanguages\" \"Languages\"\n            , text \"|\"\n            , menuLink ShowAbout \"btnAbout\" \"About\"\n            ]\n        ]\n\n\nmenuLink : Msg -> String -> String -> Html Msg\nmenuLink message viewId label =\n    a\n        [ id viewId\n        , href \"javascript://\"\n        , onClick message\n        , class \"white px2\"\n        ]\n        [ text label ]\n\n\npageView : AppModel -> Html Msg\npageView model =\n    case model.route of\n        HomeRoute ->\n            div [ class \"p2\" ]\n                [ h1 [ id \"title\", class \"m0\" ] [ text \"Home\" ]\n                , div [] [ text \"Click on Languages to start routing\" ]\n                ]\n\n        AboutRoute ->\n            div [ class \"p2\" ]\n                [ h1 [ id \"title\", class \"m0\" ] [ text \"About\" ]\n                ]\n\n        LanguagesRoutes languagesRoute ->\n            let\n                viewModel =\n                    { languages = model.languages\n                    , route = languagesRoute\n                    , address = model.address\n                    }\n            in\n                div [ class \"p2\" ]\n                    [ h1 [ id \"title\", class \"m0\" ] [ text \"Languages\" ]\n                    , Html.App.map LanguagesMsg (Languages.View.view viewModel)\n                    ]\n\n        NotFoundRoute ->\n            notFoundView model\n\n\nnotFoundView : AppModel -> Html msg\nnotFoundView model =\n    div []\n        [ text \"Not Found\"\n        ]\n"
  },
  {
    "path": "examples/full/src/index.js",
    "content": "'use strict';\n\nrequire('ace-css/css/ace.css');\n\nvar Elm = require('./Main.elm');\nvar mountNode = document.getElementById('main');\n\nvar app = Elm.Main.embed(mountNode);\n"
  },
  {
    "path": "examples/full/webpack.config.js",
    "content": "var path = require(\"path\");\n\n/*\npublicPath is used for finding the bundles during dev\ne.g. http://localhost:3000/bundles/app.js\nWhen the index.html is served using the webpack server then just specify the path.\nWhen index.html is served using a framework e.g. from Rails, Phoenix or Go\nthen you must specify the full url where the webpack dev server is running e.g. http://localhost:4000/bundles/\nThis path is also used for resolving relative assets e.g. fonts from css. So for production and staging this path has to be\noverriden. See webpack.prod.config.js\n*/\nvar publicPath = '/bundles/'\n\nmodule.exports = {\n  entry: {\n    app: [\n      './src/index.js'\n    ]\n  },\n\n  output: {\n    path: path.resolve(__dirname + '/dist'),\n    filename: '[name].js',\n    publicPath:  publicPath,\n  },\n\n  module: {\n    loaders: [\n      {\n        test: /\\.(css|scss)$/,\n        loaders: [\n          'style-loader',\n          'css-loader',\n        ]\n      },\n      {\n        test:    /\\.html$/,\n        exclude: /node_modules/,\n        loader:  'file?name=[name].[ext]',\n      },\n      {\n        test:    /\\.elm$/,\n        exclude: [/elm-stuff/, /node_modules/],\n        loader:  'elm-webpack',\n      },\n      {\n        test: /\\.woff(2)?(\\?v=[0-9]\\.[0-9]\\.[0-9])?$/,\n        loader: 'url-loader?limit=10000&minetype=application/font-woff',\n      },\n      {\n        test: /\\.(ttf|eot|svg)(\\?v=[0-9]\\.[0-9]\\.[0-9])?$/,\n        loader: 'file-loader',\n      },\n    ],\n\n    noParse: /\\.elm$/,\n  },\n\n};\n"
  },
  {
    "path": "license.md",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015-present, Sebastian Porto\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"Hop\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"\",\n  \"scripts\": {\n    \"test\": \"elm-test\"\n  },\n  \"author\": \"\",\n  \"license\": \"MIT\",\n  \"devDependencies\": {\n    \"elm-test\": \"^0.17.3\"\n  }\n}\n"
  },
  {
    "path": "readme.md",
    "content": "# Hop: Navigation and routing helpers for Elm SPAs\n\n[![Build Status](https://semaphoreci.com/api/v1/sporto/hop/branches/master/badge.svg)](https://semaphoreci.com/sporto/hop)\n\n![alt Hop](https://raw.githubusercontent.com/sporto/hop/master/assets/logo.png)\n\n__With the release of Elm 0.18 the official libraries `Navigation` and `UrlParser` have become a lot more robust and useful. They now integrate a lot of the functionality that Hop used to provide for previous versions of Elm. For example `UrlParser` now has `parseHash` and `parsePath`. Because of this I'm not convinced that Hop needs to be upgraded to Elm 0.18. I'll wait and see if this library could provide value in 0.18.__\n\nHop is a helper library meant to be used with:\n\n- [__Navigation v1__](http://package.elm-lang.org/packages/elm-lang/navigation) for listening to location changes in the browser and pushing changes to it.\n- [__UrlParser v1__](http://package.elm-lang.org/packages/evancz/url-parser) for constructing routes and parsing URLs.\n\n## What Hop provides\n\nOn top of these two packages above, Hop helps with:\n\n- Abstracting the differences between push or hash routing\n- Providing helpers for working with the query string\n- Encode / Decode the location path\n- Encode / Decode the query string\n\n## Getting Started\n\nPlease see this [example app](https://github.com/sporto/hop/blob/master/examples/basic/Main.elm). It explains how to wire everything in the comments.\n\n## Docs\n\n### [Building routes](https://github.com/sporto/hop/blob/master/docs/building-routes.md)\n### [Nesting routes](https://github.com/sporto/hop/blob/master/docs/nesting-routes.md)\n### [Matching routes](https://github.com/sporto/hop/blob/master/docs/matching-routes.md)\n### [Navigating](https://github.com/sporto/hop/blob/master/docs/navigating.md)\n### [Reverse routing](https://github.com/sporto/hop/blob/master/docs/reverse-routing.md)\n### [API](http://package.elm-lang.org/packages/sporto/hop/latest/)\n### [Changelog](./docs/changelog.md)\n### [Testing Hop](https://github.com/sporto/hop/blob/master/docs/testing.md)\n\n## More docs\n\n### [Upgrade guide 5 to 6](https://github.com/sporto/hop/blob/master/docs/upgrade-5-to-6.md)\n### [Upgrade guide 4 to 5](https://github.com/sporto/hop/blob/master/docs/upgrade-4-to-5.md)\n### [Upgrade guide 3 to 4](https://github.com/sporto/hop/blob/master/docs/upgrade-3-to-4.md)\n### [Upgrade guide 2.1 to 3.0](https://github.com/sporto/hop/blob/master/docs/upgrade-2-to-3.md)\n\n### [Version 5 documentation](https://github.com/sporto/hop/tree/v5)\n### [Version 4 documentation](https://github.com/sporto/hop/tree/v4)\n### [Version 3 documentation](https://github.com/sporto/hop/tree/v3)\n### [Version 2 documentation](https://github.com/sporto/hop/tree/v2)\n\n### Hash routing\n\nA proper url should have the query before the hash e.g. `?keyword=Ja#/users/1`,\nbut when using hash routing, query parameters are appended after the hash path e.g. `#/users/1?keyword=Ja`. \nThis is done for aesthetics and so the router is fully controlled by the hash fragment.\n\n## Examples\n\nSee `examples/basic` and `examples/full` folders. To run the example apps:\n\n- Clone this repo\n- Go to example folder\n- Follow the readme in that folder\n"
  },
  {
    "path": "src/Hop/Address.elm",
    "content": "module Hop.Address exposing (..)\n\nimport Dict\nimport String\nimport Http exposing (uriEncode, uriDecode)\nimport Hop.Types exposing (..)\n\n{-|\nGet the Path\n-}\ngetPath : Address -> String\ngetPath address =\n    address.path\n        |> List.map uriEncode\n        |> String.join \"/\"\n        |> String.append \"/\"\n\n\n{-|\nGet the query string from a Address.\nIncluding ?\n-}\ngetQuery : Address -> String\ngetQuery address =\n    if Dict.isEmpty address.query then\n        \"\"\n    else\n        address.query\n            |> Dict.toList\n            |> List.map (\\( k, v ) -> ( uriEncode k, uriEncode v ))\n            |> List.map (\\( k, v ) -> k ++ \"=\" ++ v)\n            |> String.join \"&\"\n            |> String.append \"?\"\n\n\n\n--------------------------------------------------------------------------------\n-- PARSING\n-- Parse a path into a Address\n--------------------------------------------------------------------------------\n\n\nparse : String -> Address\nparse route =\n    { path = parsePath route\n    , query = parseQuery route\n    }\n\n\nextractPath : String -> String\nextractPath route =\n    route\n        |> String.split \"#\"\n        |> List.reverse\n        |> List.head\n        |> Maybe.withDefault \"\"\n        |> String.split \"?\"\n        |> List.head\n        |> Maybe.withDefault \"\"\n\n\nparsePath : String -> List String\nparsePath route =\n    route\n        |> extractPath\n        |> String.split \"/\"\n        |> List.filter (\\segment -> not (String.isEmpty segment))\n        |> List.map uriDecode\n\n\nextractQuery : String -> String\nextractQuery route =\n    route\n        |> String.split \"?\"\n        |> List.drop 1\n        |> List.head\n        |> Maybe.withDefault \"\"\n\n\nparseQuery : String -> Query\nparseQuery route =\n    route\n        |> extractQuery\n        |> String.split \"&\"\n        |> List.filter (not << String.isEmpty)\n        |> List.map queryKVtoTuple\n        |> Dict.fromList\n\n\n{-| @priv\nConvert a string to a tuple. Decode on the way.\n\n    \"k=1\" --> (\"k\", \"1\")\n-}\nqueryKVtoTuple : String -> ( String, String )\nqueryKVtoTuple kv =\n    let\n        splitted =\n            kv\n                |> String.split \"=\"\n\n        first =\n            splitted\n                |> List.head\n                |> Maybe.withDefault \"\"\n\n        firstDecoded =\n            uriDecode first\n\n        second =\n            splitted\n                |> List.drop 1\n                |> List.head\n                |> Maybe.withDefault \"\"\n\n        secondDecoded =\n            uriDecode second\n    in\n        ( firstDecoded, secondDecoded )\n"
  },
  {
    "path": "src/Hop/AddressTest.elm",
    "content": "module Hop.AddressTest exposing (..)\n\nimport Dict\nimport Expect\nimport Hop.Address as Address\nimport Hop.Types as Types\nimport Test exposing (..)\n\n\ngetPathTest : Test\ngetPathTest =\n    let\n        inputs =\n            [ ( \"it works\"\n              , { path = [ \"users\", \"1\" ], query = Dict.singleton \"k\" \"1\" }\n              , \"/users/1\"\n              )\n            , ( \"it encodes\"\n              , { path = [ \"us/ers\", \"1\" ], query = Dict.empty }\n              , \"/us%2Fers/1\"\n              )\n            ]\n\n        run ( testCase, address, expected ) =\n            test testCase\n                <| \\() ->\n                    let\n                        actual =\n                            Address.getPath address\n                    in\n                        Expect.equal expected actual\n    in\n        describe \"getPath\" (List.map run inputs)\n\n\ngetQuery : Test\ngetQuery =\n    let\n        inputs =\n            [ ( \"it works\"\n              , { path = [], query = Dict.singleton \"k\" \"1\" }\n              , \"?k=1\"\n              )\n            , ( \"it encoders\"\n              , { path = [], query = Dict.singleton \"k\" \"a/b\" }\n              , \"?k=a%2Fb\"\n              )\n            ]\n\n        run ( testCase, address, expected ) =\n            test testCase\n                <| \\() ->\n                    let\n                        actual =\n                            Address.getQuery address\n                    in\n                        Expect.equal expected actual\n    in\n        describe \"getQuery\" (List.map run inputs)\n\n\nparseTest : Test\nparseTest =\n    let\n        inputs =\n            [ ( \"it parses\"\n              , \"/users/1?a=1\"\n              , { path = [ \"users\", \"1\" ], query = Dict.singleton \"a\" \"1\" }\n              )\n            , ( \"it decodes\"\n              , \"/a%2Fb/1?k=x%2Fy\"\n              , { path = [ \"a/b\", \"1\" ], query = Dict.singleton \"k\" \"x/y\" }\n              )\n            ]\n\n        run ( testCase, location, expected ) =\n            test testCase\n                <| \\() ->\n                    let\n                        actual =\n                            Address.parse location\n                    in\n                        Expect.equal expected actual\n    in\n        describe \"parse\" (List.map run inputs)\n\n\nall : Test\nall =\n    describe \"Location\"\n        [ getPathTest\n        , getQuery\n        , parseTest\n        ]\n"
  },
  {
    "path": "src/Hop/In.elm",
    "content": "module Hop.In exposing (..)\n\nimport Regex\nimport String\nimport Hop.Types exposing (Address, Config)\nimport Hop.Address exposing (parse)\n\n\n{-| @priv\n-}\ningest : Config -> String -> Address\ningest config href =\n    href\n        |> removeProtocol\n        |> removeDomain\n        |> getRelevantPathWithQuery config\n        |> parse\n\n\n{-| @priv\n-}\nremoveProtocol : String -> String\nremoveProtocol href =\n    href\n        |> String.split \"//\"\n        |> List.reverse\n        |> List.head\n        |> Maybe.withDefault \"\"\n\n\n{-| @priv\n-}\nremoveDomain : String -> String\nremoveDomain href =\n    href\n        |> String.split \"/\"\n        |> List.tail\n        |> Maybe.withDefault []\n        |> String.join \"/\"\n        |> String.append \"/\"\n\n\n{-| @priv\n-}\ngetRelevantPathWithQuery : Config -> String -> String\ngetRelevantPathWithQuery config href =\n    if config.hash then\n        href\n            |> String.split \"#\"\n            |> List.drop 1\n            |> List.head\n            |> Maybe.withDefault \"\"\n    else\n        href\n            |> String.split \"#\"\n            |> List.head\n            |> Maybe.withDefault \"\"\n            |> removeBase config\n\n\n{-| @priv\nRemove the basePath from a path\n\n\"/basepath/a/b?k=1\" -> \"/a/b?k=1\"\n-}\nremoveBase : Config -> String -> String\nremoveBase config pathWithQuery =\n    let\n        regex =\n            Regex.regex config.basePath\n    in\n        Regex.replace (Regex.AtMost 1) regex (always \"\") pathWithQuery\n"
  },
  {
    "path": "src/Hop/InTest.elm",
    "content": "module Hop.InTest exposing (..)\n\nimport Dict\nimport Expect\nimport Hop.In exposing (ingest)\nimport Test exposing (..)\n\ntype Route\n    = NotFound\n\n\nconfig =\n    { hash = True\n    , basePath = \"\"\n    }\n\n\nconfigWithPath =\n    { config | hash = False }\n\n\nconfigWithPathAndBase =\n    { configWithPath | basePath = \"/app/v1\" }\n\n\ninputTest : Test\ninputTest =\n    let\n        inputs =\n            [ ( \"it parses an empty hash\"\n              , config\n              , \"http://localhost:3000/basepath\"\n              , { path = [], query = Dict.empty }\n              )\n            , ( \"it parses a hash\"\n              , config\n              , \"http://localhost:3000/basepath#/users/1\"\n              , { path = [ \"users\", \"1\" ], query = Dict.empty }\n              )\n            , ( \"it parses a path\"\n              , configWithPath\n              , \"http://localhost:3000/users/1\"\n              , { path = [ \"users\", \"1\" ], query = Dict.empty }\n              )\n            , ( \"it parses a path with basepath\"\n              , configWithPathAndBase\n              , \"http://localhost:3000/app/v1/users/1\"\n              , { path = [ \"users\", \"1\" ], query = Dict.empty }\n              )\n            , ( \"it parses a hash with query\"\n              , config\n              , \"http://localhost:3000/basepath#/users/1?a=1\"\n              , { path = [ \"users\", \"1\" ], query = Dict.singleton \"a\" \"1\" }\n              )\n            , ( \"it parses a path with query\"\n              , configWithPath\n              , \"http://localhost:3000/users/1?a=1\"\n              , { path = [ \"users\", \"1\" ], query = Dict.singleton \"a\" \"1\" }\n              )\n            , ( \"it parses a path with basepath and query\"\n              , configWithPathAndBase\n              , \"http://localhost:3000/app/v1/users/1?a=1\"\n              , { path = [ \"users\", \"1\" ], query = Dict.singleton \"a\" \"1\" }\n              )\n            , ( \"it decodes the query\"\n              , config\n              , \"http://localhost:3000/basepath#/?a%20b%26c%3Fd=1%202%263%3F4\"\n              , { path = [], query = Dict.singleton \"a b&c?d\" \"1 2&3?4\" }\n              )\n            ]\n\n        run ( testCase, config, href, expected ) =\n            test testCase\n                <| \\() ->\n                    let\n                        actual =\n                            ingest config href\n                    in\n                        Expect.equal expected actual\n    in\n        describe \"ingest\" (List.map run inputs)\n\n\nall : Test\nall =\n    describe \"In\"\n        [ inputTest\n        ]\n"
  },
  {
    "path": "src/Hop/Out.elm",
    "content": "module Hop.Out exposing (..)\n\nimport String\nimport Hop.Types exposing (Address, Config)\nimport Hop.Utils exposing (dedupSlash)\nimport Hop.Address exposing (parse)\n\n\n{-|\nMake a real path from an address record.\nThis will add the hash and the basePath as necessary.\n\n    fromAddress config { path = [\"users\", \"1\"], query = Dict.empty }\n\n    ==\n\n    \"#/users/1\"\n\n-}\noutput : Config -> Address -> String\noutput config address =\n    let\n        -- path -> \"/a/1\"\n        path =\n            Hop.Address.getPath address\n\n        -- query -> \"?a=1\"\n        query =\n            Hop.Address.getQuery address\n\n        url =\n            if config.hash then\n                \"#\" ++ path ++ query\n            else if String.isEmpty config.basePath then\n                path ++ query\n            else if path == \"/\" then\n                \"/\" ++ config.basePath ++ query\n            else\n                \"/\" ++ config.basePath ++ path ++ query\n\n        realPath =\n            dedupSlash url\n    in\n        if realPath == \"\" then\n            \"/\"\n        else\n            realPath\n\n\n{-|\nMake a real path from a simulated path.\nThis will add the hash and the basePath as necessary.\n\n    toRealPath config \"/users\"\n\n    ==\n\n    \"#/users\"\n-}\noutputFromPath : Config -> String -> String\noutputFromPath config path =\n    path\n        |> Hop.Address.parse\n        |> output config\n"
  },
  {
    "path": "src/Hop/OutTest.elm",
    "content": "module Hop.OutTest exposing (..)\n\nimport Dict\nimport Expect\nimport Hop.Out as Out\nimport Hop.Types exposing (newAddress)\nimport Hop.TestHelper exposing (configWithHash, configWithPath, configPathAndBasePath)\nimport Test exposing (..)\n\n\noutputTest : Test\noutputTest =\n    let\n        empty =\n            newAddress\n\n        inputs =\n            [ ( \"hash: it is empty when empty\"\n              , configWithHash\n              , empty\n              , \"\"\n              , \"#/\"\n              )\n            , ( \"path: it is empty when empty\"\n              , configWithPath\n              , empty\n              , \"\"\n              , \"/\"\n              )\n            , ( \"basepath: it has the basepath\"\n              , configPathAndBasePath\n              , empty\n              , \"\"\n              , \"/app/v1\"\n              )\n            , ( \"basepath: adds slash when missing\"\n              , { configPathAndBasePath | basePath = \"app/v1\" }\n              , empty\n              , \"\"\n              , \"/app/v1\"\n              )\n              -- path\n            , ( \"hash: it adds the path\"\n              , configWithHash\n              , { empty | path = [ \"a\", \"b\" ] }\n              , \"/a/b\"\n              , \"#/a/b\"\n              )\n            , ( \"path: it adds the path\"\n              , configWithPath\n              , { empty | path = [ \"a\", \"b\" ] }\n              , \"/a/b\"\n              , \"/a/b\"\n              )\n            , ( \"path: it adds the basepath and path\"\n              , configPathAndBasePath\n              , { empty | path = [ \"a\", \"b\" ] }\n              , \"/a/b\"\n              , \"/app/v1/a/b\"\n              )\n              -- query\n            , ( \"hash: it adds the query as pseudo query\"\n              , configWithHash\n              , { empty | query = Dict.singleton \"k\" \"1\" }\n              , \"?k=1\"\n              , \"#/?k=1\"\n              )\n            , ( \"path: it adds the query\"\n              , configWithPath\n              , { empty | query = Dict.singleton \"k\" \"1\" }\n              , \"?k=1\"\n              , \"/?k=1\"\n              )\n            , ( \"path: it adds the basepath query\"\n              , configPathAndBasePath\n              , { empty | query = Dict.singleton \"k\" \"1\" }\n              , \"?k=1\"\n              , \"/app/v1?k=1\"\n              )\n              -- path and query\n            , ( \"hash: it adds the path and query\"\n              , configWithHash\n              , { empty | query = Dict.singleton \"k\" \"1\", path = [ \"a\", \"b\" ] }\n              , \"/a/b?k=1\"\n              , \"#/a/b?k=1\"\n              )\n            , ( \"path: it adds the path and query\"\n              , configWithPath\n              , { empty | query = Dict.singleton \"k\" \"1\", path = [ \"a\", \"b\" ] }\n              , \"/a/b?k=1\"\n              , \"/a/b?k=1\"\n              )\n            , ( \"path: it adds the basepath, path and query\"\n              , configPathAndBasePath\n              , { empty | query = Dict.singleton \"k\" \"1\", path = [ \"a\", \"b\" ] }\n              , \"/a/b?k=1\"\n              , \"/app/v1/a/b?k=1\"\n              )\n            , ( \"hash: it encodes\"\n              , configWithHash\n              , { empty | query = Dict.singleton \"a/d\" \"1/4\", path = [ \"a/b\", \"1\" ] }\n              , \"/a%2Fb/1?a%2Fd=1%2F4\"\n              , \"#/a%2Fb/1?a%2Fd=1%2F4\"\n              )\n            ]\n\n        run ( testCase, config, address, path, expected ) =\n            [ test testCase\n                <| \\() ->\n                    let\n                        actual =\n                            Out.output config address\n                    in\n                        Expect.equal expected actual\n            , test testCase\n                <| \\() ->\n                    let\n                        actual =\n                            Out.outputFromPath config path\n                    in\n                        Expect.equal expected actual\n            ]\n        \n        tests =\n            List.concatMap  run inputs\n    in\n        describe \"output and outputFromPath\" tests\n\n\nall : Test\nall =\n    describe \"In\"\n        [ outputTest\n        ]\n"
  },
  {
    "path": "src/Hop/TestHelper.elm",
    "content": "module Hop.TestHelper exposing (..)\n\nimport Hop.Types exposing (Config)\n\n\nconfigWithHash : Config\nconfigWithHash =\n    { basePath = \"\"\n    , hash = True\n    }\n\n\nconfigWithPath : Config\nconfigWithPath =\n    { basePath = \"\"\n    , hash = False\n    }\n\n\nconfigPathAndBasePath : Config\nconfigPathAndBasePath =\n    { basePath = \"/app/v1\"\n    , hash = False\n    }\n"
  },
  {
    "path": "src/Hop/Types.elm",
    "content": "module Hop.Types exposing (Config, Query, Address, newQuery, newAddress)\n\n{-| Types used in Hop\n\n#Types\n@docs Config, Address, Query\n\n#Factories\n@docs newQuery, newAddress\n-}\n\nimport Dict\n\n{-| A Dict that holds query parameters\n\n    Dict.Dict String String\n-}\ntype alias Query =\n    Dict.Dict String String\n\n{-| A Record that represents the current location\nIncludes a `path` and a `query`\n\n    {\n      path: List String,\n      query: Query\n    }\n-}\ntype alias Address =\n    { path : List String\n    , query : Query\n    }\n\n{-| Hop Configuration\n\n- basePath: Only for pushState routing (not hash). e.g. \"/app\".\n- hash: True for hash routing, False for pushState routing.\n\n-}\ntype alias Config =\n    { basePath : String\n    , hash : Bool\n    }\n\n{-|\nCreate an empty Query record\n-}\nnewQuery : Query\nnewQuery =\n    Dict.empty\n\n\n{-|\nCreate an empty Address record\n-}\nnewAddress : Address\nnewAddress =\n    { query = newQuery\n    , path = []\n    }\n"
  },
  {
    "path": "src/Hop/Utils.elm",
    "content": "module Hop.Utils exposing (..)\n\nimport Regex\n\ndedupSlash : String -> String\ndedupSlash =\n    Regex.replace Regex.All (Regex.regex \"/+\") (\\_ -> \"/\")\n"
  },
  {
    "path": "src/Hop.elm",
    "content": "module Hop\n    exposing\n        ( addQuery\n        , clearQuery\n        , ingest\n        , makeResolver\n        , output\n        , outputFromPath\n        , pathFromAddress\n        , queryFromAddress\n        , removeQuery\n        , setQuery\n        )\n\n{-| Navigation and routing utilities for single page applications. See [readme](https://github.com/sporto/hop) for usage.\n\n# Consuming an URL from the browser\n@docs ingest, makeResolver\n\n# Preparing a URL for changing the browser location\n@docs output, outputFromPath\n\n# Work with an Address record\n@docs pathFromAddress, queryFromAddress\n\n# Modify the query string\n@docs addQuery, setQuery, removeQuery, clearQuery\n\n-}\n\nimport Dict\nimport String\nimport Hop.Address\nimport Hop.In\nimport Hop.Out\nimport Hop.Types exposing (Address, Config, Query)\n\n\n---------------------------------------\n-- INGEST\n---------------------------------------\n\n\n{-|\nConvert a raw url to an Address record. Use this function for 'normalizing' the URL before parsing it.\nThis conversion will take in account your basePath and hash configuration.\n\nE.g. with path routing\n\n    config =\n        { basePath = \"\"\n        , hash = False\n        }\n\n    ingest config \"http://localhost:3000/app/languages/1?k=1\"\n    -->\n    { path = [\"app\", \"languages\", \"1\" ], query = Dict.singleton \"k\" \"1\" }\n\nE.g. with path routing and base path\n\n    config =\n        { basePath = \"/app/v1\"\n        , hash = False\n        }\n\n    ingest config \"http://localhost:3000/app/v1/languages/1?k=1\"\n    -->\n    { path = [\"languages\", \"1\" ], query = Dict.singleton \"k\" \"1\" }\n\nE.g. with hash routing\n\n    config =\n        { basePath = \"\"\n        , hash = True\n        }\n\n    ingest config \"http://localhost:3000/app#/languages/1?k=1\"\n    -->\n    { path = [\"languages\", \"1\" ], query = Dict.singleton \"k\" \"1\" }\n-}\ningest : Config -> String -> Address\ningest =\n    Hop.In.ingest\n\n\n{-|\n`makeResolver` normalizes the URL using your config and then gives that normalised URL to your parser.\n\nUse this for creating a function to give to `Navigation.makeParser`. \nSee examples in `docs/matching-routes.md`.\n\n    Hop.makeResolver hopConfig parse\n\n`makeResolver` takes 2 arguments.\n\n### Config e.g.\n\n    { basePath = \"\"\n    , hash = False\n    }\n\n### Parse function\n\nA function that receives the normalised path and returns the result of parsing it.\n\n    parse path =\n        path\n            |> UrlParser.parse identity routes\n            |> Result.withDefault NotFoundRoute\n\nYou parse function will receive the path like this:\n\n`http://example.com/users/1` --> 'users/1/'\n\nSo it won't have a leading /, but it will have a trailing /. This is because the way UrlParse works.\n\n### Return value from resolver\n\nAfter being called with a URL the resolver will return a tuple with `(parse result, address)` e.g.\n\n    resolver =\n        Hop.makeResolver hopConfig parse\n\n    resolver \"http://example.com/index.html#/users/2\"\n\n    -->\n\n    ( UserRoute 2, { path = [\"users\", \"2\"], query = ...} )\n\n### Example\n\nA complete example looks like:\n\n    urlParser : Navigation.Parser ( Route, Address )\n    urlParser =\n        let\n            parse path =\n                path\n                    |> UrlParser.parse identity routes\n                    |> Result.withDefault NotFoundRoute\n\n            resolver =\n                Hop.makeResolver hopConfig parse\n        in\n            Navigation.makeParser (.href >> resolver)\n\n-}\nmakeResolver :\n    Config\n    -> (String -> result)\n    -> String\n    -> (result, Address)\nmakeResolver config parse rawInput =\n    let\n        address =\n            rawInput\n                |> ingest config\n\n        parseResult =\n            pathFromAddress address\n                ++ \"/\"\n                |> String.dropLeft 1\n                |> parse\n    in\n        (parseResult, address)\n\n\n\n---------------------------------------\n-- CREATE OUTBOUND URLs\n---------------------------------------\n\n\n{-|\nConvert an Address record to an URL to feed the browser.\nThis will take in account your basePath and hash config.\n\nE.g. with path routing\n\n    output config { path = [\"languages\", \"1\" ], query = Dict.singleton \"k\" \"1\" }\n    -->\n    \"/languages/1?k=1\"\n\nE.g. with hash routing\n\n    output config { path = [\"languages\", \"1\" ], query = Dict.singleton \"k\" \"1\" }\n    -->\n    \"#/languages/1?k=1\"\n-}\noutput : Config -> Address -> String\noutput =\n    Hop.Out.output\n\n\n{-|\nConvert a string to an URL to feed the browser.\nThis will take in account your basePath and hash config.\n\nE.g. with path routing\n\n    outputFromPath config \"/languages/1?k=1\"\n    -->\n    \"/languages/1?k=1\"\n\nE.g. with path routing + basePath\n\n    outputFromPath config \"/languages/1?k=1\"\n    -->\n    \"/app/languages/1?k=1\"\n\nE.g. with hash routing\n\n    output config \"/languages/1?k=1\"\n    -->\n    \"#/languages/1?k=1\"\n-}\noutputFromPath : Config -> String -> String\noutputFromPath =\n    Hop.Out.outputFromPath\n\n\n\n---------------------------------------\n-- WORK WITH ADDRESS\n---------------------------------------\n\n\n{-|\nGet the path as a string from an Address record.\n\n    address = { path = [\"languages\", \"1\" ], query = Dict.singleton \"k\" \"1\" }\n\n    pathFromAddress address\n    -->\n    \"/languages/1\"\n-}\npathFromAddress : Address -> String\npathFromAddress =\n    Hop.Address.getPath\n\n\n{-|\nGet the query as a string from an Address record.\n\n    address = { path = [\"app\"], query = Dict.singleton \"k\" \"1\" }\n\n    queryFromAddress address\n    -->\n    \"?k=1\"\n-}\nqueryFromAddress : Address -> String\nqueryFromAddress =\n    Hop.Address.getQuery\n\n\n\n-------------------------------------------------------------------------------\n-- QUERY MUTATION\n-------------------------------------------------------------------------------\n\n\n{-|\nAdd query string values (patches any existing values) to an Address record.\n\n    addQuery query address\n\n    addQuery (Dict.Singleton \"b\" \"2\") { path = [], query = Dict.fromList [(\"a\", \"1\")] }\n\n    ==\n\n    { path = [], query = Dict.fromList [(\"a\", \"1\"), (\"b\", \"2\")] }\n\n- query is a dictionary with keys to add\n\nTo remove a key / value pair set the value to \"\"\n-}\naddQuery : Query -> Address -> Address\naddQuery query location =\n    let\n        updatedQuery =\n            Dict.union query location.query\n    in\n        { location | query = updatedQuery }\n\n\n{-|\nSet the whole query string (removes any existing values).\n\n    setQuery query address\n-}\nsetQuery : Query -> Address -> Address\nsetQuery query location =\n    { location | query = query }\n\n\n{-|\nRemove one key from the query string\n\n    removeQuery key address\n-}\nremoveQuery : String -> Address -> Address\nremoveQuery key location =\n    let\n        updatedQuery =\n            Dict.remove key location.query\n    in\n        { location | query = updatedQuery }\n\n\n{-| Clear all query string values\n\n    clearQuery address\n-}\nclearQuery : Address -> Address\nclearQuery location =\n    { location | query = Dict.empty }\n"
  },
  {
    "path": "src/HopTest.elm",
    "content": "module HopTest exposing (..)\n\nimport Expect\nimport Test exposing (..)\nimport Hop.TestHelper exposing (configWithHash, configWithPath, configPathAndBasePath)\nimport Hop\n\n\nmakeResolverTest : Test\nmakeResolverTest =\n    let\n        inputs =\n            [ ( \"path\"\n              , configWithPath\n              , \"http://example.com/users/1\"\n              , \"users/1/\"\n              )\n            , ( \"path with base\"\n              , configPathAndBasePath\n              , \"http://example.com/app/v1/users/1\"\n              , \"users/1/\"\n              )\n            , ( \"path\"\n              , configWithHash\n              , \"http://example.com/app#/users/1\"\n              , \"users/1/\"\n              )\n            ]\n\n        run ( testCase, config, href, expected ) =\n            test testCase\n                <| \\() ->\n                    let\n                        resolver =\n                            Hop.makeResolver config identity\n\n                        ( actual, _ ) =\n                            resolver href\n                    in\n                        Expect.equal expected actual\n    in\n        describe \"makeResolver\" (List.map run inputs)\n\n\nall : Test\nall =\n    describe \"Hop\"\n        [ makeResolverTest\n        ]\n"
  },
  {
    "path": "tests/IntegrationTest.elm",
    "content": "module IntegrationTest exposing (..)\n\nimport UrlParser exposing ((</>), oneOf, int, s)\nimport Navigation exposing (Location)\nimport Expect\nimport String\nimport Test exposing (..)\nimport Hop.TestHelper exposing (configWithHash, configWithPath, configPathAndBasePath)\nimport Hop\nimport Hop.Types exposing (Address, Config)\n\n\ntype alias UserId =\n    Int\n\n\ntype UserRoute\n    = UsersRoute\n    | UserRoute UserId\n    | UserEditRoute UserId\n\n\ntype MainRoute\n    = HomeRoute\n    | AboutRoute\n    | UsersRoutes UserRoute\n    | NotFoundRoute\n\n\nusersMatchers =\n    [ UrlParser.format UserEditRoute (int </> s \"edit\")\n    , UrlParser.format UserRoute (int)\n    , UrlParser.format UsersRoute (s \"\")\n    ]\n\n\nmainMatchers =\n    [ UrlParser.format HomeRoute (s \"\")\n    , UrlParser.format AboutRoute (s \"about\")\n    , UrlParser.format UsersRoutes (s \"users\" </> (oneOf usersMatchers))\n    ]\n\n\nroutes =\n    oneOf mainMatchers\n\n\nnewLocation : Location\nnewLocation =\n    { hash = \"\"\n    , host = \"example.com\"\n    , hostname = \"example.com\"\n    , href = \"\"\n    , origin = \"\"\n    , password = \"\"\n    , pathname = \"\"\n    , port_ = \"\"\n    , protocol = \"http\"\n    , search = \"\"\n    , username = \"\"\n    }\n\n\nparseWithUrlParser : Config -> Location -> ( MainRoute, Address )\nparseWithUrlParser currentConfig =\n    let\n        parse path =\n            path\n                |> UrlParser.parse identity routes\n                |> Result.withDefault NotFoundRoute\n    in\n        .href >> Hop.makeResolver currentConfig parse\n\n\n\n------------------------------\n-- Example urlParsers\n------------------------------\n\n\nurlParserRouteAddress : Navigation.Parser ( MainRoute, Address )\nurlParserRouteAddress =\n    let\n        parse path =\n            path\n                |> UrlParser.parse identity routes\n                |> Result.withDefault NotFoundRoute\n\n        solver =\n            Hop.makeResolver configWithHash parse\n    in\n        Navigation.makeParser (.href >> solver)\n\n\nurlParserOnlyRoute : Navigation.Parser MainRoute\nurlParserOnlyRoute =\n    let\n        parse path =\n            path\n                |> UrlParser.parse identity routes\n                |> Result.withDefault NotFoundRoute\n\n        solver =\n            Hop.makeResolver configWithHash parse\n    in\n        Navigation.makeParser (.href >> solver >> fst)\n\nurlParserResultAddress : Navigation.Parser (Result String MainRoute, Address)\nurlParserResultAddress =\n    let\n        parse path =\n            path\n                |> UrlParser.parse identity routes\n\n        solver =\n            Hop.makeResolver configWithHash parse\n    in\n        Navigation.makeParser (.href >> solver)\n\n\nurlParserIntegrationTest : Test\nurlParserIntegrationTest =\n    let\n        inputs =\n            [ ( \"Home page\"\n              , configWithPath\n              , \"http://example.com\"\n              , HomeRoute\n              , \"/\"\n              )\n            , ( \"Base: Home page\"\n              , configPathAndBasePath\n              , \"http://example.com/app/v1\"\n              , HomeRoute\n              , \"/app/v1\"\n              )\n            , ( \"Hash: Home page with /#\"\n              , configWithHash\n              , \"http://example.com/#\"\n              , HomeRoute\n              , \"#/\"\n              )\n            , ( \"Hash: Home page with /#/\"\n              , configWithHash\n              , \"http://example.com/#/\"\n              , HomeRoute\n              , \"#/\"\n              )\n            , ( \"Hash: Home page without hash\"\n              , configWithHash\n              , \"http://example.com\"\n              , HomeRoute\n              , \"#/\"\n              )\n            , ( \"Hash: Home page\"\n              , configWithHash\n              , \"http://example.com/index.html\"\n              , HomeRoute\n              , \"#/\"\n              )\n              -- about\n            , ( \"AboutRoute\"\n              , configWithPath\n              , \"http://example.com/about\"\n              , AboutRoute\n              , \"/about\"\n              )\n            , ( \"Base: AboutRoute\"\n              , configPathAndBasePath\n              , \"http://example.com/app/v1/about\"\n              , AboutRoute\n              , \"/app/v1/about\"\n              )\n            , ( \"Hash: AboutRoute\"\n              , configWithHash\n              , \"http://example.com/#/about\"\n              , AboutRoute\n              , \"#/about\"\n              )\n            , ( \"Hash: AboutRoute with slash\"\n              , configWithHash\n              , \"http://example.com/app#/about\"\n              , AboutRoute\n              , \"#/about\"\n              )\n              -- users\n            , ( \"UsersRoute\"\n              , configWithPath\n              , \"http://example.com/users\"\n              , UsersRoutes UsersRoute\n              , \"/users\"\n              )\n            , ( \"Base: UsersRoute\"\n              , configPathAndBasePath\n              , \"http://example.com/app/v1/users\"\n              , UsersRoutes UsersRoute\n              , \"/app/v1/users\"\n              )\n            , ( \"Hash: UsersRoute\"\n              , configWithHash\n              , \"http://example.com/#/users\"\n              , UsersRoutes UsersRoute\n              , \"#/users\"\n              )\n              -- users with query\n            , ( \"UsersRoute\"\n              , configWithPath\n              , \"http://example.com/users?k=1\"\n              , UsersRoutes UsersRoute\n              , \"/users?k=1\"\n              )\n            , ( \"Base: UsersRoute\"\n              , configPathAndBasePath\n              , \"http://example.com/app/v1/users?k=1\"\n              , UsersRoutes UsersRoute\n              , \"/app/v1/users?k=1\"\n              )\n            , ( \"Hash: UsersRoute\"\n              , configWithHash\n              , \"http://example.com/#/users?k=1\"\n              , UsersRoutes UsersRoute\n              , \"#/users?k=1\"\n              )\n              -- user\n            , ( \"UserRoute\"\n              , configWithPath\n              , \"http://example.com/users/2\"\n              , UsersRoutes (UserRoute 2)\n              , \"/users/2\"\n              )\n            , ( \"Base: UserRoute\"\n              , configPathAndBasePath\n              , \"http://example.com/app/v1/users/2\"\n              , UsersRoutes (UserRoute 2)\n              , \"/app/v1/users/2\"\n              )\n            , ( \"Hash: UserRoute\"\n              , configWithHash\n              , \"http://example.com/#/users/2\"\n              , UsersRoutes (UserRoute 2)\n              , \"#/users/2\"\n              )\n              -- user edit\n            , ( \"UserRoute\"\n              , configWithPath\n              , \"http://example.com/users/2/edit\"\n              , UsersRoutes (UserEditRoute 2)\n              , \"/users/2/edit\"\n              )\n            , ( \"Base: UserRoute\"\n              , configPathAndBasePath\n              , \"http://example.com/app/v1/users/2/edit\"\n              , UsersRoutes (UserEditRoute 2)\n              , \"/app/v1/users/2/edit\"\n              )\n            , ( \"Hash: UserRoute\"\n              , configWithHash\n              , \"http://example.com/#/users/2/edit\"\n              , UsersRoutes (UserEditRoute 2)\n              , \"#/users/2/edit\"\n              )\n            ]\n\n        run ( testCase, currentConfig, href, expected, expectedRoundTrip ) =\n            [ test testCase\n                <| \\() ->\n                    let\n                        location =\n                            { newLocation | href = href }\n\n                        ( actual, _ ) =\n                            parseWithUrlParser currentConfig location\n                    in\n                        Expect.equal expected actual\n            , test (testCase ++ \" - output\")\n                <| \\() ->\n                    let\n                        location =\n                            { newLocation | href = href }\n\n                        ( _, address ) =\n                            parseWithUrlParser currentConfig location\n\n                        actual =\n                            Hop.output currentConfig address\n                    in\n                        Expect.equal expectedRoundTrip actual\n            ]\n    in\n        describe \"UrlParser integration\" (List.concatMap run inputs)\n\n\nall : Test\nall =\n    describe \"Integration\"\n        [ urlParserIntegrationTest\n        ]\n"
  },
  {
    "path": "tests/Main.elm",
    "content": "port module Main exposing (..)\n\nimport Tests\nimport Test.Runner.Node exposing (run)\nimport Json.Encode exposing (Value)\n\n\nmain : Program Value\nmain =\n    run emit Tests.all\n\n\nport emit : ( String, Value ) -> Cmd msg\n"
  },
  {
    "path": "tests/Tests.elm",
    "content": "module Tests exposing (..)\n\nimport HopTest\nimport Hop.AddressTest\nimport Hop.InTest\nimport Hop.OutTest\nimport IntegrationTest\nimport Test exposing (..)\n\n\nall : Test\nall =\n  describe \"Hop\"\n    [ HopTest.all\n    , Hop.AddressTest.all\n    , Hop.InTest.all\n    , Hop.OutTest.all\n    , IntegrationTest.all\n    ]\n"
  },
  {
    "path": "tests/elm-package.json",
    "content": "{\n    \"version\": \"0.0.1\",\n    \"summary\": \"A router for SPAs in Elm\",\n    \"repository\": \"https://github.com/sporto/hop.git\",\n    \"license\": \"MIT\",\n    \"source-directories\": [\n        \".\",\n        \"../src\"\n    ],\n    \"exposed-modules\": [],\n    \"dependencies\": {\n        \"elm-community/elm-test\": \"2.1.0 <= v < 3.0.0\",\n        \"elm-lang/core\": \"4.0.0 <= v < 5.0.0\",\n        \"elm-lang/navigation\": \"1.0.0 <= v < 2.0.0\",\n        \"evancz/elm-http\": \"3.0.1 <= v < 4.0.0\",\n        \"evancz/url-parser\": \"1.0.0 <= v < 2.0.0\",\n        \"rtfeldman/node-test-runner\": \"2.0.0 <= v < 3.0.0\"\n    },\n    \"elm-version\": \"0.16.0 <= v < 0.18.0\"\n}"
  },
  {
    "path": "tests/install-packages.sh",
    "content": "#!/bin/bash\n\nn=0\nuntil [ $n -ge 5 ]\ndo\n  elm package install -y && break\n  n=$[$n+1]\n  sleep 15\ndone\n"
  },
  {
    "path": "tests/package.json",
    "content": "{\n  \"name\": \"test\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"elm.js\",\n  \"scripts\": {\n    \"test\": \"elm-test\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"elm-test\": \"^0.17.3\"\n  }\n}\n"
  }
]