| @@ -0,0 +1,195 @@ | ||
| var _elm_lang$http$Http_Progress$onSelfMsg = F3( | ||
| function (router, _p0, state) { | ||
| return _elm_lang$core$Task$succeed(state); | ||
| }); | ||
| var _elm_lang$http$Http_Progress$addSub = F2( | ||
| function (_p1, subDict) { | ||
| var _p2 = _p1; | ||
| var _p3 = _p2._1; | ||
| var request = _p3.request; | ||
| var uid = A2( | ||
| _elm_lang$core$Basics_ops['++'], | ||
| _p2._0, | ||
| A2(_elm_lang$core$Basics_ops['++'], request.method, request.url)); | ||
| return A3(_elm_lang$core$Dict$insert, uid, _p3, subDict); | ||
| }); | ||
| var _elm_lang$http$Http_Progress$collectSubs = function (subs) { | ||
| return A3(_elm_lang$core$List$foldl, _elm_lang$http$Http_Progress$addSub, _elm_lang$core$Dict$empty, subs); | ||
| }; | ||
| var _elm_lang$http$Http_Progress$toTask = F2( | ||
| function (router, _p4) { | ||
| var _p5 = _p4; | ||
| return A2( | ||
| _elm_lang$core$Task$onError, | ||
| function (_p6) { | ||
| return A2( | ||
| _elm_lang$core$Platform$sendToApp, | ||
| router, | ||
| _p5.toError(_p6)); | ||
| }, | ||
| A2( | ||
| _elm_lang$core$Task$andThen, | ||
| _elm_lang$core$Platform$sendToApp(router), | ||
| A2( | ||
| _elm_lang$http$Native_Http.toTask, | ||
| _p5.request, | ||
| _elm_lang$core$Maybe$Just( | ||
| function (_p7) { | ||
| return A2( | ||
| _elm_lang$core$Platform$sendToApp, | ||
| router, | ||
| _p5.toProgress(_p7)); | ||
| })))); | ||
| }); | ||
| var _elm_lang$http$Http_Progress$spawnRequests = F3( | ||
| function (router, trackedRequests, state) { | ||
| var _p8 = trackedRequests; | ||
| if (_p8.ctor === '[]') { | ||
| return _elm_lang$core$Task$succeed(state); | ||
| } else { | ||
| return A2( | ||
| _elm_lang$core$Task$andThen, | ||
| function (process) { | ||
| return A3( | ||
| _elm_lang$http$Http_Progress$spawnRequests, | ||
| router, | ||
| _p8._1, | ||
| A3(_elm_lang$core$Dict$insert, _p8._0._0, process, state)); | ||
| }, | ||
| _elm_lang$core$Process$spawn( | ||
| A2(_elm_lang$http$Http_Progress$toTask, router, _p8._0._1))); | ||
| } | ||
| }); | ||
| var _elm_lang$http$Http_Progress$onEffects = F3( | ||
| function (router, subs, state) { | ||
| var rightStep = F3( | ||
| function (id, trackedRequest, _p9) { | ||
| var _p10 = _p9; | ||
| return { | ||
| ctor: '_Tuple3', | ||
| _0: _p10._0, | ||
| _1: _p10._1, | ||
| _2: { | ||
| ctor: '::', | ||
| _0: {ctor: '_Tuple2', _0: id, _1: trackedRequest}, | ||
| _1: _p10._2 | ||
| } | ||
| }; | ||
| }); | ||
| var bothStep = F4( | ||
| function (id, process, _p12, _p11) { | ||
| var _p13 = _p11; | ||
| return { | ||
| ctor: '_Tuple3', | ||
| _0: _p13._0, | ||
| _1: A3(_elm_lang$core$Dict$insert, id, process, _p13._1), | ||
| _2: _p13._2 | ||
| }; | ||
| }); | ||
| var leftStep = F3( | ||
| function (id, process, _p14) { | ||
| var _p15 = _p14; | ||
| return { | ||
| ctor: '_Tuple3', | ||
| _0: { | ||
| ctor: '::', | ||
| _0: _elm_lang$core$Process$kill(process), | ||
| _1: _p15._0 | ||
| }, | ||
| _1: _p15._1, | ||
| _2: _p15._2 | ||
| }; | ||
| }); | ||
| var subDict = _elm_lang$http$Http_Progress$collectSubs(subs); | ||
| var _p16 = A6( | ||
| _elm_lang$core$Dict$merge, | ||
| leftStep, | ||
| bothStep, | ||
| rightStep, | ||
| state, | ||
| subDict, | ||
| { | ||
| ctor: '_Tuple3', | ||
| _0: {ctor: '[]'}, | ||
| _1: _elm_lang$core$Dict$empty, | ||
| _2: {ctor: '[]'} | ||
| }); | ||
| var dead = _p16._0; | ||
| var ongoing = _p16._1; | ||
| var $new = _p16._2; | ||
| return A2( | ||
| _elm_lang$core$Task$andThen, | ||
| function (_p17) { | ||
| return A3(_elm_lang$http$Http_Progress$spawnRequests, router, $new, ongoing); | ||
| }, | ||
| _elm_lang$core$Task$sequence(dead)); | ||
| }); | ||
| var _elm_lang$http$Http_Progress$init = _elm_lang$core$Task$succeed(_elm_lang$core$Dict$empty); | ||
| var _elm_lang$http$Http_Progress$map = F2( | ||
| function (func, _p18) { | ||
| var _p19 = _p18; | ||
| return { | ||
| request: A2(_elm_lang$http$Http_Internal$map, func, _p19.request), | ||
| toProgress: function (_p20) { | ||
| return func( | ||
| _p19.toProgress(_p20)); | ||
| }, | ||
| toError: function (_p21) { | ||
| return func( | ||
| _p19.toError(_p21)); | ||
| } | ||
| }; | ||
| }); | ||
| var _elm_lang$http$Http_Progress$subscription = _elm_lang$core$Native_Platform.leaf('Http.Progress'); | ||
| var _elm_lang$http$Http_Progress$TrackedRequest = F3( | ||
| function (a, b, c) { | ||
| return {request: a, toProgress: b, toError: c}; | ||
| }); | ||
| var _elm_lang$http$Http_Progress$Done = function (a) { | ||
| return {ctor: 'Done', _0: a}; | ||
| }; | ||
| var _elm_lang$http$Http_Progress$Fail = function (a) { | ||
| return {ctor: 'Fail', _0: a}; | ||
| }; | ||
| var _elm_lang$http$Http_Progress$Some = function (a) { | ||
| return {ctor: 'Some', _0: a}; | ||
| }; | ||
| var _elm_lang$http$Http_Progress$None = {ctor: 'None'}; | ||
| var _elm_lang$http$Http_Progress$Track = F2( | ||
| function (a, b) { | ||
| return {ctor: 'Track', _0: a, _1: b}; | ||
| }); | ||
| var _elm_lang$http$Http_Progress$track = F3( | ||
| function (id, toMessage, _p22) { | ||
| var _p23 = _p22; | ||
| return _elm_lang$http$Http_Progress$subscription( | ||
| A2( | ||
| _elm_lang$http$Http_Progress$Track, | ||
| id, | ||
| { | ||
| request: A2( | ||
| _elm_lang$http$Http_Internal$map, | ||
| function (_p24) { | ||
| return toMessage( | ||
| _elm_lang$http$Http_Progress$Done(_p24)); | ||
| }, | ||
| _p23._0), | ||
| toProgress: function (_p25) { | ||
| return toMessage( | ||
| _elm_lang$http$Http_Progress$Some(_p25)); | ||
| }, | ||
| toError: function (_p26) { | ||
| return toMessage( | ||
| _elm_lang$http$Http_Progress$Fail(_p26)); | ||
| } | ||
| })); | ||
| }); | ||
| var _elm_lang$http$Http_Progress$subMap = F2( | ||
| function (func, _p27) { | ||
| var _p28 = _p27; | ||
| return A2( | ||
| _elm_lang$http$Http_Progress$Track, | ||
| _p28._0, | ||
| A2(_elm_lang$http$Http_Progress$map, func, _p28._1)); | ||
| }); | ||
| _elm_lang$core$Native_Platform.effectManagers['Http.Progress'] = {pkg: 'elm-lang/http', init: _elm_lang$http$Http_Progress$init, onEffects: _elm_lang$http$Http_Progress$onEffects, onSelfMsg: _elm_lang$http$Http_Progress$onSelfMsg, tag: 'sub', subMap: _elm_lang$http$Http_Progress$subMap}; |
| @@ -0,0 +1,94 @@ | ||
| var _elm_lang$http$Http$decodeUri = _elm_lang$http$Native_Http.decodeUri; | ||
| var _elm_lang$http$Http$encodeUri = _elm_lang$http$Native_Http.encodeUri; | ||
| var _elm_lang$http$Http$expectStringResponse = _elm_lang$http$Native_Http.expectStringResponse; | ||
| var _elm_lang$http$Http$expectJson = function (decoder) { | ||
| return _elm_lang$http$Http$expectStringResponse( | ||
| function (response) { | ||
| return A2(_elm_lang$core$Json_Decode$decodeString, decoder, response.body); | ||
| }); | ||
| }; | ||
| var _elm_lang$http$Http$expectString = _elm_lang$http$Http$expectStringResponse( | ||
| function (response) { | ||
| return _elm_lang$core$Result$Ok(response.body); | ||
| }); | ||
| var _elm_lang$http$Http$multipartBody = _elm_lang$http$Native_Http.multipart; | ||
| var _elm_lang$http$Http$stringBody = _elm_lang$http$Http_Internal$StringBody; | ||
| var _elm_lang$http$Http$jsonBody = function (value) { | ||
| return A2( | ||
| _elm_lang$http$Http_Internal$StringBody, | ||
| 'application/json', | ||
| A2(_elm_lang$core$Json_Encode$encode, 0, value)); | ||
| }; | ||
| var _elm_lang$http$Http$emptyBody = _elm_lang$http$Http_Internal$EmptyBody; | ||
| var _elm_lang$http$Http$header = _elm_lang$http$Http_Internal$Header; | ||
| var _elm_lang$http$Http$request = _elm_lang$http$Http_Internal$Request; | ||
| var _elm_lang$http$Http$post = F3( | ||
| function (url, body, decoder) { | ||
| return _elm_lang$http$Http$request( | ||
| { | ||
| method: 'POST', | ||
| headers: {ctor: '[]'}, | ||
| url: url, | ||
| body: body, | ||
| expect: _elm_lang$http$Http$expectJson(decoder), | ||
| timeout: _elm_lang$core$Maybe$Nothing, | ||
| withCredentials: false | ||
| }); | ||
| }); | ||
| var _elm_lang$http$Http$get = F2( | ||
| function (url, decoder) { | ||
| return _elm_lang$http$Http$request( | ||
| { | ||
| method: 'GET', | ||
| headers: {ctor: '[]'}, | ||
| url: url, | ||
| body: _elm_lang$http$Http$emptyBody, | ||
| expect: _elm_lang$http$Http$expectJson(decoder), | ||
| timeout: _elm_lang$core$Maybe$Nothing, | ||
| withCredentials: false | ||
| }); | ||
| }); | ||
| var _elm_lang$http$Http$getString = function (url) { | ||
| return _elm_lang$http$Http$request( | ||
| { | ||
| method: 'GET', | ||
| headers: {ctor: '[]'}, | ||
| url: url, | ||
| body: _elm_lang$http$Http$emptyBody, | ||
| expect: _elm_lang$http$Http$expectString, | ||
| timeout: _elm_lang$core$Maybe$Nothing, | ||
| withCredentials: false | ||
| }); | ||
| }; | ||
| var _elm_lang$http$Http$toTask = function (_p0) { | ||
| var _p1 = _p0; | ||
| return A2(_elm_lang$http$Native_Http.toTask, _p1._0, _elm_lang$core$Maybe$Nothing); | ||
| }; | ||
| var _elm_lang$http$Http$send = F2( | ||
| function (resultToMessage, request) { | ||
| return A2( | ||
| _elm_lang$core$Task$attempt, | ||
| resultToMessage, | ||
| _elm_lang$http$Http$toTask(request)); | ||
| }); | ||
| var _elm_lang$http$Http$Response = F4( | ||
| function (a, b, c, d) { | ||
| return {url: a, status: b, headers: c, body: d}; | ||
| }); | ||
| var _elm_lang$http$Http$BadPayload = F2( | ||
| function (a, b) { | ||
| return {ctor: 'BadPayload', _0: a, _1: b}; | ||
| }); | ||
| var _elm_lang$http$Http$BadStatus = function (a) { | ||
| return {ctor: 'BadStatus', _0: a}; | ||
| }; | ||
| var _elm_lang$http$Http$NetworkError = {ctor: 'NetworkError'}; | ||
| var _elm_lang$http$Http$Timeout = {ctor: 'Timeout'}; | ||
| var _elm_lang$http$Http$BadUrl = function (a) { | ||
| return {ctor: 'BadUrl', _0: a}; | ||
| }; | ||
| var _elm_lang$http$Http$StringPart = F2( | ||
| function (a, b) { | ||
| return {ctor: 'StringPart', _0: a, _1: b}; | ||
| }); | ||
| var _elm_lang$http$Http$stringPart = _elm_lang$http$Http$StringPart; |
| @@ -0,0 +1,16 @@ | ||
| var _evancz$elm_markdown$Markdown$toHtmlWith = _evancz$elm_markdown$Native_Markdown.toHtml; | ||
| var _evancz$elm_markdown$Markdown$defaultOptions = { | ||
| githubFlavored: _elm_lang$core$Maybe$Just( | ||
| {tables: false, breaks: false}), | ||
| defaultHighlighting: _elm_lang$core$Maybe$Nothing, | ||
| sanitize: false, | ||
| smartypants: false | ||
| }; | ||
| var _evancz$elm_markdown$Markdown$toHtml = F2( | ||
| function (attrs, string) { | ||
| return A3(_evancz$elm_markdown$Native_Markdown.toHtml, _evancz$elm_markdown$Markdown$defaultOptions, attrs, string); | ||
| }); | ||
| var _evancz$elm_markdown$Markdown$Options = F4( | ||
| function (a, b, c, d) { | ||
| return {githubFlavored: a, defaultHighlighting: b, sanitize: c, smartypants: d}; | ||
| }); |
| @@ -1,5 +1,7 @@ | ||
| { | ||
| "elm-lang/virtual-dom": "2.0.4", | ||
| "evancz/elm-markdown": "3.0.1", | ||
| "elm-lang/html": "2.0.0", | ||
| "elm-lang/http": "1.0.0", | ||
| "elm-lang/core": "5.1.1" | ||
| } |
| @@ -0,0 +1 @@ | ||
| elm-stuff |
| @@ -0,0 +1,30 @@ | ||
| Copyright (c) 2016-present, Evan Czaplicki | ||
|
|
||
| All rights reserved. | ||
|
|
||
| Redistribution and use in source and binary forms, with or without | ||
| modification, are permitted provided that the following conditions are met: | ||
|
|
||
| * Redistributions of source code must retain the above copyright | ||
| notice, this list of conditions and the following disclaimer. | ||
|
|
||
| * Redistributions in binary form must reproduce the above | ||
| copyright notice, this list of conditions and the following | ||
| disclaimer in the documentation and/or other materials provided | ||
| with the distribution. | ||
|
|
||
| * Neither the name of Evan Czaplicki nor the names of other | ||
| contributors may be used to endorse or promote products derived | ||
| from this software without specific prior written permission. | ||
|
|
||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| @@ -0,0 +1,57 @@ | ||
| # HTTP in Elm | ||
|
|
||
| Make HTTP requests in Elm. | ||
|
|
||
| ```elm | ||
| import Http | ||
| import Json.Decode as Decode | ||
| -- GET A STRING | ||
| getWarAndPeace : Http.Request String | ||
| getWarAndPeace = | ||
| Http.getString "https://example.com/books/war-and-peace" | ||
| -- GET JSON | ||
| getMetadata : Http.Request Metadata | ||
| getMetadata = | ||
| Http.get "https://example.com/books/war-and-peace/metadata" decodeMetadata | ||
| type alias Metadata = | ||
| { author : String | ||
| , pages : Int | ||
| } | ||
| decodeMetadata : Decode.Decoder Metadata | ||
| decodeMetadata = | ||
| Decode.map2 Metadata | ||
| (Decode.field "author" Decode.string) | ||
| (Decode.field "pages" Decode.int) | ||
| -- SEND REQUESTS | ||
| type Msg | ||
| = LoadMetadata (Result Http.Error Metadata) | ||
| send : Cmd Msg | ||
| send = | ||
| Http.send LoadMetadata getMetadata | ||
| ``` | ||
|
|
||
|
|
||
| ## Examples | ||
|
|
||
| - GET requests - [demo and code](http://elm-lang.org/examples/http) | ||
| - Download progress - [demo](https://hirafuji.com.br/elm/http-progress-example/) and [code](https://gist.github.com/pablohirafuji/fa373d07c42016756d5bca28962008c4) | ||
|
|
||
|
|
||
| ## Learn More | ||
|
|
||
| To understand how HTTP works in Elm, check out: | ||
|
|
||
| - [The HTTP example in the guide](https://guide.elm-lang.org/architecture/effects/http.html) to see a simple usage with some explanation. | ||
| - [The Elm Architecture](https://guide.elm-lang.org/architecture/) to understand how HTTP fits into Elm in a more complete way. This will explain concepts like `Cmd` and `Sub` that appear in this package. |
| @@ -0,0 +1,220 @@ | ||
| [ | ||
| { | ||
| "name": "Http.Progress", | ||
| "comment": " Track the progress of an HTTP request. This can be useful if you are\nrequesting a large amount of data and want to show the user a progress bar\nor something.\n\nHere is an example usage: [demo][] and [code][].\n\n[demo]: https://hirafuji.com.br/elm/http-progress-example/\n[code]: https://gist.github.com/pablohirafuji/fa373d07c42016756d5bca28962008c4\n\n**Note:** If you stop tracking progress, you cancel the request.\n\n# Progress\n@docs Progress, track\n\n", | ||
| "aliases": [], | ||
| "types": [ | ||
| { | ||
| "name": "Progress", | ||
| "comment": " The progress of an HTTP request.\n\nYou start with `None`. As data starts to come in, you will see `Some`. The\n`bytesExpected` field will match the `Content-Length` header, indicating how\nlong the response body is in bytes (8-bits). The `bytes` field indicates how\nmany bytes have been loaded so far, so if you want progress as a percentage,\nyou would say:\n\n Some { bytes, bytesExpected } ->\n toFloat bytes / toFloat bytesExpected\n\nYou will end up with `Fail` or `Done` depending on the success of the request.\n", | ||
| "args": [ | ||
| "data" | ||
| ], | ||
| "cases": [ | ||
| [ | ||
| "None", | ||
| [] | ||
| ], | ||
| [ | ||
| "Some", | ||
| [ | ||
| "{ bytes : Int, bytesExpected : Int }" | ||
| ] | ||
| ], | ||
| [ | ||
| "Fail", | ||
| [ | ||
| "Http.Error" | ||
| ] | ||
| ], | ||
| [ | ||
| "Done", | ||
| [ | ||
| "data" | ||
| ] | ||
| ] | ||
| ] | ||
| } | ||
| ], | ||
| "values": [ | ||
| { | ||
| "name": "track", | ||
| "comment": " Create a subscription that tracks the progress of an HTTP request.\n\nSee it in action in this example: [demo][] and [code][].\n\n[demo]: https://hirafuji.com.br/elm/http-progress-example/\n[code]: https://gist.github.com/pablohirafuji/fa373d07c42016756d5bca28962008c4\n", | ||
| "type": "String -> (Http.Progress.Progress data -> msg) -> Http.Request data -> Platform.Sub.Sub msg" | ||
| } | ||
| ], | ||
| "generated-with-elm-version": "0.18.0" | ||
| }, | ||
| { | ||
| "name": "Http", | ||
| "comment": " Create and send HTTP requests.\n\n# Send Requests\n@docs Request, send, Error\n\n# GET\n@docs getString, get\n\n# POST\n@docs post\n\n# Custom Requests\n@docs request\n\n## Headers\n@docs Header, header\n\n## Request Bodies\n@docs Body, emptyBody, jsonBody, stringBody, multipartBody, Part, stringPart\n\n## Responses\n@docs Expect, expectString, expectJson, expectStringResponse, Response\n\n# Low-Level\n@docs encodeUri, decodeUri, toTask\n\n", | ||
| "aliases": [ | ||
| { | ||
| "name": "Body", | ||
| "comment": " Represents the body of a `Request`.\n", | ||
| "args": [], | ||
| "type": "Http.Internal.Body" | ||
| }, | ||
| { | ||
| "name": "Expect", | ||
| "comment": " Logic for interpreting a response body.\n", | ||
| "args": [ | ||
| "a" | ||
| ], | ||
| "type": "Http.Internal.Expect a" | ||
| }, | ||
| { | ||
| "name": "Header", | ||
| "comment": " An HTTP header for configuring requests. See a bunch of common headers\n[here][].\n\n[here]: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields\n", | ||
| "args": [], | ||
| "type": "Http.Internal.Header" | ||
| }, | ||
| { | ||
| "name": "Request", | ||
| "comment": " Describes an HTTP request.\n", | ||
| "args": [ | ||
| "a" | ||
| ], | ||
| "type": "Http.Internal.Request a" | ||
| }, | ||
| { | ||
| "name": "Response", | ||
| "comment": " The response from a `Request`.\n", | ||
| "args": [ | ||
| "body" | ||
| ], | ||
| "type": "{ url : String , status : { code : Int, message : String } , headers : Dict.Dict String String , body : body }" | ||
| } | ||
| ], | ||
| "types": [ | ||
| { | ||
| "name": "Error", | ||
| "comment": " A `Request` can fail in a couple ways:\n\n - `BadUrl` means you did not provide a valid URL.\n - `Timeout` means it took too long to get a response.\n - `NetworkError` means the user turned off their wifi, went in a cave, etc.\n - `BadStatus` means you got a response back, but the [status code][sc]\n indicates failure.\n - `BadPayload` means you got a response back with a nice status code, but\n the body of the response was something unexpected. The `String` in this\n case is a debugging message that explains what went wrong with your JSON\n decoder or whatever.\n\n[sc]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html\n", | ||
| "args": [], | ||
| "cases": [ | ||
| [ | ||
| "BadUrl", | ||
| [ | ||
| "String" | ||
| ] | ||
| ], | ||
| [ | ||
| "Timeout", | ||
| [] | ||
| ], | ||
| [ | ||
| "NetworkError", | ||
| [] | ||
| ], | ||
| [ | ||
| "BadStatus", | ||
| [ | ||
| "Http.Response String" | ||
| ] | ||
| ], | ||
| [ | ||
| "BadPayload", | ||
| [ | ||
| "String", | ||
| "Http.Response String" | ||
| ] | ||
| ] | ||
| ] | ||
| }, | ||
| { | ||
| "name": "Part", | ||
| "comment": " Contents of a multi-part body. Right now it only supports strings, but we\nwill support blobs and files when we get an API for them in Elm.\n", | ||
| "args": [], | ||
| "cases": [] | ||
| } | ||
| ], | ||
| "values": [ | ||
| { | ||
| "name": "decodeUri", | ||
| "comment": " Use this to unescape query parameters. It converts things like `%2F` to\n`/`. It can fail in some cases. For example, there is no way to unescape `%`\nbecause it could never appear alone in a properly escaped string.\n\nIt works just like `decodeURIComponent` in JavaScript.\n", | ||
| "type": "String -> Maybe.Maybe String" | ||
| }, | ||
| { | ||
| "name": "emptyBody", | ||
| "comment": " Create an empty body for your `Request`. This is useful for GET requests\nand POST requests where you are not sending any data.\n", | ||
| "type": "Http.Body" | ||
| }, | ||
| { | ||
| "name": "encodeUri", | ||
| "comment": " Use this to escape query parameters. Converts characters like `/` to `%2F`\nso that it does not clash with normal URL\n\nIt work just like `encodeURIComponent` in JavaScript.\n", | ||
| "type": "String -> String" | ||
| }, | ||
| { | ||
| "name": "expectJson", | ||
| "comment": " Expect the response body to be JSON. You provide a `Decoder` to turn that\nJSON into an Elm value. If the body cannot be parsed as JSON or if the JSON\ndoes not match the decoder, the request will resolve to a `BadPayload` error.\n", | ||
| "type": "Json.Decode.Decoder a -> Http.Expect a" | ||
| }, | ||
| { | ||
| "name": "expectString", | ||
| "comment": " Expect the response body to be a `String`.\n", | ||
| "type": "Http.Expect String" | ||
| }, | ||
| { | ||
| "name": "expectStringResponse", | ||
| "comment": " Maybe you want the whole `Response`: status code, headers, body, etc. This\nlets you get all of that information. From there you can use functions like\n`Json.Decode.decodeString` to interpret it as JSON or whatever else you want.\n", | ||
| "type": "(Http.Response String -> Result.Result String a) -> Http.Expect a" | ||
| }, | ||
| { | ||
| "name": "get", | ||
| "comment": " Create a `GET` request and try to decode the response body from JSON to\nsome Elm value.\n\n import Http\n import Json.Decode exposing (list, string)\n\n getBooks : Http.Request (List String)\n getBooks =\n Http.get \"https://example.com/books\" (list string)\n\nYou can learn more about how JSON decoders work [here][] in the guide.\n\n[here]: https://guide.elm-lang.org/interop/json.html\n", | ||
| "type": "String -> Json.Decode.Decoder a -> Http.Request a" | ||
| }, | ||
| { | ||
| "name": "getString", | ||
| "comment": " Create a `GET` request and interpret the response body as a `String`.\n\n import Http\n\n getWarAndPeace : Http.Request String\n getWarAndPeace =\n Http.getString \"https://example.com/books/war-and-peace\"\n", | ||
| "type": "String -> Http.Request String" | ||
| }, | ||
| { | ||
| "name": "header", | ||
| "comment": " Create a `Header`.\n\n header \"If-Modified-Since\" \"Sat 29 Oct 1994 19:43:31 GMT\"\n header \"Max-Forwards\" \"10\"\n header \"X-Requested-With\" \"XMLHttpRequest\"\n\n**Note:** In the future, we may split this out into an `Http.Headers` module\nand provide helpers for cases that are common on the client-side. If this\nsounds nice to you, open an issue [here][] describing the helper you want and\nwhy you need it.\n\n[here]: https://github.com/elm-lang/http/issues\n", | ||
| "type": "String -> String -> Http.Header" | ||
| }, | ||
| { | ||
| "name": "jsonBody", | ||
| "comment": " Put some JSON value in the body of your `Request`. This will automatically\nadd the `Content-Type: application/json` header.\n", | ||
| "type": "Json.Encode.Value -> Http.Body" | ||
| }, | ||
| { | ||
| "name": "multipartBody", | ||
| "comment": " Create multi-part bodies for your `Request`, automatically adding the\n`Content-Type: multipart/form-data` header.\n", | ||
| "type": "List Http.Part -> Http.Body" | ||
| }, | ||
| { | ||
| "name": "post", | ||
| "comment": " Create a `POST` request and try to decode the response body from JSON to\nan Elm value. For example, if we want to send a POST without any data in the\nrequest body, it would be like this:\n\n import Http\n import Json.Decode exposing (list, string)\n\n postBooks : Http.Request (List String)\n postBooks =\n Http.post \"https://example.com/books\" Http.emptyBody (list string)\n\nSee [`jsonBody`](#jsonBody) to learn how to have a more interesting request\nbody. And check out [this section][here] of the guide to learn more about\nJSON decoders.\n\n[here]: https://guide.elm-lang.org/interop/json.html\n\n", | ||
| "type": "String -> Http.Body -> Json.Decode.Decoder a -> Http.Request a" | ||
| }, | ||
| { | ||
| "name": "request", | ||
| "comment": " Create a custom request. For example, a custom PUT request would look like\nthis:\n\n put : String -> Body -> Request ()\n put url body =\n request\n { method = \"PUT\"\n , headers = []\n , url = url\n , body = body\n , expect = expectStringResponse (\\_ -> Ok ())\n , timeout = Nothing\n , withCredentials = False\n }\n", | ||
| "type": "{ method : String , headers : List Http.Header , url : String , body : Http.Body , expect : Http.Expect a , timeout : Maybe.Maybe Time.Time , withCredentials : Bool } -> Http.Request a" | ||
| }, | ||
| { | ||
| "name": "send", | ||
| "comment": " Send a `Request`. We could get the text of “War and Peace” like this:\n\n import Http\n\n type Msg = Click | NewBook (Result Http.Error String)\n\n update : Msg -> Model -> Model\n update msg model =\n case msg of\n Click ->\n ( model, getWarAndPeace )\n\n NewBook (Ok book) ->\n ...\n\n NewBook (Err _) ->\n ...\n\n getWarAndPeace : Cmd Msg\n getWarAndPeace =\n Http.send NewBook <|\n Http.getString \"https://example.com/books/war-and-peace.md\"\n", | ||
| "type": "(Result.Result Http.Error a -> msg) -> Http.Request a -> Platform.Cmd.Cmd msg" | ||
| }, | ||
| { | ||
| "name": "stringBody", | ||
| "comment": " Put some string in the body of your `Request`. Defining `jsonBody` looks\nlike this:\n\n import Json.Encode as Encode\n\n jsonBody : Encode.Value -> Body\n jsonBody value =\n stringBody \"application/json\" (Encode.encode 0 value)\n\nNotice that the first argument is a [MIME type][mime] so we know to add\n`Content-Type: application/json` to our request headers. Make sure your\nMIME type matches your data. Some servers are strict about this!\n\n[mime]: https://en.wikipedia.org/wiki/Media_type\n", | ||
| "type": "String -> String -> Http.Body" | ||
| }, | ||
| { | ||
| "name": "stringPart", | ||
| "comment": " A named chunk of string data.\n\n body =\n multipartBody\n [ stringPart \"user\" \"tom\"\n , stringPart \"payload\" \"42\"\n ]\n", | ||
| "type": "String -> String -> Http.Part" | ||
| }, | ||
| { | ||
| "name": "toTask", | ||
| "comment": " Convert a `Request` into a `Task`. This is only really useful if you want\nto chain together a bunch of requests (or any other tasks) in a single command.\n", | ||
| "type": "Http.Request a -> Task.Task Http.Error a" | ||
| } | ||
| ], | ||
| "generated-with-elm-version": "0.18.0" | ||
| } | ||
| ] |
| @@ -0,0 +1,18 @@ | ||
| { | ||
| "version": "1.0.0", | ||
| "summary": "Make HTTP requests (download progress, rate-limit, debounce, throttle)", | ||
| "repository": "https://github.com/elm-lang/http.git", | ||
| "license": "BSD3", | ||
| "source-directories": [ | ||
| "src" | ||
| ], | ||
| "exposed-modules": [ | ||
| "Http", | ||
| "Http.Progress" | ||
| ], | ||
| "native-modules": true, | ||
| "dependencies": { | ||
| "elm-lang/core": "5.0.0 <= v < 6.0.0" | ||
| }, | ||
| "elm-version": "0.18.0 <= v < 0.19.0" | ||
| } |
| @@ -0,0 +1,40 @@ | ||
| ## Custom Rate-Limiting Strategies | ||
|
|
||
| This package has `Http.RateLimit` which helps you rate-limit the HTTP requests you make. Instead of sending one request per keystroke, you filter it down because not all requests are important. | ||
|
|
||
| The `Http.RateLimit` module comes with a `debounce` strategy that covers the common case, but you may want to define a custom strategy with other characteristics. Maybe you want to send the first request. Maybe you want to send when the previous request is done instead of using timers. Etc. | ||
|
|
||
| If so, you can define a custom strategy with `Http.RateLimit.customStrategy`. For example, you would define `throttle` like this: | ||
|
|
||
| ```elm | ||
| import Http.RateLimit as Limit | ||
| throttle : Time -> Limit.Strategy | ||
| throttle ms = | ||
| Limit.customStrategy <| \timeNow event state -> | ||
| case event of | ||
| Limit.New _ -> | ||
| -- wait after a new request | ||
| [ Limit.WakeUpIn ms ] | ||
| Limit.Done _ -> | ||
| -- we do not care when requests finish | ||
| [] | ||
| Limit.WakeUp -> | ||
| case state.next of | ||
| Nothing -> | ||
| -- do nothing if there is no pending request | ||
| [] | ||
| Just req -> | ||
| -- send if enough time has passed since the previous request | ||
| case state.prev of | ||
| Nothing -> | ||
| [ Limit.Send req.id ] | ||
| Just prev -> | ||
| if timeNow - prev.time >= ms then [ Limit.Send req.id ] else [] | ||
| ``` | ||
|
|
||
| It would be nice to have some useful strategies defined in a separate package so folks can experiment and find names and implementations that work well for specific scenarios. |
| @@ -0,0 +1,45 @@ | ||
| module Http.Internal exposing | ||
| ( Request(..) | ||
| , RawRequest | ||
| , Expect | ||
| , Body(..) | ||
| , Header(..) | ||
| , map | ||
| ) | ||
|
|
||
|
|
||
| import Native.Http | ||
| import Time exposing (Time) | ||
|
|
||
|
|
||
|
|
||
| type Request a = Request (RawRequest a) | ||
|
|
||
|
|
||
| type alias RawRequest a = | ||
| { method : String | ||
| , headers : List Header | ||
| , url : String | ||
| , body : Body | ||
| , expect : Expect a | ||
| , timeout : Maybe Time | ||
| , withCredentials : Bool | ||
| } | ||
|
|
||
|
|
||
| type Expect a = Expect | ||
|
|
||
|
|
||
| type Body | ||
| = EmptyBody | ||
| | StringBody String String | ||
| | FormDataBody | ||
|
|
||
|
|
||
|
|
||
| type Header = Header String String | ||
|
|
||
|
|
||
| map : (a -> b) -> RawRequest a -> RawRequest b | ||
| map func request = | ||
| { request | expect = Native.Http.mapExpect func request.expect } |
| @@ -0,0 +1,200 @@ | ||
| effect module Http.Progress where { subscription = MySub } exposing | ||
| ( Progress(..) | ||
| , track | ||
| ) | ||
|
|
||
| {-| Track the progress of an HTTP request. This can be useful if you are | ||
| requesting a large amount of data and want to show the user a progress bar | ||
| or something. | ||
| Here is an example usage: [demo][] and [code][]. | ||
| [demo]: https://hirafuji.com.br/elm/http-progress-example/ | ||
| [code]: https://gist.github.com/pablohirafuji/fa373d07c42016756d5bca28962008c4 | ||
| **Note:** If you stop tracking progress, you cancel the request. | ||
| # Progress | ||
| @docs Progress, track | ||
| -} | ||
|
|
||
|
|
||
| import Dict | ||
| import Http | ||
| import Http.Internal exposing ( Request(Request) ) | ||
| import Task exposing (Task) | ||
| import Platform exposing (Router) | ||
| import Process | ||
|
|
||
|
|
||
|
|
||
| -- PROGRESS | ||
|
|
||
|
|
||
| {-| The progress of an HTTP request. | ||
| You start with `None`. As data starts to come in, you will see `Some`. The | ||
| `bytesExpected` field will match the `Content-Length` header, indicating how | ||
| long the response body is in bytes (8-bits). The `bytes` field indicates how | ||
| many bytes have been loaded so far, so if you want progress as a percentage, | ||
| you would say: | ||
| Some { bytes, bytesExpected } -> | ||
| toFloat bytes / toFloat bytesExpected | ||
| You will end up with `Fail` or `Done` depending on the success of the request. | ||
| -} | ||
| type Progress data | ||
| = None | ||
| | Some { bytes : Int, bytesExpected : Int} | ||
| | Fail Http.Error | ||
| | Done data | ||
|
|
||
|
|
||
|
|
||
| -- TRACK | ||
|
|
||
|
|
||
| {-| Create a subscription that tracks the progress of an HTTP request. | ||
| See it in action in this example: [demo][] and [code][]. | ||
| [demo]: https://hirafuji.com.br/elm/http-progress-example/ | ||
| [code]: https://gist.github.com/pablohirafuji/fa373d07c42016756d5bca28962008c4 | ||
| -} | ||
| track : String -> (Progress data -> msg) -> Http.Request data -> Sub msg | ||
| track id toMessage (Request request) = | ||
| subscription <| Track id <| | ||
| { request = Http.Internal.map (Done >> toMessage) request | ||
| , toProgress = Some >> toMessage | ||
| , toError = Fail >> toMessage | ||
| } | ||
|
|
||
|
|
||
| type alias TrackedRequest msg = | ||
| { request : Http.Internal.RawRequest msg | ||
| , toProgress : { bytes : Int, bytesExpected : Int } -> msg | ||
| , toError : Http.Error -> msg | ||
| } | ||
|
|
||
|
|
||
| map : (a -> b) -> TrackedRequest a -> TrackedRequest b | ||
| map func { request, toProgress, toError } = | ||
| { request = Http.Internal.map func request | ||
| , toProgress = toProgress >> func | ||
| , toError = toError >> func | ||
| } | ||
|
|
||
|
|
||
|
|
||
| -- SUBSCRIPTIONS | ||
|
|
||
|
|
||
| type MySub msg = | ||
| Track String (TrackedRequest msg) | ||
|
|
||
|
|
||
| subMap : (a -> b) -> MySub a -> MySub b | ||
| subMap func (Track id trackedRequest) = | ||
| Track id (map func trackedRequest) | ||
|
|
||
|
|
||
|
|
||
| -- EFFECT MANAGER | ||
|
|
||
|
|
||
| type alias State = | ||
| Dict.Dict String Process.Id | ||
|
|
||
|
|
||
| init : Task Never State | ||
| init = | ||
| Task.succeed Dict.empty | ||
|
|
||
|
|
||
|
|
||
| -- APP MESSAGES | ||
|
|
||
|
|
||
| onEffects : Platform.Router msg Never -> List (MySub msg) -> State -> Task Never State | ||
| onEffects router subs state = | ||
| let | ||
| subDict = | ||
| collectSubs subs | ||
|
|
||
| leftStep id process (dead, ongoing, new) = | ||
| ( Process.kill process :: dead | ||
| , ongoing | ||
| , new | ||
| ) | ||
|
|
||
| bothStep id process _ (dead, ongoing, new) = | ||
| ( dead | ||
| , Dict.insert id process ongoing | ||
| , new | ||
| ) | ||
|
|
||
| rightStep id trackedRequest (dead, ongoing, new) = | ||
| ( dead | ||
| , ongoing | ||
| , (id, trackedRequest) :: new | ||
| ) | ||
|
|
||
| (dead, ongoing, new) = | ||
| Dict.merge leftStep bothStep rightStep state subDict ([], Dict.empty, []) | ||
| in | ||
| Task.sequence dead | ||
| |> Task.andThen (\_ -> spawnRequests router new ongoing) | ||
|
|
||
|
|
||
| spawnRequests : Router msg Never -> List (String, TrackedRequest msg) -> State -> Task Never State | ||
| spawnRequests router trackedRequests state = | ||
| case trackedRequests of | ||
| [] -> | ||
| Task.succeed state | ||
|
|
||
| (id, trackedRequest) :: others -> | ||
| Process.spawn (toTask router trackedRequest) | ||
| |> Task.andThen (\process -> spawnRequests router others (Dict.insert id process state)) | ||
|
|
||
|
|
||
| toTask : Router msg Never -> TrackedRequest msg -> Task Never () | ||
| toTask router { request, toProgress, toError } = | ||
| Native.Http.toTask request (Just (Platform.sendToApp router << toProgress)) | ||
| |> Task.andThen (Platform.sendToApp router) | ||
| |> Task.onError (Platform.sendToApp router << toError) | ||
|
|
||
|
|
||
|
|
||
| -- COLLECT SUBS AS DICT | ||
|
|
||
|
|
||
| type alias SubDict msg = | ||
| Dict.Dict String (TrackedRequest msg) | ||
|
|
||
|
|
||
| collectSubs : List (MySub msg) -> SubDict msg | ||
| collectSubs subs = | ||
| List.foldl addSub Dict.empty subs | ||
|
|
||
|
|
||
| addSub : MySub msg -> SubDict msg -> SubDict msg | ||
| addSub (Track id trackedRequest) subDict = | ||
| let | ||
| request = | ||
| trackedRequest.request | ||
|
|
||
| uid = | ||
| id ++ request.method ++ request.url | ||
| in | ||
| Dict.insert uid trackedRequest subDict | ||
|
|
||
|
|
||
|
|
||
| -- SELF MESSAGES | ||
|
|
||
|
|
||
| onSelfMsg : Platform.Router msg Never -> Never -> State -> Task Never State | ||
| onSelfMsg router _ state = | ||
| Task.succeed state |
| @@ -0,0 +1,238 @@ | ||
| var _elm_lang$http$Native_Http = function() { | ||
|
|
||
|
|
||
| // ENCODING AND DECODING | ||
|
|
||
| function encodeUri(string) | ||
| { | ||
| return encodeURIComponent(string); | ||
| } | ||
|
|
||
| function decodeUri(string) | ||
| { | ||
| try | ||
| { | ||
| return _elm_lang$core$Maybe$Just(decodeURIComponent(string)); | ||
| } | ||
| catch(e) | ||
| { | ||
| return _elm_lang$core$Maybe$Nothing; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| // SEND REQUEST | ||
|
|
||
| function toTask(request, maybeProgress) | ||
| { | ||
| return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) | ||
| { | ||
| var xhr = new XMLHttpRequest(); | ||
|
|
||
| configureProgress(xhr, maybeProgress); | ||
|
|
||
| xhr.addEventListener('error', function() { | ||
| callback(_elm_lang$core$Native_Scheduler.fail({ ctor: 'NetworkError' })); | ||
| }); | ||
| xhr.addEventListener('timeout', function() { | ||
| callback(_elm_lang$core$Native_Scheduler.fail({ ctor: 'Timeout' })); | ||
| }); | ||
| xhr.addEventListener('load', function() { | ||
| callback(handleResponse(xhr, request.expect.responseToResult)); | ||
| }); | ||
|
|
||
| try | ||
| { | ||
| xhr.open(request.method, request.url, true); | ||
| } | ||
| catch (e) | ||
| { | ||
| return callback(_elm_lang$core$Native_Scheduler.fail({ ctor: 'BadUrl', _0: request.url })); | ||
| } | ||
|
|
||
| configureRequest(xhr, request); | ||
| send(xhr, request.body); | ||
|
|
||
| return function() { xhr.abort(); }; | ||
| }); | ||
| } | ||
|
|
||
| function configureProgress(xhr, maybeProgress) | ||
| { | ||
| if (maybeProgress.ctor === 'Nothing') | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| xhr.addEventListener('progress', function(event) { | ||
| if (!event.lengthComputable) | ||
| { | ||
| return; | ||
| } | ||
| _elm_lang$core$Native_Scheduler.rawSpawn(maybeProgress._0({ | ||
| bytes: event.loaded, | ||
| bytesExpected: event.total | ||
| })); | ||
| }); | ||
| } | ||
|
|
||
| function configureRequest(xhr, request) | ||
| { | ||
| function setHeader(pair) | ||
| { | ||
| xhr.setRequestHeader(pair._0, pair._1); | ||
| } | ||
|
|
||
| A2(_elm_lang$core$List$map, setHeader, request.headers); | ||
| xhr.responseType = request.expect.responseType; | ||
| xhr.withCredentials = request.withCredentials; | ||
|
|
||
| if (request.timeout.ctor === 'Just') | ||
| { | ||
| xhr.timeout = request.timeout._0; | ||
| } | ||
| } | ||
|
|
||
| function send(xhr, body) | ||
| { | ||
| switch (body.ctor) | ||
| { | ||
| case 'EmptyBody': | ||
| xhr.send(); | ||
| return; | ||
|
|
||
| case 'StringBody': | ||
| xhr.setRequestHeader('Content-Type', body._0); | ||
| xhr.send(body._1); | ||
| return; | ||
|
|
||
| case 'FormDataBody': | ||
| xhr.send(body._0); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| // RESPONSES | ||
|
|
||
| function handleResponse(xhr, responseToResult) | ||
| { | ||
| var response = toResponse(xhr); | ||
|
|
||
| if (xhr.status < 200 || 300 <= xhr.status) | ||
| { | ||
| response.body = xhr.responseText; | ||
| return _elm_lang$core$Native_Scheduler.fail({ | ||
| ctor: 'BadStatus', | ||
| _0: response | ||
| }); | ||
| } | ||
|
|
||
| var result = responseToResult(response); | ||
|
|
||
| if (result.ctor === 'Ok') | ||
| { | ||
| return _elm_lang$core$Native_Scheduler.succeed(result._0); | ||
| } | ||
| else | ||
| { | ||
| response.body = xhr.responseText; | ||
| return _elm_lang$core$Native_Scheduler.fail({ | ||
| ctor: 'BadPayload', | ||
| _0: result._0, | ||
| _1: response | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| function toResponse(xhr) | ||
| { | ||
| return { | ||
| status: { code: xhr.status, message: xhr.statusText }, | ||
| headers: parseHeaders(xhr.getAllResponseHeaders()), | ||
| url: xhr.responseURL, | ||
| body: xhr.response | ||
| }; | ||
| } | ||
|
|
||
| function parseHeaders(rawHeaders) | ||
| { | ||
| var headers = _elm_lang$core$Dict$empty; | ||
|
|
||
| if (!rawHeaders) | ||
| { | ||
| return headers; | ||
| } | ||
|
|
||
| var headerPairs = rawHeaders.split('\u000d\u000a'); | ||
| for (var i = headerPairs.length; i--; ) | ||
| { | ||
| var headerPair = headerPairs[i]; | ||
| var index = headerPair.indexOf('\u003a\u0020'); | ||
| if (index > 0) | ||
| { | ||
| var key = headerPair.substring(0, index); | ||
| var value = headerPair.substring(index + 2); | ||
|
|
||
| headers = A3(_elm_lang$core$Dict$update, key, function(oldValue) { | ||
| if (oldValue.ctor === 'Just') | ||
| { | ||
| return _elm_lang$core$Maybe$Just(value + ', ' + oldValue._0); | ||
| } | ||
| return _elm_lang$core$Maybe$Just(value); | ||
| }, headers); | ||
| } | ||
| } | ||
|
|
||
| return headers; | ||
| } | ||
|
|
||
|
|
||
| // EXPECTORS | ||
|
|
||
| function expectStringResponse(responseToResult) | ||
| { | ||
| return { | ||
| responseType: 'text', | ||
| responseToResult: responseToResult | ||
| }; | ||
| } | ||
|
|
||
| function mapExpect(func, expect) | ||
| { | ||
| return { | ||
| responseType: expect.responseType, | ||
| responseToResult: function(response) { | ||
| var convertedResponse = expect.responseToResult(response); | ||
| return A2(_elm_lang$core$Result$map, func, convertedResponse); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
|
|
||
| // BODY | ||
|
|
||
| function multipart(parts) | ||
| { | ||
| var formData = new FormData(); | ||
|
|
||
| while (parts.ctor !== '[]') | ||
| { | ||
| var part = parts._0; | ||
| formData.append(part._0, part._1); | ||
| parts = parts._1; | ||
| } | ||
|
|
||
| return { ctor: 'FormDataBody', _0: formData }; | ||
| } | ||
|
|
||
| return { | ||
| toTask: F2(toTask), | ||
| expectStringResponse: expectStringResponse, | ||
| mapExpect: F2(mapExpect), | ||
| multipart: multipart, | ||
| encodeUri: encodeUri, | ||
| decodeUri: decodeUri | ||
| }; | ||
|
|
||
| }(); |
| @@ -0,0 +1 @@ | ||
| elm-stuff |
| @@ -0,0 +1,30 @@ | ||
| Copyright (c) 2014, Evan Czaplicki | ||
|
|
||
| All rights reserved. | ||
|
|
||
| Redistribution and use in source and binary forms, with or without | ||
| modification, are permitted provided that the following conditions are met: | ||
|
|
||
| * Redistributions of source code must retain the above copyright | ||
| notice, this list of conditions and the following disclaimer. | ||
|
|
||
| * Redistributions in binary form must reproduce the above | ||
| copyright notice, this list of conditions and the following | ||
| disclaimer in the documentation and/or other materials provided | ||
| with the distribution. | ||
|
|
||
| * Neither the name of Evan Czaplicki nor the names of other | ||
| contributors may be used to endorse or promote products derived | ||
| from this software without specific prior written permission. | ||
|
|
||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| @@ -0,0 +1,38 @@ | ||
| # Markdown in Elm | ||
|
|
||
| This package is for markdown parsing and rendering. It is based on the [marked][] project | ||
| which focuses on speed. | ||
|
|
||
| [marked]: https://github.com/chjj/marked | ||
|
|
||
| ## Basic Usage | ||
|
|
||
| ```elm | ||
| content : Html msg | ||
| content = | ||
| Markdown.toHtml [class "content"] """ | ||
| # Apple Pie Recipe | ||
| 1. Invent the universe. | ||
| 2. Bake an apple pie. | ||
| """ | ||
| ``` | ||
|
|
||
| **Warning:** Calling `Markdown.toHtml` parses the whole block, so try not to | ||
| call it for no reason. In the `content` example above we only have to parse | ||
| the text once, but if we put it in a function we may be doing a lot of | ||
| unnecessary parsing. | ||
|
|
||
|
|
||
| ## Code Blocks | ||
|
|
||
| For highlighting any code blocks, the package relies on the | ||
| [highlight.js](https://highlightjs.org/) project. So if you want to | ||
| see highlighting of code blocks in the rendering result, you need to | ||
| make sure that your page/app binds a version of that library | ||
| (supporting the languages you want to handle) to `window.hljs` in | ||
| Javascript. [This is how package.elm-lang.org does | ||
| that.](https://github.com/elm-lang/package.elm-lang.org/blob/e0b7aa4282038475612722ff7a57195866f8645b/backend/ServeFile.hs#L54) | ||
|
|
| @@ -0,0 +1,33 @@ | ||
| [ | ||
| { | ||
| "name": "Markdown", | ||
| "comment": " A library for markdown parsing. This is just an Elm API built on top of the\n[marked](https://github.com/chjj/marked) project which focuses on speed.\n\n# Parsing Markdown\n@docs toHtml\n\n# Parsing with Custom Options\n@docs Options, defaultOptions, toHtmlWith\n", | ||
| "aliases": [ | ||
| { | ||
| "name": "Options", | ||
| "comment": " Some parser options so you can tweak things for your particular case.\n\n * `githubFlavored` — overall reasonable improvements on the original\n markdown parser as described [here][gfm]. This includes stuff like [fenced\n code blocks][fenced]. There are some odd parts though, such as [tables][]\n and a setting to turn all newlines into newlines in the resulting output,\n so there are settings to turn those on or off based on your preference.\n\n * `defaultHighlighting` — a default language to use for code blocks that do\n not have a language tag. So setting this to `Just \"elm\"` will treat all\n unlabeled code blocks as Elm code. (This relies on [highlight.js][highlight]\n as explained in the README [here](../#code-blocks).)\n\n * `sanitize` — this determines if all HTML should be escaped. If you\n are parsing user markdown or user input can somehow reach the markdown\n parser, you should almost certainly turn on sanitation. If it is just you\n writing markdown, turning sanitation off is a nice way to do some HTML\n tricks if it is needed.\n\n * `smartypants` — This will automatically upgrade quotes to the\n prettier versions and turn dashes into [em dashes or en dashes][dash]\n\n\n[gfm]: https://help.github.com/articles/github-flavored-markdown/\n[fenced]: https://help.github.com/articles/github-flavored-markdown/#fenced-code-blocks\n[tables]: https://help.github.com/articles/github-flavored-markdown/#tables\n[highlight]: https://highlightjs.org/\n[dash]: http://en.wikipedia.org/wiki/Dash\n", | ||
| "args": [], | ||
| "type": "{ githubFlavored : Maybe.Maybe { tables : Bool, breaks : Bool } , defaultHighlighting : Maybe.Maybe String , sanitize : Bool , smartypants : Bool }" | ||
| } | ||
| ], | ||
| "types": [], | ||
| "values": [ | ||
| { | ||
| "name": "defaultOptions", | ||
| "comment": " The `Options` used by the `toElement` and `toHtml` functions.\n\n { githubFlavored = Just { tables = False, breaks = False }\n , defaultHighlighting = Nothing\n , sanitize = False\n , smartypants = False\n }\n", | ||
| "type": "Markdown.Options" | ||
| }, | ||
| { | ||
| "name": "toHtml", | ||
| "comment": " Turn a markdown string into an HTML element, using the `defaultOptions`.\n\n recipe : Html msg\n recipe =\n Markdown.toHtml [class \"recipe\"] \"\"\"\n\n # Apple Pie Recipe\n\n First, invent the universe. Then bake an apple pie.\n\n \"\"\"\n", | ||
| "type": "List (Html.Attribute msg) -> String -> Html.Html msg" | ||
| }, | ||
| { | ||
| "name": "toHtmlWith", | ||
| "comment": " Maybe you want to parse user input into markdown. To stop them from adding\n`<script>` tags, you can use modified parsing options.\n\n options : Options\n options =\n { defaultOptions | sanitize = True }\n\n toMarkdown : String -> Html\n toMarkdown userInput =\n Markdown.toHtmlWith options [] userInput\n", | ||
| "type": "Markdown.Options -> List (Html.Attribute msg) -> String -> Html.Html msg" | ||
| } | ||
| ], | ||
| "generated-with-elm-version": "0.18.0" | ||
| } | ||
| ] |
| @@ -0,0 +1,18 @@ | ||
| { | ||
| "version": "3.0.1", | ||
| "summary": "Fast markdown parsing and rendering", | ||
| "repository": "https://github.com/evancz/elm-markdown.git", | ||
| "license": "BSD3", | ||
| "source-directories": [ | ||
| "src" | ||
| ], | ||
| "exposed-modules": [ | ||
| "Markdown" | ||
| ], | ||
| "native-modules": true, | ||
| "dependencies": { | ||
| "elm-lang/core": "5.0.0 <= v < 6.0.0", | ||
| "elm-lang/html": "2.0.0 <= v < 3.0.0" | ||
| }, | ||
| "elm-version": "0.18.0 <= v < 0.19.0" | ||
| } |
| @@ -0,0 +1,104 @@ | ||
| module Markdown exposing | ||
| ( toHtml | ||
| , Options, defaultOptions, toHtmlWith | ||
| ) | ||
|
|
||
| {-| A library for markdown parsing. This is just an Elm API built on top of the | ||
| [marked](https://github.com/chjj/marked) project which focuses on speed. | ||
| # Parsing Markdown | ||
| @docs toHtml | ||
| # Parsing with Custom Options | ||
| @docs Options, defaultOptions, toHtmlWith | ||
| -} | ||
|
|
||
| import Html exposing (Html, Attribute) | ||
| import Native.Markdown | ||
|
|
||
|
|
||
| {-| Turn a markdown string into an HTML element, using the `defaultOptions`. | ||
| recipe : Html msg | ||
| recipe = | ||
| Markdown.toHtml [class "recipe"] """ | ||
| # Apple Pie Recipe | ||
| First, invent the universe. Then bake an apple pie. | ||
| """ | ||
| -} | ||
| toHtml : List (Attribute msg) -> String -> Html msg | ||
| toHtml attrs string = | ||
| Native.Markdown.toHtml defaultOptions attrs string | ||
|
|
||
|
|
||
| {-| Some parser options so you can tweak things for your particular case. | ||
| * `githubFlavored` — overall reasonable improvements on the original | ||
| markdown parser as described [here][gfm]. This includes stuff like [fenced | ||
| code blocks][fenced]. There are some odd parts though, such as [tables][] | ||
| and a setting to turn all newlines into newlines in the resulting output, | ||
| so there are settings to turn those on or off based on your preference. | ||
| * `defaultHighlighting` — a default language to use for code blocks that do | ||
| not have a language tag. So setting this to `Just "elm"` will treat all | ||
| unlabeled code blocks as Elm code. (This relies on [highlight.js][highlight] | ||
| as explained in the README [here](../#code-blocks).) | ||
| * `sanitize` — this determines if all HTML should be escaped. If you | ||
| are parsing user markdown or user input can somehow reach the markdown | ||
| parser, you should almost certainly turn on sanitation. If it is just you | ||
| writing markdown, turning sanitation off is a nice way to do some HTML | ||
| tricks if it is needed. | ||
| * `smartypants` — This will automatically upgrade quotes to the | ||
| prettier versions and turn dashes into [em dashes or en dashes][dash] | ||
| [gfm]: https://help.github.com/articles/github-flavored-markdown/ | ||
| [fenced]: https://help.github.com/articles/github-flavored-markdown/#fenced-code-blocks | ||
| [tables]: https://help.github.com/articles/github-flavored-markdown/#tables | ||
| [highlight]: https://highlightjs.org/ | ||
| [dash]: http://en.wikipedia.org/wiki/Dash | ||
| -} | ||
| type alias Options = | ||
| { githubFlavored : Maybe { tables : Bool, breaks : Bool } | ||
| , defaultHighlighting : Maybe String | ||
| , sanitize : Bool | ||
| , smartypants : Bool | ||
| } | ||
|
|
||
|
|
||
| {-| The `Options` used by the `toElement` and `toHtml` functions. | ||
| { githubFlavored = Just { tables = False, breaks = False } | ||
| , defaultHighlighting = Nothing | ||
| , sanitize = False | ||
| , smartypants = False | ||
| } | ||
| -} | ||
| defaultOptions : Options | ||
| defaultOptions = | ||
| { githubFlavored = Just { tables = False, breaks = False } | ||
| , defaultHighlighting = Nothing | ||
| , sanitize = False | ||
| , smartypants = False | ||
| } | ||
|
|
||
|
|
||
| {-| Maybe you want to parse user input into markdown. To stop them from adding | ||
| `<script>` tags, you can use modified parsing options. | ||
| options : Options | ||
| options = | ||
| { defaultOptions | sanitize = True } | ||
| toMarkdown : String -> Html | ||
| toMarkdown userInput = | ||
| Markdown.toHtmlWith options [] userInput | ||
| -} | ||
| toHtmlWith : Options -> List (Attribute msg) -> String -> Html msg | ||
| toHtmlWith = | ||
| Native.Markdown.toHtml |
| @@ -0,0 +1,19 @@ | ||
| defmodule LearningElm.Seat do | ||
| use LearningElm.Web, :model | ||
|
|
||
| schema "seats" do | ||
| field :seat_no, :integer | ||
| field :occupied, :boolean, default: false | ||
|
|
||
| timestamps() | ||
| end | ||
|
|
||
| @doc """ | ||
| Builds a changeset based on the `struct` and `params`. | ||
| """ | ||
| def changeset(struct, params \\ %{}) do | ||
| struct | ||
| |> cast(params, [:seat_no, :occupied]) | ||
| |> validate_required([:seat_no, :occupied]) | ||
| end | ||
| end |
| @@ -0,0 +1,19 @@ | ||
| defmodule LearningElm.ChangesetView do | ||
| use LearningElm.Web, :view | ||
|
|
||
| @doc """ | ||
| Traverses and translates changeset errors. | ||
| See `Ecto.Changeset.traverse_errors/2` and | ||
| `LearningElm.ErrorHelpers.translate_error/1` for more details. | ||
| """ | ||
| def translate_errors(changeset) do | ||
| Ecto.Changeset.traverse_errors(changeset, &translate_error/1) | ||
| end | ||
|
|
||
| def render("error.json", %{changeset: changeset}) do | ||
| # When encoded, the changeset returns its errors | ||
| # as a JSON object. So we just pass it forward. | ||
| %{errors: translate_errors(changeset)} | ||
| end | ||
| end |
| @@ -0,0 +1,17 @@ | ||
| defmodule LearningElm.SeatView do | ||
| use LearningElm.Web, :view | ||
|
|
||
| def render("index.json", %{seats: seats}) do | ||
| %{data: render_many(seats, LearningElm.SeatView, "seat.json")} | ||
| end | ||
|
|
||
| def render("show.json", %{seat: seat}) do | ||
| %{data: render_one(seat, LearningElm.SeatView, "seat.json")} | ||
| end | ||
|
|
||
| def render("seat.json", %{seat: seat}) do | ||
| %{id: seat.id, | ||
| seatNo: seat.seat_no, | ||
| occupied: seat.occupied} | ||
| end | ||
| end |