diff --git a/.gitignore b/.gitignore index 1bd093e..9ffdce2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,4 @@ -bower_components elm-stuff node_modules serve dist -.publish -.idea -# Compiled tests -elm.js -test.html diff --git a/.travis.yml b/.travis.yml index cd8ffbe..f8944a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,11 +2,27 @@ language: node_js node_js: - "4.1" + +cache: + directories: + - sysconfcpus + install: - - npm install -g elm@0.16.0 casperjs http-server + - npm install -g elm@~0.17.0 casperjs http-server - elm-package install -y + # Getting elm-make to run quicker. + # See https://github.com/elm-lang/elm-compiler/issues/1473#issuecomment-245704142 + - | + if [ ! -d sysconfcpus/bin ]; + then + git clone https://github.com/obmarg/libsysconfcpus.git; + cd libsysconfcpus; + ./configure --prefix=$TRAVIS_BUILD_DIR/sysconfcpus; + make && make install; + cd ..; + fi before_script: - - elm-make ./src/elm/TestRunner.elm --output test.html + - $TRAVIS_BUILD_DIR/sysconfcpus/bin/sysconfcpus -n 2 elm-make ./src/elm/TestRunner.elm --output test.html - http-server '.' & # Wait for compilation is done. - until $(curl --output /dev/null --silent --head --fail http://127.0.0.1:8080/test.html); do echo "." && sleep 1; done diff --git a/README.md b/README.md index 1e2db69..16e949b 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,22 @@ An Elm single page application. The server side code is [here](https://github.co [Read more](http://www.gizra.com/content/elm-hedley-overview/) about this project. -## Install +## Installation -Install Elm v0.16.0 (`npm install -g elm@0.16.0`) +Make sure the following are installed: -``` -npm install && bower install -elm-package install -y -``` +* NodeJs (and npm) +* Elm (e.g. `npm install -g elm@0.17.0`) +* Compass (for SASS) (`gem update --system && gem install compass`) -Execute with `gulp` +## Usage -## Testing +1. Serve locally, and watch file changes: `gulp` -In order to view the tests on the browser Start elm reactor (`elm-reactor`) and navigate to [http://localhost:8000/src/elm/TestRunner.elm](http://localhost:8000/src/elm/TestRunner.elm) +## Unit Tests + +In order to view the tests on the browser Start elm reactor (elm-reactor) and navigate to http://0.0.0.0:8000/src/elm/TestRunner.elm + +## License + +BSD3 diff --git a/bower.json b/bower.json deleted file mode 100644 index 5d5724e..0000000 --- a/bower.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "elm-hedley", - "version": "0.1.0", - "homepage": "https://github.com/amitaibu/elm-login", - "authors": [ - "Amitai Burstein " - ], - "license": "MIT", - "ignore": [ - "**/.*", - "node_modules", - "bower_components", - "test", - "tests" - ], - "dependencies": { - "dropzone": "~4.2.0", - "leaflet": "~0.7.5" - } -} diff --git a/elm-package.json b/elm-package.json index 7019013..0ef75da 100644 --- a/elm-package.json +++ b/elm-package.json @@ -1,26 +1,21 @@ { "version": "1.0.0", "summary": "helpful summary of your project, less than 80 characters", - "repository": "https://github.com/gizra/elm-hedley.git", + "repository": "https://github.com/Gizra/elm-hedley.git", "license": "BSD3", "source-directories": [ - "src/elm", - "src/vendor/elm-param-parsing/src", - "src/vendor/elm-storage/src", - "src/vendor/elm-web-api/src" + "src/elm" ], "exposed-modules": [], - "native-modules": true, "dependencies": { - "deadfoxygrandpa/elm-test": "3.0.1 <= v < 4.0.0", - "elm-lang/core": "3.0.0 <= v < 4.0.0", - "evancz/elm-effects": "2.0.1 <= v < 3.0.0", - "evancz/elm-html": "4.0.2 <= v < 5.0.0", - "evancz/elm-http": "3.0.0 <= v < 4.0.0", - "evancz/start-app": "2.0.2 <= v < 3.0.0", - "evancz/task-tutorial": "1.0.3 <= v < 2.0.0", - "rgrempel/elm-route-hash": "1.0.5 <= v < 2.0.0", - "truqu/elm-base64": "1.0.3 <= v < 2.0.0" + "elm-community/elm-test": "3.1.0 <= v < 4.0.0", + "elm-community/json-extra": "2.0.0 <= v < 3.0.0", + "elm-lang/core": "5.0.0 <= v < 6.0.0", + "elm-lang/html": "2.0.0 <= v < 3.0.0", + "elm-lang/navigation": "2.0.1 <= v < 3.0.0", + "elm-lang/elm-http": "1.0.0 <= v < 2.0.0", + "krisajenkins/remotedata": "4.0.1 <= v < 5.0.0", + "rgrempel/elm-route-url": "2.0.1 <= v < 3.0.0" }, - "elm-version": "0.16.0 <= v < 0.17.0" + "elm-version": "0.18.0 <= v < 0.19.0" } diff --git a/gulpfile.js b/gulpfile.js index 6b4ac25..d7db975 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -17,8 +17,6 @@ var elm = require('gulp-elm'); var fs = require('fs'); -var ga = require('gulp-ga'); - // merge is used to merge the output from two different streams into the same stream var merge = require("merge-stream"); // Need a command for reloading webpages using BrowserSync @@ -126,17 +124,6 @@ gulp.task('bower', function () { .pipe(gulp.dest("serve")); }); -gulp.task('ga', function(){ - gulp.src('src/index.html') - .pipe(ga({ - anonymizeIp: false, - sendPageView: true, - url: 'auto', - uid: 'UA-6558346-13' - })) - .pipe(gulp.dest('serve')); -}); - // Optimizes all the CSS, HTML and concats the JS etc gulp.task("minify", ["styles"], function () { @@ -172,7 +159,7 @@ gulp.task("minify", ["styles"], function () { gulp.task("deploy", [], function () { // Deploys your optimized site, you can change the settings in the html task if you want to return gulp.src("dist/**/*") - .pipe($.ghPages({branch: "gh-pages", cacheDir: ".publish"})); + .pipe($.ghPages({branch: "gh-pages"})); }); gulp.task('elm-init', elm.init); @@ -231,7 +218,7 @@ gulp.task("default", ["serve:dev", "watch"]); // Builds the site but doesnt serve it to you // @todo: Add "bower" here -gulp.task("build", gulpSequence("clean:dev", ["styles", "copy:dev", "elm"], "ga")); +gulp.task("build", gulpSequence("clean:dev", ["styles", "copy:dev", "elm"])); // Builds your site with the "build" command and then runs all the optimizations on // it and outputs it to "./dist" diff --git a/package.json b/package.json index c48cddc..69a6f5f 100644 --- a/package.json +++ b/package.json @@ -7,14 +7,13 @@ "devDependencies": { "browser-sync": "1.9.2", "del": "1.2.1", - "gulp": "~3.9.0", + "gulp": "3.9.1", "gulp-autoprefixer": "2.3.1", "gulp-cache": "0.2.10", "gulp-cached": "1.0.1", "gulp-changed": "1.0.0", - "gulp-elm": "0.2.0", + "gulp-elm": "0.4.1", "gulp-filter": "2.0.2", - "gulp-ga": "0.0.7", "gulp-gh-pages": "0.4.0", "gulp-gzip": "0.0.8", "gulp-htmlmin": "0.2.0", diff --git a/runTests.js b/runTests.js index 1d82418..a57cb6e 100644 --- a/runTests.js +++ b/runTests.js @@ -6,8 +6,8 @@ casper.test.begin('Run the Elm tests', function suite(test) { casper.start(url).then(function() { // The test runner doesn't provide a class, so we have to do this query // selector. - casper.waitForSelector('body > div > div > div', function() { - test.assertSelectorHasText('body > div > div > div', 'All tests passed'); + casper.waitForSelector('pre', function() { + test.assertSelectorHasText('pre', 'All tests passed'); }); }); diff --git a/src/assets/images/default@2x.png b/src/assets/images/default@2x.png deleted file mode 100644 index 06cfe3a..0000000 Binary files a/src/assets/images/default@2x.png and /dev/null differ diff --git a/src/assets/images/selected@2x.png b/src/assets/images/selected@2x.png deleted file mode 100644 index cb2b43f..0000000 Binary files a/src/assets/images/selected@2x.png and /dev/null differ diff --git a/src/assets/scss/_article-page.scss b/src/assets/scss/_article-page.scss deleted file mode 100644 index b5cb523..0000000 --- a/src/assets/scss/_article-page.scss +++ /dev/null @@ -1,84 +0,0 @@ -$br: 2px; - -#article-page { - - h3 { - margin-bottom: 20px; - } - - form { - .input-group { - width: 100%; - - &:nth-child(n+3) { - margin-top: 25px; - } - } - - label { - margin-bottom: 5px; - color: $blue; - font-weight: 400; - } - - button { - margin: 18px 0; - transition: all 0.4s ease-in; - @extend .blue-pill ; - - &:hover:not([disabled]) { - color: white; - background: $blue; - } - } - } - - .articles { - margin-bottom: 20px; - padding: 0; - list-style: none; - - > li { - padding: 12px; - border-radius: 2px; - border: 1px solid #ccc; - @extend .shadow; - - &:nth-child(n+2) { - margin-top: 30px; - } - - .title { - margin-bottom: 12px; - @extend .blue-pill ; - } - } - } - - input, - .dropzone, - #cke_body { - border-radius: $br; - @extend .shadow; - } - - #cke_body { - - .cke_inner { - border-radius: $br; - - #cke_1_top { - border-radius: $br $br 0 0; - } - - #cke_1_contents { - border-radius: 0 0 $br $br; - } - } - } - - .dropzone, - #cke_body { - border: 1px solid #ccc; - } -} diff --git a/src/assets/scss/_events-page.scss b/src/assets/scss/_events-page.scss deleted file mode 100644 index c9c4c0b..0000000 --- a/src/assets/scss/_events-page.scss +++ /dev/null @@ -1,90 +0,0 @@ -#events-page { - - #events { - margin-bottom: 35px; - } - - .unselect { - position: absolute; - top: 0; - bottom: 0; - right: 7px; - color: $error; - line-height: 36px; - font-size: 18px; - text-decoration: none; - border-left: 1px solid #E2E2E2; - padding-left: 8px; - transition: color 0.2s ease-in; - - &:hover { - color: $error-light; - } - } - - .first .wrapper:nth-child(n+2) { - margin-top: 30px; - } - - .companies { - margin: 10px 0; - padding: 4px; - width: 100%; - border-radius: 3px; - color:$blue; - background: white; - } - - input.search { - margin-top: 18px; - border-radius: 3px; - color: $blue; - } - - .authors, - .events { - list-style: none; - margin: 15px 0; - padding: 0; - - li { - position: relative; - margin-top: 10px; - padding: 5px 9px; - border: 1px solid #E2E2E2; - border-radius: 5px; - transition: background 0.2s ease-in; - - &:first-child { - margin-top: 0; - } - - &.-active { - background: $light-blue; - border-bottom: 1px solid #BFD7EB; - - .unselect { - border-left: 1px solid #BFD7EB; - } - } - } - } - - .map-container { - padding-bottom: 26px; - - .map-wrapper { - position: relative; - padding-top: 6px; - height: 600px; - - #map { - position: absolute; - height: 100%; - width: 100%; - padding: 0px; - margin: 0px; - } - } - } -} diff --git a/src/assets/scss/_footer.scss b/src/assets/scss/_footer.scss deleted file mode 100644 index 293ef65..0000000 --- a/src/assets/scss/_footer.scss +++ /dev/null @@ -1,41 +0,0 @@ -.main-footer { - position: absolute; - left: 0px; - right: 0px; - bottom: 0px; - text-align: center; - padding: 9px; - border-top: 7px solid white; - color: white; - background-color: #2D2D38; - font-size: 15px; - - &:before { - content: ""; - position: absolute; - width: 100%; - left: 0px; - top: -7px; - border-top: 1px solid #C8C8CB; - } - - .divider { - color: rgba(255, 255, 255, 0.38); - display: inline-block; - padding-left: 8px; - padding-right: 8px; - } - - a { - color: white; - - &.gizra-logo { - padding-left: 2px; - font-size: 17px; - } - } - - i.fa.fa-heart { - font-size: 14px; - } -} diff --git a/src/assets/scss/_header.scss b/src/assets/scss/_header.scss deleted file mode 100644 index a42caae..0000000 --- a/src/assets/scss/_header.scss +++ /dev/null @@ -1,128 +0,0 @@ -#main-header { - margin-bottom: 50px; - position: relative; - padding: 17px 9px 9px 9px; - border-bottom: 7px solid white; - color: white; - background-color: #2D2D38; - font-size: 15px; - - &:before { - content: ""; - position: absolute; - width: 100%; - left: 0px; - bottom: -7px; - border-bottom: 1px solid #C8C8CB; - } - - .brand { - color: $blue; - font-size: 30px; - font-family: 'Fjalla One', sans-serif; - transition: color 0.3s ease-in; - - &:hover { - color: $dark-blue; - } - } - - .navbar-header { - a { - margin-top: 2px; - text-decoration: none; - } - - .navbar-toggle { - border-color: $blue; - margin-right: 0; - transition: all 0.3s ease-in; - - &:hover, - &:focus { - background: $dark-blue; - border-color: $dark-blue; - - .icon-bar { - background: white; - } - } - - .icon-bar { - background: $blue; - transition: all 0.3s ease-in; - } - } - } - - .main-nav { - - ul.nav { - li { - padding: 8px; - - &:hover { - a, - i { - color: $blue; - } - } - - a, - i { - transition: color 0.3s ease-in; - } - - a { - padding: 0 12px 0 10px; - display: inline-block; - color: white; - - &.brand { - @extend .brand; - } - } - - &.active { - - a, - i { - color: $blue; - background: none; - } - } - } - } - } - - .main-nav { - @media only screen and (max-width : 767px) { - border: none; - box-shadow: none; - } - - ul.nav { - li { - - @media only screen and (min-width : 768px) { - a { - border-right: 1px solid $blue; - } - - &:first-child a, - &:last-child a { - border-right: none; - } - } - - @media only screen and (max-width : 767px) { - border-bottom: 1px solid rgba(255, 255, 255, 0.12); - a { - padding-top: 4px; - padding-bottom: 8px; - } - } - } - } - } -} diff --git a/src/assets/scss/_login.scss b/src/assets/scss/_login.scss deleted file mode 100644 index 0c53e3c..0000000 --- a/src/assets/scss/_login.scss +++ /dev/null @@ -1,71 +0,0 @@ -#login-page { - margin-top: 15%; - - .wrapper { - margin: 55px auto; - max-width: 380px; - } - - .form-signin { - @extend .box; - max-width: 380px; - - h2 { - margin-top: 0px; - margin-bottom: 20px; - text-transform: capitalize; - } - - .btn, - .input-group-addon { - color: white; - border: none; - } - - .btn a { - color: white; - } - - .input-group, - .input-group-addon, - .form-control, - .btn { - border-radius: 0px; - } - - .btn { - transition: background 0.4s ease-in; - background: $blue; - - &[disabled], - &[disabled]:hover { - background: $light-gray; - } - - &:hover { - background: $dark-blue; - } - } - - .input-group { - margin-bottom: 15px; - - .input-group-addon { - min-width: 45px; - background: $light-gray; - - .form-control { - border-radius: 0px; - position: relative; - font-size: 16px; - height: auto; - padding: 10px; - - &:focus { - z-index: 2; - } - } - } - } - } -} diff --git a/src/assets/scss/_mixin.scss b/src/assets/scss/_mixin.scss deleted file mode 100644 index ecab79b..0000000 --- a/src/assets/scss/_mixin.scss +++ /dev/null @@ -1,13 +0,0 @@ -/* Variables */ -$main-bg: rgb(234, 233, 227); -$light-blue: #E1EAF2; -$blue: #5F90BB; -$dark-blue: darken($blue, 15%); -$dark-gray: #2D2D38; -$light-gray: #606071; -$gray: #989898; -$text-gray: #B1B1B1; -$gizra: #e27058; -$gizra-dark: darken(#e27058, 3.5%); -$error: #D25263; -$error-light: #D81630; diff --git a/src/assets/scss/_my-account.scss b/src/assets/scss/_my-account.scss deleted file mode 100644 index 0fd7d34..0000000 --- a/src/assets/scss/_my-account.scss +++ /dev/null @@ -1,35 +0,0 @@ -#my-account { - - .name { - margin-top: 25px; - @extend .blue-pill; - } - - .company-title { - margin-top: 10px 0; - } - - .companies { - max-width: 141px; - margin-top: 15px; - margin-bottom: 20px; - padding-left: 22px; - - li { - position: relative; - margin-top: 11px; - padding-bottom: 7px; - - &:before { - content: ""; - position: absolute; - bottom: 0; - left: -17px; - right: 0; - height: 1px; - margin-left: -4px; - border-bottom: 1px solid rgba(0, 0, 0, 0.07); - } - } - } -} diff --git a/src/assets/scss/_page-not-found.scss b/src/assets/scss/_page-not-found.scss deleted file mode 100644 index d42247b..0000000 --- a/src/assets/scss/_page-not-found.scss +++ /dev/null @@ -1,17 +0,0 @@ -#page-not-found { - - a { - display: inline-block; - margin-top: 5px; - padding: 7px; - color: white; - background: $blue; - border-radius: 2px; - text-decoration: none; - transition: background 0.4s ease-in; - - &:hover { - background: $dark-blue; - } - } -} diff --git a/src/assets/scss/style.scss b/src/assets/scss/style.scss index 583a0a6..b27d7e2 100644 --- a/src/assets/scss/style.scss +++ b/src/assets/scss/style.scss @@ -1,103 +1,7 @@ -@import "mixin"; -@import "footer"; -@import "header"; -@import "login"; -@import "page-not-found"; -@import "my-account"; -@import "article-page"; -@import "events-page"; - -html { - position: relative; - min-height: 100%; -} - body { - font-family: 'Roboto Condensed', arial, sans-serif; - font-weight: 300; - background: $main-bg; - padding-bottom: 100px; -} - -h4 { - font-weight: 300; -} - -hr { - display: block; - margin: 11px 0px; - height: 1px; - border-bottom: 1px solid rgb(218, 217, 209); - background: #EAE9E3; -} - -.shadow { - box-shadow: 0px 7px 2px 0px rgba(0, 0, 0, 0.05); -} - -.blue-pill { - display: inline-block; - padding: 2px 7px; - color: $blue; - background: rgba(186, 219, 249, 0.28); - border-radius: 2px; - border: 1px solid rgba(186, 219, 249, 0.63); -} - -.container .wrapper:nth-child(n+2) { - margin-top: 45px; -} - -.wrapper { - padding: 12px; - border: 1px solid #C8C8CB; - border-radius: 5px; - background: white; - @extend .shadow; - - &.-suffix { - position: relative; - - &:after { - content: ""; - position: absolute; - bottom: 9px; - left: 0; - right: 0; - height: 1px; - background: rgba(200, 200, 203, 0.64); - } - } -} - -h3.title { - margin: -13px -13px 5px -13px; - padding: 12px; - color: $text-gray; - background: $dark-gray; - font-size: 18px; - border-radius: 5px 5px 0 0; + padding: 20px; - .icon { - margin-right: 5px + .container { + padding: 20px; } - -} - -.box { - padding: 15px; - color: white; - background: $dark-gray; - border: 1px solid rgba(0,0,0,0.1); -} - -a.gizra-logo { - text-decoration: none; - text-transform: lowercase; - font-family: Abril Fatface; - color: $gizra; - - &:hover { - color: $gizra-dark; - } } diff --git a/src/elm/App/Model.elm b/src/elm/App/Model.elm index 614e1a6..9dbb1e2 100644 --- a/src/elm/App/Model.elm +++ b/src/elm/App/Model.elm @@ -1,53 +1,30 @@ -module App.Model where +module App.Model exposing (emptyModel, Model, Page(..)) -import Config.Model as Config exposing (initialModel, Model) -import Company.Model as Company exposing (Model) +import Config.Model exposing (Model) +import RemoteData exposing (RemoteData(..), WebData) +import User.Model exposing (..) +import Pages.Login.Model exposing (emptyModel, Model) --- Pages import - -import Pages.Article.Model as Article exposing (initialModel, Model) -import Pages.Event.Model as Event exposing (initialModel, Model) -import Pages.GithubAuth.Model as GithubAuth exposing (Model) -import Pages.Login.Model as Login exposing (initialModel, Model) -import Pages.User.Model as User exposing (initialModel, Model) - -type alias AccessToken = String -type alias CompanyId = Int type Page - = Article - | Event (Maybe CompanyId) - | GithubAuth - | Login - | PageNotFound - | User + = AccessDenied + | Login + | MyAccount + | PageNotFound + type alias Model = - { accessToken : AccessToken - , activePage : Page - , article : Article.Model - , config : Config.Model - , configError : Bool - , companies : List Company.Model - , events : Event.Model - , githubAuth: GithubAuth.Model - , login: Login.Model - -- If the user is anonymous, we want to know where to redirect them. - , nextPage : Maybe Page - , user : User.Model - } + { activePage : Page + , config : RemoteData String Config.Model.Model + , pageLogin : Pages.Login.Model.Model + , user : WebData User + } + -initialModel : Model -initialModel = - { accessToken = "" - , activePage = Login - , article = Article.initialModel - , config = Config.initialModel - , configError = False - , companies = [] - , events = Event.initialModel - , githubAuth = GithubAuth.initialModel - , login = Login.initialModel - , nextPage = Nothing - , user = User.initialModel - } +emptyModel : Model +emptyModel = + { activePage = Login + , config = NotAsked + , pageLogin = Pages.Login.Model.emptyModel + , user = NotAsked + } diff --git a/src/elm/App/Router.elm b/src/elm/App/Router.elm index 826ecbb..e0feee2 100644 --- a/src/elm/App/Router.elm +++ b/src/elm/App/Router.elm @@ -1,65 +1,62 @@ -module App.Router where +module App.Router exposing (delta2url, location2messages) -import App.Model as App exposing (Model) -import App.Update exposing (Action) -import Pages.Event.Router as Event exposing (delta2update, location2company) -import RouteHash exposing (HashUpdate) +import App.Model exposing (..) +import App.Update exposing (..) +import Config exposing (configs) +import Dict exposing (..) +import Navigation exposing (Location) +import RouteUrl exposing (HistoryEntry(..), UrlChange) -type alias Model = App.Model -delta2update : Model -> Model -> Maybe HashUpdate -delta2update previous current = - case current.activePage of - App.Article -> - Just <| RouteHash.set ["articles"] +delta2url : Model -> Model -> Maybe UrlChange +delta2url previous current = + case current.activePage of + AccessDenied -> + Nothing - App.Event companyId -> - -- First, we ask the submodule for a HashUpdate. Then, we use - -- `map` to prepend something to the URL. - RouteHash.map ((::) "events") <| - Event.delta2update previous.events current.events + Login -> + Just <| UrlChange NewEntry "/#login" - App.GithubAuth -> - RouteHash.map (\_ -> ["auth", "github"]) Nothing + MyAccount -> + Just <| UrlChange NewEntry "/#my-account" - App.Login -> - if current.login.hasAccessTokenInStorage - -- The user has access token, but not yet logged in, so we don't change - -- the url to "login", as we are just waiting for the server to fetch - -- the user info. - then Nothing - else Just <| RouteHash.set ["login"] + PageNotFound -> + Just <| UrlChange NewEntry "/#404" - App.PageNotFound -> - Nothing +location2messages : Location -> List Msg +location2messages location = + let + cmd = + case location.hash of + "" -> + [] - App.User -> - Just <| RouteHash.set ["my-account"] + "#login" -> + [ SetActivePage Login ] + "#my-account" -> + [ SetActivePage MyAccount ] --- Here, we basically do the reverse of what delta2update does -location2action : List String -> List Action -location2action list = - case list of - ["auth", "github"] -> - ( App.Update.SetActivePage App.GithubAuth ) :: [] + "#404" -> + [ SetActivePage PageNotFound ] - "articles" :: rest -> - ( App.Update.SetActivePage App.Article ) :: [] + _ -> + [ SetActivePage PageNotFound ] + in + getConfigFromLocation location :: cmd - "login" :: rest -> - ( App.Update.SetActivePage App.Login ) :: [] - "my-account" :: rest -> - ( App.Update.SetActivePage App.User ) :: [] - "events" :: rest -> - ( App.Update.SetActivePage <| App.Event (Event.location2company rest) ) :: [] +-- @todo: We calcualte the config over and over again. It's not expensive +-- but redundent. - "" :: rest -> - ( App.Update.SetActivePage <| App.Event Nothing ) :: [] +getConfigFromLocation : Location -> Msg +getConfigFromLocation location = + case (Dict.get location.hostname configs) of + Just val -> + SetConfig val - _ -> - ( App.Update.SetActivePage App.PageNotFound ) :: [] + Nothing -> + SetConfigError diff --git a/src/elm/App/Test.elm b/src/elm/App/Test.elm new file mode 100644 index 0000000..b65598b --- /dev/null +++ b/src/elm/App/Test.elm @@ -0,0 +1,50 @@ +module App.Test exposing (all) + +import ElmTest exposing (..) +import RemoteData exposing (RemoteData(..)) +import App.Model exposing (..) +import App.Update exposing (..) + + +setActivePage : Test +setActivePage = + suite "SetActivePage msg" + [ test "set new active page" + (assertEqual PageNotFound (getPageAsAnonymous PageNotFound)) + , test "set Login page for anonymous user" + (assertEqual Login (getPageAsAnonymous Login)) + , test "set My account page for anonymous user" + (assertEqual AccessDenied (getPageAsAnonymous MyAccount)) + , test "set Login page for authenticated user" + (assertEqual AccessDenied (getPageAsAuthenticated Login)) + , test "set My account page for authenticated user" + (assertEqual MyAccount (getPageAsAuthenticated MyAccount)) + ] + + +getPageAsAnonymous : Page -> Page +getPageAsAnonymous page = + update (SetActivePage page) emptyModel + |> fst + |> .activePage + + +getPageAsAuthenticated : Page -> Page +getPageAsAuthenticated page = + let + dummyUser = + { name = Just "Foo", login = "foo", avatarUrl = "https://example.com" } + + model = + { emptyModel | user = Success dummyUser } + in + update (SetActivePage page) model + |> fst + |> .activePage + + +all : Test +all = + suite "App tests" + [ setActivePage + ] diff --git a/src/elm/App/Update.elm b/src/elm/App/Update.elm index 3c45f4b..05b7a8f 100644 --- a/src/elm/App/Update.elm +++ b/src/elm/App/Update.elm @@ -1,369 +1,91 @@ -module App.Update where +module App.Update exposing (init, update, Msg(..)) -import App.Model as App exposing (initialModel, Model) +import App.Model exposing (..) +import Config.Model as Config +import Pages.Login.Update exposing (Msg) +import RemoteData exposing (RemoteData(..), WebData) +import User.Model exposing (..) -import Config.Update exposing (init, Action) -import Company.Model as Company exposing (Model) -import Effects exposing (Effects) -import Json.Encode as JE exposing (string, Value) -import String exposing (isEmpty) -import Storage exposing (removeItem, setItem) -import Task exposing (succeed) --- Pages import +type Msg + = Logout + | PageLogin Pages.Login.Update.Msg + | SetActivePage Page + | SetConfig Config.Model + | SetConfigError -import Pages.Article.Update exposing (Action) -import Pages.Event.Update exposing (Action) -import Pages.GithubAuth.Update exposing (Action) -import Pages.Login.Update exposing (Action) -import Pages.User.Model exposing (User) -import Pages.User.Update exposing (Action) -type alias AccessToken = String -type alias Model = App.Model - -initialEffects : List (Effects Action) -initialEffects = - [ Effects.map ChildConfigAction <| snd Config.Update.init - , Effects.map ChildLoginAction <| snd Pages.Login.Update.init - ] - -init : (Model, Effects Action) +init : ( Model, Cmd Msg ) init = - ( App.initialModel - , Effects.batch initialEffects - ) - -type Action - = ChildArticleAction Pages.Article.Update.Action - | ChildConfigAction Config.Update.Action - | ChildEventAction Pages.Event.Update.Action - | ChildGithubAuthAction Pages.GithubAuth.Update.Action - | ChildLoginAction Pages.Login.Update.Action - | ChildUserAction Pages.User.Update.Action - | Logout - | SetAccessToken AccessToken - | SetActivePage App.Page - | SetConfigError - | UpdateCompanies (List Company.Model) - - -- NoOp actions - | NoOp - | NoOpLogout (Maybe ()) - | NoOpSetAccessToken (Result AccessToken ()) - - -update : Action -> Model -> (Model, Effects Action) -update action model = - case action of - ChildArticleAction act -> - let - -- Pass the access token along to the child components. - context = - { accessToken = model.accessToken - , backendConfig = (.config >> .backendConfig) model - } - - (childModel, childEffects) = Pages.Article.Update.update context act model.article - in - ( {model | article = childModel } - , Effects.map ChildArticleAction childEffects - ) - - ChildConfigAction act -> - let - (childModel, childEffects) = Config.Update.update act model.config - - defaultEffects = - [ Effects.map ChildConfigAction childEffects ] - - effects' = - case act of - Config.Update.SetError -> - -- Set configuration error. - (Task.succeed SetConfigError |> Effects.task) - :: - defaultEffects - _ -> - defaultEffects - - in - ( { model - | config = childModel - } - , Effects.batch effects' - ) - - ChildEventAction act -> - let - -- Pass the access token along to the child components. - context = - { accessToken = model.accessToken - , backendConfig = (.config >> .backendConfig) model - , companies = model.companies - } - - (childModel, childEffects) = Pages.Event.Update.update context act model.events - in - ( {model | events = childModel } - , Effects.map ChildEventAction childEffects - ) - - ChildGithubAuthAction act -> - let - - context = - { backendConfig = (.config >> .backendConfig) model } - - (childModel, childEffects) = Pages.GithubAuth.Update.update context act model.githubAuth - - defaultEffect = - Effects.map ChildGithubAuthAction childEffects - - -- A convinence variable to hold the default effect as a list. - defaultEffects = - [ defaultEffect ] - - effects' = - case act of - -- User's token was fetched, so we can set it in the accessToken - -- root property, and also get the user info, which will in turn - -- redirect the user from the login page. - Pages.GithubAuth.Update.SetAccessToken token -> - (Task.succeed (SetAccessToken token) |> Effects.task) - :: - defaultEffects - - _ -> - defaultEffects - - in - ( {model | githubAuth = childModel } - , Effects.batch effects' - ) - - ChildLoginAction act -> - let - context = - { backendConfig = (.config >> .backendConfig) model } - - (childModel, childEffects) = Pages.Login.Update.update context act model.login - - defaultEffect = - Effects.map ChildLoginAction childEffects - - -- A convinence variable to hold the default effect as a list. - defaultEffects = - [ defaultEffect ] - - effects' = - case act of - -- -- User's token was fetched, so we can set it in the accessToken - -- -- root property, and also get the user info, which will in turn - -- -- redirect the user from the login page. - Pages.Login.Update.SetAccessToken token -> - (Task.succeed (SetAccessToken token) |> Effects.task) - :: - (Task.succeed (ChildUserAction Pages.User.Update.GetDataFromServer) |> Effects.task) - :: - defaultEffects - - _ -> - defaultEffects - - in - ( {model | login = childModel } - , Effects.batch effects' - ) - - - ChildUserAction act -> - let - context = - { accessToken = model.accessToken - , backendConfig = (.config >> .backendConfig) model - } - - (childModel, childEffects) = Pages.User.Update.update context act model.user + emptyModel ! [] - defaultEffect = - Effects.map ChildUserAction childEffects - defaultEffects = - [ defaultEffect ] - - model' = - { model | user = childModel } - - (model'', effects') = - case act of - -- Bubble up the SetAccessToken to the App level. - Pages.User.Update.SetAccessToken token -> - ( model' - , (Task.succeed (SetAccessToken token) |> Effects.task) - :: - defaultEffects - ) - - -- Act when user was successfully fetched from the server. - Pages.User.Update.UpdateDataFromServer result -> - case result of - -- We reach out into the companies that is passed to the child - -- action. - Ok (id, name, companies) -> - let - nextPage = - case model.nextPage of - Just page -> - page - Nothing -> - App.Event Nothing - - in - -- User data was successfully fetched, so we can redirect to - -- the next page, and update their companies. - ( { model' | nextPage = Nothing } - , (Task.succeed (UpdateCompanies companies) |> Effects.task) - :: - (Task.succeed (SetActivePage nextPage) |> Effects.task) - :: - defaultEffects - ) - - Err _ -> - ( model' - , defaultEffects - ) - - _ -> - ( model' - , defaultEffects - ) - - in - (model'', Effects.batch effects') - - Logout -> - ( App.initialModel - , Effects.batch <| removeStorageItem :: initialEffects - ) - - SetAccessToken accessToken -> - let - defaultEffects = - [sendInputToStorage accessToken] - - effects' = - if (String.isEmpty accessToken) - then - -- Setting an empty access token should result with a logout. - (Task.succeed Logout |> Effects.task) - :: - defaultEffects - else - (Task.succeed (ChildUserAction Pages.User.Update.GetDataFromServer) |> Effects.task) - :: - defaultEffects - - in - ( { model | accessToken = accessToken} - , Effects.batch effects' - ) - - SetActivePage page -> - let - (page', nextPage) = - if model.user.name == Pages.User.Model.Anonymous - then - case page of - App.GithubAuth -> - (App.GithubAuth, model.nextPage) - - -- The user is anonymous and we are asked to set the active page - -- to login, then we make sure that the next page doesn't - -- change, so they won't be rediected back to the login page. - App.Login -> - (App.Login, model.nextPage) - - -- When the page is not found, we should keep the URL as is, - -- and even after the user info was fetched, we should keep it - -- so we set the next Page also to the error page. - App.PageNotFound -> - (page, Just page) +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + let + backendUrl = + case model.config of + Success config -> + config.backendUrl _ -> - (App.Login, Just page) - - -- Authenticated user. - else (page, Nothing) - - currentPageEffects = - case model.activePage of - App.Event companyId -> - Task.succeed (ChildEventAction Pages.Event.Update.Deactivate) |> Effects.task - - _ -> - Effects.none - - newPageEffects = - case page' of - App.Article -> - Task.succeed (ChildArticleAction Pages.Article.Update.Activate) |> Effects.task - - App.Event companyId -> - Task.succeed (ChildEventAction <| Pages.Event.Update.Activate companyId) |> Effects.task - - App.GithubAuth -> - Task.succeed (ChildGithubAuthAction Pages.GithubAuth.Update.Activate) |> Effects.task - - _ -> - Effects.none - - in - if model.activePage == page' - then - -- Requesting the same page, so don't do anything. - -- @todo: Because login and myAccount are under the same page (User) - -- we set the nextPage here as-well. - ( { model | nextPage = nextPage }, Effects.none) - else - ( { model - | activePage = page' - , nextPage = nextPage - } - , Effects.batch - [ currentPageEffects - , newPageEffects - ] - ) - - SetConfigError -> - ( { model | configError = True} - , Effects.none - ) - - UpdateCompanies companies -> - ( { model | companies = companies} - , Effects.none - ) - - -- NoOp actions - _ -> - ( model, Effects.none ) - --- EFFECTS - -sendInputToStorage : String -> Effects Action -sendInputToStorage val = - Storage.setItem "access_token" (JE.string val) - |> Task.toResult - |> Task.map NoOpSetAccessToken - |> Effects.task + "" + in + case msg of + Logout -> + init + + PageLogin msg -> + let + ( val, cmds, user ) = + Pages.Login.Update.update backendUrl model.user msg model.pageLogin + + model' = + { model + | pageLogin = val + , user = user + } + + model'' = + case user of + -- If user was successfuly fetched, reditect to my + -- account page. + Success _ -> + update (SetActivePage MyAccount) model' + |> fst + + _ -> + model' + in + ( model'', Cmd.map PageLogin cmds ) + + SetActivePage page -> + { model | activePage = setActivePageAccess model.user page } ! [] + + SetConfig config -> + { model | config = Success config } ! [] + + SetConfigError -> + { model | config = Failure "No config found" } ! [] + + +{-| Determine is a page can be accessed by a user (anonymous or authenticated), +and if not return a access denied page. + +If the user is authenticated, don't allow them to revisit Login page. Do the +opposite for anonumous user - don't allow them to visit the MyAccount page. +-} +setActivePageAccess : WebData User -> Page -> Page +setActivePageAccess user page = + case user of + Success _ -> + if page == Login then + AccessDenied + else + page --- Task to remove the access token from localStorage. -removeStorageItem : Effects Action -removeStorageItem = - Storage.removeItem "access_token" - |> Task.toMaybe - |> Task.map NoOpLogout - |> Effects.task + _ -> + if page == MyAccount then + AccessDenied + else + page diff --git a/src/elm/App/View.elm b/src/elm/App/View.elm index f536b64..f6310c4 100644 --- a/src/elm/App/View.elm +++ b/src/elm/App/View.elm @@ -1,200 +1,147 @@ -module App.View where - -import App.Model as App exposing (initialModel, Model) -import App.Update exposing (init, Action) +module App.View exposing (..) import Config.View exposing (view) -import Html exposing (a, div, h2, i, li, node, span, text, ul, button, Html) -import Html.Attributes exposing (class, classList, id, href, style, target, attribute) +import Html exposing (..) +import Html.Attributes exposing (class, classList, href, src, style, target) +import Html.App as Html import Html.Events exposing (onClick) +import App.Model exposing (..) +import App.Update exposing (..) +import User.Model exposing (..) +import Pages.Login.View exposing (..) +import Pages.MyAccount.View exposing (..) +import Pages.PageNotFound.View exposing (..) +import RemoteData exposing (RemoteData(..), WebData) + + +view : Model -> Html Msg +view model = + case model.config of + Failure err -> + Config.View.view + + _ -> + div [] + [ div [ class "ui container main" ] + [ viewHeader model + , viewMainContent model + , pre [ class "ui padded secondary segment" ] + [ div [] [ text <| "activePage: " ++ toString model.activePage ] + , div [] [ text <| "pageLogin: " ++ toString model.pageLogin ] + , div [] [ text <| "user: " ++ toString model.user ] + , div [] [ text <| "config: " ++ toString model.config ] + ] + ] + , viewFooter + ] + + +viewHeader : Model -> Html Msg +viewHeader model = + let + navbar = + case model.user of + Success _ -> + navbarAuthenticated --- Pages import - -import Pages.Article.View exposing (view) -import Pages.Event.View exposing (view) -import Pages.GithubAuth.View exposing (view) -import Pages.Login.View exposing (view) -import Pages.PageNotFound.View exposing (view) -import Pages.User.Model exposing (User) -import Pages.User.View exposing (view) - -type alias Page = App.Page - -isActivePage : Page -> Page -> Bool -isActivePage activePage page = - case activePage of - App.Event companyId -> - page == (App.Event Nothing) - _-> - activePage == page - -view : Signal.Address Action -> Model -> Html -view address model = - if model.configError == True - then - Config.View.view - - else - div - [] - [ (navbar address model) - , (mainContent address model) - , footer + _ -> + navbarAnonymous + in + div [ class "ui secondary pointing menu" ] (navbar model) + + +navbarAnonymous : Model -> List (Html Msg) +navbarAnonymous model = + [ a + [ classByPage Login model.activePage + , onClick <| SetActivePage Login ] + [ text "Login" ] + , viewPageNotFoundItem model.activePage + ] + -mainContent : Signal.Address Action -> Model -> Html -mainContent address model = - case model.activePage of - App.Article -> - let - childAddress = - Signal.forwardTo address App.Update.ChildArticleAction - in - div [ style myStyle ] [ Pages.Article.View.view childAddress model.article ] - - App.Event companyId -> - let - childAddress = - Signal.forwardTo address App.Update.ChildEventAction - - context = - { companies = model.companies } - in - div [ style myStyle ] [ Pages.Event.View.view context childAddress model.events ] - - App.GithubAuth -> - let - childAddress = - Signal.forwardTo address App.Update.ChildGithubAuthAction - in - div [ style myStyle ] [ Pages.GithubAuth.View.view childAddress model.githubAuth ] - - App.Login -> - let - childAddress = - Signal.forwardTo address App.Update.ChildLoginAction - - context = - { backendConfig = (.config >> .backendConfig) model } - - in - div [ style myStyle ] [ Pages.Login.View.view context childAddress model.login ] - - App.PageNotFound -> - div [] [ Pages.PageNotFound.View.view ] - - - App.User -> - let - childAddress = - Signal.forwardTo address App.Update.ChildUserAction - in - div [ style myStyle ] [ Pages.User.View.view childAddress model.user ] - -navbar : Signal.Address Action -> Model -> Html -navbar address model = - case model.user.name of - Pages.User.Model.Anonymous -> - div [] [] - - Pages.User.Model.LoggedIn name -> - navbarLoggedIn address model - -footer : Html -footer = - - div [class "main-footer"] - [ div [class "container"] - [ span [] - [ text "With " - , i [ class "fa fa-heart" ] [] - , text " from " - , a [ href "http://gizra.com", target "_blank", class "gizra-logo" ] [text "gizra"] - , span [ class "divider" ] [ text "|" ] - , text "Backend powered by " - , a [ href "https://pantheon.io/", target "_blank" ] [ text "Pantheon" ] - , span [ class "divider" ] [text "|"] - , a [href "https://github.com/Gizra/elm-hedley", target "_blank"] [text "Github repo"] +navbarAuthenticated : Model -> List (Html Msg) +navbarAuthenticated model = + [ a + [ classByPage MyAccount model.activePage + , onClick <| SetActivePage MyAccount ] - ] - ] - --- Navbar for Auth user. -navbarLoggedIn : Signal.Address Action -> Model -> Html -navbarLoggedIn address model = - let - activeClass page = - [ ("active", isActivePage model.activePage page) ] - - childAddress = - Signal.forwardTo address App.Update.ChildUserAction - - hrefVoid = - href "javascript:void(0);" - - - navCollapseButton = - let - iconBar index = - span [ class "icon-bar" ] [] - - in - button - [ class "navbar-toggle" - , attribute "data-toggle" "collapse" - , attribute "data-target" ".main-nav" - ] - [ span [ class "sr-only"] [ text "Menu" ] - , span [] ( List.map iconBar [0..2] ) - ] - - in - node "nav" - [ id "main-header" - , class "navbar navbar-default" ] - [ div - [ class "container" ] - [ div - [ class "navbar-header" ] - [ a [ class "brand visible-xs pull-left", href "#!/" ] [ text "Hedley" ] - , navCollapseButton - ] - , div - [ class "collapse navbar-collapse main-nav" ] - [ ul - [ class "nav navbar-nav"] - [ li [] [ a [ class "brand hidden-xs", href "#!/" ] [ text "Hedley" ] ] - , li - [ classList (activeClass App.User) ] - [ i [ class "glyphicon glyphicon-user" ] [] - , a [ hrefVoid, onClick address (App.Update.SetActivePage App.User) ] [ text "My account" ] - ] - , li - [ classList (activeClass (App.Event Nothing)) ] - [ i [ class "fa fa-map-marker" ] [] - , a [ hrefVoid, onClick address (App.Update.SetActivePage <| App.Event Nothing) ] [ text "Events" ] - ] - , li - [ classList (activeClass App.Article) ] - [ i [ class "fa fa-file-o" ] [] - , a [ hrefVoid, onClick address (App.Update.SetActivePage App.Article) ] [ text "Articles"] - ] - , li - [ classList (activeClass App.PageNotFound) ] - [ i [ class "fa fa-exclamation-circle" ] [] - , a [ href "#!/error-page" ] [ text "PageNotFound (404)" ] - ] - , li - [] - [ i [ class "fa fa-sign-out" ] [] - , a [ hrefVoid, onClick address App.Update.Logout ] [ text "Logout" ] - ] + [ text "My Account" ] + , viewPageNotFoundItem model.activePage + , div [ class "right menu" ] + [ viewAvatar model.user + , a + [ class "ui item" + , onClick <| Logout + ] + [ text "Logout" ] + ] + ] + + +viewPageNotFoundItem : Page -> Html Msg +viewPageNotFoundItem activePage = + a + [ classByPage PageNotFound activePage + , onClick <| SetActivePage PageNotFound + ] + [ text "404 page" ] + + +viewAvatar : WebData User -> Html Msg +viewAvatar user = + case user of + Success user' -> + a + [ onClick <| SetActivePage MyAccount + , class "ui item" ] - ] - ] - ] + [ img + [ class "ui avatar image" + , src user'.avatarUrl + ] + [] + ] + + _ -> + div [] [] + + +viewMainContent : Model -> Html Msg +viewMainContent model = + case model.activePage of + AccessDenied -> + div [] [ text "Access denied" ] + + Login -> + Html.map PageLogin (Pages.Login.View.view model.user model.pageLogin) + + MyAccount -> + Pages.MyAccount.View.view model.user + PageNotFound -> + -- We don't need to pass any cmds, so we can call the view directly + Pages.PageNotFound.View.view -myStyle : List (String, String) -myStyle = - [ ("font-size", "1.2em") ] + +viewFooter : Html Msg +viewFooter = + div + [ class "ui inverted vertical footer segment form-page" + ] + [ div [ class "ui container" ] + [ div [] [ text "Copyright statement will be here" ] + ] + ] + + +{-| Get menu items classes. This function gets the active page and checks if +it is indeed the page used. +-} +classByPage : Page -> Page -> Attribute a +classByPage page activePage = + classList + [ ( "item", True ) + , ( "active", page == activePage ) + ] diff --git a/src/elm/Article/Decoder.elm b/src/elm/Article/Decoder.elm deleted file mode 100644 index 9be9d04..0000000 --- a/src/elm/Article/Decoder.elm +++ /dev/null @@ -1,36 +0,0 @@ -module Article.Decoder where - -import Article.Model as Article exposing (Model) - -import Json.Decode as JD exposing ((:=)) -import String exposing (toInt, toFloat) - -decode : JD.Decoder Article.Model -decode = - let - -- Cast String to Int. - number : JD.Decoder Int - number = - JD.oneOf [ JD.int, JD.customDecoder JD.string String.toInt ] - - - numberFloat : JD.Decoder Float - numberFloat = - JD.oneOf [ JD.float, JD.customDecoder JD.string String.toFloat ] - - decodeAuthor = - JD.object2 Article.Author - ("id" := number) - ("label" := JD.string) - - decodeImage = - JD.at ["styles"] - ("thumbnail" := JD.string) - - in - JD.object5 Article.Model - ("user" := decodeAuthor) - (JD.oneOf [ "body" := JD.string, JD.succeed "" ]) - ("id" := number) - (JD.maybe ("image" := decodeImage)) - ("label" := JD.string) diff --git a/src/elm/Article/Model.elm b/src/elm/Article/Model.elm deleted file mode 100644 index 71f6f33..0000000 --- a/src/elm/Article/Model.elm +++ /dev/null @@ -1,16 +0,0 @@ -module Article.Model where - -type alias Id = Int - -type alias Model = - { author : Author - , body : String - , id : Id - , image : Maybe String - , label : String - } - -type alias Author = - { id : Id - , name : String - } diff --git a/src/elm/ArticleForm/Model.elm b/src/elm/ArticleForm/Model.elm deleted file mode 100644 index 52fe2d6..0000000 --- a/src/elm/ArticleForm/Model.elm +++ /dev/null @@ -1,35 +0,0 @@ -module ArticleForm.Model where - -type PostStatus = Busy | Done | Ready - -type alias ArticleForm = - { label : String - , body : String - , image : Maybe Int - , show : Bool - } - -type UserMessage - = None - | Error String - -type alias Model = - { articleForm : ArticleForm - , postStatus : PostStatus - , userMessage : UserMessage - } - -initialArticleForm : ArticleForm -initialArticleForm = - { label = "" - , body = "" - , image = Nothing - , show = True - } - -initialModel : Model -initialModel = - { articleForm = initialArticleForm - , postStatus = Ready - , userMessage = None - } diff --git a/src/elm/ArticleForm/Update.elm b/src/elm/ArticleForm/Update.elm deleted file mode 100644 index 79b8e93..0000000 --- a/src/elm/ArticleForm/Update.elm +++ /dev/null @@ -1,172 +0,0 @@ -module ArticleForm.Update where - -import Article.Decoder exposing (decode) -import Article.Model as Article exposing (Author, Model) -import ArticleForm.Model as ArticleForm exposing (initialArticleForm, initialModel, ArticleForm, Model, UserMessage) - -import Config.Model exposing (BackendConfig) -import Effects exposing (Effects) -import Http exposing (post, Error) -import Json.Decode as JD exposing ((:=)) -import Json.Encode as JE exposing (string) -import Task exposing (andThen, Task) -import Utils.Http exposing (getErrorMessageFromHttpResponse) - -init : (ArticleForm.Model, Effects Action) -init = - ( initialModel - , Effects.none - ) - -type Action - = ResetForm - | SubmitForm - | SetImageId (Maybe Int) - | SetUserMessage UserMessage - | UpdateBody String - | UpdateLabel String - | UpdatePostArticle (Result Http.Error Article.Model) - - -type alias Model = ArticleForm.Model - -type alias Context = - { accessToken : String - , backendConfig : BackendConfig - } - -update : Context -> Action -> Model -> (Model, Effects Action, Maybe Article.Model) -update context action model = - case action of - ResetForm -> - ( { model - | articleForm = initialArticleForm - , postStatus = ArticleForm.Ready - } - , Effects.none - , Nothing - ) - - SetImageId maybeVal -> - let - articleForm = - model.articleForm - - articleForm' = - { articleForm | image = maybeVal } - in - ( { model | articleForm = articleForm' } - , Effects.none - , Nothing - ) - - SetUserMessage userMessage -> - ( { model | userMessage = userMessage } - , Effects.none - , Nothing - ) - - SubmitForm -> - let - backendUrl = - (.backendConfig >> .backendUrl) context - - url = - backendUrl ++ "/api/v1.0/articles" - in - if model.postStatus == ArticleForm.Ready - then - ( { model | postStatus = ArticleForm.Busy } - , postArticle url context.accessToken model.articleForm - , Nothing - ) - - else - ( model - , Effects.none - , Nothing - ) - - -- @todo: Create a helper function. - UpdateBody val -> - let - articleForm = - model.articleForm - - articleForm' = - { articleForm | body = val } - in - ( { model | articleForm = articleForm' } - , Effects.none - , Nothing - ) - - UpdateLabel val -> - let - articleForm = - model.articleForm - - articleForm' = - { articleForm | label = val } - in - ( { model | articleForm = articleForm' } - , Effects.none - , Nothing - ) - - UpdatePostArticle result -> - case result of - Ok article -> - -- Append the new article to the articles list. - ( { model | postStatus = ArticleForm.Done } - -- We can reset the form, as it was posted successfully. - , Task.succeed ResetForm |> Effects.task - -- Return the article to the parent component. - , Just article - ) - - Err err -> - ( model - , Task.succeed (SetUserMessage <| ArticleForm.Error (getErrorMessageFromHttpResponse err)) |> Effects.task - , Nothing - ) - - --- EFFECTS - -postArticle : String -> String -> ArticleForm.ArticleForm -> Effects Action -postArticle url accessToken data = - let - params = - [ ("access_token", accessToken) ] - - encodedUrl = - Http.url url params - in - Http.post - decodePostArticle - encodedUrl - (Http.string <| dataToJson data ) - |> Task.toResult - |> Task.map UpdatePostArticle - |> Effects.task - - -dataToJson : ArticleForm.ArticleForm -> String -dataToJson data = - let - intOrNull maybeVal = - case maybeVal of - Just val -> JE.int val - Nothing -> JE.null - in - JE.encode 0 - <| JE.object - [ ("label", JE.string data.label) - , ("body", JE.string data.body) - , ("image", intOrNull data.image) - ] - -decodePostArticle : JD.Decoder Article.Model -decodePostArticle = - JD.at ["data", "0"] <| Article.Decoder.decode diff --git a/src/elm/ArticleForm/View.elm b/src/elm/ArticleForm/View.elm deleted file mode 100644 index 9872289..0000000 --- a/src/elm/ArticleForm/View.elm +++ /dev/null @@ -1,80 +0,0 @@ -module ArticleForm.View where - -import ArticleForm.Model exposing (initialModel, Model, UserMessage) -import ArticleForm.Update exposing (Action) -import Html exposing (i, button, div, label, h2, h3, input, img, li, text, textarea, span, ul, Html) -import Html.Attributes exposing (action, class, id, disabled, name, placeholder, property, required, size, src, style, type', value) -import Html.Events exposing (on, onClick, onSubmit, targetValue) -import String exposing (toInt, toFloat) - -view : Signal.Address Action -> Model -> Html -view address model = - div - [ class "wrapper -suffix"] - [ viewUserMessage model.userMessage - , viewForm address model - ] - -viewUserMessage : UserMessage -> Html -viewUserMessage userMessage = - case userMessage of - ArticleForm.Model.None -> - div [] [] - ArticleForm.Model.Error message -> - div [ style [("text-align", "center")] ] [ text message ] - -viewForm :Signal.Address Action -> Model -> Html -viewForm address model = - Html.form - [ onSubmit address ArticleForm.Update.SubmitForm - , action "javascript:void(0);" - ] - [ h3 - [ class "title" ] - [ i [ class "fa fa-pencil" ] [] - , text " Add new article" - ] - -- Label - , div - [ class "input-group" ] - [ label [] [ text "Label" ] - , input - [ type' "text" - , class "form-control" - , value model.articleForm.label - , on "input" targetValue (Signal.message address << ArticleForm.Update.UpdateLabel) - , required True - ] - [] - ] - -- End label - - -- Body - , div - [ class "input-group" ] - [ label [] [ text "Body"] - , textarea - [ class "form-control" - , name "body" - , placeholder "Body" - , value model.articleForm.body - , on "input" targetValue (Signal.message address << ArticleForm.Update.UpdateBody) - ] - [] - ] -- End body - - -- File upload - , div - [ class "input-group " ] - [ label [] [ text "Upload File" ] - , div [ class "dropzone" ] [] - ] - - -- Submit button - , button - [ onClick address ArticleForm.Update.SubmitForm - , class "btn btn-lg btn-primary" - , disabled (String.isEmpty model.articleForm.label) - ] - [ text "Submit" ] -- End submit button - ] diff --git a/src/elm/ArticleList/Model.elm b/src/elm/ArticleList/Model.elm deleted file mode 100644 index 2409cc6..0000000 --- a/src/elm/ArticleList/Model.elm +++ /dev/null @@ -1,25 +0,0 @@ -module ArticleList.Model where - -import Article.Model as Article exposing (Model) -import Http exposing (Error) -import Time exposing (Time) - -type alias Id = Int - -type Status = - Init - | Fetching - | Fetched Time.Time - | HttpError Http.Error - - -type alias Model = - { articles : List Article.Model - , status : Status - } - -initialModel : Model -initialModel = - { articles = [] - , status = Init - } diff --git a/src/elm/ArticleList/Update.elm b/src/elm/ArticleList/Update.elm deleted file mode 100644 index 58755d1..0000000 --- a/src/elm/ArticleList/Update.elm +++ /dev/null @@ -1,137 +0,0 @@ -module ArticleList.Update where - -import Article.Decoder exposing (decode) -import Article.Model as Article exposing (Model) -import ArticleList.Model exposing (initialModel, Model) -import Config exposing (cacheTtl) -import Config.Model exposing (BackendConfig) -import Effects exposing (Effects) -import Http exposing (post, Error) -import Json.Decode as JD exposing ((:=)) -import Task exposing (andThen, Task) -import TaskTutorial exposing (getCurrentTime) -import Time exposing (Time) - -init : (ArticleList.Model.Model, Effects Action) -init = - ( initialModel - , Effects.none - ) - -type Action - = AppendArticle Article.Model - | GetData - | GetDataFromServer - | NoOp - | UpdateDataFromServer (Result Http.Error (List Article.Model)) Time.Time - - - -type alias UpdateContext = - { accessToken : String - , backendConfig : BackendConfig - } - -update : UpdateContext -> Action -> ArticleList.Model.Model -> (ArticleList.Model.Model, Effects Action) -update context action model = - case action of - AppendArticle article -> - ( { model | articles = article :: model.articles } - , Effects.none - ) - - GetData -> - let - effects = - case model.status of - ArticleList.Model.Fetching -> - Effects.none - - _ -> - getDataFromCache model.status - in - ( model - , effects - ) - - - GetDataFromServer -> - let - backendUrl = - (.backendConfig >> .backendUrl) context - - url = - backendUrl ++ "/api/v1.0/articles" - in - ( { model | status = ArticleList.Model.Fetching } - , getJson url context.accessToken - ) - - - UpdateDataFromServer result timestamp' -> - case result of - Ok articles -> - ( { model - | articles = articles - , status = ArticleList.Model.Fetched timestamp' - } - , Effects.none - ) - - Err err -> - ( { model | status = ArticleList.Model.HttpError err } - , Effects.none - ) - - NoOp -> - (model, Effects.none) - --- EFFECTS - -getDataFromCache : ArticleList.Model.Status -> Effects Action -getDataFromCache status = - let - actionTask = - case status of - ArticleList.Model.Fetched fetchTime -> - Task.map (\currentTime -> - if fetchTime + Config.cacheTtl > currentTime - then NoOp - else GetDataFromServer - ) getCurrentTime - - _ -> - Task.succeed GetDataFromServer - - in - Effects.task actionTask - - -getJson : String -> String -> Effects Action -getJson url accessToken = - let - params = - [ ("access_token", accessToken) - , ("sort", "-id") - ] - - encodedUrl = Http.url url params - - httpTask = - Task.toResult <| - Http.get decodeData encodedUrl - - actionTask = - httpTask `andThen` (\result -> - Task.map (\timestamp' -> - UpdateDataFromServer result timestamp' - ) getCurrentTime - ) - - in - Effects.task actionTask - - -decodeData : JD.Decoder (List Article.Model) -decodeData = - JD.at ["data"] <| JD.list <| Article.Decoder.decode diff --git a/src/elm/ArticleList/View.elm b/src/elm/ArticleList/View.elm deleted file mode 100644 index 16805cb..0000000 --- a/src/elm/ArticleList/View.elm +++ /dev/null @@ -1,50 +0,0 @@ -module ArticleList.View where - -import Article.Model exposing (Model) - -import ArticleList.Model exposing (initialModel, Model) -import ArticleList.Update exposing (Action) - -import Html exposing (i, button, div, label, h2, h3, input, img, li, text, textarea, span, ul, Html) -import Html.Attributes exposing (action, class, id, disabled, name, placeholder, property, required, size, src, style, type', value) -import Html.Events exposing (on, onClick, onSubmit, targetValue) -import Json.Encode as JE exposing (string) -import String exposing (toInt, toFloat) - - -type alias Article = Article.Model.Model - -type alias Model = ArticleList.Model.Model - -view : Signal.Address Action -> Model -> Html -view address model = - viewRecentArticles model.articles - -viewRecentArticles : List Article -> Html -viewRecentArticles articles = - div - [ class "wrapper -suffix" ] - [ h3 - [ class "title" ] - [ i [ class "fa fa-file-o icon" ] [] - , text "Recent articles" - ] - , ul [ class "articles" ] (List.map viewArticles articles) - ] - -viewArticles : Article -> Html -viewArticles article = - let - image = - case article.image of - Just val -> img [ src val ] [] - Nothing -> div [] [] - in - li - [] - [ div [ class "title" ] [ text article.label ] - -- Allow attaching HTML without escaping it. XSS is escaped in the server - -- side. - , div [ property "innerHTML" <| JE.string article.body ] [] - , image - ] diff --git a/src/elm/Company/Model.elm b/src/elm/Company/Model.elm deleted file mode 100644 index c0fb4d5..0000000 --- a/src/elm/Company/Model.elm +++ /dev/null @@ -1,14 +0,0 @@ -module Company.Model where - -type alias CompanyId = Int - -type alias Model = - { id : CompanyId - , label : String - } - -initialModel : Model -initialModel = - { id = 0 - , label = "" - } diff --git a/src/elm/Config.elm b/src/elm/Config.elm index 1f619ea..a605856 100644 --- a/src/elm/Config.elm +++ b/src/elm/Config.elm @@ -1,29 +1,32 @@ -module Config where +module Config exposing (..) -import Config.Model as Config exposing (BackendConfig) +import Config.Model as Config exposing (Model) +import Dict exposing (..) import Time exposing (Time) -localBackend : BackendConfig -localBackend = - { backendUrl = "https://dev-hedley-elm.pantheonsite.io" - , githubClientId = "e5661c832ed931ae176c" - , name = "local" - , hostname = "localhost" - } - -prodBackend : BackendConfig -prodBackend = - { backendUrl = "https://live-hedley-elm.pantheonsite.io" - , githubClientId = "4aef0ced83d72bd48d00" - , name = "gh-pages" - , hostname = "gizra.github.io" - } - -backends : List BackendConfig -backends = - [ localBackend - , prodBackend - ] + +local : Model +local = + { backendUrl = "https://api.github.com" + , name = "local" + } + + +production : Model +production = + { backendUrl = "https://api.github.com" + , name = "gh-pages" + } + + +configs : Dict String Model +configs = + Dict.fromList + [ ( "localhost", local ) + , ( "example.com", production ) + ] + cacheTtl : Time.Time -cacheTtl = (5 * Time.second) +cacheTtl = + (5 * Time.second) diff --git a/src/elm/Config/Model.elm b/src/elm/Config/Model.elm index e04e030..b3af191 100644 --- a/src/elm/Config/Model.elm +++ b/src/elm/Config/Model.elm @@ -1,28 +1,11 @@ -module Config.Model where +module Config.Model exposing (..) -type alias BackendConfig = - { backendUrl : String - , githubClientId : String - , name : String - -- Url information - , hostname : String - } -initialBackendConfig : BackendConfig -initialBackendConfig = - { backendUrl = "" - , githubClientId = "" - , name = "" - , hostname = "" - } +type alias BackendUrl = + String -type alias Model = - { backendConfig : BackendConfig - , error : Bool - } -initialModel : Model -initialModel = - { backendConfig = initialBackendConfig - , error = False - } +type alias Model = + { backendUrl : BackendUrl + , name : String + } diff --git a/src/elm/Config/Test.elm b/src/elm/Config/Test.elm deleted file mode 100644 index 7088993..0000000 --- a/src/elm/Config/Test.elm +++ /dev/null @@ -1,25 +0,0 @@ -module Config.Test where - -import ElmTest exposing (..) - -import Config.Model exposing (initialBackendConfig, initialModel, Model) -import Config.Update exposing (update, Action) -import Effects exposing (Effects) - -type alias Action = Config.Update.Action -type alias Model = Config.Model.Model - -setErrorTest : Test -setErrorTest = - test "set error action" (assertEqual True (.error <| fst(setError))) - -setError : (Model, Effects Action) -setError = - Config.Update.update Config.Update.SetError initialModel - - -all : Test -all = - suite "Config tests" - [ setErrorTest - ] diff --git a/src/elm/Config/Update.elm b/src/elm/Config/Update.elm deleted file mode 100644 index 72d3dc5..0000000 --- a/src/elm/Config/Update.elm +++ /dev/null @@ -1,51 +0,0 @@ -module Config.Update where - -import Config exposing (backends) -import Config.Model exposing (initialModel, Model) -import Effects exposing (Effects) -import Task exposing (map) -import WebAPI.Location exposing (location) - -init : (Model, Effects Action) -init = - ( initialModel - , getConfigFromUrl - ) - -type Action - = SetConfig Config.Model.BackendConfig - | SetError - -update : Action -> Model -> (Model, Effects Action) -update action model = - case action of - SetConfig backendConfig -> - ( { model | backendConfig = backendConfig } - , Effects.none - ) - - SetError -> - ( { model | error = True } - , Effects.none - ) - - --- EFFECTS - -getConfigFromUrl : Effects Action -getConfigFromUrl = - let - getAction location = - let - config = - List.filter (\backend -> backend.hostname == location.hostname) backends - |> List.head - in - case config of - Just val -> SetConfig val - Nothing -> SetError - - actionTask = - Task.map getAction WebAPI.Location.location - in - Effects.task actionTask diff --git a/src/elm/Config/View.elm b/src/elm/Config/View.elm index f880f05..daf759c 100644 --- a/src/elm/Config/View.elm +++ b/src/elm/Config/View.elm @@ -1,13 +1,16 @@ -module Config.View where +module Config.View exposing (..) import Html exposing (div, h2, text, Html) import Html.Attributes exposing (class) + -- A plain function that always returns the error message -view : Html -view = - div - [ class "config-error" ] - [ h2 [] [ text "Configuration error" ] - , div [] [ text "Check your Config.elm file and make sure you have defined the enviorement properly" ] - ] + + +view : Html msg +view = + div + [ class "config-error" ] + [ h2 [] [ text "Configuration error" ] + , div [] [ text "Check your Config.elm file and make sure you have defined the enviorement properly" ] + ] diff --git a/src/elm/Event/Decoder.elm b/src/elm/Event/Decoder.elm deleted file mode 100644 index 05f062d..0000000 --- a/src/elm/Event/Decoder.elm +++ /dev/null @@ -1,36 +0,0 @@ -module Event.Decoder where - -import Event.Model as Event exposing (Author, Event, Marker) -import Json.Decode as JD exposing ((:=)) -import String exposing (toInt, toFloat) - -decode : JD.Decoder (List Event) -decode = - let - -- Cast String to Int. - number : JD.Decoder Int - number = - JD.oneOf [ JD.int, JD.customDecoder JD.string String.toInt ] - - - numberFloat : JD.Decoder Float - numberFloat = - JD.oneOf [ JD.float, JD.customDecoder JD.string String.toFloat ] - - marker = - JD.object2 Marker - ("lat" := numberFloat) - ("lng" := numberFloat) - - author = - JD.object2 Author - ("id" := number) - ("label" := JD.string) - in - JD.at ["data"] - <| JD.list - <| JD.object4 Event - ("user" := author) - ("id" := number) - ("label" := JD.string) - ("location" := marker) diff --git a/src/elm/Event/Model.elm b/src/elm/Event/Model.elm deleted file mode 100644 index f9205ed..0000000 --- a/src/elm/Event/Model.elm +++ /dev/null @@ -1,20 +0,0 @@ -module Event.Model where - -type alias Id = Int - -type alias Marker = - { lat: Float - , lng : Float - } - -type alias Author = - { id : Id - , name : String - } - -type alias Event = - { author : Author - , id : Id - , label : String - , marker : Marker - } diff --git a/src/elm/EventAuthorFilter/Model.elm b/src/elm/EventAuthorFilter/Model.elm deleted file mode 100644 index 273bd12..0000000 --- a/src/elm/EventAuthorFilter/Model.elm +++ /dev/null @@ -1,6 +0,0 @@ -module EventAuthorFilter.Model where - -type alias Model = Maybe Int - -initialModel : Model -initialModel = Nothing diff --git a/src/elm/EventAuthorFilter/Update.elm b/src/elm/EventAuthorFilter/Update.elm deleted file mode 100644 index 850521e..0000000 --- a/src/elm/EventAuthorFilter/Update.elm +++ /dev/null @@ -1,19 +0,0 @@ -module EventAuthorFilter.Update where - -import EventAuthorFilter.Model as EventAuthorFilter exposing (initialModel, Model) - -init : EventAuthorFilter.Model -init = initialModel - -type Action - = SelectAuthor Int - | UnSelectAuthor - -update : Action -> Model -> Model -update action model = - case action of - SelectAuthor id -> - Just id - - UnSelectAuthor -> - Nothing diff --git a/src/elm/EventAuthorFilter/View.elm b/src/elm/EventAuthorFilter/View.elm deleted file mode 100644 index 916e17b..0000000 --- a/src/elm/EventAuthorFilter/View.elm +++ /dev/null @@ -1,80 +0,0 @@ -module EventAuthorFilter.View where - -import EventAuthorFilter.Model as EventAuthorFilter exposing (initialModel, Model) -import EventAuthorFilter.Update exposing (Action) - -import Dict exposing (Dict) -import Event.Model as Event exposing (Author, Event) -import Html exposing (h3, a, i, div, input, text, select, span, li, option, ul, Html) -import Html.Attributes exposing (class, hidden, href, id, placeholder, selected, style, value) -import Html.Events exposing (on, onClick, targetValue) - -type alias Model = EventAuthorFilter.Model - -view : List Event -> Signal.Address Action -> Model -> Html -view events address eventAuthorFilter = - div - [ class "wrapper -suffix" ] - [ h3 - [ class "title" ] - [ i [ class "glyphicon glyphicon-user" ] [] - , text <| " " ++ "Event Authors" - ] - , ul [ class "authors" ] (viewEventsByAuthors events address eventAuthorFilter) - ] - -viewEventsByAuthors : List Event -> Signal.Address Action -> Maybe Int -> List Html -viewEventsByAuthors events address eventAuthorFilter = - let - getText : Author -> Int -> Html - getText author count = - let - authorRaw = - text (author.name ++ " (" ++ toString(count) ++ ")") - - authorSelect = - a [ href "javascript:void(0);", onClick address (EventAuthorFilter.Update.SelectAuthor author.id) ] [ authorRaw ] - - authorUnselect = - span [] - [ a - [ class "unselect fa fa-times-circle" - , href "javascript:void(0);" - , onClick address (EventAuthorFilter.Update.UnSelectAuthor) - ] [] - , authorRaw - ] - in - case eventAuthorFilter of - Just id -> - if author.id == id then authorUnselect else authorSelect - - Nothing -> - authorSelect - - viewAuthor (author, count) = - li [] [getText author count] - in - -- Get HTML from the grouped events - groupEventsByAuthors events |> - Dict.values |> - List.map viewAuthor - -groupEventsByAuthors : List Event -> Dict Int (Author, Int) -groupEventsByAuthors events = - let - handleEvent : Event -> Dict Int (Author, Int) -> Dict Int (Author, Int) - handleEvent event dict = - let - currentValue = - Maybe.withDefault (event.author, 0) <| - Dict.get event.author.id dict - - newValue = - case currentValue of - (author, count) -> (author, count + 1) - in - Dict.insert event.author.id newValue dict - - in - List.foldl handleEvent Dict.empty events diff --git a/src/elm/EventCompanyFilter/Model.elm b/src/elm/EventCompanyFilter/Model.elm deleted file mode 100644 index 42643d5..0000000 --- a/src/elm/EventCompanyFilter/Model.elm +++ /dev/null @@ -1,6 +0,0 @@ -module EventCompanyFilter.Model where - -type alias Model = Maybe Int - -initialModel : Model -initialModel = Nothing diff --git a/src/elm/EventCompanyFilter/Test.elm b/src/elm/EventCompanyFilter/Test.elm deleted file mode 100644 index ad75fa4..0000000 --- a/src/elm/EventCompanyFilter/Test.elm +++ /dev/null @@ -1,34 +0,0 @@ -module EventCompanyFilter.Test where - -import ElmTest exposing (..) - -import Company.Model as Company exposing (Model) -import EventCompanyFilter.Model as EventCompanyFilter exposing (initialModel, Model) -import EventCompanyFilter.Update exposing (Action) - -type alias Model = EventCompanyFilter.Model - -selectCompanySuite : Test -selectCompanySuite = - suite "SelectCompany action" - [ test "no company" (assertEqual Nothing (selectCompany Nothing)) - , test "valid company" (assertEqual (Just 1) (selectCompany <| Just 1)) - , test "invalid company" (assertEqual Nothing (selectCompany <| Just 100)) - ] - -selectCompany : Maybe Int -> Model -selectCompany val = - EventCompanyFilter.Update.update companies (EventCompanyFilter.Update.SelectCompany val) initialModel - -companies : List Company.Model -companies = - [ Company.Model 1 "foo" - , Company.Model 2 "bar" - , Company.Model 3 "baz" - ] - -all : Test -all = - suite "EventCompanyFilter" - [ selectCompanySuite - ] diff --git a/src/elm/EventCompanyFilter/Update.elm b/src/elm/EventCompanyFilter/Update.elm deleted file mode 100644 index 0d34d30..0000000 --- a/src/elm/EventCompanyFilter/Update.elm +++ /dev/null @@ -1,37 +0,0 @@ -module EventCompanyFilter.Update where - -import EventCompanyFilter.Model as EventCompanyFilter exposing (initialModel, Model) - -import Company.Model as Company exposing (Model) - - -init : Model -init = initialModel - -type Action - = SelectCompany (Maybe Int) - -type alias Model = EventCompanyFilter.Model - -update : List Company.Model -> Action -> Model -> Model -update companies action model = - case action of - SelectCompany maybeCompanyId -> - let - isValidCompany val = - companies - |> List.filter (\company -> company.id == val) - |> List.length - - - eventCompanyFilter = - case maybeCompanyId of - Just val -> - -- Make sure the given company ID is a valid one. - if ((isValidCompany val) > 0) - then Just val - else Nothing - Nothing -> - Nothing - in - eventCompanyFilter diff --git a/src/elm/EventCompanyFilter/View.elm b/src/elm/EventCompanyFilter/View.elm deleted file mode 100644 index 3e7a57d..0000000 --- a/src/elm/EventCompanyFilter/View.elm +++ /dev/null @@ -1,66 +0,0 @@ -module EventCompanyFilter.View where - -import EventCompanyFilter.Model as EventCompanyFilter exposing (initialModel, Model) -import EventCompanyFilter.Update exposing (Action) - -import Company.Model as Company exposing (Model) -import Html exposing (h3, a, i, div, input, text, select, span, li, option, ul, Html) -import Html.Attributes exposing (class, hidden, href, id, placeholder, selected, style, value) -import Html.Events exposing (on, onClick, targetValue) -import String exposing (toInt) - -type alias Model = EventCompanyFilter.Model - -view : List Company.Model -> Signal.Address Action -> Model -> Html -view companies address model = - div - [ class "wrapper -suffix" ] - [ h3 - [ class "title" ] - [ i [ class "fa fa-briefcase" ] [] - , text <| " " ++ "Companies" - ] - , companyListForSelect address companies model - ] - -companyListForSelect : Signal.Address Action -> List Company.Model -> Model -> Html -companyListForSelect address companies eventCompanyFilter = - let - selectedText = - case eventCompanyFilter of - Just id -> toString id - Nothing -> "" - - textToMaybe string = - if string == "0" - then Nothing - else - -- Converting to int return a result. - case (String.toInt string) of - Ok val -> - Just val - Err _ -> - Nothing - - - -- Add an "All companies" option - companies' = - (Company.Model 0 "-- All companies --") :: companies - - -- The selected company ID. - selectedId = - case eventCompanyFilter of - Just id -> - id - Nothing -> - 0 - - getOption company = - option [value <| toString company.id, selected (company.id == selectedId)] [ text company.label] - in - select - [ class "companies" - , value selectedText - , on "change" targetValue (\str -> Signal.message address <| EventCompanyFilter.Update.SelectCompany <| textToMaybe str) - ] - (List.map getOption companies') diff --git a/src/elm/EventList/Model.elm b/src/elm/EventList/Model.elm deleted file mode 100644 index 19ac5d4..0000000 --- a/src/elm/EventList/Model.elm +++ /dev/null @@ -1,12 +0,0 @@ -module EventList.Model where - -type alias Model = - { filterString : String - , selectedEvent : Maybe Int - } - -initialModel : Model -initialModel = - { filterString = "" - , selectedEvent = Nothing - } diff --git a/src/elm/EventList/Update.elm b/src/elm/EventList/Update.elm deleted file mode 100644 index 806bfee..0000000 --- a/src/elm/EventList/Update.elm +++ /dev/null @@ -1,29 +0,0 @@ -module EventList.Update where - -import Event.Model exposing (Event) -import EventList.Model as EventList exposing (initialModel, Model) - -type Action - = FilterEvents String - -- Select event might get values from JS (i.e. selecting a leaflet marker) - -- so we allow passing a Maybe Int, instead of just Int. - | SelectEvent (Maybe Int) - | UnSelectEvent - -type alias Model = EventList.Model - -init : Model -init = initialModel - - -update : List Event -> Action -> Model -> Model -update events action model = - case action of - FilterEvents val -> - { model | filterString = val } - - SelectEvent val -> - { model | selectedEvent = val } - - UnSelectEvent -> - { model | selectedEvent = Nothing } diff --git a/src/elm/EventList/Utils.elm b/src/elm/EventList/Utils.elm deleted file mode 100644 index e29774a..0000000 --- a/src/elm/EventList/Utils.elm +++ /dev/null @@ -1,19 +0,0 @@ -module EventList.Utils (filterEventsByString) where - -import Event.Model exposing (Event) -import String exposing (isEmpty, trim, toLower) - -filterEventsByString : List Event -> String -> List Event -filterEventsByString events filterString = - let - filterString' = - String.trim filterString - in - if String.isEmpty filterString' - then - -- Return all the events. - events - - else - -- Filter out the events that do not contain the string. - List.filter (\event -> String.contains (String.toLower filterString') (String.toLower event.label)) events diff --git a/src/elm/EventList/View.elm b/src/elm/EventList/View.elm deleted file mode 100644 index 24aff04..0000000 --- a/src/elm/EventList/View.elm +++ /dev/null @@ -1,82 +0,0 @@ -module EventList.View (view) where - -import Event.Model as Event exposing (Event) -import EventList.Model as EventList exposing (initialModel, Model) -import EventList.Update exposing (Action) -import EventList.Utils exposing (filterEventsByString) - -import Html exposing (h3, a, i, div, input, text, select, span, li, option, ul, Html) -import Html.Attributes exposing (class, hidden, href, id, placeholder, selected, style, value) -import Html.Events exposing (on, onClick, targetValue) - -type alias Model = EventList.Model - -view : List Event -> Signal.Address Action -> Model -> Html -view events address model = - div - [ id "events" - , class "wrapper -suffix" ] - [ h3 - [ class "title" ] - [ i [ class "fa fa-map-marker" ] [] - , text <| " " ++ "Event List" - ] - , (viewFilterString address model) - , (viewListEvents events address model) - ] - - -viewFilterString : Signal.Address Action -> Model -> Html -viewFilterString address model = - div [] - [ input - [ class "search form-control" - , placeholder "Filter events" - , value model.filterString - , on "input" targetValue (Signal.message address << EventList.Update.FilterEvents) - ] - [] - ] - - -viewListEvents : List Event -> Signal.Address Action -> Model -> Html -viewListEvents events address model = - let - filteredEvents = - filterEventsByString events model.filterString - - hrefVoid = - href "javascript:void(0);" - - eventSelect event = - li [] - [ a [ hrefVoid , onClick address (EventList.Update.SelectEvent <| Just event.id) ] [ text event.label ] ] - - eventUnselect event = - li [] - [ span [] - [ a - [ class "unselect fa fa-times-circle" - , href "javascript:void(0);" - , onClick address (EventList.Update.UnSelectEvent) - ] [] - , text event.label - ] - ] - - getListItem : Event -> Html - getListItem event = - case model.selectedEvent of - Just id -> - if event.id == id - then eventUnselect(event) - else eventSelect(event) - - Nothing -> - eventSelect(event) - in - if List.isEmpty filteredEvents - then - div [] [ text "No results found" ] - else - ul [ class "authors" ] (List.map getListItem filteredEvents) diff --git a/src/elm/Leaflet/Model.elm b/src/elm/Leaflet/Model.elm deleted file mode 100644 index cbd5f13..0000000 --- a/src/elm/Leaflet/Model.elm +++ /dev/null @@ -1,20 +0,0 @@ -module Leaflet.Model where - -type alias Marker = - { id : Int - , lat : Float - , lng : Float - } - -type alias Model = - { markers : List Marker - , selectedMarker : Maybe Int - , showMap : Bool - } - -initialModel : Model -initialModel = - { markers = [] - , selectedMarker = Nothing - , showMap = False - } diff --git a/src/elm/Leaflet/Update.elm b/src/elm/Leaflet/Update.elm deleted file mode 100644 index 6bd2309..0000000 --- a/src/elm/Leaflet/Update.elm +++ /dev/null @@ -1,35 +0,0 @@ -module Leaflet.Update where - -import Event.Model exposing (Event) -import Leaflet.Model as Leaflet exposing (initialModel, Marker, Model) - -init : Model -init = - initialModel - -type Action - = SelectMarker (Maybe Int) - | SetMarkers (List Event) - | ToggleMap - | UnselectMarker - - -update : Action -> Model -> Model -update action model = - case action of - SelectMarker val -> - { model | selectedMarker = val } - - SetMarkers events -> - { model | markers = eventToMarkers events } - - ToggleMap -> - { model | showMap = (not model.showMap) } - - UnselectMarker -> - { model | selectedMarker = Nothing } - -eventToMarkers : List Event -> List Leaflet.Marker -eventToMarkers events = - events - |> List.map (\event -> Leaflet.Marker event.id event.marker.lat event.marker.lng) diff --git a/src/elm/Leaflet/View.elm b/src/elm/Leaflet/View.elm deleted file mode 100644 index 374c044..0000000 --- a/src/elm/Leaflet/View.elm +++ /dev/null @@ -1,20 +0,0 @@ -module Leaflet.View where - -import Leaflet.Model exposing (initialModel, Model) -import Leaflet.Update exposing (Action) - -import Html exposing (div, span, Html) -import Html.Attributes exposing (id, style) - -view : Signal.Address Action -> Model -> Html -view address model = - if model.showMap - then div [ style myStyle, id "map" ] [] - -- We use span, so the div element will be completely removed. - else span [] [] - -myStyle : List (String, String) -myStyle = - [ ("width", "600px") - , ("height", "400px") - ] diff --git a/src/elm/Main.elm b/src/elm/Main.elm index 2bdbb4a..a193069 100644 --- a/src/elm/Main.elm +++ b/src/elm/Main.elm @@ -1,115 +1,28 @@ -import App.Model as App exposing (Model) -import App.Router exposing (delta2update, location2action) -import App.Update exposing (init, update) -import App.View exposing (view) -import ArticleForm.Model exposing (PostStatus) -import ArticleForm.Update exposing (Action) -import Effects exposing (Never) -import EventList.Update exposing (Action) -import Pages.Event.Update exposing (Action) -import Leaflet.Model exposing (Model) -import Pages.Article.Update exposing (Action) -import RouteHash -import StartApp as StartApp -import Task exposing (Task) +module Main exposing (..) +import App.Model exposing (Model) +import App.Router exposing (..) +import App.Update exposing (init, update, Msg) +import App.View exposing (view) +import RouteUrl -app = - StartApp.start - { init = App.Update.init - , update = App.Update.update - , view = App.View.view - , inputs = - [ messages.signal - , Signal.map (App.Update.ChildArticleAction << Pages.Article.Update.ChildArticleFormAction << ArticleForm.Update.SetImageId) dropzoneUploadedFile - , Signal.map (App.Update.ChildArticleAction << Pages.Article.Update.ChildArticleFormAction << ArticleForm.Update.UpdateBody) ckeditor - , Signal.map (App.Update.ChildEventAction << Pages.Event.Update.ChildEventListAction << EventList.Update.SelectEvent) selectEvent - ] - } +main : Program Never main = - app.html - -messages : Signal.Mailbox App.Update.Action -messages = - Signal.mailbox App.Update.NoOp - -port tasks : Signal (Task.Task Never ()) -port tasks = - app.tasks - -port routeTasks : Signal (Task () ()) -port routeTasks = - RouteHash.start - { prefix = RouteHash.defaultPrefix - , address = messages.address - , models = app.model - , delta2update = App.Router.delta2update - , location2action = App.Router.location2action - } - --- Interactions with Leaflet maps - -type alias LeafletPort = - { leaflet : Leaflet.Model.Model - , events : List Int - } - -port mapManager : Signal LeafletPort -port mapManager = - let - getEvents model = - (.events >> .events) model - - getLeaflet model = - (.events >> .leaflet) model - - getLeafletPort model = - { events = List.map .id <| getEvents model - , leaflet = getLeaflet model - } - - in - Signal.map getLeafletPort app.model - -port selectEvent : Signal (Maybe Int) - --- Dropzone - -type alias ActivePagePort = - { accessToken : String - , activePage : String - , backendUrl : String - , postStatus : String - } + RouteUrl.program + { delta2url = delta2url + , location2messages = location2messages + , init = App.Update.init + , update = App.Update.update + , view = App.View.view + , subscriptions = subscriptions + } -port activePage : Signal ActivePagePort -port activePage = - let - pageAsString page = - case page of - App.Article -> "Article" - App.Event _ -> "Event" - App.GithubAuth -> "GithubAuth" - App.Login -> "Login" - App.PageNotFound -> "PageNotFound" - App.User -> "User" - postStatusAsString status = - case status of - ArticleForm.Model.Busy -> "Busy" - ArticleForm.Model.Done -> "Done" - ArticleForm.Model.Ready -> "Ready" - getPortData model = - { accessToken = model.accessToken - , activePage = pageAsString model.activePage - , backendUrl = (.config >> .backendConfig >> .backendUrl) model - , postStatus = postStatusAsString model.article.articleForm.postStatus - } - in - Signal.map getPortData app.model +-- SUBSCRIPTIONS -port dropzoneUploadedFile : Signal (Maybe Int) -port ckeditor : Signal String +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none diff --git a/src/elm/Pages/Article/Model.elm b/src/elm/Pages/Article/Model.elm deleted file mode 100644 index 29f1633..0000000 --- a/src/elm/Pages/Article/Model.elm +++ /dev/null @@ -1,15 +0,0 @@ -module Pages.Article.Model where - -import ArticleForm.Model as ArticleForm exposing (initialModel, Model) -import ArticleList.Model as ArticleList exposing (initialModel, Model) - -type alias Model = - { articleForm : ArticleForm.Model - , articleList : ArticleList.Model - } - -initialModel : Model -initialModel = - { articleForm = ArticleForm.initialModel - , articleList = ArticleList.initialModel - } diff --git a/src/elm/Pages/Article/Update.elm b/src/elm/Pages/Article/Update.elm deleted file mode 100644 index d0a6359..0000000 --- a/src/elm/Pages/Article/Update.elm +++ /dev/null @@ -1,62 +0,0 @@ -module Pages.Article.Update where - -import ArticleForm.Update exposing (Action) -import ArticleList.Update exposing (Action) -import Config.Model exposing (BackendConfig) -import Effects exposing (Effects) -import Pages.Article.Model exposing (Model) -import Task exposing (succeed) - -type Action - = Activate - | ChildArticleFormAction ArticleForm.Update.Action - | ChildArticleListAction ArticleList.Update.Action - -type alias UpdateContext = - { accessToken : String - , backendConfig : BackendConfig - } - -init : (Model, Effects Action) -init = - ( Pages.Article.Model.initialModel - , Effects.none - ) - -update : UpdateContext -> Action -> Pages.Article.Model.Model -> (Pages.Article.Model.Model, Effects Action) -update context action model = - case action of - Activate -> - ( model - , Task.succeed (ChildArticleListAction ArticleList.Update.GetData) |> Effects.task - ) - - ChildArticleFormAction act -> - let - (childModel, childEffects, maybeArticle) = ArticleForm.Update.update context act model.articleForm - - defaultEffects = - [ Effects.map ChildArticleFormAction childEffects ] - - effects' = - case maybeArticle of - Just article -> - (Task.succeed (ChildArticleListAction <| ArticleList.Update.AppendArticle article) |> Effects.task) - :: - defaultEffects - Nothing -> - defaultEffects - - in - - ( { model | articleForm = childModel } - , Effects.batch effects' - ) - - ChildArticleListAction act -> - let - (childModel, childEffects) = ArticleList.Update.update context act model.articleList - in - ( { model | articleList = childModel } - , Effects.map ChildArticleListAction childEffects - ) diff --git a/src/elm/Pages/Article/View.elm b/src/elm/Pages/Article/View.elm deleted file mode 100644 index e2a6e8e..0000000 --- a/src/elm/Pages/Article/View.elm +++ /dev/null @@ -1,26 +0,0 @@ -module Pages.Article.View where - -import ArticleForm.View as ArticleForm exposing (view) -import ArticleList.View as ArticleList exposing (view) -import Html exposing (i, button, div, label, h2, h3, input, img, li, text, textarea, span, ul, Html) -import Html.Attributes exposing (action, class, id, disabled, name, placeholder, property, required, size, src, style, type', value) -import Pages.Article.Model as Article exposing (Model) -import Pages.Article.Update exposing (Action) - - -view : Signal.Address Action -> Article.Model -> Html -view address model = - let - childArticleFormAddress = - Signal.forwardTo address Pages.Article.Update.ChildArticleFormAction - - childArticleListAddress = - Signal.forwardTo address Pages.Article.Update.ChildArticleListAction - in - div - [ id "article-page" - , class "container" - ] - [ ArticleForm.view childArticleFormAddress model.articleForm - , ArticleList.view childArticleListAddress model.articleList - ] diff --git a/src/elm/Pages/Event/Model.elm b/src/elm/Pages/Event/Model.elm deleted file mode 100644 index 4109637..0000000 --- a/src/elm/Pages/Event/Model.elm +++ /dev/null @@ -1,38 +0,0 @@ -module Pages.Event.Model where - -import Event.Model exposing (Event) -import EventAuthorFilter.Model as EventAuthorFilter exposing (initialModel, Model) -import EventCompanyFilter.Model as EventCompanyFilter exposing (initialModel, Model) -import EventList.Model as EventList exposing (initialModel, Model) - -import Http exposing (Error) -import Leaflet.Model exposing (initialModel, Model) -import Time exposing (Time) - -type alias Id = Int -type alias CompanyId = Int - -type Status = - Init - | Fetching (Maybe CompanyId) - | Fetched (Maybe CompanyId) Time.Time - | HttpError Http.Error - -type alias Model = - { events : List Event - , eventList: EventList.Model - , status : Status - , eventCompanyFilter : EventCompanyFilter.Model - , eventAuthorFilter : EventAuthorFilter.Model - , leaflet : Leaflet.Model.Model - } - -initialModel : Model -initialModel = - { events = [] - , eventList = EventList.initialModel - , status = Init - , eventCompanyFilter = EventCompanyFilter.initialModel - , eventAuthorFilter = EventAuthorFilter.initialModel - , leaflet = Leaflet.Model.initialModel - } diff --git a/src/elm/Pages/Event/Router.elm b/src/elm/Pages/Event/Router.elm deleted file mode 100644 index 201009e..0000000 --- a/src/elm/Pages/Event/Router.elm +++ /dev/null @@ -1,28 +0,0 @@ -module Pages.Event.Router where - -import Pages.Event.Model as Event exposing (Model) -import RouteHash exposing (HashUpdate) -import String exposing (toInt) - -delta2update : Model -> Model -> Maybe HashUpdate -delta2update previous current = - let - url = - case current.eventCompanyFilter of - Just companyId -> [ toString (companyId) ] - Nothing -> [] - in - Just <| RouteHash.set url - -location2company : List String -> Maybe Int -location2company list = - case List.head list of - Just eventId -> - case String.toInt eventId of - Ok val -> - Just val - Err _ -> - Nothing - - Nothing -> - Nothing diff --git a/src/elm/Pages/Event/Update.elm b/src/elm/Pages/Event/Update.elm deleted file mode 100644 index 57b6850..0000000 --- a/src/elm/Pages/Event/Update.elm +++ /dev/null @@ -1,262 +0,0 @@ -module Pages.Event.Update where - -import Config exposing (cacheTtl) -import Config.Model exposing (BackendConfig) -import Company.Model as Company exposing (Model) -import Effects exposing (Effects) -import Event.Decoder exposing (decode) -import Event.Model exposing (Event) -import EventAuthorFilter.Update exposing (Action) -import EventCompanyFilter.Update exposing (Action) -import EventList.Update exposing (Action) -import EventList.Utils exposing (filterEventsByString) -import Http exposing (Error) -import Leaflet.Update exposing (Action) -import Pages.Event.Model as Event exposing (Model) -import Pages.Event.Utils exposing (filterEventsByAuthor) -import Task exposing (andThen, succeed) -import TaskTutorial exposing (getCurrentTime) -import Time exposing (Time) - -type alias Id = Int -type alias CompanyId = Int -type alias Model = Event.Model - -init : (Model, Effects Action) -init = - ( Event.initialModel - , Effects.none - ) - -type Action - = NoOp - | GetData (Maybe CompanyId) - | GetDataFromServer (Maybe CompanyId) - | UpdateDataFromServer (Result Http.Error (List Event)) (Maybe CompanyId) Time.Time - - -- Child actions - | ChildEventAuthorFilterAction EventAuthorFilter.Update.Action - | ChildEventCompanyFilterAction EventCompanyFilter.Update.Action - | ChildEventListAction EventList.Update.Action - | ChildLeafletAction Leaflet.Update.Action - - -- Page - | Activate (Maybe CompanyId) - | Deactivate - - -type alias Context = - { accessToken : String - , backendConfig : BackendConfig - , companies : List Company.Model - } - -update : Context -> Action -> Model -> (Model, Effects Action) -update context action model = - case action of - ChildEventAuthorFilterAction act -> - let - -- The child component doesn't have effects. - childModel = - EventAuthorFilter.Update.update act model.eventAuthorFilter - in - ( { model | eventAuthorFilter = childModel } - -- Filter out the events, before sending the events' markers. - , Task.succeed (ChildLeafletAction <| Leaflet.Update.SetMarkers (filterEventsByAuthor model.events childModel)) |> Effects.task - ) - - ChildEventCompanyFilterAction act -> - let - childModel = - EventCompanyFilter.Update.update context.companies act model.eventCompanyFilter - - maybeCompanyId = - -- Reach into the selected company, in order to invoke getting the - -- data from server. - case act of - EventCompanyFilter.Update.SelectCompany maybeCompanyId -> - maybeCompanyId - - in - ( { model | eventCompanyFilter = childModel } - , Task.succeed (GetData maybeCompanyId) |> Effects.task - ) - - ChildEventListAction act -> - let - filteredEventsByAuthor = - filterEventsByAuthor model.events model.eventAuthorFilter - - -- The child component doesn't have effects. - childModel = - EventList.Update.update filteredEventsByAuthor act model.eventList - - childAction = - case act of - EventList.Update.FilterEvents val -> - -- Filter out the events, before sending the events' markers. - Leaflet.Update.SetMarkers (filterEventsByString filteredEventsByAuthor val) - - EventList.Update.SelectEvent val -> - Leaflet.Update.SelectMarker val - - EventList.Update.UnSelectEvent -> - Leaflet.Update.UnselectMarker - in - ( { model | eventList = childModel } - , Task.succeed (ChildLeafletAction <| childAction) |> Effects.task - ) - - ChildLeafletAction act -> - let - childModel = - Leaflet.Update.update act model.leaflet - in - ( {model | leaflet = childModel } - , Effects.none - ) - - GetData maybeCompanyId -> - let - noFx = - (model, Effects.none) - - getFx = - (model, getDataFromCache model.status maybeCompanyId) - in - case model.status of - Event.Fetching id -> - if id == maybeCompanyId - -- We are already fetching this data - then noFx - -- We are fetching data, but for a different company ID, - -- so we need to re-fetch. - else getFx - - _ -> - getFx - - GetDataFromServer maybeCompanyId -> - let - backendUrl = - (.backendConfig >> .backendUrl) context - - url = - backendUrl ++ "/api/v1.0/events" - in - ( { model | status = Event.Fetching maybeCompanyId } - , getJson url maybeCompanyId context.accessToken - ) - - NoOp -> - (model, Effects.none) - - UpdateDataFromServer result maybeCompanyId timestamp -> - case result of - Ok events -> - let - filteredEventsByAuthor = - filterEventsByAuthor events model.eventAuthorFilter - - filteredEvents = - filterEventsByString filteredEventsByAuthor model.eventList.filterString - in - ( { model - | events = events - , status = Event.Fetched maybeCompanyId timestamp - } - , Effects.batch - [ Task.succeed (ChildEventAuthorFilterAction EventAuthorFilter.Update.UnSelectAuthor) |> Effects.task - , Task.succeed (ChildEventListAction EventList.Update.UnSelectEvent) |> Effects.task - , Task.succeed (ChildEventListAction <| EventList.Update.FilterEvents "") |> Effects.task - ] - ) - - Err msg -> - ( { model | status = Event.HttpError msg } - , Effects.none - ) - - Activate maybeCompanyId -> - let - childModel = - Leaflet.Update.update Leaflet.Update.ToggleMap model.leaflet - - in - ( { model | leaflet = childModel } - , Effects.batch - [ Task.succeed (GetData maybeCompanyId) |> Effects.task - , Task.succeed (ChildEventCompanyFilterAction <| EventCompanyFilter.Update.SelectCompany maybeCompanyId) |> Effects.task - ] - ) - - Deactivate -> - let - childModel = - Leaflet.Update.update Leaflet.Update.ToggleMap model.leaflet - in - ( { model | leaflet = childModel } - , Effects.none - ) - - --- EFFECTS - -getDataFromCache : Event.Status -> Maybe CompanyId -> Effects Action -getDataFromCache status maybeCompanyId = - let - getFx = - Task.succeed <| GetDataFromServer maybeCompanyId - - actionTask = - case status of - Event.Fetched id fetchTime -> - if id == maybeCompanyId - then - Task.map (\currentTime -> - if fetchTime + Config.cacheTtl > currentTime - then NoOp - else GetDataFromServer maybeCompanyId - ) getCurrentTime - else - getFx - - _ -> - getFx - - in - Effects.task actionTask - - -getJson : String -> Maybe CompanyId -> String -> Effects Action -getJson url maybeCompanyId accessToken = - let - params = - [ ("access_token", accessToken) ] - - params' = - case maybeCompanyId of - Just id -> - -- Filter by company - ("filter[company]", toString id) :: params - - Nothing -> - params - - - encodedUrl = - Http.url url params' - - httpTask = - Task.toResult <| - Http.get Event.Decoder.decode encodedUrl - - actionTask = - httpTask `andThen` (\result -> - Task.map (\timestamp -> - UpdateDataFromServer result maybeCompanyId timestamp - ) getCurrentTime - ) - - in - Effects.task actionTask diff --git a/src/elm/Pages/Event/Utils.elm b/src/elm/Pages/Event/Utils.elm deleted file mode 100644 index 1a57aec..0000000 --- a/src/elm/Pages/Event/Utils.elm +++ /dev/null @@ -1,13 +0,0 @@ -module Pages.Event.Utils where - -import EventAuthorFilter.Model as EventAuthorFilter exposing (Model) -import Event.Model exposing (Event) - -filterEventsByAuthor : List Event -> EventAuthorFilter.Model -> List Event -filterEventsByAuthor events authorFilter = - case authorFilter of - Just id -> - List.filter (\event -> event.author.id == id) events - - Nothing -> - events diff --git a/src/elm/Pages/Event/View.elm b/src/elm/Pages/Event/View.elm deleted file mode 100644 index 70d366f..0000000 --- a/src/elm/Pages/Event/View.elm +++ /dev/null @@ -1,63 +0,0 @@ -module Pages.Event.View where - -import Company.Model as Company exposing (Model) -import EventAuthorFilter.View exposing (view) -import EventCompanyFilter.View exposing (view) -import EventList.View exposing (view) -import Html exposing (h3, a, i, div, input, text, select, span, li, option, ul, Html) -import Html.Attributes exposing (class, hidden, href, id, placeholder, selected, style, value) -import Pages.Event.Model exposing (initialModel, Model) -import Pages.Event.Update exposing (Action) -import Pages.Event.Utils exposing (filterEventsByAuthor) - -type alias Action = Pages.Event.Update.Action -type alias CompanyId = Int -type alias Model = Pages.Event.Model.Model - -type alias Context = - { companies : List Company.Model } - -view : Context -> Signal.Address Action -> Model -> Html -view context address model = - let - - childEventAuthorFilterAddress = - Signal.forwardTo address Pages.Event.Update.ChildEventAuthorFilterAction - - childEventCompanyFilterAddress = - Signal.forwardTo address Pages.Event.Update.ChildEventCompanyFilterAction - - childEventListAddress = - Signal.forwardTo address Pages.Event.Update.ChildEventListAction - - filteredEvents = - filterEventsByAuthor model.events model.eventAuthorFilter - - in - div - [ id "events-page" - , class "container" - ] - [ div [class "row"] - [ div [class "col-md-3 first"] - [ (EventCompanyFilter.View.view context.companies childEventCompanyFilterAddress model.eventCompanyFilter) - , (EventAuthorFilter.View.view model.events childEventAuthorFilterAddress model.eventAuthorFilter) - , (EventList.View.view filteredEvents childEventListAddress model.eventList) - ] - - , div - [ class "col-md-9 last"] - [ div - [ class "map-container wrapper -suffix" ] - [ h3 - [ class "title" ] - [ i [ class "fa fa-globe" ] [] - , text <| " " ++ "Map" - ] - , div - [ class "map-wrapper" ] - [ div [ id "map"] [] ] - ] - ] - ] - ] diff --git a/src/elm/Pages/GithubAuth/Model.elm b/src/elm/Pages/GithubAuth/Model.elm deleted file mode 100644 index 95b1766..0000000 --- a/src/elm/Pages/GithubAuth/Model.elm +++ /dev/null @@ -1,25 +0,0 @@ -module Pages.GithubAuth.Model where - -import Http exposing (Error) - -type alias AccessToken = String - -type Status = Init - | Error String - | Fetching - | Fetched - | HttpError Http.Error - - -type alias Model = - { accessToken: AccessToken - , status : Status - , code : Maybe String - } - -initialModel : Model -initialModel = - { accessToken = "" - , status = Init - , code = Nothing - } diff --git a/src/elm/Pages/GithubAuth/Update.elm b/src/elm/Pages/GithubAuth/Update.elm deleted file mode 100644 index ee8990d..0000000 --- a/src/elm/Pages/GithubAuth/Update.elm +++ /dev/null @@ -1,116 +0,0 @@ -module Pages.GithubAuth.Update where - -import Config.Model exposing (BackendConfig) -import Dict exposing (get) -import Effects exposing (Effects) -import Http exposing (Error) -import Json.Decode as JD exposing ((:=)) -import Json.Encode as JE exposing (..) -import Pages.GithubAuth.Model as GithubAuth exposing (initialModel, Model) -import Task exposing (map) -import UrlParameterParser exposing (ParseResult, parseSearchString) -import WebAPI.Location exposing (location) - -type alias AccessToken = String - -init : (Model, Effects Action) -init = - ( initialModel - , Effects.none - ) - - -type Action - = Activate - | AuthorizeUser String - | SetError String - | SetAccessToken AccessToken - | UpdateAccessTokenFromServer (Result Http.Error AccessToken) - -type alias UpdateContext = - { backendConfig : BackendConfig - } - -update : UpdateContext -> Action -> Model -> (Model, Effects Action) -update context action model = - case action of - Activate -> - (model, getCodeFromUrl) - - AuthorizeUser code -> - let - backendUrl = - (.backendConfig >> .backendUrl) context - in - ( model - , getJson backendUrl code - ) - - SetError msg -> - ( { model | status = GithubAuth.Error msg } - , Effects.none - ) - - SetAccessToken token -> - ( { model | accessToken = token } - , Effects.none - ) - - UpdateAccessTokenFromServer result -> - case result of - Ok token -> - ( { model | status = GithubAuth.Fetched } - , Task.succeed (SetAccessToken token) |> Effects.task - ) - Err msg -> - ( { model | status = GithubAuth.HttpError msg } - -- @todo: Improve. - , Task.succeed (SetError "HTTP error") |> Effects.task - ) - --- EFFECTS - -getCodeFromUrl : Effects Action -getCodeFromUrl = - let - errAction = - SetError "code property is missing form URL." - - getAction location = - case (parseSearchString location.search) of - UrlParameterParser.UrlParams dict -> - case (Dict.get "code" dict) of - Just val -> - AuthorizeUser val - Nothing -> - errAction - - UrlParameterParser.Error _ -> - errAction - - actionTask = - Task.map getAction WebAPI.Location.location - in - Effects.task actionTask - - -getJson : String -> String -> Effects Action -getJson backendUrl code = - Http.post - decodeAccessToken - (backendUrl ++ "/auth/github") - (Http.string <| dataToJson code ) - |> Task.toResult - |> Task.map UpdateAccessTokenFromServer - |> Effects.task - - -dataToJson : String -> String -dataToJson code = - JE.encode 0 - <| JE.object - [ ("code", JE.string code) ] - -decodeAccessToken : JD.Decoder AccessToken -decodeAccessToken = - JD.at ["access_token"] <| JD.string diff --git a/src/elm/Pages/GithubAuth/View.elm b/src/elm/Pages/GithubAuth/View.elm deleted file mode 100644 index 50a1689..0000000 --- a/src/elm/Pages/GithubAuth/View.elm +++ /dev/null @@ -1,36 +0,0 @@ -module Pages.GithubAuth.View where - --- import Config.Model exposing (BackendConfig) --- import Dict exposing (get) --- import Effects exposing (Effects) -import Html exposing (a, div, i, text, Html) -import Html.Attributes exposing (class, href, id) --- import Http exposing (Error) --- import Json.Decode as JD exposing ((:=)) --- import Json.Encode as JE exposing (..) -import Pages.GithubAuth.Model as GithubAuth exposing (initialModel, Model) -import Pages.GithubAuth.Update exposing (Action) - - -view : Signal.Address Action -> Model -> Html -view address model = - let - spinner = - i [ class "fa fa-spinner fa-spin" ] [] - - content = - case model.status of - GithubAuth.Error msg -> - div [] - [text <| "Error:" ++ msg - , a [ href "#!/login"] [text "Back to Login"] - ] - - _ -> - spinner - - in - div - [ id "github-auth-page" ] - [ div [ class "container"] [ content ] - ] diff --git a/src/elm/Pages/Login/Model.elm b/src/elm/Pages/Login/Model.elm index 0c4324c..a2d5559 100644 --- a/src/elm/Pages/Login/Model.elm +++ b/src/elm/Pages/Login/Model.elm @@ -1,39 +1,47 @@ -module Pages.Login.Model where +module Pages.Login.Model exposing (emptyModel, Model) -import Http exposing (Error) +import Http + + +type alias AccessToken = + String -type alias AccessToken = String type alias LoginForm = - { name: String - , pass : String - } + { name : String + , pass : String + } + type UserMessage - = None - | Error String + = None + | Error String + type Status - = Init - | Fetching - | Fetched - | HttpError Http.Error + = Init + | Fetching + | Fetched + | HttpError Http.Error + type alias Model = - { accessToken: AccessToken - , hasAccessTokenInStorage : Bool - , loginForm : LoginForm - , status : Status - , userMessage : UserMessage - } - -initialModel : Model -initialModel = - { accessToken = "" - -- We start by assuming there's already an access token it the localStorage. - -- While this property is set to True, the login form will not appear. - , hasAccessTokenInStorage = True - , loginForm = LoginForm "demo" "1234" - , status = Init - , userMessage = None - } + { accessToken : AccessToken + , hasAccessTokenInStorage : Bool + , loginForm : LoginForm + , status : Status + , userMessage : UserMessage + } + + +emptyModel : Model +emptyModel = + { accessToken = + "" + -- We start by assuming there's already an access token it the localStorage. + -- While this property is set to True, the login form will not appear. + , hasAccessTokenInStorage = True + , loginForm = LoginForm "demo" "1234" + , status = Init + , userMessage = None + } diff --git a/src/elm/Pages/Login/Test.elm b/src/elm/Pages/Login/Test.elm index 95f0189..3c51e8a 100644 --- a/src/elm/Pages/Login/Test.elm +++ b/src/elm/Pages/Login/Test.elm @@ -1,83 +1,75 @@ -module Pages.Login.Test where +module Pages.Login.Test exposing (all) import ElmTest exposing (..) +import RemoteData exposing (RemoteData(..), WebData) +import Pages.Login.Model exposing (..) +import Pages.Login.Update exposing (..) +import User.Model exposing (..) + + +setLogin : Test +setLogin = + suite "SetLogin msg" + [ test "set name without spaces" + (assertEqual "noSpaces" (getLogin "noSpaces")) + , test "set name with space" + (assertEqual "withSpaces" (getLogin "with Spaces")) + , test "set name with multiple spaces" + (assertEqual "withSpaces" (getLogin " with Spaces ")) + , test "set name should result with NotAsked user status if name changed" + (assertEqual NotAsked (getUserStatusAfterSetLogin Loading "someName" emptyModel)) + , test "set name should result with existing user status if name didn't change" + (assertEqual Loading (getUserStatusAfterSetLogin Loading " someName " { login = "someName" })) + ] + + +dummyUser : User +dummyUser = + { name = Just "Foo", login = "foo", avatarUrl = "https://example.com" } + + +getLogin : String -> String +getLogin login = + let + ( model, _, _ ) = + update "https://example.com" NotAsked (SetLogin login) emptyModel + in + model.login + + +getUserStatusAfterSetLogin : WebData User -> String -> Model -> WebData User +getUserStatusAfterSetLogin user login model = + let + ( _, _, user ) = + update "https://example.com" user (SetLogin login) model + in + user + + + +{- Test the returned status after TryLogin msg. -} + + +tryLogin : Test +tryLogin = + suite "TryLogin msg" + [ test "Fetch empty name" + (assertEqual NotAsked (getTryLogin NotAsked { login = "" })) + ] + + +getTryLogin : WebData User -> Model -> WebData User +getTryLogin user model = + let + ( _, _, userStatus ) = + update "https://example.com" user TryLogin model + in + userStatus -import Config.Model exposing (initialBackendConfig) -import Effects exposing (Effects) -import Http exposing (Error) -import Pages.Login.Model exposing (initialModel, Model) -import Pages.Login.Update exposing (Action) - -type alias Model = Pages.Login.Model.Model - -accessTokenSuite : Test -accessTokenSuite = - let - pass = - .loginForm >> .pass - in - suite "Set access token tests" - [ test "on empty set access token, password is reset" (assertEqual "" (pass <| fst(setAccessToken ""))) - , test "on set access token, password is reset" (assertEqual "" (pass <| fst(setAccessToken "accessToken"))) - ] - -formSuite : Test -formSuite = - let - -- Shorthand, to get to the form's properties. - name = - .loginForm >> .name - - pass = - .loginForm >> .pass - in - suite "Login form tests" - [ test "empty name" (assertEqual "" (name <| fst(updateName ""))) - , test "simple name" (assertEqual "foo" (name <| fst(updateName "foo"))) - -- Password - , test "empty password" (assertEqual "" (pass <| fst(updatePass ""))) - , test "simple password" (assertEqual "bar" (pass <| fst(updatePass "bar"))) - - -- Submit form - , test "first submit, status is Fetching" (assertEqual Pages.Login.Model.Fetching (.status <| fst(submitForm Pages.Login.Model.Init))) - , test "ongoing submit" (assertEqual Pages.Login.Model.Fetching (.status <| fst(submitForm Pages.Login.Model.Fetching))) - , test "submit done without errors" (assertEqual Pages.Login.Model.Fetched (.status <| fst(submitForm Pages.Login.Model.Fetched))) - , test "submit after another submit with errors" (assertEqual Pages.Login.Model.Fetching (.status <| fst(submitForm <| Pages.Login.Model.HttpError Http.NetworkError))) - ] - -updateName : String -> (Model, Effects Action) -updateName val = - Pages.Login.Update.update updateContext (Pages.Login.Update.UpdateName val) Pages.Login.Model.initialModel - -updatePass : String -> (Model, Effects Action) -updatePass val = - Pages.Login.Update.update updateContext (Pages.Login.Update.UpdatePass val) Pages.Login.Model.initialModel - -submitForm : Pages.Login.Model.Status -> (Model, Effects Action) -submitForm status = - let - model = - Pages.Login.Model.initialModel - - model' = - { model | status = status } - - in - Pages.Login.Update.update updateContext Pages.Login.Update.SubmitForm model' - -setAccessToken : String -> (Model, Effects Action) -setAccessToken val = - Pages.Login.Update.update updateContext (Pages.Login.Update.SetAccessToken val) Pages.Login.Model.initialModel - - -updateContext : Pages.Login.Update.Context -updateContext = - { backendConfig = initialBackendConfig - } all : Test all = - suite "All Login tests" - [ accessTokenSuite - , formSuite - ] + suite "Pages.Login tests" + [ setLogin + , tryLogin + ] diff --git a/src/elm/Pages/Login/Update.elm b/src/elm/Pages/Login/Update.elm index 52200e5..f04c6b9 100644 --- a/src/elm/Pages/Login/Update.elm +++ b/src/elm/Pages/Login/Update.elm @@ -1,156 +1,101 @@ -module Pages.Login.Update where +module Pages.Login.Update exposing (update, Msg(..)) -import Pages.Login.Model exposing (initialModel, Model) +import Config.Model exposing (BackendUrl) +import Http +import Regex exposing (regex, replace, HowMany(All)) +import String exposing (isEmpty) +import Task +import User.Decoder exposing (..) +import User.Model exposing (..) +import Pages.Login.Model as Login exposing (..) +import RemoteData exposing (RemoteData(..), WebData) -import Base64 exposing (encode) -import Config.Model exposing (BackendConfig) -import Effects exposing (Effects) -import Http exposing (Error) -import Json.Decode as JD exposing ((:=)) -import Storage exposing (getItem) -import Task exposing (Task) -import Utils.Http exposing (getErrorMessageFromHttpResponse) -type alias AccessToken = String +type Msg + = FetchFail Http.Error + | FetchSucceed User + | SetLogin String + | TryLogin -init : (Model, Effects Action) -init = - ( initialModel - -- Try to get an existing access token. - , getInputFromStorage - ) - -type Action - = UpdateAccessTokenFromServer (Result Http.Error AccessToken) - | UpdateAccessTokenFromStorage (Result String AccessToken) - | UpdateName String - | UpdatePass String - | SetAccessToken AccessToken - | SetUserMessage Pages.Login.Model.UserMessage - | SubmitForm - -type alias Context = - { backendConfig : BackendConfig - } - -update : Context -> Action -> Model -> (Model, Effects Action) -update context action model = - case action of - UpdateName name -> - let - loginForm = model.loginForm - updatedLoginForm = { loginForm | name = name } - in - ( { model | loginForm = updatedLoginForm } - , Effects.none - ) - - UpdatePass pass -> - let - loginForm = model.loginForm - updatedLoginForm = { loginForm | pass = pass } - in - ( {model | loginForm = updatedLoginForm } - , Effects.none - ) - - SubmitForm -> - let - backendUrl = - (.backendConfig >> .backendUrl) context +init : ( Model, Cmd Msg ) +init = + emptyModel ! [] + + +update : BackendUrl -> WebData User -> Msg -> Model -> ( Model, Cmd Msg, WebData User ) +update backendUrl user msg model = + case msg of + FetchSucceed github -> + ( model, Cmd.none, Success github ) + + FetchFail err -> + ( model, Cmd.none, Failure err ) + + SetLogin login -> + let + -- Remove spaces from login. + noSpacesLogin = + replace All (regex " ") (\_ -> "") login + + userStatus = + getUserStatusFromNameChange user model.login noSpacesLogin + in + ( { model | login = noSpacesLogin }, Cmd.none, userStatus ) + + TryLogin -> + let + ( cmd, userStatus ) = + getCmdAndUserStatusForTryLogin backendUrl user model.login + in + ( model, cmd, userStatus ) + + +{-| Try to fetch the user from GitHub only if it was not asked yet. +In case we are still loading, error or a succesful fetch we don't want to repeat +it. +-} +getCmdAndUserStatusForTryLogin : BackendUrl -> WebData User -> String -> ( Cmd Msg, WebData User ) +getCmdAndUserStatusForTryLogin backendUrl user login = + case user of + NotAsked -> + if isEmpty login then + -- login was not asked, however it is empty. + ( Cmd.none, NotAsked ) + else + -- Fetch the login from GitHub, and indicate we are + -- in the middle of "Loading". + ( fetchFromGitHub backendUrl login, Loading ) + + _ -> + -- We are not in "NotAsked" state, so return the existing + -- value + ( Cmd.none, user ) + + +{-| Determine if the user status should change after setting a new name. +When there is a valid name change, status should change to NotAsked. +However if for example a user just tried to add a space to the name, so after +triming it's actually the same. Thus, we avoid changing the user status to +prevent from re-fetching a possibly wrong name. +For example, if the user status would have been Failure, the existing name is +"foo" and user tried to pass "foo " (notice the trailing space), then in fact no +change should happen. +-} +getUserStatusFromNameChange : WebData User -> String -> String -> WebData User +getUserStatusFromNameChange user currentName newName = + if currentName == newName then + user + else + NotAsked + + +{-| Get data from GitHub. +-} +fetchFromGitHub : BackendUrl -> String -> Cmd Msg +fetchFromGitHub backendUrl login = + let url = - backendUrl ++ "/api/login-token" - - credentials : String - credentials = encodeCredentials(model.loginForm.name, model.loginForm.pass) - in - if model.status == Pages.Login.Model.Fetching || model.status == Pages.Login.Model.Fetched - then - (model, Effects.none) - else - ( { model | status = Pages.Login.Model.Fetching } - , Effects.batch - [ Task.succeed (SetUserMessage Pages.Login.Model.None) |> Effects.task - , getJson url credentials - ] - ) - - SetAccessToken token -> - ( { model - | accessToken = token - -- This is a good time also to hide the password. - , loginForm = Pages.Login.Model.LoginForm model.loginForm.name "" - } - , Effects.none - ) - - SetUserMessage userMessage -> - ( { model | userMessage = userMessage } - , Effects.none - ) - - UpdateAccessTokenFromServer result -> - case result of - Ok token -> - ( { model | status = Pages.Login.Model.Fetched } - , Task.succeed (SetAccessToken token) |> Effects.task - ) - Err err -> - let - message = - getErrorMessageFromHttpResponse err - in - ( { model | status = Pages.Login.Model.HttpError err } - , Task.succeed (SetUserMessage <| Pages.Login.Model.Error message) |> Effects.task - ) - - UpdateAccessTokenFromStorage result -> - case result of - Ok token -> - ( model - , Task.succeed (SetAccessToken token) |> Effects.task - ) - Err err -> - -- There was no access token in the storage, so show the login form - ( { model | hasAccessTokenInStorage = False } - , Effects.none - ) - - -getInputFromStorage : Effects Action -getInputFromStorage = - Storage.getItem "access_token" JD.string - |> Task.toResult - |> Task.map UpdateAccessTokenFromStorage - |> Effects.task - - --- EFFECTS - -encodeCredentials : (String, String) -> String -encodeCredentials (name, pass) = - let - base64 = Base64.encode(name ++ ":" ++ pass) - in - case base64 of - Ok result -> result - Err err -> "" - -getJson : String -> String -> Effects Action -getJson url credentials = - Http.send Http.defaultSettings - { verb = "GET" - , headers = [("Authorization", "Basic " ++ credentials)] - , url = url - , body = Http.empty - } - |> Http.fromJson decodeAccessToken - |> Task.toResult - |> Task.map UpdateAccessTokenFromServer - |> Effects.task - - -decodeAccessToken : JD.Decoder AccessToken -decodeAccessToken = - JD.at ["access_token"] <| JD.string + backendUrl ++ "/users/" ++ login + in + Task.perform FetchFail FetchSucceed (Http.get decodeFromGithub url) diff --git a/src/elm/Pages/Login/View.elm b/src/elm/Pages/Login/View.elm index 00b8b7c..426cbf5 100644 --- a/src/elm/Pages/Login/View.elm +++ b/src/elm/Pages/Login/View.elm @@ -1,134 +1,58 @@ -module Pages.Login.View where - -import Pages.Login.Model exposing (initialModel, Model) -import Pages.Login.Update exposing (Action) - -import Config.Model exposing (BackendConfig) -import Html exposing (a, button, div, i, input, h2, hr, span, text, Html) -import Html.Attributes exposing (action, class, disabled, id, hidden, href, placeholder, required, size, style, type', value) -import Html.Events exposing (on, onClick, onSubmit, targetValue) -import String exposing (isEmpty) - -type alias ViewContext = - { backendConfig : BackendConfig - } - -view : ViewContext -> Signal.Address Action -> Model -> Html -view context address model = - let - modelForm = - model.loginForm - - isFormEmpty = - String.isEmpty modelForm.name || String.isEmpty modelForm.pass - - isFetchStatus = - model.status == Pages.Login.Model.Fetching || model.status == Pages.Login.Model.Fetched - - githubClientId = - (.backendConfig >> .githubClientId) context - - githubUrl = - "https://github.com/login/oauth/authorize?client_id=" ++ githubClientId ++ "&scope=user:email" - - - githubLogin = - div - [ class "btn btn-lg btn-primary btn-block"] - [ a - [ href githubUrl] - [ i [ class "fa fa-github", style [("margin-right", "10px")] ] [] - , span [] [ text "Login with GitHub" ] - ] - ] - - loginForm = - Html.form - [ onSubmit address Pages.Login.Update.SubmitForm - , action "javascript:void(0);" - , class "form-signin" - -- Don't show the form while checking for the access token from the - -- storage. - , hidden model.hasAccessTokenInStorage - ] - - -- Form title - [ h2 [] [ text "Please login" ] - -- UserName - , githubLogin - , div - [ style [("margin-bottom", "20px"), ("margin-top", "20px"), ("text-align", "center")] ] - [ text "OR" ] - , div - [ class "input-group" ] - [ span - [ class "input-group-addon" ] - [ i [ class "glyphicon glyphicon-user" ] [] ] - , input +module Pages.Login.View exposing (view) + +import RemoteData exposing (RemoteData(..), WebData) +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (onClick, onInput, onSubmit) +import User.Model exposing (..) +import Pages.Login.Model exposing (..) +import Pages.Login.Update exposing (..) + + +view : WebData User -> Model -> Html Msg +view user model = + let + spinner = + i [ class "notched circle loading icon" ] [] + + ( isLoading, isError ) = + case user of + Loading -> + ( True, False ) + + Failure _ -> + ( False, True ) + + _ -> + ( False, False ) + + inputClasses = + classList + [ ( "ui action input", True ) + , ( "error", isError ) + ] + in + Html.form + [ onSubmit TryLogin + , action "javascript:void(0);" + , class "ui stacked segment" + ] + [ div [ inputClasses ] + [ input [ type' "text" - , class "form-control" - , placeholder "Name" - , value model.loginForm.name - , on "input" targetValue (Signal.message address << Pages.Login.Update.UpdateName) - , size 40 - , required True - , disabled (isFetchStatus) + , placeholder "Github name" + , onInput SetLogin + , value model.login ] [] - ] - -- Password - , div - [ class "input-group"] - [ span - [ class "input-group-addon" ] - [ i [ class "fa fa-lock fa-lg" ] [] ] - , input - [ type' "password" - , class "form-control" - , placeholder "Password" - , value modelForm.pass - , on "input" targetValue (Signal.message address << Pages.Login.Update.UpdatePass) - , size 40 - , required True - , disabled (isFetchStatus) - ] - [] - ] - - -- Submit button - , button - [ onClick address Pages.Login.Update.SubmitForm - , class "btn btn-lg btn-primary btn-block" - , disabled (isFetchStatus || isFormEmpty) - ] - [ span [ hidden <| not isFetchStatus] [ spinner ] - , span [ hidden isFetchStatus ] [ text "Login" ] ] - ] - - spinner = - i [ class "fa fa-spinner fa-spin" ] [] - - userMessage = - case model.userMessage of - Pages.Login.Model.None -> - div [] [] - Pages.Login.Model.Error message -> - div [ style [("text-align", "center")]] [ text message ] - - in - div [ id "login-page" ] [ - hr [] [] - , div - [ class "container" ] - [ userMessage - , div - [ class "wrapper" ] - [ loginForm - , div - [ class "text-center" - , hidden (not (model.status == Pages.Login.Model.Fetching) && not model.hasAccessTokenInStorage) ] - [ text "Loading ..." ] - ] + -- Submit button + , button + [ onClick TryLogin + , disabled (isLoading || isError) + , class "ui primary button" + ] + [ span [ hidden <| not isLoading ] [ spinner ] + , span [ hidden isLoading ] [ text "Login" ] + ] + ] ] - , hr [] [] - ] diff --git a/src/elm/Pages/MyAccount/View.elm b/src/elm/Pages/MyAccount/View.elm new file mode 100644 index 0000000..472227a --- /dev/null +++ b/src/elm/Pages/MyAccount/View.elm @@ -0,0 +1,38 @@ +module Pages.MyAccount.View exposing (view) + +import RemoteData exposing (RemoteData(..), WebData) +import Html exposing (a, div, h2, i, p, text, img, Html) +import Html.Attributes exposing (class, href, src) +import User.Model exposing (..) + + +-- VIEW + + +view : WebData User -> Html a +view user = + let + ( name, login, avatar ) = + case user of + Success val -> + let + name' = + case val.name of + Just name -> + name + + Nothing -> + val.login + in + ( name', val.login, img [ src val.avatarUrl ] [] ) + + _ -> + ( "", "", div [] [] ) + in + div [ class "ui centered card" ] + [ div [ class "image" ] [ avatar ] + , div [ class "content" ] + [ div [ class "header" ] [ text <| "Welcome " ++ name ] + , div [ class "meta" ] [ text <| "@" ++ login ] + ] + ] diff --git a/src/elm/Pages/PageNotFound/View.elm b/src/elm/Pages/PageNotFound/View.elm index 0bb34b7..8576698 100644 --- a/src/elm/Pages/PageNotFound/View.elm +++ b/src/elm/Pages/PageNotFound/View.elm @@ -1,23 +1,14 @@ -module Pages.PageNotFound.View where +module Pages.PageNotFound.View exposing (view) + +import Html exposing (a, div, h2, text, Html) +import Html.Attributes exposing (class, href) -import Html exposing (a, i, div, h2, text, Html) -import Html.Attributes exposing (class, id, href, style) -- VIEW -view : Html + +view : Html a view = - div - [ id "page-not-found" - , class "container" - ] - [ div - [ class "wrapper text-center" ] - [ - div - [ class "box" ] - [ h2 [] [ text "This is a 404 page!" ] - , a [ href "#!/" ] [ text "Back to safety" ] - ] + div [ class "ui segment center aligned" ] + [ h2 [] [ text "This is a 404 page!" ] ] - ] diff --git a/src/elm/Pages/User/Decoder.elm b/src/elm/Pages/User/Decoder.elm deleted file mode 100644 index 0c482db..0000000 --- a/src/elm/Pages/User/Decoder.elm +++ /dev/null @@ -1,29 +0,0 @@ -module Pages.User.Decoder where - -import Company.Model as Company exposing (Model) -import Effects exposing (Effects, Never) -import Http exposing (Error) -import Json.Decode as Json exposing ((:=)) -import String -import Task - -import Pages.User.Model as User exposing (..) - -decode : Json.Decoder (User.Id, String, List Company.Model) -decode = - let - -- Cast String to Int. - number : Json.Decoder Int - number = - Json.oneOf [ Json.int, Json.customDecoder Json.string String.toInt ] - - company = - Json.object2 Company.Model - ("id" := number) - ("label" := Json.string) - in - Json.at ["data", "0"] - <| Json.object3 (,,) - ("id" := number) - ("label" := Json.string) - ("companies" := Json.list company) diff --git a/src/elm/Pages/User/Model.elm b/src/elm/Pages/User/Model.elm deleted file mode 100644 index 8e1a2ea..0000000 --- a/src/elm/Pages/User/Model.elm +++ /dev/null @@ -1,37 +0,0 @@ -module Pages.User.Model where - -import Company.Model as Company exposing (initialModel, Model) -import Http exposing (Error) - -type alias Id = Int -type alias AccessToken = String - -type User = Anonymous | LoggedIn String - -type Status = - Init - | Fetching - | Fetched - | HttpError Error - -type alias Model = - { name : User - , id : Id - , status : Status - , accessToken : AccessToken - - -- Child components - , companies : List Company.Model - } - - -initialModel : Model -initialModel = - { name = Anonymous - , id = 0 - , status = Init - , accessToken = "" - - -- Child components - , companies = [Company.initialModel] - } diff --git a/src/elm/Pages/User/Update.elm b/src/elm/Pages/User/Update.elm deleted file mode 100644 index 566c3fe..0000000 --- a/src/elm/Pages/User/Update.elm +++ /dev/null @@ -1,101 +0,0 @@ -module Pages.User.Update where - -import Config.Model exposing (BackendConfig) -import Company.Model as Company exposing (initialModel, Model) -import Effects exposing (Effects, Never) -import Http exposing (Error) -import Pages.User.Model as User exposing (Model) -import Pages.User.Decoder exposing (decode) -import Task exposing (succeed) - -type alias Id = Int -type alias AccessToken = String -type alias Model = User.Model - -type alias UpdateContext = - { accessToken : AccessToken - , backendConfig : BackendConfig - } - -type Action - = GetDataFromServer - | NoOp (Maybe ()) - | SetAccessToken AccessToken - | UpdateDataFromServer (Result Http.Error (Id, String, List Company.Model)) - -init : (Model, Effects Action) -init = - ( User.initialModel - , Effects.none - ) - -update : UpdateContext -> Action -> Model -> (Model, Effects Action) -update context action model = - case action of - NoOp _ -> - (model, Effects.none) - - GetDataFromServer -> - let - backendUrl = - (.backendConfig >> .backendUrl) context - - url = - backendUrl ++ "/api/v1.0/me" - in - if model.status == User.Fetching || model.status == User.Fetched - then - (model, Effects.none) - else - ( { model | status = User.Fetching } - , getJson url context.accessToken - ) - - UpdateDataFromServer result -> - let - model' = - { model | status = User.Fetched} - in - case result of - Ok (id, name, companies) -> - ( {model' - | id = id - , name = User.LoggedIn name - , companies = companies - } - , Effects.none - ) - Err msg -> - let - effects = - case msg of - Http.BadResponse code _ -> - if (code == 401) - -- Token is wrong, so remove any existing one. - then Task.succeed (SetAccessToken "") |> Effects.task - else Effects.none - - _ -> - Effects.none - - in - ( { model' | status = User.HttpError msg } - , effects - ) - - SetAccessToken accessToken -> - ( {model | accessToken = accessToken} - , Effects.none - ) - --- Effects - -getJson : String -> AccessToken -> Effects Action -getJson url accessToken = - let - encodedUrl = Http.url url [ ("access_token", accessToken) ] - in - Http.get decode encodedUrl - |> Task.toResult - |> Task.map UpdateDataFromServer - |> Effects.task diff --git a/src/elm/Pages/User/View.elm b/src/elm/Pages/User/View.elm deleted file mode 100644 index 29e2371..0000000 --- a/src/elm/Pages/User/View.elm +++ /dev/null @@ -1,42 +0,0 @@ -module Pages.User.View where - -import Company.Model as Company exposing (Model) -import Html exposing (..) -import Html.Attributes exposing (..) -import Pages.User.Model as User exposing (Model) -import Pages.User.Update exposing (Action) - - -view : Signal.Address Action -> User.Model -> Html -view address model = - case model.name of - User.Anonymous -> - div [] [ text "This is wrong - anon user cannot reach this!"] - - User.LoggedIn name -> - let - mainTitle = - h3 - [ class "title" ] - [ i [ class "glyphicon glyphicon-user" ] [] - , text " My account" - ] - in - div - [ - id "my-account" - , class "container" - ] - [ div - [ class "wrapper -suffix"] - [ mainTitle - , h4 [ class "name" ] [ text <| "Welcome " ++ name ] - , h4 [ class "company-title"] [ text "Your companies are:" ] - , ol [ class "companies" ] (List.map viewCompanies model.companies) - ] - ] - - -viewCompanies : Company.Model -> Html -viewCompanies company = - li [] [ text company.label ] diff --git a/src/elm/TestRunner.elm b/src/elm/TestRunner.elm index 074e845..1cb3a4d 100644 --- a/src/elm/TestRunner.elm +++ b/src/elm/TestRunner.elm @@ -1,22 +1,20 @@ -module Main where - -import Graphics.Element exposing (Element) +module Main exposing (..) import ElmTest exposing (..) - -import Config.Test as Config -import EventCompanyFilter.Test as EventCompanyFilter -import Pages.Login.Test as Login +import App.Test as App exposing (..) +import Pages.Counter.Test as Counter exposing (..) +import Pages.Login.Test as Login exposing (..) allTests : Test allTests = - suite "All tests" - [ Config.all - , EventCompanyFilter.all - , Login.all - ] + suite "All tests" + [ App.all + , Counter.all + , Login.all + ] + -main : Element +main : Program Never main = - elementRunner allTests + runSuiteHtml allTests diff --git a/src/elm/User/Decoder.elm b/src/elm/User/Decoder.elm new file mode 100644 index 0000000..2c3f1c0 --- /dev/null +++ b/src/elm/User/Decoder.elm @@ -0,0 +1,13 @@ +module User.Decoder exposing (decodeFromGithub) + +import Json.Decode exposing ((:=)) +import Json.Decode.Extra exposing ((|:)) +import User.Model exposing (..) + + +decodeFromGithub : Json.Decode.Decoder User +decodeFromGithub = + Json.Decode.succeed User + |: ("avatar_url" := Json.Decode.string) + |: ("login" := Json.Decode.string) + |: ("name" := Json.Decode.maybe Json.Decode.string) diff --git a/src/elm/User/Model.elm b/src/elm/User/Model.elm new file mode 100644 index 0000000..9fdacaf --- /dev/null +++ b/src/elm/User/Model.elm @@ -0,0 +1,11 @@ +module User.Model exposing (..) + + +type alias User = + { avatarUrl : String + , login : + String + -- In GitHub user might not have a name, just the login handler, so this + -- is a "Maybe" value. + , name : Maybe String + } diff --git a/src/elm/Utils/Http.elm b/src/elm/Utils/Http.elm deleted file mode 100644 index f9d0545..0000000 --- a/src/elm/Utils/Http.elm +++ /dev/null @@ -1,23 +0,0 @@ -module Utils.Http where - -import Http exposing (Error) - -getErrorMessageFromHttpResponse : Http.Error -> String -getErrorMessageFromHttpResponse err = - case err of - Http.Timeout -> - "Connection has timed out" - - Http.BadResponse code _ -> - if code == 401 then - "Wrong username or password" - else if code == 429 then - "Too many login requests with the wrong username or password. Wait a few hours before trying again" - else - "Some error has occurred on the server" - - Http.NetworkError -> - "A network error has occurred" - - Http.UnexpectedPayload string -> - "Unknown error has occurred: " ++ string diff --git a/src/index.html b/src/index.html index bdabe60..edd08c4 100644 --- a/src/index.html +++ b/src/index.html @@ -3,36 +3,18 @@ - Elm - - - - - - + - - - - - - - - - - - - - - - - + + diff --git a/src/js/README.md b/src/js/README.md new file mode 100644 index 0000000..b9dfb1e --- /dev/null +++ b/src/js/README.md @@ -0,0 +1 @@ +Place JS, and JS-inerop files in this folder. diff --git a/src/js/elm-interop.js b/src/js/elm-interop.js deleted file mode 100644 index 4d90572..0000000 --- a/src/js/elm-interop.js +++ /dev/null @@ -1,270 +0,0 @@ -"use strict"; - -var initialValues = { - ckeditor : '', - dropzoneUploadedFile : null, - selectEvent: null -}; - -var elmApp = Elm.fullscreen(Elm.Main, initialValues); - -// Maintain the map and marker state. -var mapEl = undefined; -var markersEl = {}; - -var defaultIcon = L.icon({ - iconUrl: 'default@2x.png', - iconRetinaUrl: 'default@2x.png', - iconSize: [35, 46] -}); - -var selectedIcon = L.icon({ - iconUrl: 'selected@2x.png', - iconRetinaUrl: 'selected@2x.png', - iconSize: [35, 46] -}); - -elmApp.ports.mapManager.subscribe(function(model) { - if (!model.leaflet.showMap && !!mapEl) { - // Hide the map. - mapEl.remove(); - mapEl = undefined; - markersEl = {}; - return; - } - - // We use timeout, to let virtual-dom add the div we need to bind to. - waitForElement('#map', mapManager, model); -}); - - -/** - * Wait for selector to appear before invoking related functions. - */ -function waitForElement(selector, fn, model, tryCount) { - - // Repeat the timeout only maximum 5 times, which sohuld be enough for the - // element to appear. - tryCount = tryCount || 5; - --tryCount; - if (tryCount == 0) { - return; - } - - setTimeout(function() { - - var result = fn.call(null, selector, model, tryCount); - if (!result) { - // Element still doesn't exist, so wait some more. - waitForElement(selector, fn, model, tryCount); - } - }, 50); -} - -/** - * Attach or detach the Leaflet map and markers. - * - * @return bool - * Determines if mapManager completed it's operation. True means we don't need - * ro re-call this function. - */ -function mapManager(selector, model) { - if (!model.leaflet.showMap) { - return true; - } - - var element = document.querySelector(selector); - if (!element) { - // Element doesn't exist yet. - return false; - } - - mapEl = mapEl || addMap(); - - // The event Ids holds the array of all the current events - even the one that - // might be filtered out. By unsetting the ones that have visible markers, we - // remain with the ones that should be removed. - // We make sure to clonse the event Ids, so we can always query the original - // events. - var eventIds = JSON.parse(JSON.stringify(model.events)); - - var selectedMarker = undefined; - - model.leaflet.markers.forEach(function(marker) { - var id = marker.id; - if (!markersEl[id]) { - markersEl[id] = L.marker([marker.lat, marker.lng]).addTo(mapEl); - selectMarker(markersEl[id], id); - } - else { - markersEl[id].setLatLng([marker.lat, marker.lng]); - } - - var isSelected = !!model.leaflet.selectedMarker && model.leaflet.selectedMarker === id; - - if (isSelected) { - // Center the map around the selected event. - selectedMarker = markersEl[id]; - } - - // Set the marker's icon. - markersEl[id].setIcon(isSelected ? selectedIcon : defaultIcon); - - // Unset the marker form the event IDs list. - var index = eventIds.indexOf(id); - eventIds.splice(index, 1); - }); - - // When there are markers available, fit the map around them. - if (model.leaflet.markers.length) { - - // Try to see is there are bounds. If there are not, center the map. - try { - mapEl.getBounds(); - } - catch (err) { - mapEl.fitBounds(model.leaflet.markers); - } - - // When a marker is selected, center the map around it. - if (selectedMarker) { - mapEl.panTo(selectedMarker._latlng); - } - else { - mapEl.fitBounds(model.leaflet.markers); - } - } - else { - // Show the entire world when no markers are set. - mapEl.setZoom(1); - } - - // Hide filtered markers. - eventIds.forEach(function(id) { - if (markersEl[id]) { - mapEl.removeLayer(markersEl[id]); - markersEl[id] = undefined; - } - }); - - - // Iterate over all the existing markers, and make sure they part of the - // existing events list. Otherwise, remove them. - for (var id in markersEl) { - if (model.events.indexOf(parseInt(id)) > -1) { - // Marker doesn't exist in the current event list. - continue; - } - - if (!markersEl[id]) { - // Marker is already invisible. - continue; - } - - mapEl.removeLayer(markersEl[id]); - markersEl[id] = undefined; - } - - // Map was binded properly. - return true; -} - -/** - * Send marker click event to Elm. - */ -function selectMarker(markerEl, id) { - markerEl.on('click', function(e) { - elmApp.ports.selectEvent.send(id); - }); -} - -/** - * Initialize a Leaflet map. - */ -function addMap() { - // Leaflet map. - var mapEl = L.map('map'); - - L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpandmbXliNDBjZWd2M2x6bDk3c2ZtOTkifQ._QA7i5Mpkd_m30IGElHziw', { - maxZoom: 10, - id: 'mapbox.streets' - }).addTo(mapEl); - - return mapEl; -} - - - -// Dropzone -// @todo: Move to own file. - -var ck = undefined; -var dropZone = undefined; - -elmApp.ports.activePage.subscribe(function(model) { - if (model.activePage != 'Article') { - // Reset dropzone variable, in case we switch between pages. - ck = undefined; - dropZone = undefined; - return; - } - - waitForElement('.dropzone', attachDropzone, model); -}); - -function attachDropzone(selector, model) { - if (model.activePage != 'Article') { - return false; - } - - var element = document.querySelector(selector); - if (!element) { - // Element doesn't exist yet. - return false; - } - - if (!!dropZone) { - - // Check if we need to remove files. - if (model.postStatus == "Done") { - // Remove all files, even the ones being currently uploaded. - dropZone.removeAllFiles(true); - - // Clear the CKeditor text area. - ck.setData(''); - } - - // Widgets were already attached once. - return true; - } - - - // Set the backend url with the access token. - var url = model.backendUrl + '/api/file-upload?access_token=' + model.accessToken; - - dropZone = new Dropzone(selector, { url: url}); - - dropZone.on('complete', function(file) { - if (!file.accepted) { - // File was not uploaded. - return; - } - - if (file.xhr.status !== 200) { - return; - } - - var data = JSON.parse(file.xhr.response); - - // Get the file ID, and send it to Elm. - var id = parseInt(data.data[0]['id']); - elmApp.ports.dropzoneUploadedFile.send(id); - }); - - ck = CKEDITOR.replace('body'); - - // Send the data to Elm. - ck.on('change', function() { - elmApp.ports.ckeditor.send(ck.getData()); - }); -} diff --git a/src/vendor/elm-param-parsing/.gitignore b/src/vendor/elm-param-parsing/.gitignore deleted file mode 100644 index 61037f8..0000000 --- a/src/vendor/elm-param-parsing/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -elm.js -elm-stuff -Gemfile -Gemfile.lock -_site -node_modules -*.js diff --git a/src/vendor/elm-param-parsing/README.md b/src/vendor/elm-param-parsing/README.md deleted file mode 100644 index 74cf719..0000000 --- a/src/vendor/elm-param-parsing/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Elm Parameter Parsing - -This is an elm library for parsing parameters on the URL. - -## To use it - -```elm package install jessitron/elm-param-parsing``` - -To use this, you'll need -to create an input port, pass the search string to Elm, and then parse -them with this function, then that can populate your model. - -For instance, in the web page: - - var app = Elm.fullscreen(Elm.YourModule, - { locationSearch: window.location.search }); - -in YourModule.elm, declare the port and then parse what comes into it. This example discards errors: - -```elm -import Dict exposing (Dict) - -port locationSearch : String - -parameters : Dict String String -parameters = - case (parseSearchString locationSearch) of - Error _ -> Dict.empty - UrlParams dict -> dict -``` - -Then use that dict when you call your init function that needs the value -of the parameter. - -```elm -init (Dict.get parameters "customerID") - -init : Maybe String -> Model -init maybeID = ... -``` - -## see it in action - -The example app is built into this repo. -[source](https://github.com/jessitron/elm-param-parsing/tree/ui); -[result](http://jessitron.github.io/elm-param-parsing) diff --git a/src/vendor/elm-param-parsing/build b/src/vendor/elm-param-parsing/build deleted file mode 100755 index 99c2570..0000000 --- a/src/vendor/elm-param-parsing/build +++ /dev/null @@ -1 +0,0 @@ -elm-make src/UrlParameterParser.elm diff --git a/src/vendor/elm-param-parsing/elm-package.json b/src/vendor/elm-param-parsing/elm-package.json deleted file mode 100644 index e8bde90..0000000 --- a/src/vendor/elm-param-parsing/elm-package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "version": "1.0.2", - "summary": "utility method to parse URL parameters", - "repository": "https://github.com/jessitron/elm-param-parsing.git", - "license": "BSD3", - "source-directories": [ - "src" - ], - "exposed-modules": [ - "UrlParameterParser" - ], - "dependencies": { - "elm-lang/core": "3.0.0 <= v < 4.0.0" - }, - "elm-version": "0.16.0 <= v < 0.17.0" -} diff --git a/src/vendor/elm-param-parsing/src/UrlParameterParser.elm b/src/vendor/elm-param-parsing/src/UrlParameterParser.elm deleted file mode 100644 index b4d6990..0000000 --- a/src/vendor/elm-param-parsing/src/UrlParameterParser.elm +++ /dev/null @@ -1,68 +0,0 @@ -module UrlParameterParser(ParseResult(..), parseSearchString) where - -{-| Parse URL parameters. To use this, you'll need to create an input port, pass the search string to Elm, -and then parse them with this function, then that can populate your model. - -For instance, in the web page: -``` - var app = Elm.fullscreen(Elm.YourModule, - { locationSearch: window.location.search }); -``` -in YourModule.elm: -``` -port locationSearch : String -``` - -Then parse the value of the port - this example discards errors: -``` -parameters : Dict String String -parameters = - case (parseSearchString locationSearch) of - Error _ -> Dict.empty - UrlParams dict -> dict -``` - -Then use that dict when you call your init function that needs the value of the parameter. It'll get a Maybe String. -``` -init (Dict.get parameters "customerID") - -init : Maybe String -> Model -init maybeID = ... -``` - -# Method -@docs parseSearchString - -# Return type -@docs ParseResult --} - -import Dict exposing (Dict) -import String -import UrlParseUtil exposing (..) - -{-| If parsing is successful, you get a UrlParams containing a dictionary of keys to values. -Otherwise, an error string. -If there are no parameters, you'll get an error description. --} -type ParseResult - = Error String - | UrlParams (Dict String String) - -{-| Given a search string of the form "?key=value&key2=val2" -parse these into a dictionary of key to value. --} -parseSearchString : String -> ParseResult -parseSearchString startsWithQuestionMarkThenParams = - case (String.uncons startsWithQuestionMarkThenParams) of - Nothing -> Error "No URL params" - Just ('?', rest) -> parseParams rest - _ -> Error "No URL params" - -parseParams : String -> ParseResult -parseParams stringWithAmpersands = - let - eachParam = (String.split "&" stringWithAmpersands) - eachPair = List.map (splitAtFirst '=') eachParam - in - UrlParams (Dict.fromList eachPair) diff --git a/src/vendor/elm-param-parsing/src/UrlParseUtil.elm b/src/vendor/elm-param-parsing/src/UrlParseUtil.elm deleted file mode 100644 index 5083b5e..0000000 --- a/src/vendor/elm-param-parsing/src/UrlParseUtil.elm +++ /dev/null @@ -1,20 +0,0 @@ -module UrlParseUtil where - -import String - -{-| These are broken out only to be testable - without being exposed --} - -splitAtFirst : Char -> String -> (String, String) -splitAtFirst c s = - case (firstOccurrence c s) of - Nothing -> (s, "") - Just i -> ((String.left i s), (String.dropLeft (i + 1) s)) - - -firstOccurrence : Char -> String -> Maybe Int -firstOccurrence c s = - case (String.indexes (String.fromChar c) s) of - [] -> Nothing - head :: _ -> Just head diff --git a/src/vendor/elm-param-parsing/test.sh b/src/vendor/elm-param-parsing/test.sh deleted file mode 100755 index 16be50f..0000000 --- a/src/vendor/elm-param-parsing/test.sh +++ /dev/null @@ -1,4 +0,0 @@ -elm-make src/*.elm -elm-make test/*.elm --output test/raw-test.js -bash test/elm-io.sh test/raw-test.js test/test.js -node test/test.js diff --git a/src/vendor/elm-storage/Example.elm b/src/vendor/elm-storage/Example.elm deleted file mode 100644 index 1ee9b5a..0000000 --- a/src/vendor/elm-storage/Example.elm +++ /dev/null @@ -1,49 +0,0 @@ -import Storage exposing (..) - -import Html exposing (text, div, input, form) -import Html.Attributes exposing (type') -import Html.Events exposing (on, targetValue) -import Json.Encode exposing (string, Value) -import Json.Decode as Decode - -import Signal exposing (Signal, Mailbox, Message, mailbox, message, send) -import Task exposing (Task, succeed, andThen, mapError, onError, fail) - -sendInputMailbox : Mailbox (Task String ()) -sendInputMailbox = mailbox (succeed ()) - -currentValueMailbox : Mailbox String -currentValueMailbox = mailbox "" - -sendInputToStorage : String -> Task String () -sendInputToStorage = - setItem "Test" << string - -getInputFromStorage : Task String String -getInputFromStorage = - getItem "Test" Decode.string - - -sendInput : String -> Task String () -sendInput value = sendInputToStorage value - `andThen` \_ -> getInputFromStorage - `andThen` \val -> send currentValueMailbox.address val - - - -view model = - div [] - [ input - [ on "input" targetValue (message sendInputMailbox.address << sendInput) - ] [] - , text model - ] - - -port sendInputPort : Signal (Task String ()) -port sendInputPort = - sendInputMailbox.signal - - - -main = Signal.map view currentValueMailbox.signal diff --git a/src/vendor/elm-storage/README.md b/src/vendor/elm-storage/README.md deleted file mode 100644 index f0e67db..0000000 --- a/src/vendor/elm-storage/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# elm-storage - -> Local Storage in Elm - -## Example - -```bash -cd example -elm-reactor -``` - diff --git a/src/vendor/elm-storage/elm-package.json b/src/vendor/elm-storage/elm-package.json deleted file mode 100644 index c5c78e3..0000000 --- a/src/vendor/elm-storage/elm-package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "version": "1.0.0", - "summary": "LocalForage Bindings in Elm", - "repository": "https://github.com/USER/PROJECT.git", - "license": "BSD3", - "source-directories": [ - "src" - ], - "exposed-modules": [], - "native-modules" : true, - "dependencies": { - "elm-lang/core": "2.0.0 <= v < 3.0.0", - "evancz/elm-html": "3.0.0 <= v < 4.0.0" - }, - "elm-version": "0.15.0 <= v < 0.16.0" -} diff --git a/src/vendor/elm-storage/example/.gitignore b/src/vendor/elm-storage/example/.gitignore deleted file mode 100644 index 3bd52a1..0000000 --- a/src/vendor/elm-storage/example/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -elm-stuff -elm.js diff --git a/src/vendor/elm-storage/example/App.elm b/src/vendor/elm-storage/example/App.elm deleted file mode 100644 index 4c39c15..0000000 --- a/src/vendor/elm-storage/example/App.elm +++ /dev/null @@ -1,92 +0,0 @@ -module App where - -import Effects exposing (Effects) -import Json.Encode as JE exposing (string, Value) -import Json.Decode as JD -import Html exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (on, targetValue) -import Storage exposing (..) -import Task exposing (..) - -import Debug - -type alias Model = Maybe String - -initialModel : Model -initialModel = - Nothing - -init : (Model, Effects Action) -init = - ( initialModel - , getInputFromStorage - ) - --- UPDATE - -type Action - = GetStorage (Result String ()) - | SetStorage String - | UpdateModel (Result String String) - - -update : Action -> Model -> (Model, Effects Action) -update action model = - case action of - GetStorage result -> - case result of - Ok s -> - (model, getInputFromStorage) - Err err -> - (model, Effects.none) - - - SetStorage s -> - -- Don't update the model here, instead after this action is done, the - -- effect should call another action to update the model. - (model, sendInputToStorage s) - - UpdateModel result -> - case result of - Ok s -> - (Just s, Effects.none) - Err err -> - (model, Effects.none) - - -sendInputToStorage : String -> Effects Action -sendInputToStorage s = - Storage.setItem "Test" (JE.string s) - |> Task.toResult - |> Task.map GetStorage - |> Effects.task - -getInputFromStorage : Effects Action -getInputFromStorage = - getItem "Test" JD.string - |> Task.toResult - |> Task.map UpdateModel - |> Effects.task - --- VIEW - -view : Signal.Address Action -> Model -> Html -view address model = - let - existingValue = - case model of - Just s -> - text s - Nothing -> - text "" - in - div [] - [ h2 [] [ text "Storage example"] - , input - [ on "input" targetValue (Signal.message address << SetStorage) - , required True - , placeholder "Add some text" - ] [] - , existingValue - ] diff --git a/src/vendor/elm-storage/example/Main.elm b/src/vendor/elm-storage/example/Main.elm deleted file mode 100644 index 630e9e9..0000000 --- a/src/vendor/elm-storage/example/Main.elm +++ /dev/null @@ -1,24 +0,0 @@ -module Main where - -import Effects exposing (Never) -import App exposing (init, update, view) -import StartApp -import Task - - -app = - StartApp.start - { init = init - , update = update - , view = view - , inputs = [] - } - - -main = - app.html - - -port tasks : Signal (Task.Task Never ()) -port tasks = - app.tasks diff --git a/src/vendor/elm-storage/example/elm-package.json b/src/vendor/elm-storage/example/elm-package.json deleted file mode 100644 index 5ceeaf1..0000000 --- a/src/vendor/elm-storage/example/elm-package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": "1.0.0", - "summary": "helpful summary of your project, less than 80 characters", - "repository": "https://github.com/USER/PROJECT.git", - "license": "BSD3", - "source-directories": [ - "../src", - "." - ], - "exposed-modules": [], - "native-modules": true, - "dependencies": { - "elm-lang/core": "2.1.0 <= v < 3.0.0", - "evancz/elm-effects": "2.0.0 <= v < 3.0.0", - "evancz/elm-html": "4.0.1 <= v < 5.0.0", - "evancz/start-app": "2.0.1 <= v < 3.0.0" - }, - "elm-version": "0.15.1 <= v < 0.16.0" -} diff --git a/src/vendor/elm-storage/src/Native/Storage.js b/src/vendor/elm-storage/src/Native/Storage.js deleted file mode 100644 index f96ac2f..0000000 --- a/src/vendor/elm-storage/src/Native/Storage.js +++ /dev/null @@ -1,2595 +0,0 @@ -/*! - localForage -- Offline Storage, Improved - Version 1.2.2 - https://mozilla.github.io/localForage - (c) 2013-2015 Mozilla, Apache License 2.0 -*/ -(function() { -var define, requireModule, require, requirejs; - -(function() { - var registry = {}, seen = {}; - - define = function(name, deps, callback) { - registry[name] = { deps: deps, callback: callback }; - }; - - requirejs = require = requireModule = function(name) { - requirejs._eak_seen = registry; - - if (seen[name]) { return seen[name]; } - seen[name] = {}; - - if (!registry[name]) { - throw new Error("Could not find module " + name); - } - - var mod = registry[name], - deps = mod.deps, - callback = mod.callback, - reified = [], - exports; - - for (var i=0, l=deps.length; i string data storage. - var BASE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; - - var SERIALIZED_MARKER = '__lfsc__:'; - var SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER.length; - - // OMG the serializations! - var TYPE_ARRAYBUFFER = 'arbf'; - var TYPE_BLOB = 'blob'; - var TYPE_INT8ARRAY = 'si08'; - var TYPE_UINT8ARRAY = 'ui08'; - var TYPE_UINT8CLAMPEDARRAY = 'uic8'; - var TYPE_INT16ARRAY = 'si16'; - var TYPE_INT32ARRAY = 'si32'; - var TYPE_UINT16ARRAY = 'ur16'; - var TYPE_UINT32ARRAY = 'ui32'; - var TYPE_FLOAT32ARRAY = 'fl32'; - var TYPE_FLOAT64ARRAY = 'fl64'; - var TYPE_SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER_LENGTH + - TYPE_ARRAYBUFFER.length; - - // Serialize a value, afterwards executing a callback (which usually - // instructs the `setItem()` callback/promise to be executed). This is how - // we store binary data with localStorage. - function serialize(value, callback) { - var valueString = ''; - if (value) { - valueString = value.toString(); - } - - // Cannot use `value instanceof ArrayBuffer` or such here, as these - // checks fail when running the tests using casper.js... - // - // TODO: See why those tests fail and use a better solution. - if (value && (value.toString() === '[object ArrayBuffer]' || - value.buffer && - value.buffer.toString() === '[object ArrayBuffer]')) { - // Convert binary arrays to a string and prefix the string with - // a special marker. - var buffer; - var marker = SERIALIZED_MARKER; - - if (value instanceof ArrayBuffer) { - buffer = value; - marker += TYPE_ARRAYBUFFER; - } else { - buffer = value.buffer; - - if (valueString === '[object Int8Array]') { - marker += TYPE_INT8ARRAY; - } else if (valueString === '[object Uint8Array]') { - marker += TYPE_UINT8ARRAY; - } else if (valueString === '[object Uint8ClampedArray]') { - marker += TYPE_UINT8CLAMPEDARRAY; - } else if (valueString === '[object Int16Array]') { - marker += TYPE_INT16ARRAY; - } else if (valueString === '[object Uint16Array]') { - marker += TYPE_UINT16ARRAY; - } else if (valueString === '[object Int32Array]') { - marker += TYPE_INT32ARRAY; - } else if (valueString === '[object Uint32Array]') { - marker += TYPE_UINT32ARRAY; - } else if (valueString === '[object Float32Array]') { - marker += TYPE_FLOAT32ARRAY; - } else if (valueString === '[object Float64Array]') { - marker += TYPE_FLOAT64ARRAY; - } else { - callback(new Error('Failed to get type for BinaryArray')); - } - } - - callback(marker + bufferToString(buffer)); - } else if (valueString === '[object Blob]') { - // Conver the blob to a binaryArray and then to a string. - var fileReader = new FileReader(); - - fileReader.onload = function() { - var str = bufferToString(this.result); - - callback(SERIALIZED_MARKER + TYPE_BLOB + str); - }; - - fileReader.readAsArrayBuffer(value); - } else { - try { - callback(JSON.stringify(value)); - } catch (e) { - window.console.error("Couldn't convert value into a JSON " + - 'string: ', value); - - callback(null, e); - } - } - } - - // Deserialize data we've inserted into a value column/field. We place - // special markers into our strings to mark them as encoded; this isn't - // as nice as a meta field, but it's the only sane thing we can do whilst - // keeping localStorage support intact. - // - // Oftentimes this will just deserialize JSON content, but if we have a - // special marker (SERIALIZED_MARKER, defined above), we will extract - // some kind of arraybuffer/binary data/typed array out of the string. - function deserialize(value) { - // If we haven't marked this string as being specially serialized (i.e. - // something other than serialized JSON), we can just return it and be - // done with it. - if (value.substring(0, - SERIALIZED_MARKER_LENGTH) !== SERIALIZED_MARKER) { - return JSON.parse(value); - } - - // The following code deals with deserializing some kind of Blob or - // TypedArray. First we separate out the type of data we're dealing - // with from the data itself. - var serializedString = value.substring(TYPE_SERIALIZED_MARKER_LENGTH); - var type = value.substring(SERIALIZED_MARKER_LENGTH, - TYPE_SERIALIZED_MARKER_LENGTH); - - var buffer = stringToBuffer(serializedString); - - // Return the right type based on the code/type set during - // serialization. - switch (type) { - case TYPE_ARRAYBUFFER: - return buffer; - case TYPE_BLOB: - return new Blob([buffer]); - case TYPE_INT8ARRAY: - return new Int8Array(buffer); - case TYPE_UINT8ARRAY: - return new Uint8Array(buffer); - case TYPE_UINT8CLAMPEDARRAY: - return new Uint8ClampedArray(buffer); - case TYPE_INT16ARRAY: - return new Int16Array(buffer); - case TYPE_UINT16ARRAY: - return new Uint16Array(buffer); - case TYPE_INT32ARRAY: - return new Int32Array(buffer); - case TYPE_UINT32ARRAY: - return new Uint32Array(buffer); - case TYPE_FLOAT32ARRAY: - return new Float32Array(buffer); - case TYPE_FLOAT64ARRAY: - return new Float64Array(buffer); - default: - throw new Error('Unkown type: ' + type); - } - } - - function stringToBuffer(serializedString) { - // Fill the string into a ArrayBuffer. - var bufferLength = serializedString.length * 0.75; - var len = serializedString.length; - var i; - var p = 0; - var encoded1, encoded2, encoded3, encoded4; - - if (serializedString[serializedString.length - 1] === '=') { - bufferLength--; - if (serializedString[serializedString.length - 2] === '=') { - bufferLength--; - } - } - - var buffer = new ArrayBuffer(bufferLength); - var bytes = new Uint8Array(buffer); - - for (i = 0; i < len; i+=4) { - encoded1 = BASE_CHARS.indexOf(serializedString[i]); - encoded2 = BASE_CHARS.indexOf(serializedString[i+1]); - encoded3 = BASE_CHARS.indexOf(serializedString[i+2]); - encoded4 = BASE_CHARS.indexOf(serializedString[i+3]); - - /*jslint bitwise: true */ - bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); - bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); - bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); - } - return buffer; - } - - // Converts a buffer to a string to store, serialized, in the backend - // storage library. - function bufferToString(buffer) { - // base64-arraybuffer - var bytes = new Uint8Array(buffer); - var base64String = ''; - var i; - - for (i = 0; i < bytes.length; i += 3) { - /*jslint bitwise: true */ - base64String += BASE_CHARS[bytes[i] >> 2]; - base64String += BASE_CHARS[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; - base64String += BASE_CHARS[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; - base64String += BASE_CHARS[bytes[i + 2] & 63]; - } - - if ((bytes.length % 3) === 2) { - base64String = base64String.substring(0, base64String.length - 1) + '='; - } else if (bytes.length % 3 === 1) { - base64String = base64String.substring(0, base64String.length - 2) + '=='; - } - - return base64String; - } - - var localforageSerializer = { - serialize: serialize, - deserialize: deserialize, - stringToBuffer: stringToBuffer, - bufferToString: bufferToString - }; - - if (typeof module !== 'undefined' && module.exports) { - module.exports = localforageSerializer; - } else if (typeof define === 'function' && define.amd) { - define('localforageSerializer', function() { - return localforageSerializer; - }); - } else { - this.localforageSerializer = localforageSerializer; - } -}).call(window); -// Some code originally from async_storage.js in -// [Gaia](https://github.com/mozilla-b2g/gaia). -(function() { - 'use strict'; - - // Originally found in https://github.com/mozilla-b2g/gaia/blob/e8f624e4cc9ea945727278039b3bc9bcb9f8667a/shared/js/async_storage.js - - // Promises! - var Promise = (typeof module !== 'undefined' && module.exports) ? - require('promise') : this.Promise; - - // Initialize IndexedDB; fall back to vendor-prefixed versions if needed. - var indexedDB = indexedDB || this.indexedDB || this.webkitIndexedDB || - this.mozIndexedDB || this.OIndexedDB || - this.msIndexedDB; - - // If IndexedDB isn't available, we get outta here! - if (!indexedDB) { - return; - } - - // Open the IndexedDB database (automatically creates one if one didn't - // previously exist), using any options set in the config. - function _initStorage(options) { - var self = this; - var dbInfo = { - db: null - }; - - if (options) { - for (var i in options) { - dbInfo[i] = options[i]; - } - } - - return new Promise(function(resolve, reject) { - var openreq = indexedDB.open(dbInfo.name, dbInfo.version); - openreq.onerror = function() { - reject(openreq.error); - }; - openreq.onupgradeneeded = function() { - // First time setup: create an empty object store - openreq.result.createObjectStore(dbInfo.storeName); - }; - openreq.onsuccess = function() { - dbInfo.db = openreq.result; - self._dbInfo = dbInfo; - resolve(); - }; - }); - } - - function getItem(key, callback) { - var self = this; - - // Cast the key to a string, as that's all we can set as a key. - if (typeof key !== 'string') { - window.console.warn(key + - ' used as a key, but it is not a string.'); - key = String(key); - } - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - var dbInfo = self._dbInfo; - var store = dbInfo.db.transaction(dbInfo.storeName, 'readonly') - .objectStore(dbInfo.storeName); - var req = store.get(key); - - req.onsuccess = function() { - var value = req.result; - if (value === undefined) { - value = null; - } - - resolve(value); - }; - - req.onerror = function() { - reject(req.error); - }; - })["catch"](reject); - }); - - executeDeferedCallback(promise, callback); - return promise; - } - - // Iterate over all items stored in database. - function iterate(iterator, callback) { - var self = this; - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - var dbInfo = self._dbInfo; - var store = dbInfo.db.transaction(dbInfo.storeName, 'readonly') - .objectStore(dbInfo.storeName); - - var req = store.openCursor(); - var iterationNumber = 1; - - req.onsuccess = function() { - var cursor = req.result; - - if (cursor) { - var result = iterator(cursor.value, cursor.key, iterationNumber++); - - if (result !== void(0)) { - resolve(result); - } else { - cursor["continue"](); - } - } else { - resolve(); - } - }; - - req.onerror = function() { - reject(req.error); - }; - })["catch"](reject); - }); - - executeDeferedCallback(promise, callback); - - return promise; - } - - function setItem(key, value, callback) { - var self = this; - - // Cast the key to a string, as that's all we can set as a key. - if (typeof key !== 'string') { - window.console.warn(key + - ' used as a key, but it is not a string.'); - key = String(key); - } - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - var dbInfo = self._dbInfo; - var transaction = dbInfo.db.transaction(dbInfo.storeName, 'readwrite'); - var store = transaction.objectStore(dbInfo.storeName); - - // The reason we don't _save_ null is because IE 10 does - // not support saving the `null` type in IndexedDB. How - // ironic, given the bug below! - // See: https://github.com/mozilla/localForage/issues/161 - if (value === null) { - value = undefined; - } - - var req = store.put(value, key); - transaction.oncomplete = function() { - // Cast to undefined so the value passed to - // callback/promise is the same as what one would get out - // of `getItem()` later. This leads to some weirdness - // (setItem('foo', undefined) will return `null`), but - // it's not my fault localStorage is our baseline and that - // it's weird. - if (value === undefined) { - value = null; - } - - resolve(value); - }; - transaction.onabort = transaction.onerror = function() { - reject(req.error); - }; - })["catch"](reject); - }); - - executeDeferedCallback(promise, callback); - return promise; - } - - function removeItem(key, callback) { - var self = this; - - // Cast the key to a string, as that's all we can set as a key. - if (typeof key !== 'string') { - window.console.warn(key + - ' used as a key, but it is not a string.'); - key = String(key); - } - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - var dbInfo = self._dbInfo; - var transaction = dbInfo.db.transaction(dbInfo.storeName, 'readwrite'); - var store = transaction.objectStore(dbInfo.storeName); - - // We use a Grunt task to make this safe for IE and some - // versions of Android (including those used by Cordova). - // Normally IE won't like `.delete()` and will insist on - // using `['delete']()`, but we have a build step that - // fixes this for us now. - var req = store["delete"](key); - transaction.oncomplete = function() { - resolve(); - }; - - transaction.onerror = function() { - reject(req.error); - }; - - // The request will be aborted if we've exceeded our storage - // space. In this case, we will reject with a specific - // "QuotaExceededError". - transaction.onabort = function(event) { - var error = event.target.error; - if (error === 'QuotaExceededError') { - reject(error); - } - }; - })["catch"](reject); - }); - - executeDeferedCallback(promise, callback); - return promise; - } - - function clear(callback) { - var self = this; - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - var dbInfo = self._dbInfo; - var transaction = dbInfo.db.transaction(dbInfo.storeName, 'readwrite'); - var store = transaction.objectStore(dbInfo.storeName); - var req = store.clear(); - - transaction.oncomplete = function() { - resolve(); - }; - - transaction.onabort = transaction.onerror = function() { - reject(req.error); - }; - })["catch"](reject); - }); - - executeDeferedCallback(promise, callback); - return promise; - } - - function length(callback) { - var self = this; - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - var dbInfo = self._dbInfo; - var store = dbInfo.db.transaction(dbInfo.storeName, 'readonly') - .objectStore(dbInfo.storeName); - var req = store.count(); - - req.onsuccess = function() { - resolve(req.result); - }; - - req.onerror = function() { - reject(req.error); - }; - })["catch"](reject); - }); - - executeCallback(promise, callback); - return promise; - } - - function key(n, callback) { - var self = this; - - var promise = new Promise(function(resolve, reject) { - if (n < 0) { - resolve(null); - - return; - } - - self.ready().then(function() { - var dbInfo = self._dbInfo; - var store = dbInfo.db.transaction(dbInfo.storeName, 'readonly') - .objectStore(dbInfo.storeName); - - var advanced = false; - var req = store.openCursor(); - req.onsuccess = function() { - var cursor = req.result; - if (!cursor) { - // this means there weren't enough keys - resolve(null); - - return; - } - - if (n === 0) { - // We have the first key, return it if that's what they - // wanted. - resolve(cursor.key); - } else { - if (!advanced) { - // Otherwise, ask the cursor to skip ahead n - // records. - advanced = true; - cursor.advance(n); - } else { - // When we get here, we've got the nth key. - resolve(cursor.key); - } - } - }; - - req.onerror = function() { - reject(req.error); - }; - })["catch"](reject); - }); - - executeCallback(promise, callback); - return promise; - } - - function keys(callback) { - var self = this; - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - var dbInfo = self._dbInfo; - var store = dbInfo.db.transaction(dbInfo.storeName, 'readonly') - .objectStore(dbInfo.storeName); - - var req = store.openCursor(); - var keys = []; - - req.onsuccess = function() { - var cursor = req.result; - - if (!cursor) { - resolve(keys); - return; - } - - keys.push(cursor.key); - cursor["continue"](); - }; - - req.onerror = function() { - reject(req.error); - }; - })["catch"](reject); - }); - - executeCallback(promise, callback); - return promise; - } - - function executeCallback(promise, callback) { - if (callback) { - promise.then(function(result) { - callback(null, result); - }, function(error) { - callback(error); - }); - } - } - - function executeDeferedCallback(promise, callback) { - if (callback) { - promise.then(function(result) { - deferCallback(callback, result); - }, function(error) { - callback(error); - }); - } - } - - // Under Chrome the callback is called before the changes (save, clear) - // are actually made. So we use a defer function which wait that the - // call stack to be empty. - // For more info : https://github.com/mozilla/localForage/issues/175 - // Pull request : https://github.com/mozilla/localForage/pull/178 - function deferCallback(callback, result) { - if (callback) { - return setTimeout(function() { - return callback(null, result); - }, 0); - } - } - - var asyncStorage = { - _driver: 'asyncStorage', - _initStorage: _initStorage, - iterate: iterate, - getItem: getItem, - setItem: setItem, - removeItem: removeItem, - clear: clear, - length: length, - key: key, - keys: keys - }; - - if (typeof module !== 'undefined' && module.exports) { - module.exports = asyncStorage; - } else if (typeof define === 'function' && define.amd) { - define('asyncStorage', function() { - return asyncStorage; - }); - } else { - this.asyncStorage = asyncStorage; - } -}).call(window); -// If IndexedDB isn't available, we'll fall back to localStorage. -// Note that this will have considerable performance and storage -// side-effects (all data will be serialized on save and only data that -// can be converted to a string via `JSON.stringify()` will be saved). -(function() { - 'use strict'; - - // Promises! - var Promise = (typeof module !== 'undefined' && module.exports) ? - require('promise') : this.Promise; - - var globalObject = this; - var serializer = null; - var localStorage = null; - - // If the app is running inside a Google Chrome packaged webapp, or some - // other context where localStorage isn't available, we don't use - // localStorage. This feature detection is preferred over the old - // `if (window.chrome && window.chrome.runtime)` code. - // See: https://github.com/mozilla/localForage/issues/68 - try { - // If localStorage isn't available, we get outta here! - // This should be inside a try catch - if (!this.localStorage || !('setItem' in this.localStorage)) { - return; - } - // Initialize localStorage and create a variable to use throughout - // the code. - localStorage = this.localStorage; - } catch (e) { - return; - } - - var ModuleType = { - DEFINE: 1, - EXPORT: 2, - WINDOW: 3 - }; - - // Attaching to window (i.e. no module loader) is the assumed, - // simple default. - var moduleType = ModuleType.WINDOW; - - // Find out what kind of module setup we have; if none, we'll just attach - // localForage to the main window. - if (typeof module !== 'undefined' && module.exports) { - moduleType = ModuleType.EXPORT; - } else if (typeof define === 'function' && define.amd) { - moduleType = ModuleType.DEFINE; - } - - // Config the localStorage backend, using options set in the config. - function _initStorage(options) { - var self = this; - var dbInfo = {}; - if (options) { - for (var i in options) { - dbInfo[i] = options[i]; - } - } - - dbInfo.keyPrefix = dbInfo.name + '/'; - - self._dbInfo = dbInfo; - - var serializerPromise = new Promise(function(resolve/*, reject*/) { - // We allow localForage to be declared as a module or as a - // library available without AMD/require.js. - if (moduleType === ModuleType.DEFINE) { - require(['localforageSerializer'], resolve); - } else if (moduleType === ModuleType.EXPORT) { - // Making it browserify friendly - resolve(require('./../utils/serializer')); - } else { - resolve(globalObject.localforageSerializer); - } - }); - - return serializerPromise.then(function(lib) { - serializer = lib; - return Promise.resolve(); - }); - } - - // Remove all keys from the datastore, effectively destroying all data in - // the app's key/value store! - function clear(callback) { - var self = this; - var promise = self.ready().then(function() { - var keyPrefix = self._dbInfo.keyPrefix; - - for (var i = localStorage.length - 1; i >= 0; i--) { - var key = localStorage.key(i); - - if (key.indexOf(keyPrefix) === 0) { - localStorage.removeItem(key); - } - } - }); - - executeCallback(promise, callback); - return promise; - } - - // Retrieve an item from the store. Unlike the original async_storage - // library in Gaia, we don't modify return values at all. If a key's value - // is `undefined`, we pass that value to the callback function. - function getItem(key, callback) { - var self = this; - - // Cast the key to a string, as that's all we can set as a key. - if (typeof key !== 'string') { - window.console.warn(key + - ' used as a key, but it is not a string.'); - key = String(key); - } - - var promise = self.ready().then(function() { - var dbInfo = self._dbInfo; - var result = localStorage.getItem(dbInfo.keyPrefix + key); - - // If a result was found, parse it from the serialized - // string into a JS object. If result isn't truthy, the key - // is likely undefined and we'll pass it straight to the - // callback. - if (result) { - result = serializer.deserialize(result); - } - - return result; - }); - - executeCallback(promise, callback); - return promise; - } - - // Iterate over all items in the store. - function iterate(iterator, callback) { - var self = this; - - var promise = self.ready().then(function() { - var keyPrefix = self._dbInfo.keyPrefix; - var keyPrefixLength = keyPrefix.length; - var length = localStorage.length; - - for (var i = 0; i < length; i++) { - var key = localStorage.key(i); - var value = localStorage.getItem(key); - - // If a result was found, parse it from the serialized - // string into a JS object. If result isn't truthy, the - // key is likely undefined and we'll pass it straight - // to the iterator. - if (value) { - value = serializer.deserialize(value); - } - - value = iterator(value, key.substring(keyPrefixLength), i + 1); - - if (value !== void(0)) { - return value; - } - } - }); - - executeCallback(promise, callback); - return promise; - } - - // Same as localStorage's key() method, except takes a callback. - function key(n, callback) { - var self = this; - var promise = self.ready().then(function() { - var dbInfo = self._dbInfo; - var result; - try { - result = localStorage.key(n); - } catch (error) { - result = null; - } - - // Remove the prefix from the key, if a key is found. - if (result) { - result = result.substring(dbInfo.keyPrefix.length); - } - - return result; - }); - - executeCallback(promise, callback); - return promise; - } - - function keys(callback) { - var self = this; - var promise = self.ready().then(function() { - var dbInfo = self._dbInfo; - var length = localStorage.length; - var keys = []; - - for (var i = 0; i < length; i++) { - if (localStorage.key(i).indexOf(dbInfo.keyPrefix) === 0) { - keys.push(localStorage.key(i).substring(dbInfo.keyPrefix.length)); - } - } - - return keys; - }); - - executeCallback(promise, callback); - return promise; - } - - // Supply the number of keys in the datastore to the callback function. - function length(callback) { - var self = this; - var promise = self.keys().then(function(keys) { - return keys.length; - }); - - executeCallback(promise, callback); - return promise; - } - - // Remove an item from the store, nice and simple. - function removeItem(key, callback) { - var self = this; - - // Cast the key to a string, as that's all we can set as a key. - if (typeof key !== 'string') { - window.console.warn(key + - ' used as a key, but it is not a string.'); - key = String(key); - } - - var promise = self.ready().then(function() { - var dbInfo = self._dbInfo; - localStorage.removeItem(dbInfo.keyPrefix + key); - }); - - executeCallback(promise, callback); - return promise; - } - - // Set a key's value and run an optional callback once the value is set. - // Unlike Gaia's implementation, the callback function is passed the value, - // in case you want to operate on that value only after you're sure it - // saved, or something like that. - function setItem(key, value, callback) { - var self = this; - - // Cast the key to a string, as that's all we can set as a key. - if (typeof key !== 'string') { - window.console.warn(key + - ' used as a key, but it is not a string.'); - key = String(key); - } - - var promise = self.ready().then(function() { - // Convert undefined values to null. - // https://github.com/mozilla/localForage/pull/42 - if (value === undefined) { - value = null; - } - - // Save the original value to pass to the callback. - var originalValue = value; - - return new Promise(function(resolve, reject) { - serializer.serialize(value, function(value, error) { - if (error) { - reject(error); - } else { - try { - var dbInfo = self._dbInfo; - localStorage.setItem(dbInfo.keyPrefix + key, value); - resolve(originalValue); - } catch (e) { - // localStorage capacity exceeded. - // TODO: Make this a specific error/event. - if (e.name === 'QuotaExceededError' || - e.name === 'NS_ERROR_DOM_QUOTA_REACHED') { - reject(e); - } - reject(e); - } - } - }); - }); - }); - - executeCallback(promise, callback); - return promise; - } - - function executeCallback(promise, callback) { - if (callback) { - promise.then(function(result) { - callback(null, result); - }, function(error) { - callback(error); - }); - } - } - - var localStorageWrapper = { - _driver: 'localStorageWrapper', - _initStorage: _initStorage, - // Default API, from Gaia/localStorage. - iterate: iterate, - getItem: getItem, - setItem: setItem, - removeItem: removeItem, - clear: clear, - length: length, - key: key, - keys: keys - }; - - if (moduleType === ModuleType.EXPORT) { - module.exports = localStorageWrapper; - } else if (moduleType === ModuleType.DEFINE) { - define('localStorageWrapper', function() { - return localStorageWrapper; - }); - } else { - this.localStorageWrapper = localStorageWrapper; - } -}).call(window); -/* - * Includes code from: - * - * base64-arraybuffer - * https://github.com/niklasvh/base64-arraybuffer - * - * Copyright (c) 2012 Niklas von Hertzen - * Licensed under the MIT license. - */ -(function() { - 'use strict'; - - // Promises! - var Promise = (typeof module !== 'undefined' && module.exports) ? - require('promise') : this.Promise; - - var globalObject = this; - var serializer = null; - var openDatabase = this.openDatabase; - - // If WebSQL methods aren't available, we can stop now. - if (!openDatabase) { - return; - } - - var ModuleType = { - DEFINE: 1, - EXPORT: 2, - WINDOW: 3 - }; - - // Attaching to window (i.e. no module loader) is the assumed, - // simple default. - var moduleType = ModuleType.WINDOW; - - // Find out what kind of module setup we have; if none, we'll just attach - // localForage to the main window. - if (typeof module !== 'undefined' && module.exports) { - moduleType = ModuleType.EXPORT; - } else if (typeof define === 'function' && define.amd) { - moduleType = ModuleType.DEFINE; - } - - // Open the WebSQL database (automatically creates one if one didn't - // previously exist), using any options set in the config. - function _initStorage(options) { - var self = this; - var dbInfo = { - db: null - }; - - if (options) { - for (var i in options) { - dbInfo[i] = typeof(options[i]) !== 'string' ? - options[i].toString() : options[i]; - } - } - - var serializerPromise = new Promise(function(resolve/*, reject*/) { - // We allow localForage to be declared as a module or as a - // library available without AMD/require.js. - if (moduleType === ModuleType.DEFINE) { - require(['localforageSerializer'], resolve); - } else if (moduleType === ModuleType.EXPORT) { - // Making it browserify friendly - resolve(require('./../utils/serializer')); - } else { - resolve(globalObject.localforageSerializer); - } - }); - - var dbInfoPromise = new Promise(function(resolve, reject) { - // Open the database; the openDatabase API will automatically - // create it for us if it doesn't exist. - try { - dbInfo.db = openDatabase(dbInfo.name, String(dbInfo.version), - dbInfo.description, dbInfo.size); - } catch (e) { - return self.setDriver(self.LOCALSTORAGE).then(function() { - return self._initStorage(options); -}).then(resolve)["catch"](reject); - } - - // Create our key/value table if it doesn't exist. - dbInfo.db.transaction(function(t) { - t.executeSql('CREATE TABLE IF NOT EXISTS ' + dbInfo.storeName + - ' (id INTEGER PRIMARY KEY, key unique, value)', [], - function() { - self._dbInfo = dbInfo; - resolve(); - }, function(t, error) { - reject(error); - }); - }); - }); - - return serializerPromise.then(function(lib) { - serializer = lib; - return dbInfoPromise; - }); - } - - function getItem(key, callback) { - var self = this; - - // Cast the key to a string, as that's all we can set as a key. - if (typeof key !== 'string') { - window.console.warn(key + - ' used as a key, but it is not a string.'); - key = String(key); - } - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - var dbInfo = self._dbInfo; - dbInfo.db.transaction(function(t) { - t.executeSql('SELECT * FROM ' + dbInfo.storeName + - ' WHERE key = ? LIMIT 1', [key], - function(t, results) { - var result = results.rows.length ? - results.rows.item(0).value : null; - - // Check to see if this is serialized content we need to - // unpack. - if (result) { - result = serializer.deserialize(result); - } - - resolve(result); - }, function(t, error) { - - reject(error); - }); - }); - })["catch"](reject); - }); - - executeCallback(promise, callback); - return promise; - } - - function iterate(iterator, callback) { - var self = this; - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - var dbInfo = self._dbInfo; - - dbInfo.db.transaction(function(t) { - t.executeSql('SELECT * FROM ' + dbInfo.storeName, [], - function(t, results) { - var rows = results.rows; - var length = rows.length; - - for (var i = 0; i < length; i++) { - var item = rows.item(i); - var result = item.value; - - // Check to see if this is serialized content - // we need to unpack. - if (result) { - result = serializer.deserialize(result); - } - - result = iterator(result, item.key, i + 1); - - // void(0) prevents problems with redefinition - // of `undefined`. - if (result !== void(0)) { - resolve(result); - return; - } - } - - resolve(); - }, function(t, error) { - reject(error); - }); - }); - })["catch"](reject); - }); - - executeCallback(promise, callback); - return promise; - } - - function setItem(key, value, callback) { - var self = this; - - // Cast the key to a string, as that's all we can set as a key. - if (typeof key !== 'string') { - window.console.warn(key + - ' used as a key, but it is not a string.'); - key = String(key); - } - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - // The localStorage API doesn't return undefined values in an - // "expected" way, so undefined is always cast to null in all - // drivers. See: https://github.com/mozilla/localForage/pull/42 - if (value === undefined) { - value = null; - } - - // Save the original value to pass to the callback. - var originalValue = value; - - serializer.serialize(value, function(value, error) { - if (error) { - reject(error); - } else { - var dbInfo = self._dbInfo; - dbInfo.db.transaction(function(t) { - t.executeSql('INSERT OR REPLACE INTO ' + - dbInfo.storeName + - ' (key, value) VALUES (?, ?)', - [key, value], function() { - resolve(originalValue); - }, function(t, error) { - reject(error); - }); - }, function(sqlError) { // The transaction failed; check - // to see if it's a quota error. - if (sqlError.code === sqlError.QUOTA_ERR) { - // We reject the callback outright for now, but - // it's worth trying to re-run the transaction. - // Even if the user accepts the prompt to use - // more storage on Safari, this error will - // be called. - // - // TODO: Try to re-run the transaction. - reject(sqlError); - } - }); - } - }); - })["catch"](reject); - }); - - executeCallback(promise, callback); - return promise; - } - - function removeItem(key, callback) { - var self = this; - - // Cast the key to a string, as that's all we can set as a key. - if (typeof key !== 'string') { - window.console.warn(key + - ' used as a key, but it is not a string.'); - key = String(key); - } - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - var dbInfo = self._dbInfo; - dbInfo.db.transaction(function(t) { - t.executeSql('DELETE FROM ' + dbInfo.storeName + - ' WHERE key = ?', [key], function() { - - resolve(); - }, function(t, error) { - - reject(error); - }); - }); - })["catch"](reject); - }); - - executeCallback(promise, callback); - return promise; - } - - // Deletes every item in the table. - // TODO: Find out if this resets the AUTO_INCREMENT number. - function clear(callback) { - var self = this; - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - var dbInfo = self._dbInfo; - dbInfo.db.transaction(function(t) { - t.executeSql('DELETE FROM ' + dbInfo.storeName, [], - function() { - resolve(); - }, function(t, error) { - reject(error); - }); - }); - })["catch"](reject); - }); - - executeCallback(promise, callback); - return promise; - } - - // Does a simple `COUNT(key)` to get the number of items stored in - // localForage. - function length(callback) { - var self = this; - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - var dbInfo = self._dbInfo; - dbInfo.db.transaction(function(t) { - // Ahhh, SQL makes this one soooooo easy. - t.executeSql('SELECT COUNT(key) as c FROM ' + - dbInfo.storeName, [], function(t, results) { - var result = results.rows.item(0).c; - - resolve(result); - }, function(t, error) { - - reject(error); - }); - }); - })["catch"](reject); - }); - - executeCallback(promise, callback); - return promise; - } - - // Return the key located at key index X; essentially gets the key from a - // `WHERE id = ?`. This is the most efficient way I can think to implement - // this rarely-used (in my experience) part of the API, but it can seem - // inconsistent, because we do `INSERT OR REPLACE INTO` on `setItem()`, so - // the ID of each key will change every time it's updated. Perhaps a stored - // procedure for the `setItem()` SQL would solve this problem? - // TODO: Don't change ID on `setItem()`. - function key(n, callback) { - var self = this; - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - var dbInfo = self._dbInfo; - dbInfo.db.transaction(function(t) { - t.executeSql('SELECT key FROM ' + dbInfo.storeName + - ' WHERE id = ? LIMIT 1', [n + 1], - function(t, results) { - var result = results.rows.length ? - results.rows.item(0).key : null; - resolve(result); - }, function(t, error) { - reject(error); - }); - }); - })["catch"](reject); - }); - - executeCallback(promise, callback); - return promise; - } - - function keys(callback) { - var self = this; - - var promise = new Promise(function(resolve, reject) { - self.ready().then(function() { - var dbInfo = self._dbInfo; - dbInfo.db.transaction(function(t) { - t.executeSql('SELECT key FROM ' + dbInfo.storeName, [], - function(t, results) { - var keys = []; - - for (var i = 0; i < results.rows.length; i++) { - keys.push(results.rows.item(i).key); - } - - resolve(keys); - }, function(t, error) { - - reject(error); - }); - }); - })["catch"](reject); - }); - - executeCallback(promise, callback); - return promise; - } - - function executeCallback(promise, callback) { - if (callback) { - promise.then(function(result) { - callback(null, result); - }, function(error) { - callback(error); - }); - } - } - - var webSQLStorage = { - _driver: 'webSQLStorage', - _initStorage: _initStorage, - iterate: iterate, - getItem: getItem, - setItem: setItem, - removeItem: removeItem, - clear: clear, - length: length, - key: key, - keys: keys - }; - - if (moduleType === ModuleType.DEFINE) { - define('webSQLStorage', function() { - return webSQLStorage; - }); - } else if (moduleType === ModuleType.EXPORT) { - module.exports = webSQLStorage; - } else { - this.webSQLStorage = webSQLStorage; - } -}).call(window); -(function() { - 'use strict'; - - // Promises! - var Promise = (typeof module !== 'undefined' && module.exports) ? - require('promise') : this.Promise; - - // Custom drivers are stored here when `defineDriver()` is called. - // They are shared across all instances of localForage. - var CustomDrivers = {}; - - var DriverType = { - INDEXEDDB: 'asyncStorage', - LOCALSTORAGE: 'localStorageWrapper', - WEBSQL: 'webSQLStorage' - }; - - var DefaultDriverOrder = [ - DriverType.INDEXEDDB, - DriverType.WEBSQL, - DriverType.LOCALSTORAGE - ]; - - var LibraryMethods = [ - 'clear', - 'getItem', - 'iterate', - 'key', - 'keys', - 'length', - 'removeItem', - 'setItem' - ]; - - var ModuleType = { - DEFINE: 1, - EXPORT: 2, - WINDOW: 3 - }; - - var DefaultConfig = { - description: '', - driver: DefaultDriverOrder.slice(), - name: 'localforage', - // Default DB size is _JUST UNDER_ 5MB, as it's the highest size - // we can use without a prompt. - size: 4980736, - storeName: 'keyvaluepairs', - version: 1.0 - }; - - // Attaching to window (i.e. no module loader) is the assumed, - // simple default. - var moduleType = ModuleType.WINDOW; - - // Find out what kind of module setup we have; if none, we'll just attach - // localForage to the main window. - if (typeof module !== 'undefined' && module.exports) { - moduleType = ModuleType.EXPORT; - } else if (typeof define === 'function' && define.amd) { - moduleType = ModuleType.DEFINE; - } - - // Check to see if IndexedDB is available and if it is the latest - // implementation; it's our preferred backend library. We use "_spec_test" - // as the name of the database because it's not the one we'll operate on, - // but it's useful to make sure its using the right spec. - // See: https://github.com/mozilla/localForage/issues/128 - var driverSupport = (function(self) { - // Initialize IndexedDB; fall back to vendor-prefixed versions - // if needed. - var indexedDB = indexedDB || self.indexedDB || self.webkitIndexedDB || - self.mozIndexedDB || self.OIndexedDB || - self.msIndexedDB; - - var result = {}; - - result[DriverType.WEBSQL] = !!self.openDatabase; - result[DriverType.INDEXEDDB] = !!(function() { - // We mimic PouchDB here; just UA test for Safari (which, as of - // iOS 8/Yosemite, doesn't properly support IndexedDB). - // IndexedDB support is broken and different from Blink's. - // This is faster than the test case (and it's sync), so we just - // do this. *SIGH* - // http://bl.ocks.org/nolanlawson/raw/c83e9039edf2278047e9/ - // - // We test for openDatabase because IE Mobile identifies itself - // as Safari. Oh the lulz... - if (typeof self.openDatabase !== 'undefined' && self.navigator && - self.navigator.userAgent && - /Safari/.test(self.navigator.userAgent) && - !/Chrome/.test(self.navigator.userAgent)) { - return false; - } - try { - return indexedDB && - typeof indexedDB.open === 'function' && - // Some Samsung/HTC Android 4.0-4.3 devices - // have older IndexedDB specs; if this isn't available - // their IndexedDB is too old for us to use. - // (Replaces the onupgradeneeded test.) - typeof self.IDBKeyRange !== 'undefined'; - } catch (e) { - return false; - } - })(); - - result[DriverType.LOCALSTORAGE] = !!(function() { - try { - return (self.localStorage && - ('setItem' in self.localStorage) && - (self.localStorage.setItem)); - } catch (e) { - return false; - } - })(); - - return result; - })(this); - - var isArray = Array.isArray || function(arg) { - return Object.prototype.toString.call(arg) === '[object Array]'; - }; - - function callWhenReady(localForageInstance, libraryMethod) { - localForageInstance[libraryMethod] = function() { - var _args = arguments; - return localForageInstance.ready().then(function() { - return localForageInstance[libraryMethod].apply(localForageInstance, _args); - }); - }; - } - - function extend() { - for (var i = 1; i < arguments.length; i++) { - var arg = arguments[i]; - - if (arg) { - for (var key in arg) { - if (arg.hasOwnProperty(key)) { - if (isArray(arg[key])) { - arguments[0][key] = arg[key].slice(); - } else { - arguments[0][key] = arg[key]; - } - } - } - } - } - - return arguments[0]; - } - - function isLibraryDriver(driverName) { - for (var driver in DriverType) { - if (DriverType.hasOwnProperty(driver) && - DriverType[driver] === driverName) { - return true; - } - } - - return false; - } - - var globalObject = this; - - function LocalForage(options) { - this._config = extend({}, DefaultConfig, options); - this._driverSet = null; - this._ready = false; - this._dbInfo = null; - - // Add a stub for each driver API method that delays the call to the - // corresponding driver method until localForage is ready. These stubs - // will be replaced by the driver methods as soon as the driver is - // loaded, so there is no performance impact. - for (var i = 0; i < LibraryMethods.length; i++) { - callWhenReady(this, LibraryMethods[i]); - } - - this.setDriver(this._config.driver); - } - - LocalForage.prototype.INDEXEDDB = DriverType.INDEXEDDB; - LocalForage.prototype.LOCALSTORAGE = DriverType.LOCALSTORAGE; - LocalForage.prototype.WEBSQL = DriverType.WEBSQL; - - // Set any config values for localForage; can be called anytime before - // the first API call (e.g. `getItem`, `setItem`). - // We loop through options so we don't overwrite existing config - // values. - LocalForage.prototype.config = function(options) { - // If the options argument is an object, we use it to set values. - // Otherwise, we return either a specified config value or all - // config values. - if (typeof(options) === 'object') { - // If localforage is ready and fully initialized, we can't set - // any new configuration values. Instead, we return an error. - if (this._ready) { - return new Error("Can't call config() after localforage " + - 'has been used.'); - } - - for (var i in options) { - if (i === 'storeName') { - options[i] = options[i].replace(/\W/g, '_'); - } - - this._config[i] = options[i]; - } - - // after all config options are set and - // the driver option is used, try setting it - if ('driver' in options && options.driver) { - this.setDriver(this._config.driver); - } - - return true; - } else if (typeof(options) === 'string') { - return this._config[options]; - } else { - return this._config; - } - }; - - // Used to define a custom driver, shared across all instances of - // localForage. - LocalForage.prototype.defineDriver = function(driverObject, callback, - errorCallback) { - var defineDriver = new Promise(function(resolve, reject) { - try { - var driverName = driverObject._driver; - var complianceError = new Error( - 'Custom driver not compliant; see ' + - 'https://mozilla.github.io/localForage/#definedriver' - ); - var namingError = new Error( - 'Custom driver name already in use: ' + driverObject._driver - ); - - // A driver name should be defined and not overlap with the - // library-defined, default drivers. - if (!driverObject._driver) { - reject(complianceError); - return; - } - if (isLibraryDriver(driverObject._driver)) { - reject(namingError); - return; - } - - var customDriverMethods = LibraryMethods.concat('_initStorage'); - for (var i = 0; i < customDriverMethods.length; i++) { - var customDriverMethod = customDriverMethods[i]; - if (!customDriverMethod || - !driverObject[customDriverMethod] || - typeof driverObject[customDriverMethod] !== 'function') { - reject(complianceError); - return; - } - } - - var supportPromise = Promise.resolve(true); - if ('_support' in driverObject) { - if (driverObject._support && typeof driverObject._support === 'function') { - supportPromise = driverObject._support(); - } else { - supportPromise = Promise.resolve(!!driverObject._support); - } - } - - supportPromise.then(function(supportResult) { - driverSupport[driverName] = supportResult; - CustomDrivers[driverName] = driverObject; - resolve(); - }, reject); - } catch (e) { - reject(e); - } - }); - - defineDriver.then(callback, errorCallback); - return defineDriver; - }; - - LocalForage.prototype.driver = function() { - return this._driver || null; - }; - - LocalForage.prototype.ready = function(callback) { - var self = this; - - var ready = new Promise(function(resolve, reject) { - self._driverSet.then(function() { - if (self._ready === null) { - self._ready = self._initStorage(self._config); - } - - self._ready.then(resolve, reject); - })["catch"](reject); - }); - - ready.then(callback, callback); - return ready; - }; - - LocalForage.prototype.setDriver = function(drivers, callback, - errorCallback) { - var self = this; - - if (typeof drivers === 'string') { - drivers = [drivers]; - } - - this._driverSet = new Promise(function(resolve, reject) { - var driverName = self._getFirstSupportedDriver(drivers); - var error = new Error('No available storage method found.'); - - if (!driverName) { - self._driverSet = Promise.reject(error); - reject(error); - return; - } - - self._dbInfo = null; - self._ready = null; - - if (isLibraryDriver(driverName)) { - // We allow localForage to be declared as a module or as a - // library available without AMD/require.js. - if (moduleType === ModuleType.DEFINE) { - require([driverName], function(lib) { - self._extend(lib); - - resolve(); - }); - - return; - } else if (moduleType === ModuleType.EXPORT) { - // Making it browserify friendly - var driver; - switch (driverName) { - case self.INDEXEDDB: - driver = require('./drivers/indexeddb'); - break; - case self.LOCALSTORAGE: - driver = require('./drivers/localstorage'); - break; - case self.WEBSQL: - driver = require('./drivers/websql'); - } - - self._extend(driver); - } else { - self._extend(globalObject[driverName]); - } - } else if (CustomDrivers[driverName]) { - self._extend(CustomDrivers[driverName]); - } else { - self._driverSet = Promise.reject(error); - reject(error); - return; - } - - resolve(); - }); - - function setDriverToConfig() { - self._config.driver = self.driver(); - } - this._driverSet.then(setDriverToConfig, setDriverToConfig); - - this._driverSet.then(callback, errorCallback); - return this._driverSet; - }; - - LocalForage.prototype.supports = function(driverName) { - return !!driverSupport[driverName]; - }; - - LocalForage.prototype._extend = function(libraryMethodsAndProperties) { - extend(this, libraryMethodsAndProperties); - }; - - // Used to determine which driver we should use as the backend for this - // instance of localForage. - LocalForage.prototype._getFirstSupportedDriver = function(drivers) { - if (drivers && isArray(drivers)) { - for (var i = 0; i < drivers.length; i++) { - var driver = drivers[i]; - - if (this.supports(driver)) { - return driver; - } - } - } - - return null; - }; - - LocalForage.prototype.createInstance = function(options) { - return new LocalForage(options); - }; - - // The actual localForage object that we expose as a module or via a - // global. It's extended by pulling in one of our other libraries. - var localForage = new LocalForage(); - - // We allow localForage to be declared as a module or as a library - // available without AMD/require.js. - if (moduleType === ModuleType.DEFINE) { - define('localforage', function() { - return localForage; - }); - } else if (moduleType === ModuleType.EXPORT) { - module.exports = localForage; - } else { - this.localforage = localForage; - } -}).call(window); - - - - -///////////////////////////////// -/// NATIVE MODULE STARTS HERE /// -///////////////////////////////// - -Elm.Native = Elm.Native || {}; -Elm.Native.Storage = {}; -Elm.Native.Storage.make = function(localRuntime){ - - localRuntime.Native = localRuntime.Native || {}; - localRuntime.Native.Storage = localRuntime.Native.Storage || {}; - - if (localRuntime.Native.Storage.values){ - return localRuntime.Native.Storage.values; - } - - var Task = Elm.Native.Task.make(localRuntime); - var Utils = Elm.Native.Utils.make(localRuntime); - var List = Elm.Native.List.make(localRuntime); - - // getItemAsJson : String -> Task error Value - var getItemAsJson = function(key){ - return Task.asyncFunction(function(callback){ - localforage.getItem(key).then(function(value){ - callback(Task.succeed(value)); - }).catch(function(err){ - console.log("Storage Call: getItemAsJson has failed with key: " + key); - callback(Task.fail("Storage Call: getItemAsJson has failed with key: " + key)); - }); - }); - }; - - // setItem : String -> Value -> Task error () - var setItem = function(key, value){ - return Task.asyncFunction(function(callback){ - localforage.setItem(key, value).then(function(){ - callback(Task.succeed(Utils.Tuple0)); - }).catch(function(){ - console.log("Storage Call: setItem has failed with key: " + key + " and value: " + value); - callback(Task.fail("Storage Call: setItem has failed with key: " + key + " and value: " + value)); - }); - }); - }; - - // removeItem : String -> Task error () - var removeItem = function(key){ - return Task.asyncFunction(function(callback){ - localforage.removeItem(key).then(function(){ - callback(Task.succeed(Utils.Tuple0)); - }).catch(function(){ - callback(Task.fail("Storage Call: removeItem has failed with key: " + key)); - }); - }); - }; - - // clear : Task error () - var clear = Task.asyncFunction(function(callback){ - localforage.clear().then(function(){ - callback(Task.succeed(Utils.Tuple0)); - }).catch(function(){ - callback(Task.fail("Storage Call: clear has failed")); - }); - }); - - - // keys : Task error (List String) - var keys = Task.asyncFunction(function(callback){ - localforage.keys().then(function(keys){ - callback(Task.succeed(List.fromArray(keys))); - }).catch(function(){ - callback(Task.fail("Storage Call: keys has failed")); - }); - }); - - // length : Task error Int - var length = Task.asyncFunction(function(callback){ - localforage.length().then(function(numberOfKeys){ - callback(Task.succeed(numberOfKeys)); - }).catch(function(){ - callback(Task.fail("Storage Call: length has failed")); - }); - }); - - - return { - getItemAsJson : getItemAsJson, - setItem : F2(setItem), - removeItem : removeItem, - clear : clear, - keys : keys, - length : length - }; - - -}; diff --git a/src/vendor/elm-storage/src/Storage.elm b/src/vendor/elm-storage/src/Storage.elm deleted file mode 100644 index 55c9acf..0000000 --- a/src/vendor/elm-storage/src/Storage.elm +++ /dev/null @@ -1,39 +0,0 @@ -module Storage where - -import Json.Encode exposing (Value) -import Json.Decode exposing (Decoder, decodeValue) -import Task exposing (Task, succeed, fail, andThen) -import List -import Native.Storage - - -getItemAsJson : String -> Task String Value -getItemAsJson = Native.Storage.getItemAsJson - --- Do better error detection -getItem : String -> Decoder value -> Task String value -getItem key decoder = - let decode value = case decodeValue decoder value of - Ok v -> succeed v - Err err -> fail "Failed" - in - getItemAsJson key `andThen` decode - -setItem : String -> Value -> Task String () -setItem = Native.Storage.setItem - -removeItem : String -> Task String () -removeItem = Native.Storage.removeItem - -clear : Task String () -clear = Native.Storage.clear - -keys : Task String (List String) -keys = Native.Storage.keys - --- keysArray : Task error (Array String) --- keysArray : Native.Storage.keysArray - - -length : Task String Int -length = Native.Storage.length diff --git a/src/vendor/elm-web-api/.gitignore b/src/vendor/elm-web-api/.gitignore deleted file mode 100644 index 88c4e68..0000000 --- a/src/vendor/elm-web-api/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -**/elm-stuff -**/elm.js -**/index.html -**/elm.html -**/node_modules -.*.swp diff --git a/src/vendor/elm-web-api/.travis.yml b/src/vendor/elm-web-api/.travis.yml deleted file mode 100644 index b710e11..0000000 --- a/src/vendor/elm-web-api/.travis.yml +++ /dev/null @@ -1,20 +0,0 @@ -language: node_js - -node_js: - - 0.10 - -sudo: false - -install: - - cd test - - npm install - - cd $TEST_DIR - - npm install - - ./node_modules/elm/bin/elm-package install --yes - -script: - - npm run ci - -env: - matrix: - - TEST_DIR=0.15 diff --git a/src/vendor/elm-web-api/README.md b/src/vendor/elm-web-api/README.md deleted file mode 100644 index 099e328..0000000 --- a/src/vendor/elm-web-api/README.md +++ /dev/null @@ -1,1271 +0,0 @@ -[![Build Status](https://travis-ci.org/rgrempel/elm-web-api.svg)](https://travis-ci.org/rgrempel/elm-web-api) - -[![Sauce Test Status](https://saucelabs.com/browser-matrix/elm-web-api.svg)](https://saucelabs.com/u/elm-web-api) - -# elm-web-api - -The purpose of this package is to expose various standard web APIs to Elm, -or document where they are already exposed. - -By "web APIs" I basically mean the kind of things that are listed on -Mozilla's various Web APIs pages, e.g. - -* https://developer.mozilla.org/en-US/docs/Web/API -* https://developer.mozilla.org/en-US/docs/WebAPI -* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference - -Essentially, they are various facilities available in a Javascript web -environment. - -In order for Elm to use such facilities, it is necessary to write "native" -code. So, I'm plugging away at it -- this is a work in progress. - - -## Contents - -* [Supported browsers](#supportedbrowsers) -* [Installation](#installation) -* APIs - * [WebAPI.AnimationFrame](#webapianimationframe) - * [WebAPI.Cookie](#webapicookie) - * [WebAPI.Document](#webapidocument) - * [WebAPI.Date](#webapidate) - * [WebAPI.Location](#webapilocation) - * [WebAPI.Math](#webapimath) - * [WebAPI.Number](#webapinumber) - * [WebAPI.Screen](#webapiscreen) - * [WebAPI.Storage](#webapistorage) - * [WebAPI.Window](#webapiwindow) - - -## Supported browsers - -I have now set up testing via Travis and SauceLabs, and you can see at the top -of this page a graphic that indicates which browsers I'm testing against. -Let me know if you think I should try out some older verions as well. - -For the moment, I'm not thinking too hard about supporting node.js as well. -That's a somewhat larger issue for Elm (it requires some shimming even to get -elm-lang/core to work). Furthermore, it might make sense to have a separate -package to wrap node.js-oriented APIs (and provide appropriate shims), even if -there is some overlap. - - -## Installation - -Because elm-web-api uses "native" modules, it requires approval before it can -be included in the -[Elm package repository](http://package.elm-lang.org/packages). For a variety of -reasons, it's unlikely to get such approval. Thus, you cannot currently install -it using `elm-package`. - -However, you can still install it and use it via the following steps: - -* Download this respository in one way or another. For instance, you might use: - - git clone https://github.com/rgrempel/elm-web-api.git - - Or, you might use git submodules, if you're adept at that. (I wouldn't suggest - trying it if you've never heard of them before). - -* Modify your `elm-package.json` to refer to the `src` folder. - - You can choose where you want to put the downloaded code, but wherever that - is, simply modify your `elm-package.json` file so that it can find the - `src` folder. So, the "source-directories" entry in your - `elm-package.json` file might end up looking like this: - - "source-directories": [ - "src", - "elm-web-api/src" - ], - - But, of course, that depends on where you've actually put it, and where the - rest of your code is. - -* Modify your `elm-package.json` to indicate that you're using 'native' modules. - To do this, add the following entry to `elm-package.json`: - - "native-modules": true, - -Now, doing this would have several implications which you should be aware of. - -* You would, essentially, be trusting me (or looking to verify for yourself) - that the native code in this module is of high quality and will not cause - run-time errors or other problems. - -* You would be relying on me to update that code when the mechanism for using - 'native' modules in Elm changes, or when certain other internal details of Elm's - implementation change. Furthermore, you'd have to check here whenever the Elm - compiler's version changes, or the Elm core library's version changes, to see - whether an update is required. - -* If you're using this as part of a module you'd like to publish yourself, - then you'll now also need approval before becoming available on the Elm - package repository. - -So, you may or may not really want to do this. But I thought it would be nice to -let you know how. - - -## APIs - -### WebAPI.AnimationFrame - -Bindings for -[`window.requestAnimationFrame()`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) -and [`window.cancelAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/Window/cancelAnimationFrame). - -Note that -[jwmerrill/elm-animation-frame](http://package.elm-lang.org/packages/jwmerrill/elm-animation-frame/latest) -provides for a `Signal` of animation frames. So, this module merely provides a -`Task`-oriented alternative. - -Other higher-level alternatives include -[evancz/elm-effects](http://package.elm-lang.org/packages/evancz/elm-effects/latest) -and [rgrempel/elm-ticker](https://github.com/rgrempel/elm-ticker.git). - -```elm -module WebAPI.AnimationFrame where - -{-| A task which, when executed, will call `window.requestAnimationFrame()`. -The task will complete when `requestAnimationFrame()` fires its callback, and -will pass along the value provided by the callback. - -So, to do something when the callback fires, just add an `andThen` to the task. --} -task : Task x Time - -{-| A more complex implementation of `window.requestAnimationFrame()` which -allows for cancelling the request. - -Returns a `Task` which, when executed, will call -`window.requestAnimationFrame()`, and then immediately complete with the -identifier returned by `requestAnimationFrame()`. You can supply this -identifier to `cancel` if you want to cancel the request. - -Assuming that you don't cancel the request, the following sequence of events will occur: - -* `window.requestAnimationFrame()` will eventually fire its callback, providing a timestamp -* Your function will be called with that timestamp -* The `Task` returned by your function will be immediately executed --} -request : (Time -> Task x a) -> Task y Request - -{-| Opaque type which represents an animation frame request. -} -type Request - -{-| Returns a task which, when executed, will cancel the supplied request -via `window.cancelAnimationFrame()`. --} -cancel : Request -> Task x () -``` - - ------------------ - -### WebAPI.Cookie - -Wraps the browser's -[`document.cookie`](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie) -object. - -```elm -module WebAPI.Cookie where - -{-| A `Task` which, when executed, will succeed with the cookies. - -In the resulting `Dict`, the keys and values are the key=value pairs parsed from -Javascript's `document.cookie`. The keys and values will have been uriDecoded. --} -get : Task x (Dict String String) - -{-| A task which will set a cookie using the provided key (first parameter) -and value (second parameter). - -The key and value will both be uriEncoded. --} -set : String -> String -> Task x () - -{-| A task which will set a cookie using the provided options, key (second -parameter), and value (third parameter). - -The key and value will be uriEncoded, as well as the path and domain options -(if provided). --} -setWith : Options -> String -> String -> Task x () - -{-| Options which you can provide to setWith. -} -type alias Options = - { path : Maybe String - , domain : Maybe String - , maxAge : Maybe Time - , expires : Maybe Date - , secure : Maybe Bool - } - -{-| The default options, in which all options are set to `Nothing`. - -You can use this as a starting point for `setWith`, where you only want to -specify some options. --} -defaultOptions : Options -``` - ------------------ - -### WebAPI.Date - -Generally speaking, dates are dealt with by the `Date` and `Time` modules in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -See [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date). - -Truly dealing with all the complexity of dates requires something that goes -far beyond the Javascript API -- for instance, one could ideally think in terms -of wrapping (or, even better, porting) the [moment.js](http://momentjs.com) -library (or something even better). - -That isn't in scope for this module. Instead, this is intended merely to be a -thin wrapper over the Javascript `Date` API, such as it is. - -```elm -module WebAPI.Date where - -{- --------------------------------- - Getting the current date and time - --------------------------------- -} - -{-| Get the current date, via the browser's `new Date()` -} -current : Task x Date - -{-| Get the current time, via the browser's `Date.now()` -} -now : Task x Time - -{- --------- - Timezones - --------- -} - -{-| The Javascript API allows you to perform certain operations in terms of -the "local" timezone, or in terms of UTC. So, where we wrap those APIs, we use -this type to let you pick (rather than having separate functions). Of course, -you can use partial application to create separate functions if you like. - -Note that this isn't the kind of support that a more sophisticated library -would have for timezones -- it merely wraps what Javascript provides. --} -type Timezone - = Local - | UTC - -{-| Javascript's `getTimezoneOffset()`. - -This represents what Javascript thinks is the offset between UTC and local time, -for the specified date. It can differ from date to date depending on whether -daylight savings time is in effect on that date. - -Note that this is in units of `Time`, so you can scale via `Time.inMinutes` etc. --} -timezoneOffset : Date -> Time - -{- ------------------- - The parts of a date - ------------------- -} - -{-| The parts of a date. - -Note that (as in the Javascript APIs) the month is 0-based, with January = 0. --} -type alias Parts = - { year : Int - , month : Int - , day : Int - , hour : Int - , minute : Int - , second : Int - , millisecond : Int - } - -{-| Construct a `Date` from the provided parts, using the specified timezone. - -For `Local`, this uses `new Date(...)`. - -For `UTC`, this uses `new Date(Date.UTC(...))`. --} -fromParts : Timezone -> Parts -> Date - -{-| Break a `Date` up into its parts, using the specified timezone. - -For `Local`, this uses `getFullYear()`, `getMonth()`, etc. - -For `UTC`, this uses `getUTCFullYear()`, `getUTCMonth()`, etc. --} -toParts : Timezone -> Date -> Parts - -{-| Get the day of the week corresopnding to a `Date`. - -This is handled separately from `Parts` because it is not symmetrical -- -it makes no sense for there to be a constructor based on this. --} -dayOfWeek : Timezone -> Date -> Date.Day - -{-| Converts from Javascript's 0-based months (where January = 0) to`Date.Month`. -} -toMonth : Int -> Date.Month - -{-| Converts from `Date.Month` to Javascript's 0-based months (where January = 0). -} -fromMonth : Date.Month -> Int - -{-| Converts from Javascript's 0-based days (where Sunday = 0) to`Date.Day`. -} -toDay : Int -> Date.Day - -{-| Converts from `Date.Day` to Javascript's 0-based days (where Sunday = 0). -} -fromDay : Date.Day -> Int - -{- --------------- - Date arithmetic - --------------- -} - -{-| Offset the `Date` by the supplied `Time` (i.e. positive values offset into -the future, and negative values into the past). - -You can use `day`, `week`, `Time.minute`, etc. to scale. However, that won't -always do what you actually want, since the values are treated as durations, -rather than human-oriented intervals. For instance, `offsetTime (365 * day) -date` will advance the date by 365 days. However, if a leap year is involved, -the resulting date might be a different day of the year. If you actually want -the same day in the next year, then use `offsetYear` instead. --} -offsetTime : Time -> Date -> Date - -{-| Offset the `Date` by the specified number of years (forward or backward), -using Javascript's `setFullYear()` and `getFullYear()` (or `getUTCFullYear()` -and `setUTCFullYear`). - -Leap years are handled by the underlying Javascript APIs as follows: - -* If the supplied date is February 29, and the target year has no February 29, - then you'll end up with March 1 in the target year. (Arguably, you might - prefer February 28, but I'm not sure there is a clearly correct answer). - -* If the supplied date is February 29, and the target year also has a February - 29, then you'll end up with February 29. - -* The year is interpreted in terms of either the local timezone or UTC, according - to what you specify. I think the only case in which this could make a - difference is in determining whether it is February 29. - -* If the offset "crosses" a leap day, then you'll end up with the "same" day in - the target year ... for instance, `offsetYear 1` will sometimes move 365 - days and sometimes 366 days, depending on whether a leap year is involved. - -A more sophisticated module might deal with these cases a little differently. --} -offsetYear : Timezone -> Int -> Date -> Date - -{-| Offset the `Date` by the specified number of months (forward or backward), -using Javascript's `setMonth()` and `getMonth()` (or `setUTCMonth()` and -`getUTCMonth()`). - -Here are a few notes about the underlying Javascript implementation: - -* Overflow and underflow basically do the right thing. That is, if you end up - with negative numbers, the year is decremented, and if you end up with - numbers past 11, the year is incremented. (Remember that Javascript months - are 0-based). And, in either case, the month is set to something between - 0 and 11. - -* Dates at the beginning of the month are handled as you might expect. For - instance, adding 1 month to January 1 produces February 1, and adding 1 month - to February 1 produces March 1. Thus, the actual number of days added can vary, - depending on the length of the month. - -* However, dates at the end of the month are handled in a way that could seem - odd. For instance, adding 1 month to August 31 produces October 1 ... were - you expecting September 30? That would probably be more useful, and a more - sophisticated library might arrange for that. - -* Note that the date is interpreted according to either the local timezone or UTC, - as you specify. In some cases, that will affect whether the date is - considered to be the last day of the month, or the first day of the next - month, which will in turn affect whether the "end of month" anomaly is - triggered. --} -offsetMonth : Timezone -> Int -> Date -> Date - -{- --------------------------- - Some additional time scales - --------------------------- -} - -{-| A convenience for arithmetic, analogous to `Time.hour`, `Time.minute`, etc. -} -day : Time - -{-| A convenience for arithmetic, analogous to `Time.inHours`, `Time.inMinutes`, etc. -} -inDays : Time -> Float - -{-| A convenience for arithmetic, analogous to `Time.hour`, `Time.minute`, etc. -} -week : Time - -{-| A convenience for arithmetic, analogous to `Time.inHours`, `Time.inMinutes`, etc. -} -inWeeks : Time -> Float - -{- ------------------ - String conversions - ------------------ -} - -{-| The browser's `toDateString()` -} -dateString : Date -> String - -{-| The browser's `toTimeString()` -} -timeString : Date -> String - -{-| The browser's `toISOString()` -} -isoString : Date -> String - -{-| The browser's `toUTCString()` -} -utcString : Date -> String -``` - -***See also*** - -**`new Date(String)`** - -        -Use `Date.fromString` from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`new Date(Number)`** - -        -Use `Date.fromTime` from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`getDate()`** - -        -Use `Date.day` from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest), -or `toParts Local >> .day` - -**`getDay()`** - -        -Use `Date.dayOfWeek` from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest), -or `dayOfWeek Local` - -**`getFullYear()`** - -        -Use `Date.year` from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest), -or `toParts Local >> .year` - -**`getHours()`** - -        -Use `Date.hour` from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest), -or `toParts Local >> .hour` - -**`getMilliseconds()`** - -        -Use `Date.millisecond` from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest), -or `toParts Local >> .millisecond` - -**`getMinutes()`** - -        -Use `Date.minute` from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest), -or `toParts Local >> .minute` - -**`getMonth()`** - -        -Use `Date.month` from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest), -or `toParts Local >> .month` - -**`getSeconds()`** - -        -Use `Date.second` from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest), -or `toParts Local >> .second` - -**`getTime()`** - -        -Use `Date.toTime` from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`getUTCDate()`, `getUTCFullYear()`, `getUTCHours()`, `getUTCMilliseconds()`, -`getUTCMinutes()`, `getUTCMonth()`, `getUTCSeconds()`** - -        -Use `toParts UTC`, and then pick out whichever things you need from the -resulting `Parts`. - -**`getUTCDay()`** - -        -Use `dayOfWeek UTC` - -**`parse()`** - -        -Use `Date.fromString` from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`setDate()`, `setDay()`, `setFullYear()`, `setHours()`, `setMilliseconds()`, -`setMinutes()`, `setMonth()`, `setSeconds()`** - -        -What I would suggest is - -* Use `toParts Local` -* Update whatever fields you with to update -* Use `fromParts Local` to create a new `Date` - -Alternatively, in some scenarios you could use `offsetYear Local`, `offsetMonth Local` or `offsetTime`. - -**`setUTCDate()`, `setUTCDay()`, `setUTCFullYear()`, `setUTCHours()`, `setUTCMilliseconds()`, -`setUTCMinutes()`, `setUTCMonth()`, `setUTCSeconds()`** - -        -What I would suggest is - -* Use `toParts UTC` -* Update whatever fields you with to update -* Use `fromParts UTC` to create a new `Date` - -Alternatively, in some scenarios you could use `offsetYear UTC`, `offsetMonth UTC` or `offsetTime`. - -**`toLocaleString()`, `toLocaleDateString()`, `toLocaleTimeString()`** - -        -These aren't supported by Safari, so I've left them out for the moment. - - ------------- - -### WebAPI.Document - -See Mozilla documentation for the -[`Document` object](https://developer.mozilla.org/en-US/docs/Web/API/Document). - -Since the browser's `document` object has so many facilities attached, I've split some of -them up into individual modules -- see below for the cross-references. - -TODO: Finish going through the `document` API. - -```elm -module WebAPI.Document where - -{-| Possible values for the browser's `document.readyState` -} -type ReadyState - = Loading - | Interactive - | Complete - -{-| A `Signal` of changes to the browser's `document.readyState` -} -readyState : Signal ReadyState - -{-| A task which, when executed, succeeds with the value of the browser's -`document.readyState`. --} -getReadyState : Task x ReadyState - -{-| A task which, when executed, succeeds with the value of `document.title`. -} -getTitle : Task x String - -{-| A task which, when executed, sets the value of `document.title` to the -supplied `String`. --} -setTitle : String -> Task x () -``` - -***See also*** - -**`cookie`** - -        -See [WebAPI.Cookie](#webapicookie) - - ----------- - -### WebAPI.Intl - -TODO. Note that Safari doesn't support this, so it would need a -[polyfill](https://www.npmjs.com/package/intl). - -See [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl). - - ----------- - -### WebAPI.JSON - -Generally speaking, JSON is handled by the -[`Json.Decode`](http://package.elm-lang.org/packages/elm-lang/core/2.1.0/Json-Decode) and -[`Json.Encode`](http://package.elm-lang.org/packages/elm-lang/core/2.1.0/Json-Encode) -modules in elm-lang/core. - -TODO: Check if anything is missing. - -See [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON). - - ----------- - -### WebAPI.Location - -See the [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/API/Location). - -Note that there is a `Signal`-oriented library for location-related things at -[TheSeamau5/elm-history](http://package.elm-lang.org/packages/TheSeamau5/elm-history/latest). - -```elm -module WebAPI.Location where - -{-| The parts of a location object. Note `port'`, since `port` is a reserved word. -} -type alias Location = - { href: String - , protocol: String - , host: String - , hostname: String - , port': String - , pathname: String - , search: String - , hash: String - , origin: String - } - -{-| The browser's `window.location` object. -} -location : Task x Location - -{-| Reloads the page from the current URL. -} -reload : Source -> Task String () - -{-| Whether to force `reload` to use the server, or allow the cache. -} -type Source - = ForceServer - | AllowCache -``` - -***See also*** - -**`assign`** - -        -Use `setPath` from -[TheSeamau5/elm-history](http://package.elm-lang.org/packages/TheSeamau5/elm-history/latest). - -**`replace`** - -        -Use `replacePath` from -[TheSeamau5/elm-history](http://package.elm-lang.org/packages/TheSeamau5/elm-history/latest). - - ----------- - -### WebAPI.Math - -See the Mozilla documentation for the -[Math object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math). -Note that things marked "experimental" there have been omitted here. - -```elm -module WebAPI.Math where - -{-| Returns E to the power of x, where x is the argument, and E is Euler's -constant (2.718…), the base of the natural logarithm. --} -exp : number -> Float - -{-| Natural logarithm of 2, approximately 0.693. -} -ln2 : Float - -{-| Natural logarithm of 10, approximately 2.303. -} -ln10 : Float - -{-| Returns the natural logarithm (log e, also ln) of a number. -} -log : number -> Float - -{-| Base 2 logarithm of E, approximately 1.443. -} -log2e : Float - -{-| Base 10 logarithm of E, approximately 0.434. -} -log10e : Float - -{-| Returns a pseudo-random number between 0 and 1. - -Note that there is a more sophisticated implementation of `Random` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). -However, this may sometimes be useful if you're in a `Task` context anyway. --} -random : Task x Float - -{-| Square root of 1/2; equivalently, 1 over the square root of 2, -approximately 0.707. --} -sqrt1_2 : Float - -{-| Square root of 2, approximately 1.414. -} -sqrt2 : Float -``` - -***See also*** - -**`abs`** - -        -Use `Basics.abs` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`acos`** - -        -Use `Basics.acos` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`asin`** - -        -Use `Basics.asin` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`atan`** - -        -Use `Basics.atan` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`atan2`** - -        -Use `Basics.atan2` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`ceil`** - -        -Use `Basics.ceiling` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`cos`** - -        -Use `Basics.cos` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`E`** - -        -Use `Basics.e` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`floor`** - -        -Use `Basics.floor` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`max`** - -        -Use `List.maximum` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`min`** - -        -Use `List.minimum` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`PI`** - -        -Use `Basics.pi` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`pow`** - -        -Use `Basics.(^)` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`round`** - -        -Use `Basics.round` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`sin`** - -        -Use `Basics.sin` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`sqrt`** - -        -Use `Basics.sqrt` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`tan`** - -        -Use `Basics.tan` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - - ----------- - -### WebAPI.Number - -See the Mozilla documentation for the -[Number object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number). -Note that things marked "experimental" there have been omitted here. - -```elm -module WebAPI.Number where - -{-| The largest positive representable number. -} -maxValue : Float - -{-| The smallest positive representable number - that is, the positive number -closest to zero (without actually being zero). --} -minValue : Float - -{-| Special "not a number" value. -} -nan : Float - -{-| Special value representing negative infinity; returned on overflow. -} -negativeInfinity : Float - -{-| Special value representing infinity; returned on overflow. -} -positiveInfinity : Float - -{-| A string representing the provided number in exponential notation. -} -toExponential : number -> String - -{-| Either a string representing the second parameter in exponential notation, -with the requested number of digits after the decimal point (first parameter), -or an error. An error should not occur if the requested number of digits is -between 0 and 20. --} -toExponentialDigits : Int -> number -> Result String String - -{-| A string representing the second parameter in exponential notation, -with the requested number of digits after the decimal point (first parameter). -The number of digits will be limited to between 0 and 20. --} -safeExponentialDigits : Int -> number -> String - -{-| A string representing the provided number in fixed-point notation. -} -toFixed : number -> String - -{-| Either a string representing the second parameter in fixed-point notation, -with the requested number of digits after the decimal point (first parameter), -or an error. An error should not occur if the requested number of digits is -between 0 and 20. - -Note that Firefox returns `Ok 0` rather than an `Error` for a negative number -of requested digits. --} -toFixedDigits : Int -> number -> Result String String - -{-| A string representing the second parameter in fixed-point notation, -with the requested number of digits after the decimal point (first parameter). -The number of digits will be limited to between 0 and 20. --} -safeFixedDigits : Int -> number -> String - -{-| Either a string representing the second parameter in fixed-point or -exponential notation, rounded to the requested number of significant digits -(first parameter), or an error. An error should not occur if the requested -number of digits is between 0 and 20. --} -toPrecisionDigits : Int -> number -> Result String String - -{-| A string representing the second parameter in fixed-point or exponential -notation, with the requested number of significant digits (first parameter). -The number of digits will be limited to between 1 and 20. --} -safePrecisionDigits : Int -> number -> String - -{-| Either a string representing the second parameter using the requested base -(first parameter), or an error. An error should not occur if the requested base -is between 2 and 36. --} -toStringUsingBase : Int -> number -> Result String String - -{-| A string representing the second parameter, using the requested base -(first parameter). The requested base will be limited to between 2 and 36. --} -safeStringUsingBase : Int -> number -> String - -``` - -***See also*** - -**`toLocaleString`** - -        -Not implemented for the moment, since localization requires some thought. - - ----------- - -### WebAPI.RegExp - -Generally speaking, regular expressions are handled by the -[`Regex` module](http://package.elm-lang.org/packages/elm-lang/core/2.1.0/Regex) -in elm-lang/core. - -TODO: Check if anything is missing. - -See [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp). - - ----------- - -### WebAPI.Screen - -See [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/API/Screen). - -```elm -module WebAPI.Screen where - -type alias Screen = - { availTop: Int - , availLeft: Int - , availHeight: Int - , availWidth: Int - , colorDepth: Int - , pixelDepth: Int - , height: Int - , width: Int - } - -{-| The browser's `window.screen` object. - -This is a `Task` because, in multi-monitor setups, the result depends on which -screen the browser window is in. So, it is not necessarily a constant. --} -screen : Task x Screen - -{-| A tuple of `(window.screenX, window.screenY)`. - -The first value is the horizontal distance, in CSS pixels, of the left border -of the user's browser from the left side of the screen. - -The second value is the vertical distance, in CSS pixels, of the top border of -the user's browser from the top edge of the screen. --} -screenXY : Task x (Int, Int) -``` - - ----------- - -### WebAPI.Storage - -See [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/API/Storage), -and the [WhatWG documentation](https://html.spec.whatwg.org/multipage/webstorage.html). - -There is a more sophisticated `Storage` module available at -[TheSeamau5/elm-storage](https://github.com/TheSeamau5/elm-storage). - -Note that we're essentially assuming that `window.localStorage` and -`window.sessionStorage` are, in fact, available. We could account for them -possibly not being available by using a `Maybe` type. - -```elm -module WebAPI.Storage where - -{- ----------------- - Roles for Strings - ----------------- -} - -{-| A key. -} -type alias Key = String - -{-| An old value. -} -type alias OldValue = String - -{-| A new value. -} -type alias NewValue = String - -{-| A value. -} -type alias Value = String - -{- ------------- - Storage Areas - ------------- -} - -{-| Represents the `localStorage` and `sessionStorage` areas. -} -type Storage - = Local - | Session - -{-| The browser's `localStorage` area. -} -local : Storage - -{-| The browser's `sessionStorage` area. -} -session : Storage - -{- ----- - Tasks - ----- -} - -{-| A task which, when executed, determines the number of items stored in the -storage area. --} -length : Storage -> Task x Int - -{-| A task which, when executed, determines the name of the key at the given -index (zero-based). --} -key : Storage -> Int -> Task x (Maybe Key) - -{-| A task which, when executed, gets the value at the given key. -} -get : Storage -> Key -> Task x (Maybe Value) - -{-| A task which, when executed, sets the value at the given key, or fails with -an error message. --} -set : Storage -> Key -> NewValue -> Task String () - -{-| A task which, when executed, removes the item with the given key. -} -remove : Storage -> Key -> Task x () - -{-| A task which, when executed, removes all items. -} -clear : Storage -> Task x () - -{- ------ - Events - ------ -} - -{-| A storage event. -} -type alias Event = - { area : Storage - , url : String - , change : Change - } - -{-| A change to a storage area. -} -type Change - = Add Key NewValue - | Remove Key OldValue - | Modify Key OldValue NewValue - | Clear - -{-| A signal of storage events. - -Note that a storage event is not fired within the same document that made a -storage change. Thus, you will only receive events for localStorage changes -that occur in a **separate** tab or window. - -This behaviour reflects how Javascript does things ... let me know if you'd -prefer to have *all* localStorage events go through this `Signal` -- it could -be arranged. - -At least in Safari, sessionStorage is even more restrictive than localStorage --- it is isolated per-tab, so you will only get events on sessionStorage if -using iframes. - -Note that this signal emits `Maybe Event` (rather than `Event`) because Elm -signals must have an initial value -- and there is no natural initial value for -an `Event` unless we wrap it in a `Maybe`. So, you'll often want to use -`Signal.filterMap` when you're integrating this into your own signal of -actions. --} -events : Signal (Maybe Event) -``` - - ----------- - -### WebAPI.String - -Generally speaking, strings are dealt with by the -[`String` module](http://package.elm-lang.org/packages/elm-lang/core/2.1.0/String) -in elm-lang/core. - -TODO: Check if anything is missing. - -See [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String). - - ----------- - -### WebAPI.Window - -See Mozilla documentation for the -[`Window` object](https://developer.mozilla.org/en-US/docs/Web/API/Window), -and for -[function properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects#Function_properties). - -Since the browser's `window` object has so many facilities attached, I've typically split -them up into individual modules -- see below for the cross-references. - -TODO: Finish going through the `window` API. - -```elm -module WebAPI.Window where - -{-| The browser's `window.alert()` function. -} -alert : String -> Task x () - -{-| The browser's `window.confirm()` function. - -The task will succeed if the user confirms, and fail if the user cancels. --} -confirm : String -> Task () () - -{-| The browser's `window.prompt()` function. - -The first parameter is a message, and the second parameter is a default -response. - -The task will succeed with the user's response, or fail if the user cancels or -enters blank text. --} -prompt : String -> String -> Task () String -``` - -***See also*** - -**`cancelAnimationFrame`** - -        -Use [`WebAPI.AnimationFrame`](#webapianimationframe) - -**`decodeURI`** - -        -Not implemented, since you will generally want `decodeURIComponent` instead. - -**`decodeURIComponent`** - -        -Use `Http.uriDecode` from -[evancz/elm-http](http://package.elm-lang.org/packages/evancz/elm-http/latest). - -**`encodeURI`** - -        -Not implemented, since you will generally want `encodeURIComponent` instead. - -**`encodeURIComponent`** - -        -Use `Http.uriEncode` from -[evancz/elm-http](http://package.elm-lang.org/packages/evancz/elm-http/latest). - -**`eval`** - -        -Not implemented, since it is an abomination. - -**`history`** - -        -See [TheSeamau5/elm-history](http://package.elm-lang.org/packages/TheSeamau5/elm-history/latest) - -**`innerHeight`, `innerWidth`** - -        -See `Window.dimensions` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`isFinite`** - -        -Use `not Basics.isInfinite` from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest) -(i.e. with the sense reversed). - -**`isNan`** - -        -Use `Basics.isNan` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`localStorage`** - -        -Use [`WebAPI.Storage.localStorage`](#webapistorage) - -**`location`** - -        -For a `Signal`-oriented approach to things you might do with `window.location`, see -[TheSeamau5/elm-history](http://package.elm-lang.org/packages/TheSeamau5/elm-history/latest). -For some additional `Task`-oriented approaches, see -[`WebAPI.Location`](#webapilocation). - -**`parseFloat`** - -        -Use `String.toFloat` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`parseInt`** - -        -Use `String.toInt` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -**`requestAnimationFrame`** - -        -Use [`WebAPI.AnimationFrame`](#webapianimationframe) - -**`setInterval`** - -        -Consider `Time.fps` (or its variants) from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest), -or `AnimationFrame.frame` (or its variants) from -[jwmerrill/elm-animation-frame](http://package.elm-lang.org/packages/jwmerrill/elm-animation-frame/latest), -or its variants, or `Effects.tick` from -[evancz/elm-effects](http://package.elm-lang.org/packages/evancz/elm-effects/latest). - -**`setTimeout`** - -        -Use `Task.sleep` from -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest), -and then apply an `andThen` to do something after sleeping. - -**`scroll`, `scrollBy`, `scrollTo`, `scrollX`, `scrollY`** - -        -There are a few puzzles about how to best adapt these for Elm, so I'm not sure -a simplistic approach would be best -- a module that thought through scrolling -in an Elm context would probably be better. - -**`screen`** - -        -See [`WebAPI.Screen.screen`](#webapiscreen). - -**`screenX`, `screenY`** - -        -See [`WebAPI.Screen.screenXY`](#webapiscreen). - -**`sessionStorage`** - -        -See [`WebAPI.Storage.sessionStorage`](#webapistorage) - - diff --git a/src/vendor/elm-web-api/elm-package.json b/src/vendor/elm-web-api/elm-package.json deleted file mode 100644 index 5368efb..0000000 --- a/src/vendor/elm-web-api/elm-package.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "version": "1.0.0", - "summary": "Expose Web APIs provided by the browser's Javascript runtime", - "repository": "https://github.com/rgrempel/elm-web-api.git", - "license": "BSD3", - "source-directories": [ - "src" - ], - "exposed-modules": [ - "WebAPI.AnimationFrame", - "WebAPI.Cookie", - "WebAPI.Document", - "WebAPI.Date", - "WebAPI.Location", - "WebAPI.Math", - "WebAPI.Number", - "WebAPI.Screen", - "WebAPI.Storage", - "WebAPI.Window" - ], - "native-modules": true, - "dependencies": { - "elm-lang/core": "2.0.0 <= v < 4.0.0" - }, - "elm-version": "0.15.0 <= v < 0.17.0" -} diff --git a/src/vendor/elm-web-api/examples/elm-package.json b/src/vendor/elm-web-api/examples/elm-package.json deleted file mode 100644 index 825c6bc..0000000 --- a/src/vendor/elm-web-api/examples/elm-package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": "1.0.0", - "summary": "Examples for the WebAPI module", - "repository": "https://github.com/rgrempel/elm-web-api.git", - "license": "BSD3", - "source-directories": [ - "src", - "../src" - ], - "exposed-modules": [], - "native-modules": true, - "dependencies": { - "elm-lang/core": "2.0.0 <= v < 4.0.0", - "evancz/elm-effects": "2.0.0 <= v < 3.0.0", - "evancz/elm-html": "4.0.1 <= v < 5.0.0", - "evancz/start-app": "2.0.1 <= v < 3.0.0" - }, - "elm-version": "0.15.0 <= v < 0.17.0" -} diff --git a/src/vendor/elm-web-api/examples/package.json b/src/vendor/elm-web-api/examples/package.json deleted file mode 100644 index d499401..0000000 --- a/src/vendor/elm-web-api/examples/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "elm-web-api", - "version": "1.0.0", - "description": "Tests for elm-web-api", - "main": "elm.js", - "dependencies": { - "elm": "^2.0.0" - }, - "devDependencies": {}, - "scripts": { - "test": "elm-make src/WindowExample.elm --output elm.html && open elm.html;", - "package": "elm-package install;" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/rgrempel/elm-web-api.git" - }, - "author": "Ryan Rempel ", - "license": "MIT", - "bugs": { - "url": "https://github.com/rgrempel/elm-web-api/issues" - }, - "homepage": "https://github.com/rgrempel/elm-web-api#readme" -} diff --git a/src/vendor/elm-web-api/examples/src/LocationExample.elm b/src/vendor/elm-web-api/examples/src/LocationExample.elm deleted file mode 100644 index 48aa740..0000000 --- a/src/vendor/elm-web-api/examples/src/LocationExample.elm +++ /dev/null @@ -1,78 +0,0 @@ -module LocationExample where - -import Effects exposing (Effects, Never) -import StartApp exposing (App) -import Task exposing (Task, toResult) -import Html exposing (Html, h4, div, text, button, input) -import Html.Attributes exposing (id) -import Html.Events exposing (onClick) -import Signal exposing (Signal, Address) - -import WebAPI.Location exposing (reload, Source(..)) - - -app : App Model -app = - StartApp.start - { init = init - , update = update - , view = view - , inputs = [] - } - - -main : Signal Html -main = app.html - - -port tasks : Signal (Task.Task Never ()) -port tasks = app.tasks - - -type alias Model = String - - -init : (Model, Effects Action) -init = ("Initial state", Effects.none) - - -type Action - = Reload Source - | HandleReload (Result String ()) - - -update : Action -> Model -> (Model, Effects Action) -update action model = - case action of - HandleReload result -> - ( "Reloaded (but if this stays, then that's an error)" - , Effects.none - ) - - Reload source -> - ( "About to reload" - , reload source |> - toResult |> - Task.map HandleReload |> - Effects.task - ) - - -view : Address Action -> Model -> Html -view address model = - div [] - [ button - [ id "reload-force-button" - , onClick address (Reload ForceServer) - ] - [ text "WebAPI.Location.reload ForceServer" ] - , button - [ id "reload-cache-button" - , onClick address (Reload AllowCache) - ] - [ text "WebAPI.Location.reload AllowCache" ] - , h4 [] [ text "Message" ] - , div [ id "message" ] [ text model ] - , input [ id "input" ] [] - ] - diff --git a/src/vendor/elm-web-api/examples/src/StorageExample.elm b/src/vendor/elm-web-api/examples/src/StorageExample.elm deleted file mode 100644 index 990e2e0..0000000 --- a/src/vendor/elm-web-api/examples/src/StorageExample.elm +++ /dev/null @@ -1,669 +0,0 @@ -module StorageExample where - -import Effects exposing (Effects, Never) -import StartApp exposing (App) -import Task exposing (Task, toResult) -import Html exposing (Html, h4, div, text, button, input, select, option, label, table, tr, td) -import Html.Attributes exposing (id, style, value, type', selected, for, colspan) -import Html.Events exposing (onClick, on, targetValue) -import Signal exposing (Signal, Address) -import String - -import WebAPI.Storage as Storage exposing (Storage) - - -{-| StartApp stuff. -} -app : App Model -app = - StartApp.start - { init = init - , update = update - , view = view - , inputs = [ events ] - } - - -main : Signal Html -main = app.html - - -port tasks : Signal (Task.Task Never ()) -port tasks = app.tasks - - -{-| Our model is: - -* a list of things that have happened (that we want to remember) -* an action which we're currently editing in the UI and might "send" at some point -* the last operation of each type which we've edited, so we can remember parameters - as we switch operation types in the UI --} -type alias Model = - { log : List Log - , action : StorageAction - , lastOperation : LastOperation - } - - -{-| We log: - -* actions that we send to the storage objects -* the responses we get -* events received from other open windows, tabs, etc. --} -type Log - = LogAction StorageAction - | LogResponse StorageResponse - | LogEvent Storage.Event - - - -{-| Our initial model. -} -init : (Model, Effects Action) -init = - ( { log = [] - , action = - { target = Storage.local - , operation = DoLength - } - , lastOperation = - { key = DoKey 0 - , get = DoGet "" - , set = DoSet "" "" - , remove = DoRemove "" - } - } - , Effects.none - ) - - -{-| I've modularized the Action type, even though it's all in one file. -} -type Action - = SetAction SetAction - | DoStorageAction StorageAction - | StorageResponse StorageResponse - | StorageEvent Storage.Event - | NoOp - - -{-| Signal of events from other windows or tabs. -} -events : Signal Action -events = Signal.filterMap (Maybe.map StorageEvent) NoOp Storage.events - - -{-| We dispatch to more specific functions ... often they would be in separate -files, but I've put them all here. --} -update : Action -> Model -> (Model, Effects Action) -update action model = - case action of - SetAction subaction -> - ( updateSetAction subaction model - , Effects.none - ) - - DoStorageAction subaction -> - updateStorageAction subaction model - - StorageResponse subaction -> - updateStorageResponse subaction model - - StorageEvent subaction -> - updateStorageEvent subaction model - - NoOp -> - (model, Effects.none) - - -{-| These are actions from the UI ... thus, the strings. In principle, I could -use Json.Decode in the UI to supply typed data, but this works too. --} -type SetAction - = SetStorage String - | SetOperationType String - | SetKeyIndex String - | SetGetKey String - | SetSetKey String - | SetSetValue String - | SetRemoveKey String - - -{-| An action to send to a storage object. We edit this in the UI, and we -eventually send it in `Task` form. --} -type alias StorageAction = - { target : Storage - , operation : StorageOperation - } - - -{-| An operation to perform on a storage object. -} -type StorageOperation - = DoLength - | DoKey Int - | DoGet Storage.Key - | DoSet Storage.Key Storage.NewValue - | DoRemove Storage.Key - | DoClear - - -{-| Actually perform the StorageAction, and log it. -} -updateStorageAction : StorageAction -> Model -> (Model, Effects Action) -updateStorageAction action model = - ( { model | log = LogAction action :: model.log } - , Effects.task (Task.map StorageResponse (storageAction2task action)) - ) - - -{- When we get a response, we just log it. -} -updateStorageResponse : StorageResponse -> Model -> (Model, Effects Action) -updateStorageResponse action model = - ( { model | log = LogResponse action :: model.log } - , Effects.none - ) - - -{- As with an event. -} -updateStorageEvent : Storage.Event -> Model -> (Model, Effects Action) -updateStorageEvent action model = - ( { model | log = LogEvent action :: model.log } - , Effects.none - ) - - -{-| We want to remember the parameters for the last operation we edited -of a given type, so that as we switch operation types the parameters can -be filled in. --} -type alias LastOperation = - { key : StorageOperation - , get : StorageOperation - , set : StorageOperation - , remove : StorageOperation - } - - -{-| This is for actions coming from the UI. -} -updateSetAction : SetAction -> Model -> Model -updateSetAction action model = - let - currentAction = - model.action - - lastOperation = - model.lastOperation - - in - case action of - SetStorage string -> - let - storage = - if string == phrases.localStorage - then Storage.local - - else if string == phrases.sessionStorage - then Storage.session - - else - currentAction.target - - newAction = - { currentAction | target = storage } - - in - { model | action = newAction } - - SetOperationType string -> - let - operation = - if string == phrases.length - then DoLength - - else if string == phrases.key - then - if doingKey currentAction.operation - then currentAction.operation - else lastOperation.key - - else if string == phrases.get - then - if doingGet currentAction.operation - then currentAction.operation - else lastOperation.get - - else if string == phrases.set - then - if doingSet currentAction.operation - then currentAction.operation - else lastOperation.set - - else if string == phrases.remove - then - if doingRemove currentAction.operation - then currentAction.operation - else lastOperation.remove - - else if string == phrases.clear - then - DoClear - - else - currentAction.operation - - newAction = - { currentAction | operation = operation } - - in - { model | action = newAction } - - SetKeyIndex string -> - let - operation = - case String.toInt string of - Ok int -> - DoKey int - - Err _ -> - lastOperation.key - - newAction = - { currentAction | operation = operation } - - newLastOperation = - { lastOperation | key = operation } - - in - { model - | action = newAction - , lastOperation = newLastOperation - } - - SetGetKey string -> - let - operation = - DoGet string - - newAction = - { currentAction | operation = operation } - - newLastOperation = - { lastOperation | get = operation } - - in - { model - | action = newAction - , lastOperation = newLastOperation - } - - SetRemoveKey string -> - let - operation = - DoRemove string - - newAction = - { currentAction | operation = operation } - - newLastOperation = - { lastOperation | remove = operation } - - in - { model - | action = newAction - , lastOperation = newLastOperation - } - - SetSetKey string -> - let - operation = - case currentAction.operation of - DoSet key value -> - DoSet string value - - _ -> - DoSet string "" - - newAction = - { currentAction | operation = operation } - - newLastOperation = - { lastOperation | set = operation } - - in - { model - | action = newAction - , lastOperation = newLastOperation - } - - SetSetValue string -> - let - operation = - case currentAction.operation of - DoSet key value -> - DoSet key string - - _ -> - DoSet "" string - - newAction = - { currentAction | operation = operation } - - newLastOperation = - { lastOperation | set = operation } - - in - { model - | action = newAction - , lastOperation = newLastOperation - } - - -{-| A bunch of little convenience functions so that I don't have to write out -the pattern matches too often. --} -doingLength : StorageOperation -> Bool -doingLength op = - case op of - DoLength -> True - _ -> False - - -doingKey : StorageOperation -> Bool -doingKey op = - case op of - DoKey _ -> True - _ -> False - - -doingGet : StorageOperation -> Bool -doingGet op = - case op of - DoGet _ -> True - _ -> False - - -doingSet : StorageOperation -> Bool -doingSet op = - case op of - DoSet _ _ -> True - _ -> False - - -doingRemove : StorageOperation -> Bool -doingRemove op = - case op of - DoRemove _ -> True - _ -> False - - -doingClear : StorageOperation -> Bool -doingClear op = - case op of - DoClear -> True - _ -> False - - -{- An Action type that wraps the various responses possible from -the storage tasks. --} -type StorageResponse - = HandleLength Int - | HandleKey (Maybe Storage.Key) - | HandleGet (Maybe Storage.Value) - | HandleSet (Result String ()) - | HandleRemove () - | HandleClear () - - -{-| A convenience function that takes a `StorageAction` and turns it into -a task that provides a `StorageResopnse`. --} -storageAction2task : StorageAction -> Task x StorageResponse -storageAction2task action = - case action.operation of - DoLength -> - Task.map HandleLength <| - Storage.length action.target - - DoKey int -> - Task.map HandleKey <| - Storage.key action.target int - - DoGet key -> - Task.map HandleGet <| - Storage.get action.target key - - DoSet key value -> - Task.map HandleSet <| - Task.toResult <| - Storage.set action.target key value - - DoRemove key -> - Task.map HandleRemove <| - Storage.remove action.target key - - DoClear -> - Task.map HandleClear <| - Storage.clear action.target - - -{-| It's always nice to keep the actual text of the UI in one place. It also -helps for matching the and etc. so that you can edit the -StorageAction. Also, there is a button to actually perform the action. --} -editStorageAction : Address Action -> StorageAction -> Html -editStorageAction address model = - let - notify func = - Signal.message (Signal.forwardTo address SetAction) << func - - makeCell = - td - [ style - [ ("border", "1px dotted black") - , ("padding", "3px") - ] - ] - - makeLabel id string = - td - [ style - [ ("text-align", "right") - , ("border", "1px dotted black") - , ("padding", "3px") - ] - ] - [ label - [ for id ] - [ text string ] - ] - - objectSelector = - tr [] - [ makeLabel "select-area" phrases.localOrSession - , makeCell - [ select - [ id "select-area" - , on "change" targetValue (notify SetStorage) - ] - [ option - [ selected (model.target == Storage.local) ] - [ text phrases.localStorage ] - , option - [ selected (model.target == Storage.session) ] - [ text phrases.sessionStorage ] - ] - ] - ] - - operationSelector = - tr [] - [ makeLabel "select-operation" phrases.operation - , makeCell - [ select - [ id "select-operation" - , on "change" targetValue (notify SetOperationType) - ] - [ option - [ selected (doingLength model.operation) ] - [ text phrases.length ] - , option - [ selected (doingKey model.operation) ] - [ text phrases.key ] - , option - [ selected (doingGet model.operation) ] - [ text phrases.get ] - , option - [ selected (doingSet model.operation) ] - [ text phrases.set ] - , option - [ selected (doingRemove model.operation) ] - [ text phrases.remove ] - , option - [ selected (doingClear model.operation) ] - [ text phrases.clear ] - ] - ] - ] - - parameters = - case model.operation of - -- Nothing needed for DoLength - DoLength -> - [] - - DoKey index -> - [ tr [] - [ makeLabel "select-index" phrases.indexLabel - , makeCell - [ input - [ type' "number" - , id "select-index" - , value (toString index) - , on "input" targetValue (notify SetKeyIndex) - ] [] - ] - ] - ] - - DoGet key -> - [ tr [] - [ makeLabel "select-get-key" phrases.keyLabel - , makeCell - [ input - [ value key - , id "select-get-key" - , on "input" targetValue (notify SetGetKey) - ] [] - ] - ] - ] - - DoSet key val -> - [ tr [] - [ makeLabel "select-set-key" phrases.keyLabel - , makeCell - [ input - [ value key - , id "select-set-key" - , on "input" targetValue (notify SetSetKey) - ] [] - ] - ] - , tr [] - [ makeLabel "select-set-value" phrases.valueLabel - , makeCell - [ input - [ value val - , id "select-set-value" - , on "input" targetValue (notify SetSetValue) - ] [] - ] - ] - ] - - DoRemove key -> - [ tr [] - [ makeLabel "select-remove-key" phrases.keyLabel - , makeCell - [ input - [ value key - , id "select-remove-key" - , on "input" targetValue (notify SetRemoveKey) - ] [] - ] - ] - ] - - DoClear -> - [] - - performAction = - tr [] - [ td - [ colspan 2 - , style - [ ("text-align", "center") - , ("border", "1px dotted black") - , ("padding", "3px") - ] - ] - [ button - [ id "perform-action" - , onClick address (DoStorageAction model) - ] - [ text phrases.performAction ] - ] - ] - - in - table [] <| - [ objectSelector - , operationSelector - ] - ++ - parameters - ++ - [ performAction - ] - - -viewLog : Log -> Html -viewLog log = - div - [ style [( "margin-top", "6pt")] ] - [ text <| toString log ] - - -view : Address Action -> Model -> Html -view address model = - div - [ style [ ("padding", "8px") ] - ] - [ editStorageAction address model.action - - , h4 [] [ text "Log (most recent first)" ] - , div - [ id "log" ] <| - List.map viewLog model.log - ] diff --git a/src/vendor/elm-web-api/examples/src/WindowExample.elm b/src/vendor/elm-web-api/examples/src/WindowExample.elm deleted file mode 100644 index c0dc2cb..0000000 --- a/src/vendor/elm-web-api/examples/src/WindowExample.elm +++ /dev/null @@ -1,123 +0,0 @@ -module WindowExample where - -import Effects exposing (Effects, Never) -import StartApp exposing (App) -import Task exposing (Task, toResult) -import Html exposing (Html, h4, div, text, button) -import Html.Attributes exposing (id) -import Html.Events exposing (onClick) -import Signal exposing (Signal, Address) - -import WebAPI.Window exposing (alert, confirm, prompt) - - -app : App Model -app = - StartApp.start - { init = init - , update = update - , view = view - , inputs = [] - } - - -main : Signal Html -main = app.html - - -port tasks : Signal (Task.Task Never ()) -port tasks = app.tasks - - -type alias Model = String - - -init : (Model, Effects Action) -init = ("", Effects.none) - - -type Action - = ShowAlert String - | HandleAlertResponse - | ShowConfirm String - | HandleConfirmResponse (Result () ()) - | ShowPrompt String String - | HandlePromptResponse (Result () String) - - -update : Action -> Model -> (Model, Effects Action) -update action model = - case action of - ShowAlert message -> - ( model - , alert message |> - Task.map (always HandleAlertResponse) |> - Effects.task - ) - - HandleAlertResponse -> - ( "Got alert response" - , Effects.none - ) - - ShowConfirm message -> - ( model - , confirm message |> - toResult |> - Task.map HandleConfirmResponse |> - Effects.task - ) - - HandleConfirmResponse result -> - ( case result of - Ok _ -> - "Pressed OK" - - Err _ -> - "Pressed cancel" - - , Effects.none - ) - - ShowPrompt message default -> - ( model - , prompt message default |> - toResult |> - Task.map HandlePromptResponse |> - Effects.task - ) - - HandlePromptResponse result -> - ( case result of - Ok response -> - "Got response: " ++ response - - Err _ -> - "User canceled." - - , Effects.none - ) - - -view : Address Action -> Model -> Html -view address model = - div [] - [ button - [ onClick address (ShowAlert "Hello world!") - , id "alert-button" - ] - [ text "WebAPI.Window.alert" ] - , button - [ onClick address (ShowConfirm "Do you agree?") - , id "confirm-button" - ] - [ text "WebAPI.Window.confirm" ] - , button - [ onClick address (ShowPrompt "What is your favourite colour?" "Blue") - , id "prompt-button" - ] - [ text "WebAPI.Window.prompt" ] - , h4 [] [ text "Message" ] - , div [ id "message" ] [ text model ] - ] - diff --git a/src/vendor/elm-web-api/src/Native/WebAPI/AnimationFrame.js b/src/vendor/elm-web-api/src/Native/WebAPI/AnimationFrame.js deleted file mode 100644 index dd6d006..0000000 --- a/src/vendor/elm-web-api/src/Native/WebAPI/AnimationFrame.js +++ /dev/null @@ -1,79 +0,0 @@ -Elm.Native = Elm.Native || {}; -Elm.Native.WebAPI = Elm.Native.WebAPI || {}; -Elm.Native.WebAPI.AnimationFrame = Elm.Native.WebAPI.AnimationFrame || {}; - -// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ -// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating - -// requestAnimationFrame polyfill by Erik Möller -// fixes from Paul Irish and Tino Zijdel -// list-based fallback implementation by Jonas Finnemann Jensen - -var raf = window.requestAnimationFrame; -var caf = window.cancelAnimationFrame; - -if (!raf) { - var tid = null, cbs = [], nb = 0, ts = 0; - - function animate () { - var i, clist = cbs, len = cbs.length; - tid = null; - ts = Date.now(); - cbs = []; - nb += clist.length; - - for (i = 0; i < len; i++) { - if (clist[i]) clist[i](ts); - } - } - - raf = function (cb) { - if (tid == null) { - tid = setTimeout(animate, Math.max(0, 20 + ts - Date.now())); - } - - return cbs.push(cb) + nb; - }; - - caf = function (id) { - delete cbs[id - nb - 1]; - }; -} - -Elm.Native.WebAPI.AnimationFrame.make = function (localRuntime) { - localRuntime.Native = localRuntime.Native || {}; - localRuntime.Native.WebAPI = localRuntime.Native.WebAPI || {}; - localRuntime.Native.WebAPI.AnimationFrame = localRuntime.Native.WebAPI.AnimationFrame || {}; - - if (!localRuntime.Native.WebAPI.AnimationFrame.values) { - var Task = Elm.Native.Task.make(localRuntime); - var Utils = Elm.Native.Utils.make(localRuntime); - - localRuntime.Native.WebAPI.AnimationFrame.values = { - task: Task.asyncFunction(function (callback) { - raf(function (time) { - callback(Task.succeed(time)); - }); - }), - - request: function (taskProducer) { - return Task.asyncFunction(function (callback) { - var request = raf(function (time) { - Task.perform(taskProducer(time)); - }); - - callback(Task.succeed(request)); - }); - }, - - cancel: function (request) { - return Task.asyncFunction(function (callback) { - caf(request); - callback(Task.succeed(Utils.Tuple0)); - }); - } - }; - } - - return localRuntime.Native.WebAPI.AnimationFrame.values; -}; diff --git a/src/vendor/elm-web-api/src/Native/WebAPI/Cookie.js b/src/vendor/elm-web-api/src/Native/WebAPI/Cookie.js deleted file mode 100644 index c7ff078..0000000 --- a/src/vendor/elm-web-api/src/Native/WebAPI/Cookie.js +++ /dev/null @@ -1,41 +0,0 @@ -Elm.Native = Elm.Native || {}; -Elm.Native.WebAPI = Elm.Native.WebAPI || {}; -Elm.Native.WebAPI.Cookie = Elm.Native.WebAPI.Cookie || {}; - -Elm.Native.WebAPI.Cookie.make = function (localRuntime) { - localRuntime.Native = localRuntime.Native || {}; - localRuntime.Native.WebAPI = localRuntime.Native.WebAPI || {}; - localRuntime.Native.WebAPI.Cookie = localRuntime.Native.WebAPI.Cookie || {}; - - if (!localRuntime.Native.WebAPI.Cookie.values) { - var Task = Elm.Native.Task.make(localRuntime); - var Utils = Elm.Native.Utils.make(localRuntime); - - localRuntime.Native.WebAPI.Cookie.values = { - getString: Task.asyncFunction(function (callback) { - callback(Task.succeed(document.cookie)); - }), - - setString: function (cookie) { - return Task.asyncFunction(function (callback) { - document.cookie = cookie; - callback(Task.succeed(Utils.Tuple0)); - }); - }, - - dateToUTCString: function (date) { - return date.toUTCString(); - }, - - uriEncode: function (string) { - return encodeURIComponent(string); - }, - - uriDecode: function (string) { - return decodeURIComponent(string); - } - }; - } - - return localRuntime.Native.WebAPI.Cookie.values; -}; diff --git a/src/vendor/elm-web-api/src/Native/WebAPI/Date.js b/src/vendor/elm-web-api/src/Native/WebAPI/Date.js deleted file mode 100644 index 7196e1f..0000000 --- a/src/vendor/elm-web-api/src/Native/WebAPI/Date.js +++ /dev/null @@ -1,148 +0,0 @@ -Elm.Native = Elm.Native || {}; -Elm.Native.WebAPI = Elm.Native.WebAPI || {}; -Elm.Native.WebAPI.Date = Elm.Native.WebAPI.Date || {}; - -Elm.Native.WebAPI.Date.make = function (localRuntime) { - localRuntime.Native = localRuntime.Native || {}; - localRuntime.Native.WebAPI = localRuntime.Native.WebAPI || {}; - localRuntime.Native.WebAPI.Date = localRuntime.Native.WebAPI.Date || {}; - - if (!localRuntime.Native.WebAPI.Date.values) { - var Task = Elm.Native.Task.make(localRuntime); - var Utils = Elm.Native.Utils.make(localRuntime); - - localRuntime.Native.WebAPI.Date.values = { - current : Task.asyncFunction(function (callback) { - callback( - Task.succeed( - new Date () - ) - ); - }), - - now : Task.asyncFunction(function (callback) { - callback( - Task.succeed( - Date.now() - ) - ); - }), - - fromPartsLocal : function (parts) { - return new Date( - parts.year, - parts.month, - parts.day, - parts.hour, - parts.minute, - parts.second, - parts.millisecond - ); - }, - - fromPartsUtc : function (parts) { - return new Date( - Date.UTC( - parts.year, - parts.month, - parts.day, - parts.hour, - parts.minute, - parts.second, - parts.millisecond - ) - ); - }, - - toPartsLocal : function (date) { - return { - year: date.getFullYear(), - month: date.getMonth(), - day: date.getDate(), - hour: date.getHours(), - minute: date.getMinutes(), - second: date.getSeconds(), - millisecond: date.getMilliseconds() - }; - }, - - toPartsUtc : function (date) { - return { - year: date.getUTCFullYear(), - month: date.getUTCMonth(), - day: date.getUTCDate(), - hour: date.getUTCHours(), - minute: date.getUTCMinutes(), - second: date.getUTCSeconds(), - millisecond: date.getUTCMilliseconds() - }; - }, - - timezoneOffsetInMinutes : function (date) { - return date.getTimezoneOffset(); - }, - - dayOfWeekUTC : function (date) { - return date.getUTCDay(); - }, - - offsetYearLocal : F2(function (offset, date) { - var copy = new Date(date); - copy.setFullYear(date.getFullYear() + offset); - - // For some reason, Firefox loses the milliseconds otherwise - copy.setMilliseconds(date.getMilliseconds()); - - return copy; - }), - - offsetYearUTC : F2(function (offset, date) { - var copy = new Date(date); - copy.setUTCFullYear(date.getUTCFullYear() + offset); - - // For some reason, Firefox loses the milliseconds otherwise - copy.setUTCMilliseconds(date.getUTCMilliseconds()); - - return copy; - }), - - offsetMonthLocal : F2(function (offset, date) { - var copy = new Date(date); - copy.setMonth(date.getMonth() + offset); - - // For some reason, Firefox loses the milliseconds otherwise - copy.setMilliseconds(date.getMilliseconds()); - - return copy; - }), - - offsetMonthUTC : F2(function (offset, date) { - var copy = new Date(date); - copy.setUTCMonth(date.getUTCMonth() + offset); - - // For some reason, Firefox loses the milliseconds otherwise - copy.setUTCMilliseconds(date.getUTCMilliseconds()); - - return copy; - }), - - dateString : function (date) { - return date.toDateString(); - }, - - timeString : function (date) { - return date.toTimeString(); - }, - - isoString : function (date) { - return date.toISOString(); - }, - - utcString : function (date) { - return date.toUTCString(); - } - }; - } - - return localRuntime.Native.WebAPI.Date.values; -}; diff --git a/src/vendor/elm-web-api/src/Native/WebAPI/Document.js b/src/vendor/elm-web-api/src/Native/WebAPI/Document.js deleted file mode 100644 index 8ce0c11..0000000 --- a/src/vendor/elm-web-api/src/Native/WebAPI/Document.js +++ /dev/null @@ -1,58 +0,0 @@ -Elm.Native = Elm.Native || {}; -Elm.Native.WebAPI = Elm.Native.WebAPI || {}; -Elm.Native.WebAPI.Document = Elm.Native.WebAPI.Document || {}; - -Elm.Native.WebAPI.Document.make = function (localRuntime) { - localRuntime.Native = localRuntime.Native || {}; - localRuntime.Native.WebAPI = localRuntime.Native.WebAPI || {}; - localRuntime.Native.WebAPI.Document = localRuntime.Native.WebAPI.Document || {}; - - if (!localRuntime.Native.WebAPI.Document.values) { - var Task = Elm.Native.Task.make(localRuntime); - var NS = Elm.Native.Signal.make(localRuntime); - var Utils = Elm.Native.Utils.make(localRuntime); - - var getState = function () { - switch (document.readyState) { - case "loading": - return {ctor: "Loading"}; - - case "interactive": - return {ctor: "Interactive"}; - - case "complete": - return {ctor: "Complete"}; - - default: - throw "Got unrecognized document.readyState: " + document.readyState; - } - }; - - var readyState = NS.input('WebAPI.Document.readyState', getState()); - - localRuntime.addListener([readyState.id], document, 'readystatechange', function () { - localRuntime.notify(readyState.id, getState()); - }); - - localRuntime.Native.WebAPI.Document.values = { - readyState: readyState, - - getReadyState: Task.asyncFunction(function (callback) { - callback(Task.succeed(getState())); - }), - - getTitle : Task.asyncFunction(function (callback) { - callback(Task.succeed(document.title)); - }), - - setTitle : function (title) { - return Task.asyncFunction(function (cb) { - document.title = title; - cb(Task.succeed(Utils.Tuple0)); - }); - } - }; - } - - return localRuntime.Native.WebAPI.Document.values; -}; diff --git a/src/vendor/elm-web-api/src/Native/WebAPI/Location.js b/src/vendor/elm-web-api/src/Native/WebAPI/Location.js deleted file mode 100644 index ba2f684..0000000 --- a/src/vendor/elm-web-api/src/Native/WebAPI/Location.js +++ /dev/null @@ -1,63 +0,0 @@ -Elm.Native = Elm.Native || {}; -Elm.Native.WebAPI = Elm.Native.WebAPI || {}; -Elm.Native.WebAPI.Location = Elm.Native.WebAPI.Location || {}; - -Elm.Native.WebAPI.Location.make = function (localRuntime) { - localRuntime.Native = localRuntime.Native || {}; - localRuntime.Native.WebAPI = localRuntime.Native.WebAPI || {}; - localRuntime.Native.WebAPI.Location = localRuntime.Native.WebAPI.Location || {}; - - if (!localRuntime.Native.WebAPI.Location.values) { - var Task = Elm.Native.Task.make(localRuntime); - var Utils = Elm.Native.Utils.make(localRuntime); - - // In core before 3.0.0 - var copy = Utils.copy; - - if (!copy) { - // In core from 3.0.0 - copy = function (value) { - return Utils.update(value, {}); - }; - } - - localRuntime.Native.WebAPI.Location.values = { - location: Task.asyncFunction(function (callback) { - var location = copy(window.location); - - // Deal with Elm reserved word - location["port$"] = location.port; - - // Polyfill for IE - if (!location.origin) { - location.origin = - location.protocol + "//" + - location.hostname + - (location.port ? ':' + location.port: ''); - } - - callback(Task.succeed(location)); - }), - - reload: function (forceServer) { - return Task.asyncFunction(function (callback) { - window.location.reload(forceServer); - - // Now, I suppose this won't really accomplish anything, - // but let's do it anyway. - try { - callback( - Task.succeed(Utils.Tuple0) - ); - } catch (ex) { - callback( - Task.fail(ex.message) - ); - } - }); - } - }; - } - - return localRuntime.Native.WebAPI.Location.values; -}; diff --git a/src/vendor/elm-web-api/src/Native/WebAPI/Math.js b/src/vendor/elm-web-api/src/Native/WebAPI/Math.js deleted file mode 100644 index 68c3f64..0000000 --- a/src/vendor/elm-web-api/src/Native/WebAPI/Math.js +++ /dev/null @@ -1,30 +0,0 @@ -Elm.Native = Elm.Native || {}; -Elm.Native.WebAPI = Elm.Native.WebAPI || {}; -Elm.Native.WebAPI.Math = Elm.Native.WebAPI.Math || {}; - -Elm.Native.WebAPI.Math.make = function (localRuntime) { - localRuntime.Native = localRuntime.Native || {}; - localRuntime.Native.WebAPI = localRuntime.Native.WebAPI || {}; - localRuntime.Native.WebAPI.Math = localRuntime.Native.WebAPI.Math || {}; - - if (!localRuntime.Native.WebAPI.Math.values) { - var Task = Elm.Native.Task.make(localRuntime); - - localRuntime.Native.WebAPI.Math.values = { - ln2: Math.LN2, - ln10: Math.LN10, - log2e: Math.LOG2E, - log10e: Math.LOG10E, - sqrt1_2: Math.SQRT1_2, - sqrt2: Math.SQRT2, - exp: Math.exp, - log: Math.log, - - random: Task.asyncFunction(function (callback) { - callback(Task.succeed(Math.random())); - }) - }; - } - - return localRuntime.Native.WebAPI.Math.values; -}; diff --git a/src/vendor/elm-web-api/src/Native/WebAPI/Number.js b/src/vendor/elm-web-api/src/Native/WebAPI/Number.js deleted file mode 100644 index 3b85d98..0000000 --- a/src/vendor/elm-web-api/src/Native/WebAPI/Number.js +++ /dev/null @@ -1,71 +0,0 @@ -Elm.Native = Elm.Native || {}; -Elm.Native.WebAPI = Elm.Native.WebAPI || {}; -Elm.Native.WebAPI.Number = Elm.Native.WebAPI.Number || {}; - -Elm.Native.WebAPI.Number.make = function (localRuntime) { - localRuntime.Native = localRuntime.Native || {}; - localRuntime.Native.WebAPI = localRuntime.Native.WebAPI || {}; - localRuntime.Native.WebAPI.Number = localRuntime.Native.WebAPI.Number || {}; - - if (!localRuntime.Native.WebAPI.Number.values) { - var Result = Elm.Result.make(localRuntime); - - var toExponential = function (number) { - // No try/catch needed because cannot throw exception - return number.toExponential(); - }; - - var toExponentialDigits = function (digits, number) { - try { - return Result.Ok(number.toExponential(digits)); - } catch (ex) { - return Result.Err(ex.message); - } - }; - - var toFixed = function (number) { - // No try/catch needed because cannot throw exception - return number.toFixed(); - }; - - var toFixedDigits = function (digits, number) { - try { - return Result.Ok(number.toFixed(digits)); - } catch (ex) { - return Result.Err(ex.message); - } - }; - - var toPrecisionDigits = function (digits, number) { - try { - return Result.Ok(number.toPrecision(digits)); - } catch (ex) { - return Result.Err(ex.message); - } - }; - - var toStringUsingBase = function (base, number) { - try { - return Result.Ok(number.toString(base)); - } catch (ex) { - return Result.Err(ex.message); - } - }; - - localRuntime.Native.WebAPI.Number.values = { - maxValue: Number.MAX_VALUE, - minValue: Number.MIN_VALUE, - nan: Number.NaN, - negativeInfinity: Number.NEGATIVE_INFINITY, - positiveInfinity: Number.POSITIVE_INFINITY, - toExponential: toExponential, - toExponentialDigits: F2(toExponentialDigits), - toFixed: toFixed, - toFixedDigits: F2(toFixedDigits), - toPrecisionDigits: F2(toPrecisionDigits), - toStringUsingBase: F2(toStringUsingBase) - }; - } - - return localRuntime.Native.WebAPI.Number.values; -}; diff --git a/src/vendor/elm-web-api/src/Native/WebAPI/Screen.js b/src/vendor/elm-web-api/src/Native/WebAPI/Screen.js deleted file mode 100644 index a27562d..0000000 --- a/src/vendor/elm-web-api/src/Native/WebAPI/Screen.js +++ /dev/null @@ -1,51 +0,0 @@ -Elm.Native = Elm.Native || {}; -Elm.Native.WebAPI = Elm.Native.WebAPI || {}; -Elm.Native.WebAPI.Screen = Elm.Native.WebAPI.Screen || {}; - -Elm.Native.WebAPI.Screen.make = function (localRuntime) { - localRuntime.Native = localRuntime.Native || {}; - localRuntime.Native.WebAPI = localRuntime.Native.WebAPI || {}; - localRuntime.Native.WebAPI.Screen = localRuntime.Native.WebAPI.Screen || {}; - - if (!localRuntime.Native.WebAPI.Screen.values) { - var Task = Elm.Native.Task.make(localRuntime); - var Utils = Elm.Native.Utils.make(localRuntime); - - var copy; - if (Utils.copy) { - // In core before 3.0.0 - copy = Utils.copy; - } else { - // In core from 3.0.0 - copy = function (value) { - return Utils.update(value, {}); - }; - } - - localRuntime.Native.WebAPI.Screen.values = { - // Note that this is a Task because in a multi-monitor setup, the - // result depends on which monitor the browser window is being - // displayed on. So, it's not a constant. - // - // That's also why we copy the screen object ... otherwise, it - // would be *live* reference to the screen, and thus, not constant. - screen: Task.asyncFunction(function (callback) { - callback( - Task.succeed( - copy(window.screen) - ) - ); - }), - - screenXY: Task.asyncFunction(function (callback) { - callback( - Task.succeed( - Utils.Tuple2(window.screenX, window.screenY) - ) - ); - }) - }; - } - - return localRuntime.Native.WebAPI.Screen.values; -}; diff --git a/src/vendor/elm-web-api/src/Native/WebAPI/Storage.js b/src/vendor/elm-web-api/src/Native/WebAPI/Storage.js deleted file mode 100644 index f08829b..0000000 --- a/src/vendor/elm-web-api/src/Native/WebAPI/Storage.js +++ /dev/null @@ -1,109 +0,0 @@ -Elm.Native = Elm.Native || {}; -Elm.Native.WebAPI = Elm.Native.WebAPI || {}; -Elm.Native.WebAPI.Storage = Elm.Native.WebAPI.Storage || {}; - -Elm.Native.WebAPI.Storage.make = function (localRuntime) { - localRuntime.Native = localRuntime.Native || {}; - localRuntime.Native.WebAPI = localRuntime.Native.WebAPI || {}; - localRuntime.Native.WebAPI.Storage = localRuntime.Native.WebAPI.Storage || {}; - - if (!localRuntime.Native.WebAPI.Storage.values) { - var Task = Elm.Native.Task.make(localRuntime); - var Maybe = Elm.Maybe.make(localRuntime); - var NS = Elm.Native.Signal.make(localRuntime); - var Utils = Elm.Native.Utils.make(localRuntime); - - var toMaybe = function (obj) { - return obj == null ? Maybe.Nothing : Maybe.Just(obj); - }; - - var length = function (storage) { - return Task.asyncFunction(function (callback) { - callback(Task.succeed(storage.length)); - }); - }; - - var key = function (storage, k) { - return Task.asyncFunction(function (callback) { - var result = null; - - // This check needed to avoid a problem in IE9 - if (k >= 0 && k < storage.length) { - result = storage.key(k); - } - - callback( - Task.succeed( - toMaybe(result) - ) - ); - }); - }; - - var getItem = function (storage, k) { - return Task.asyncFunction(function (callback) { - var result = storage.getItem(k); - callback( - Task.succeed( - toMaybe(result) - ) - ); - }); - }; - - var setItem = function (storage, k, v) { - return Task.asyncFunction(function (callback) { - try { - storage.setItem(k, v); - callback(Task.succeed(Utils.Tuple0)); - } catch (ex) { - callback(Task.fail(ex.message)); - } - }); - }; - - var removeItem = function (storage, k) { - return Task.asyncFunction(function (callback) { - storage.removeItem(k); - callback(Task.succeed(Utils.Tuple0)); - }); - }; - - var clear = function (storage) { - return Task.asyncFunction(function (callback) { - storage.clear(); - callback(Task.succeed(Utils.Tuple0)); - }); - }; - - var events = NS.input('WebAPI.Storage.nativeEvents', Maybe.Nothing); - - localRuntime.addListener([events.id], window, "storage", function (event) { - var e = { - key: toMaybe(event.key), - oldValue: toMaybe(event.oldValue), - newValue: toMaybe(event.newValue), - url : event.url, - storageArea: event.storageArea - }; - - localRuntime.notify(events.id, toMaybe(e)); - }); - - localRuntime.Native.WebAPI.Storage.values = { - localStorage: window.localStorage, - sessionStorage: window.sessionStorage, - - length: length, - key: F2(key), - getItem: F2(getItem), - setItem: F3(setItem), - removeItem: F2(removeItem), - clear: clear, - - nativeEvents: events - }; - } - - return localRuntime.Native.WebAPI.Storage.values; -}; diff --git a/src/vendor/elm-web-api/src/Native/WebAPI/Window.js b/src/vendor/elm-web-api/src/Native/WebAPI/Window.js deleted file mode 100644 index bb52170..0000000 --- a/src/vendor/elm-web-api/src/Native/WebAPI/Window.js +++ /dev/null @@ -1,53 +0,0 @@ -Elm.Native = Elm.Native || {}; -Elm.Native.WebAPI = Elm.Native.WebAPI || {}; -Elm.Native.WebAPI.Window = Elm.Native.WebAPI.Window || {}; - -Elm.Native.WebAPI.Window.make = function (localRuntime) { - localRuntime.Native = localRuntime.Native || {}; - localRuntime.Native.WebAPI = localRuntime.Native.WebAPI || {}; - localRuntime.Native.WebAPI.Window = localRuntime.Native.WebAPI.Window || {}; - - if (!localRuntime.Native.WebAPI.Window.values) { - var Task = Elm.Native.Task.make(localRuntime); - var Utils = Elm.Native.Utils.make(localRuntime); - - var elmAlert = function (message) { - return Task.asyncFunction(function (callback) { - window.alert(message); - callback(Task.succeed(Utils.Tuple0)); - }); - }; - - var elmConfirm = function (message) { - return Task.asyncFunction(function (callback) { - var result = window.confirm(message); - callback( - result - ? Task.succeed(Utils.Tuple0) - : Task.fail(Utils.Tuple0) - ); - }); - }; - - var elmPrompt = function (message, defaultResponse) { - return Task.asyncFunction(function (callback) { - var result = window.prompt(message, defaultResponse); - callback( - // Safari returns "" when you press cancel, so - // we need to check for that. - result == null || result == "" - ? Task.fail(Utils.Tuple0) - : Task.succeed(result) - ); - }); - }; - - localRuntime.Native.WebAPI.Window.values = { - alert: elmAlert, - confirm: elmConfirm, - prompt: F2(elmPrompt) - }; - } - - return localRuntime.Native.WebAPI.Window.values; -}; diff --git a/src/vendor/elm-web-api/src/WebAPI/AnimationFrame.elm b/src/vendor/elm-web-api/src/WebAPI/AnimationFrame.elm deleted file mode 100644 index f7f8f84..0000000 --- a/src/vendor/elm-web-api/src/WebAPI/AnimationFrame.elm +++ /dev/null @@ -1,63 +0,0 @@ -module WebAPI.AnimationFrame - ( task - , Request, request, cancel - ) where - - -{-| Bindings for `window.requestAnimationFrame()` and `window.cancelAnimationFrame`. - -Note that -[jwmerrill/elm-animation-frame](http://package.elm-lang.org/packages/jwmerrill/elm-animation-frame/latest) -provides for a `Signal` of animation frames. So, this module merely provides a -`Task`-oriented alternative. - -Other higher-level alternatives include -[evancz/elm-effects](http://package.elm-lang.org/packages/evancz/elm-effects/latest) -and [rgrempel/elm-ticker](https://github.com/rgrempel/elm-ticker.git). - -@docs task, request, Request, cancel --} - - -import Time exposing (Time) -import Task exposing (Task) -import Native.WebAPI.AnimationFrame - - -{-| A task which, when executed, will call `window.requestAnimationFrame()`. -The task will complete when `requestAnimationFrame()` fires its callback, and -will pass along the value provided by the callback. - -So, to do something when the callback fires, just add an `andThen` to the task. --} -task : Task x Time -task = Native.WebAPI.AnimationFrame.task - - -{-| Opaque type which represents an animation frame request. -} -type Request = Request - - -{-| A more complex implementation of `window.requestAnimationFrame()` which -allows for cancelling the request. - -Returns a `Task` which, when executed, will call -`window.requestAnimationFrame()`, and then immediately complete with the -identifier returned by `requestAnimationFrame()`. You can supply this -identifier to `cancel` if you want to cancel the request. - -Assuming that you don't cancel the request, the following sequence of events will occur: - -* `window.requestAnimationFrame()` will eventually fire its callback, providing a timestamp -* Your function will be called with that timestamp -* The `Task` returned by your function will be immediately executed --} -request : (Time -> Task x a) -> Task y Request -request = Native.WebAPI.AnimationFrame.request - - -{-| Returns a task which, when executed, will cancel the supplied request -via `window.cancelAnimationFrame()`. --} -cancel : Request -> Task x () -cancel = Native.WebAPI.AnimationFrame.cancel diff --git a/src/vendor/elm-web-api/src/WebAPI/Cookie.elm b/src/vendor/elm-web-api/src/WebAPI/Cookie.elm deleted file mode 100644 index 7bee024..0000000 --- a/src/vendor/elm-web-api/src/WebAPI/Cookie.elm +++ /dev/null @@ -1,140 +0,0 @@ -module WebAPI.Cookie - ( get, set - , Options, setWith, defaultOptions - ) where - -{-| Wrap the browser's -[`document.cookie`](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie) -object. - -# Getting cookies -@docs get - -# Setting cookies -@docs set, setWith, Options, defaultOptions --} - -import Native.WebAPI.Cookie - -import Task exposing (Task) -import String exposing (split, trim, join) -import Dict exposing (Dict, insert) -import Time exposing (inSeconds, Time) -import Date exposing (Date) -import List - - -{-| A `Task` which, when executed, will succeed with the cookies. - -In the resulting `Dict`, the keys and values are the key=value pairs parsed from -Javascript's `document.cookie`. The keys and values will have been uriDecoded. --} -get : Task x (Dict String String) -get = - Task.map cookieString2Dict getString - - -getString : Task x String -getString = Native.WebAPI.Cookie.getString - - -{- We pipeline the various operations inside the foldl so that we don't -iterate over the cookies more then once. Note that the uriDecode needs to -happen after the split on ';' (to divide into key-value pairs) and the split on -'=' (to divide the keys from the values). --} -cookieString2Dict : String -> Dict String String -cookieString2Dict = - let - addCookieToDict = - trim >> split "=" >> List.map uriDecode >> addKeyValueToDict - - addKeyValueToDict keyValueList = - case keyValueList of - key :: value :: _ -> insert key value - _ -> identity - - in - List.foldl addCookieToDict Dict.empty << split ";" - - -{-| A task which will set a cookie using the provided key (first parameter) -and value (second parameter). - -The key and value will both be uriEncoded. --} -set : String -> String -> Task x () -set = setWith defaultOptions - - -{-| Options which you can provide to setWith. -} -type alias Options = - { path : Maybe String - , domain : Maybe String - , maxAge : Maybe Time - , expires : Maybe Date - , secure : Maybe Bool - } - - -{-| The default options, in which all options are set to Nothing. - -You can use this as a starting point for setWith, where you only want to -specify some options. --} -defaultOptions : Options -defaultOptions = - { path = Nothing - , domain = Nothing - , maxAge = Nothing - , expires = Nothing - , secure = Nothing - } - - -{-| A task which will set a cookie using the provided options, key (second -parameter), and value (third parameter). - -The key and value will be uriEncoded, as well as the path and domain options -(if provided). --} -setWith : Options -> String -> String -> Task x () -setWith options key value = - let - andThen = - flip Maybe.andThen - - handlers = - [ always <| Just <| (uriEncode key) ++ "=" ++ (uriEncode value) - , .path >> andThen (\path -> Just <| "path=" ++ uriEncode path) - , .domain >> andThen (\domain -> Just <| "domain=" ++ uriEncode domain) - , .maxAge >> andThen (\age -> Just <| "max-age=" ++ toString (inSeconds age)) - , .expires >> andThen (\expires -> Just <| "expires=" ++ dateToUTCString expires) - , .secure >> andThen (\secure -> if secure then Just "secure" else Nothing) - ] - - cookieStrings = - List.filterMap ((|>) options) handlers - - in - setString <| join ";" cookieStrings - - -setString : String -> Task x () -setString = - Native.WebAPI.Cookie.setString - - -dateToUTCString : Date -> String -dateToUTCString = - Native.WebAPI.Cookie.dateToUTCString - - -uriEncode : String -> String -uriEncode = - Native.WebAPI.Cookie.uriEncode - - -uriDecode : String -> String -uriDecode = - Native.WebAPI.Cookie.uriDecode diff --git a/src/vendor/elm-web-api/src/WebAPI/Date.elm b/src/vendor/elm-web-api/src/WebAPI/Date.elm deleted file mode 100644 index 44d7651..0000000 --- a/src/vendor/elm-web-api/src/WebAPI/Date.elm +++ /dev/null @@ -1,426 +0,0 @@ -module WebAPI.Date - ( current, now - , Timezone(Local, UTC), timezoneOffset - , Parts, fromParts, toParts, dayOfWeek - , toMonth, fromMonth, toDay, fromDay - , offsetTime, offsetYear, offsetMonth - , day, inDays - , week, inWeeks - , dateString, timeString, isoString, utcString - ) where - - -{-| Some additional facilities for the browser's `Date` type, not already -supplied by `Date` and `Time` in -[elm-lang/core](http://package.elm-lang.org/packages/elm-lang/core/latest). - -See the [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date). - -This is intended merely to be a thin wrapper over the Javascript `Date` API. -There is some real complexity in dealing with dates that would benefit from a -more sophisticated library. - -# Getting the current date or time - -@docs current, now - -# Timezones - -@docs Timezone, timezoneOffset - -# The parts of a date - -@docs Parts, fromParts, toParts, dayOfWeek -@docs toMonth, fromMonth, toDay, fromDay - -# Date arithmetic - -@docs offsetTime, offsetYear, offsetMonth - -# Time scales - -In the spirit of `Time.hour`, `Time.inHours`, `Time.second`, `Time.inSeconds`, -`Time.millisecond`, and `Time.inMilliseconds`. - -@docs day, inDays, week, inWeeks - -# String conversions - -@docs dateString, timeString, isoString, utcString --} - - -import Date exposing (Date) -import Time exposing (Time) -import Task exposing (Task) -import Debug - -import Native.WebAPI.Date - - -{- --------------------------------- - Getting the current date and time - --------------------------------- -} - - -{-| Get the current date, via the browser's `new Date()` -} -current : Task x Date -current = Native.WebAPI.Date.current - - -{-| Get the current time, via the browser's `Date.now()` -} -now : Task x Time -now = Native.WebAPI.Date.now - - -{- --------- - Timezones - --------- -} - - -{-| The Javascript API allows you to perform certain operations in terms of -the "local" timezone, or in terms of UTC. So, where we wrap those APIs, we use -this type to let you pick (rather than having separate functions). Of course, -you can use partial application to create separate functions if you like. - -Note that this isn't the kind of support that a more sophisticated library -would have for timezones -- it merely wraps what Javascript provides. --} -type Timezone - = Local - | UTC - - -{-| Javascript's `getTimezoneOffset()`. - -This represents what Javascript thinks is the offset between UTC and local time, -for the specified date. It can differ from date to date depending on whether -daylight savings time is in effect on that date. - -Note that this is in units of `Time`, so you can scale via `Time.inMinutes` etc. --} -timezoneOffset : Date -> Time -timezoneOffset date = - (timezoneOffsetInMinutes date) * Time.minute - - -timezoneOffsetInMinutes : Date -> Float -timezoneOffsetInMinutes = Native.WebAPI.Date.timezoneOffsetInMinutes - - -{- ------------------- - The parts of a date - ------------------- -} - - -{-| The parts of a date. - -Note that (as in the Javascript APIs) the month is 0-based, with January = 0. --} -type alias Parts = - { year : Int - , month : Int - , day : Int - , hour : Int - , minute : Int - , second : Int - , millisecond : Int - } - - -{-| Construct a `Date` from the provided parts, using the specified timezone. - -For `Local`, this uses `new Date(...)`. - -For `UTC`, this uses `new Date(Date.UTC(...))`. --} -fromParts : Timezone -> Parts -> Date -fromParts zone = - case zone of - Local -> fromPartsLocal - UTC -> fromPartsUtc - - -fromPartsLocal : Parts -> Date -fromPartsLocal = Native.WebAPI.Date.fromPartsLocal - - -fromPartsUtc : Parts -> Date -fromPartsUtc = Native.WebAPI.Date.fromPartsUtc - - -{-| Break a `Date` up into its parts, using the specified timezone. - -For `Local`, this uses `getFullYear()`, `getMonth()`, etc. - -For `UTC`, this uses `getUTCFullYear()`, `getUTCMonth()`, etc. --} -toParts : Timezone -> Date -> Parts -toParts zone = - case zone of - Local -> toPartsLocal - UTC -> toPartsUtc - - -toPartsLocal : Date -> Parts -toPartsLocal = Native.WebAPI.Date.toPartsLocal - - -toPartsUtc : Date -> Parts -toPartsUtc = Native.WebAPI.Date.toPartsUtc - - -{-| Get the day of the week corresopnding to a `Date`. - -This is handled separately from `Parts` because it is not symmetrical -- -it makes no sense for there to be a constructor based on this. --} -dayOfWeek : Timezone -> Date -> Date.Day -dayOfWeek timezone date = - case timezone of - Local -> - Date.dayOfWeek date - - UTC -> - toDay (dayOfWeekUTC date) - - -dayOfWeekUTC : Date -> Int -dayOfWeekUTC = Native.WebAPI.Date.dayOfWeekUTC - - -{-| Converts from Javascript's 0-based months (where January = 0) to`Date.Month`. -} -toMonth : Int -> Date.Month -toMonth int = - let - clamped = - int `rem` 12 - - positive = - if clamped < 0 - then clamped + 12 - else clamped - - in - case positive of - 0 -> Date.Jan - 1 -> Date.Feb - 2 -> Date.Mar - 3 -> Date.Apr - 4 -> Date.May - 5 -> Date.Jun - 6 -> Date.Jul - 7 -> Date.Aug - 8 -> Date.Sep - 9 -> Date.Oct - 10 -> Date.Nov - 11 -> Date.Dec - _ -> Debug.crash "This is unreachable." - - -{-| Converts from `Date.Month` to Javascript's 0-based months (where January = 0). -} -fromMonth : Date.Month -> Int -fromMonth month = - case month of - Date.Jan -> 0 - Date.Feb -> 1 - Date.Mar -> 2 - Date.Apr -> 3 - Date.May -> 4 - Date.Jun -> 5 - Date.Jul -> 6 - Date.Aug -> 7 - Date.Sep -> 8 - Date.Oct -> 9 - Date.Nov -> 10 - Date.Dec -> 11 - - -{-| Converts from Javascript's 0-based days (where Sunday = 0) to`Date.Day`. -} -toDay : Int -> Date.Day -toDay int = - let - clamped = - int `rem` 7 - - positive = - if clamped < 0 - then clamped + 7 - else clamped - - in - case positive of - 0 -> Date.Sun - 1 -> Date.Mon - 2 -> Date.Tue - 3 -> Date.Wed - 4 -> Date.Thu - 5 -> Date.Fri - 6 -> Date.Sat - _ -> Debug.crash "This is unreachable." - - -{-| Converts from `Date.Day` to Javascript's 0-based days (where Sunday = 0). -} -fromDay : Date.Day -> Int -fromDay day = - case day of - Date.Sun -> 0 - Date.Mon -> 1 - Date.Tue -> 2 - Date.Wed -> 3 - Date.Thu -> 4 - Date.Fri -> 5 - Date.Sat -> 6 - - -{- --------------- - Date arithmetic - --------------- -} - - -{-| Offset the `Date` by the supplied `Time` (i.e. positive values offset into -the future, and negative values into the past). - -You can use `day`, `week`, `Time.minute`, etc. to scale. However, that won't -always do what you actually want, since the values are treated as durations, -rather than human-oriented intervals. For instance, `offsetTime (365 * day) -date` will advance the date by 365 days. However, if a leap year is involved, -the resulting date might be a different day of the year. If you actually want -the same day in the next year, then use `offsetYear` instead. --} -offsetTime : Time -> Date -> Date -offsetTime time date = - Date.fromTime ((Date.toTime date) + time) - - -{-| Offset the `Date` by the specified number of years (forward or backward), -using Javascript's `setFullYear()` and `getFullYear()` (or `getUTCFullYear()` -and `setUTCFullYear()`). - -Leap years are handled by the underlying Javascript APIs as follows: - -* If the supplied date is February 29, and the target year has no February 29, - then you'll end up with March 1 in the target year. (Arguably, you might - prefer February 28, but I'm not sure there is a clearly correct answer). - -* If the supplied date is February 29, and the target year also has a February - 29, then you'll end up with February 29. - -* The year is interpreted in terms of either the local timezone or UTC, according - to what you specify. I think the only case in which this could make a - difference is in determining whether it is February 29. - -* If the offset "crosses" a leap day, then you'll end up with the "same" day in - the target year ... for instance, `offsetYear 1` will sometimes move 365 - days and sometimes 366 days, depending on whether a leap year is involved. - -A more sophisticated module might deal with these cases a little differently. --} -offsetYear : Timezone -> Int -> Date -> Date -offsetYear zone = - case zone of - Local -> offsetYearLocal - UTC -> offsetYearUTC - - -offsetYearLocal : Int -> Date -> Date -offsetYearLocal = Native.WebAPI.Date.offsetYearLocal - - -offsetYearUTC : Int -> Date -> Date -offsetYearUTC = Native.WebAPI.Date.offsetYearUTC - - -{-| Offset the `Date` by the specified number of months (forward or backward), -using Javascript's `setMonth()` and `getMonth()` (or `setUTCMonth()` and -`getUTCMonth()`). - -Here are a few notes about the underlying Javascript implementation: - -* Overflow and underflow basically do the right thing. That is, if you end up - with negative numbers, the year is decremented, and if you end up with - numbers past 11, the year is incremented. (Remember that Javascript months - are 0-based). And, in either case, the month is set to something between - 0 and 11. - -* Dates at the beginning of the month are handled as you might expect. For - instance, adding 1 month to January 1 produces February 1, and adding 1 month - to February 1 produces March 1. Thus, the actual number of days added can vary, - depending on the length of the month. - -* However, dates at the end of the month are handled in a way that could seem - odd. For instance, adding 1 month to August 31 produces October 1 ... were - you expecting September 30? That would probably be more useful, and a more - sophisticated library might arrange for that. - -* Note that the date is interpreted according to either the local timezone or UTC, - as you specify. In some cases, that will affect whether the date is - considered to be the last day of the month, or the first day of the next - month, which will in turn affect whether the "end of month" anomaly is - triggered. --} -offsetMonth : Timezone -> Int -> Date -> Date -offsetMonth zone = - case zone of - Local -> offsetMonthLocal - UTC -> offsetMonthUTC - - -offsetMonthLocal : Int -> Date -> Date -offsetMonthLocal = Native.WebAPI.Date.offsetMonthLocal - - -offsetMonthUTC : Int -> Date -> Date -offsetMonthUTC = Native.WebAPI.Date.offsetMonthUTC - - -{- --------------------------- - Some additional time scales - --------------------------- -} - - -{-| A convenience for arithmetic, analogous to `Time.hour`, `Time.minute`, etc. -} -day : Time -day = 24 * Time.hour - - -{-| A convenience for arithmetic, analogous to `Time.inHours`, `Time.inMinutes`, etc. -} -inDays : Time -> Float -inDays days = days / day - - -{-| A convenience for arithmetic, analogous to `Time.hour`, `Time.minute`, etc. -} -week : Time -week = 7 * day - - -{-| A convenience for arithmetic, analogous to `Time.inHours`, `Time.inMinutes`, etc. -} -inWeeks : Time -> Float -inWeeks weeks = weeks / week - - -{- ------------------ - String conversions - ------------------ -} - - -{-| The browser's `toDateString()` -} -dateString : Date -> String -dateString = Native.WebAPI.Date.dateString - - -{-| The browser's `toTimeString()` -} -timeString : Date -> String -timeString = Native.WebAPI.Date.timeString - - -{-| The browser's `toISOString()` -} -isoString : Date -> String -isoString = Native.WebAPI.Date.isoString - - -{-| The browser's `toUTCString()` -} -utcString : Date -> String -utcString = Native.WebAPI.Date.utcString - - diff --git a/src/vendor/elm-web-api/src/WebAPI/Document.elm b/src/vendor/elm-web-api/src/WebAPI/Document.elm deleted file mode 100644 index b337d50..0000000 --- a/src/vendor/elm-web-api/src/WebAPI/Document.elm +++ /dev/null @@ -1,62 +0,0 @@ -module WebAPI.Document - ( ReadyState (Loading, Interactive, Complete) - , readyState, getReadyState - , getTitle, setTitle - ) where - -{-| See Mozilla documentation for the -[`Document` object](https://developer.mozilla.org/en-US/docs/Web/API/Document). - -## Loading - -@docs ReadyState, readyState, getReadyState - -## Others - -Since the browser's `document` object has so many facilities attached, I've -broken some of them up into individual modules -- see below for the -cross-references. - -***See also*** - -**`cookie`** - -        -See [WebAPI.Cookie](#webapicookie) --} - - -import Signal exposing (Signal) -import Task exposing (Task) -import Native.WebAPI.Document - - -{-| Possible values for the browser's `document.readyState` -} -type ReadyState - = Loading - | Interactive - | Complete - - -{-| A `Signal` of changes to the browser's `document.readyState` -} -readyState : Signal ReadyState -readyState = Native.WebAPI.Document.readyState - - -{-| A task which, when executed, succeeds with the value of the browser's -`document.readyState`. --} -getReadyState : Task x ReadyState -getReadyState = Native.WebAPI.Document.getReadyState - - -{-| A task which, when executed, succeeds with the value of `document.title`. -} -getTitle : Task x String -getTitle = Native.WebAPI.Document.getTitle - - -{-| A task which, when executed, sets the value of `document.title` to the -supplied `String`. --} -setTitle : String -> Task x () -setTitle = Native.WebAPI.Document.setTitle diff --git a/src/vendor/elm-web-api/src/WebAPI/Location.elm b/src/vendor/elm-web-api/src/WebAPI/Location.elm deleted file mode 100644 index 53e5fd6..0000000 --- a/src/vendor/elm-web-api/src/WebAPI/Location.elm +++ /dev/null @@ -1,63 +0,0 @@ -module WebAPI.Location - ( Location, location - , reload, Source(ForceServer, AllowCache) - ) where - - -{-| Facilities from the browser's `window.location` object. - -See the [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/API/Location) - -For a `Signal`-oriented version of things you might do with `window.location`, see -[TheSeamau5/elm-history](http://package.elm-lang.org/packages/TheSeamau5/elm-history/latest). - -For `assign`, use `setPath` from -[TheSeamau5/elm-history](http://package.elm-lang.org/packages/TheSeamau5/elm-history/latest). - -For `replace`, use `replacePath` from -[TheSeamau5/elm-history](http://package.elm-lang.org/packages/TheSeamau5/elm-history/latest). - -@docs Location, location, reload, Source --} - - -import Task exposing (Task) -import Native.WebAPI.Location - - -{-| The parts of a location object. Note `port'`, since `port` is a reserved word. -} -type alias Location = - { href: String - , protocol: String - , host: String - , hostname: String - , port': String - , pathname: String - , search: String - , hash: String - , origin: String - } - - -{-| The browser's `window.location` object. -} -location : Task x Location -location = Native.WebAPI.Location.location - - -{-| Reloads the page from the current URL.-} -reload : Source -> Task String () -reload source = - nativeReload <| - case source of - ForceServer -> True - AllowCache -> False - - -nativeReload : Bool -> Task String () -nativeReload = Native.WebAPI.Location.reload - - -{-| Whether to force `reload` to use the server, or allow the cache. -} -type Source - = ForceServer - | AllowCache diff --git a/src/vendor/elm-web-api/src/WebAPI/Math.elm b/src/vendor/elm-web-api/src/WebAPI/Math.elm deleted file mode 100644 index 1bd3772..0000000 --- a/src/vendor/elm-web-api/src/WebAPI/Math.elm +++ /dev/null @@ -1,83 +0,0 @@ -module WebAPI.Math - ( ln2, ln10, log2e, log10e, sqrt1_2, sqrt2 - , exp, log - , random - ) where - - -{-| Various facilities from the browser's `Math` object that are not -otherwise available in Elm. - -See the [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math). - -## Constants - -@docs ln2, ln10, log2e, log10e, sqrt1_2, sqrt2 - -## Functions - -@docs exp, log - -## Task - -@docs random --} - - -import Task exposing (Task) -import Native.WebAPI.Math - - -{-| Natural logarithm of 2, approximately 0.693. -} -ln2 : Float -ln2 = Native.WebAPI.Math.ln2 - - -{-| Natural logarithm of 10, approximately 2.303. -} -ln10 : Float -ln10 = Native.WebAPI.Math.ln10 - - -{-| Base 2 logarithm of E, approximately 1.443. -} -log2e : Float -log2e = Native.WebAPI.Math.log2e - - -{-| Base 10 logarithm of E, approximately 0.434 -} -log10e : Float -log10e = Native.WebAPI.Math.log10e - - -{-| Square root of 1/2; equivalently, 1 over the square root of 2, -approximately 0.707. --} -sqrt1_2 : Float -sqrt1_2 = Native.WebAPI.Math.sqrt1_2 - - -{-| Square root of 2, approximately 1.414. -} -sqrt2 : Float -sqrt2 = Native.WebAPI.Math.sqrt2 - - -{-| Returns E to the power of x, where x is the argument, and E is Euler's -constant (2.718…), the base of the natural logarithm. --} -exp : number -> Float -exp = Native.WebAPI.Math.exp - - -{-| Returns the natural logarithm (loge, also ln) of a number. -} -log : number -> Float -log = Native.WebAPI.Math.log - - -{-| Returns a pseudo-random number between 0 and 1. - -Note that there is a more sophisticated implementation of `Random` in -elm-lang/core. However, this may sometimes be useful if you're in a `Task` -context anyway. --} -random : Task x Float -random = Native.WebAPI.Math.random - diff --git a/src/vendor/elm-web-api/src/WebAPI/Number.elm b/src/vendor/elm-web-api/src/WebAPI/Number.elm deleted file mode 100644 index d31465d..0000000 --- a/src/vendor/elm-web-api/src/WebAPI/Number.elm +++ /dev/null @@ -1,160 +0,0 @@ -module WebAPI.Number - ( maxValue, minValue, nan, negativeInfinity, positiveInfinity - , toExponential, toExponentialDigits, safeExponentialDigits - , toFixed, toFixedDigits, safeFixedDigits - , toPrecisionDigits, safePrecisionDigits - , toStringUsingBase, safeStringUsingBase - ) where - - -{-| Various facilities from the browser's `Number` object that are not -otherwise available in Elm. - -See the [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number). - -## Constants - -@docs maxValue, minValue, nan, negativeInfinity, positiveInfinity - -## Functions - -@docs toExponential, toExponentialDigits, safeExponentialDigits -@docs toFixed, toFixedDigits, safeFixedDigits -@docs toPrecisionDigits, safePrecisionDigits -@docs toStringUsingBase, safeStringUsingBase --} - - -import Result exposing (Result) -import Debug -import Native.WebAPI.Number - - -{-| The largest positive representable number. -} -maxValue : Float -maxValue = Native.WebAPI.Number.maxValue - - -{-| The smallest positive representable number - that is, the positive number -closest to zero (without actually being zero). --} -minValue : Float -minValue = Native.WebAPI.Number.minValue - - -{-| Special "not a number" value. -} -nan : Float -nan = Native.WebAPI.Number.nan - - -{-| Special value representing negative infinity; returned on overflow. -} -negativeInfinity : Float -negativeInfinity = Native.WebAPI.Number.negativeInfinity - - -{-| Special value representing infinity; returned on overflow. -} -positiveInfinity : Float -positiveInfinity = Native.WebAPI.Number.positiveInfinity - - -{-| A string representing the provided number in exponential notation. -} -toExponential : number -> String -toExponential = Native.WebAPI.Number.toExponential - - -{-| Either a string representing the second parameter in exponential notation, -with the requested number of digits after the decimal point (first parameter), -or an error. An error should not occur if the requested number of digits is -between 0 and 20. --} -toExponentialDigits : Int -> number -> Result String String -toExponentialDigits = Native.WebAPI.Number.toExponentialDigits - - -{-| A string representing the second parameter in exponential notation, -with the requested number of digits after the decimal point (first parameter). -The number of digits will be limited to between 0 and 20. --} -safeExponentialDigits : Int -> number -> String -safeExponentialDigits digits num = - case toExponentialDigits (clamp 0 20 digits) num of - Ok value -> - value - - Err err -> - Debug.crash "Unexpected error: " ++ err - - -{-| A string representing the provided number in fixed-point notation. -} -toFixed : number -> String -toFixed = Native.WebAPI.Number.toFixed - - -{-| Either a string representing the second parameter in fixed-point notation, -with the requested number of digits after the decimal point (first parameter), -or an error. An error should not occur if the requested number of digits is -between 0 and 20. - -Note that Firefox returns `Ok 0` rather than an `Error` for a negative number -of requested digits. --} -toFixedDigits : Int -> number -> Result String String -toFixedDigits = Native.WebAPI.Number.toFixedDigits - - -{-| A string representing the second parameter in fixed-point notation, -with the requested number of digits after the decimal point (first parameter). -The number of digits will be limited to between 0 and 20. --} -safeFixedDigits : Int -> number -> String -safeFixedDigits digits num = - case toFixedDigits (clamp 0 20 digits) num of - Ok value -> - value - - Err err -> - Debug.crash "Unexpected error: " ++ err - - -{-| Either a string representing the second parameter in fixed-point or -exponential notation, with the requested number of significant digits (first -parameter), or an error. An error should not occur if the requested number of -digits is between 1 and 20. --} -toPrecisionDigits : Int -> number -> Result String String -toPrecisionDigits = Native.WebAPI.Number.toPrecisionDigits - - -{-| A string representing the second parameter in fixed-point or exponential -notation, with the requested number of significant digits (first parameter). -The number of digits will be limited to between 1 and 20. --} -safePrecisionDigits : Int -> number -> String -safePrecisionDigits digits num = - case toPrecisionDigits (clamp 1 20 digits) num of - Ok value -> - value - - Err err -> - Debug.crash "Unexpected error: " ++ err - - -{-| Either a string representing the second parameter, using the requested base -(first parameter), or an error. An error should not occur if the requested base -is between 2 and 36. --} -toStringUsingBase : Int -> number -> Result String String -toStringUsingBase = Native.WebAPI.Number.toStringUsingBase - - -{-| A string representing the second parameter, using the requested base -(first parameter). The requested base will be limited to between 2 and 36. --} -safeStringUsingBase : Int -> number -> String -safeStringUsingBase base num = - case toStringUsingBase (clamp 2 36 base) num of - Ok value -> - value - - Err err -> - Debug.crash "Unexpected error: " ++ err diff --git a/src/vendor/elm-web-api/src/WebAPI/Screen.elm b/src/vendor/elm-web-api/src/WebAPI/Screen.elm deleted file mode 100644 index e3ed9b6..0000000 --- a/src/vendor/elm-web-api/src/WebAPI/Screen.elm +++ /dev/null @@ -1,52 +0,0 @@ -module WebAPI.Screen - ( Screen, screen - , screenXY - ) where - - -{-| The browser's `Screen` type from `window.screen`. - -See the [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/API/Screen). - -@docs Screen, screen - -@docs screenXY --} - -import Task exposing (Task) - -import Native.WebAPI.Screen - - -{-| The browser's `Screen` type. -} -type alias Screen = - { availTop: Int - , availLeft: Int - , availHeight: Int - , availWidth: Int - , colorDepth: Int - , pixelDepth: Int - , height: Int - , width: Int - } - - -{-| The browser's `window.screen` object. - -This is a `Task` because in multi-monitor setups, the result depends on which screen -the browser window is in. So, it is not necessarily a constant. --} -screen : Task x Screen -screen = Native.WebAPI.Screen.screen - - -{-| A tuple of the browser's `(window.screenX, window.screenY)`. - - The first value is the horizontal distance, in CSS pixels, of the left - border of the user's browser from the left side of the screen. - - The second value is the vertical distance, in CSS pixels, of the top border - of the user's browser from the top edge of the screen. --} -screenXY : Task x (Int, Int) -screenXY = Native.WebAPI.Screen.screenXY diff --git a/src/vendor/elm-web-api/src/WebAPI/Storage.elm b/src/vendor/elm-web-api/src/WebAPI/Storage.elm deleted file mode 100644 index 1b7bb73..0000000 --- a/src/vendor/elm-web-api/src/WebAPI/Storage.elm +++ /dev/null @@ -1,271 +0,0 @@ -module WebAPI.Storage - ( Storage(Local, Session), local, session - , length, key, get, set, remove, clear - , events, Event, Change(Add, Remove, Modify, Clear) - , Key, OldValue, NewValue, Value - ) where - - -{-| Facilities from the browser's `Storage` areas (`localStorage` and `sessionStorage`). - -See the [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/API/Storage), -and the [WhatWG documentation](https://html.spec.whatwg.org/multipage/webstorage.html). - -Note that there is a more sophisticated module for `Storage` at -[TheSeamau5/elm-storage](https://github.com/TheSeamau5/elm-storage) - -## Storage Areas - -@docs Storage, local, session - -## Roles for Strings - -@docs Key, OldValue, NewValue, Value - -## Tasks - -@docs length, key, get, set, remove, clear - -## Events - -@docs events, Event, Change - --} - - -import Task exposing (Task) -import Debug -import Native.WebAPI.Storage - - -{- ----------------- - Roles for Strings - ----------------- -} - -{-| A key. -} -type alias Key = String - - -{-| An old value. -} -type alias OldValue = String - - -{-| A new value. -} -type alias NewValue = String - - -{-| A value. -} -type alias Value = String - - -{- ------------- - Storage Areas - ------------- -} - - -{-| Represents the `localStorage` and `sessionStorage` areas. -} -type Storage - = Local - | Session - - -{-| The browser's `localStorage` area. -} -local : Storage -local = Local - - -{-| The browser's `sessionStorage` area. -} -session : Storage -session = Session - - --- We expose a union type (above), rather than the actual native storage objects, --- so that what we expose can be pattern-matched against etc. -type NativeStorage = NativeStorage - - -nativeLocal : NativeStorage -nativeLocal = Native.WebAPI.Storage.localStorage - - -nativeSession : NativeStorage -nativeSession = Native.WebAPI.Storage.sessionStorage - - -toNative : Storage -> NativeStorage -toNative storage = - case storage of - Local -> nativeLocal - Session -> nativeSession - - -fromNative : NativeStorage -> Storage -fromNative native = - if native == nativeLocal - then Local - else if native == nativeSession - then Session - else Debug.crash "There shouldn't be another kind of NativeStorage" - - -{- ----- - Tasks - ----- -} - - -{-| A task which, when executed, determines the number of items stored in the -storage area. --} -length : Storage -> Task x Int -length = nativeLength << toNative - - -nativeLength : NativeStorage -> Task x Int -nativeLength = Native.WebAPI.Storage.length - - -{-| A task which, when executed, determines the name of the key at the given -index (zero-based). --} -key : Storage -> Int -> Task x (Maybe Key) -key storage = nativeKey (toNative storage) - - -nativeKey : NativeStorage -> Int -> Task x (Maybe Key) -nativeKey = Native.WebAPI.Storage.key - - -{-| A task which, when executed, gets the value at the given key. -} -get : Storage -> Key -> Task x (Maybe Value) -get storage = nativeGet (toNative storage) - - -nativeGet : NativeStorage -> Key -> Task x (Maybe Value) -nativeGet = Native.WebAPI.Storage.getItem - - -{-| A task which, when executed, sets the value at the given key, or fails with -an error message. --} -set : Storage -> Key -> NewValue -> Task String () -set storage = nativeSet (toNative storage) - - -nativeSet : NativeStorage -> Key -> NewValue -> Task String () -nativeSet = Native.WebAPI.Storage.setItem - - -{-| A task which, when executed, removes the item with the given key. -} -remove : Storage -> Key -> Task x () -remove storage = nativeRemove (toNative storage) - - -nativeRemove : NativeStorage -> Key -> Task x () -nativeRemove = Native.WebAPI.Storage.removeItem - - -{-| A task which, when executed, removes all items. -} -clear : Storage -> Task x () -clear = nativeClear << toNative - - -nativeClear : NativeStorage -> Task x () -nativeClear = Native.WebAPI.Storage.clear - - -{- ------ - Events - ------ -} - - -{- This is the signal produced by the native code ... this way, we can do -more of the processing in Elm, which is nicer. - -Note that it is a `Maybe` because Elm signals must have initial values, -and there is no natural initial value for the `NativeEvent` itself unless -we wrap it in a `Maybe`. --} -nativeEvents : Signal (Maybe NativeEvent) -nativeEvents = Native.WebAPI.Storage.nativeEvents - - -{- An event as produced by the native code. -} -type alias NativeEvent = - { key : Maybe Key - , oldValue : Maybe OldValue - , newValue : Maybe NewValue - , url : String - , storageArea : NativeStorage - } - - -{-| A storage event. -} -type alias Event = - { area : Storage - , url : String - , change : Change - } - - -{-| A change to a storage area. -} -type Change - = Add Key NewValue - | Remove Key OldValue - | Modify Key OldValue NewValue - | Clear - - -nativeEvent2Change : NativeEvent -> Change -nativeEvent2Change native = - case (native.key, native.oldValue, native.newValue) of - (Nothing, _, _) -> - Clear - - -- Safari does this - (Just "", _, _) -> - Clear - - (Just key, Just oldValue, Nothing) -> - Remove key oldValue - - (Just key, Nothing, Just newValue) -> - Add key newValue - - (Just key, Just oldValue, Just newValue) -> - Modify key oldValue newValue - - (_, Nothing, Nothing) -> - Debug.crash "The browser should never emit this." - - -nativeEvent2Event : NativeEvent -> Event -nativeEvent2Event native = - { area = fromNative native.storageArea - , url = native.url - , change = nativeEvent2Change native - } - - -{-| A signal of storage events. - -Note that a storage event is not fired within the same document that made a -storage change. Thus, you will only receive events for localStorage changes -that occur in a **separate** tab or window. - -This behaviour reflects how Javascript does things ... let me know if you'd -prefer to have *all* localStorage events go through this `Signal` -- it could -be arranged. - -At least in Safari, sessionStorage is even more restrictive than localStorage --- it is isolated per-tab, so you will only get events on sessionStorage if -using iframes. - -Note that this signal emits `Maybe Event` (rather than `Event`) because Elm -signals must have an initial value -- and there is no natural initial value for -an `Event` unless we wrap it in a `Maybe`. So, you'll often want to use -`Signal.filterMap` when you're integrating this into your own signal of -actions. --} -events : Signal (Maybe Event) -events = - Signal.map (Maybe.map nativeEvent2Event) nativeEvents diff --git a/src/vendor/elm-web-api/src/WebAPI/Window.elm b/src/vendor/elm-web-api/src/WebAPI/Window.elm deleted file mode 100644 index 7e46293..0000000 --- a/src/vendor/elm-web-api/src/WebAPI/Window.elm +++ /dev/null @@ -1,57 +0,0 @@ -module WebAPI.Window - ( alert, confirm, prompt - ) where - - -{-| Facilities from the browser's `window` object. - -See the [Mozilla documentation](https://developer.mozilla.org/en-US/docs/Web/API/Window) - -Note that some things on the browser's `window` object are handled through other -modules here. For instance: - -* localStorage, sessionStorage - See `WebAPI.Storage` - -* location - See `WebAPI.Location` - -* screen, screenX, screenY - See `WebAPI.Screen` - -* requestAnimationFrame, cancelAnimationFrame - See `WebAPI.AnimationFrame` - -@docs alert, confirm, prompt --} - - -import Task exposing (Task) -import Native.WebAPI.Window - - -{-| The browser's `window.alert()` function. --} -alert : String -> Task x () -alert = Native.WebAPI.Window.alert - - -{-| The browser's `window.confirm()` function. - -The task will succeed if the user confirms, and fail if the user cancels. --} -confirm : String -> Task () () -confirm = Native.WebAPI.Window.confirm - - -{-| The browser's `window.prompt()` function. - -The first parameter is a message, and the second parameter is a default -response. - -The task will succeed with the user's response, or fail if the user cancels -or enters blank text. --} -prompt : String -> String -> Task () String -prompt = Native.WebAPI.Window.prompt - diff --git a/src/vendor/elm-web-api/test/0.15/elm-package.json b/src/vendor/elm-web-api/test/0.15/elm-package.json deleted file mode 100644 index daab357..0000000 --- a/src/vendor/elm-web-api/test/0.15/elm-package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "version": "1.0.0", - "summary": "Tests for the WebAPI module", - "repository": "https://github.com/rgrempel/elm-web-api.git", - "license": "BSD3", - "source-directories": [ - "../src/elm", - "../../src", - "../../examples/src" - ], - "exposed-modules": [], - "native-modules": true, - "dependencies": { - "Apanatshka/elm-signal-extra": "5.6.0 <= v < 6.0.0", - "deadfoxygrandpa/Elm-Test": "1.0.4 <= v < 2.0.0", - "elm-lang/core": "2.0.0 <= v < 3.0.0", - "evancz/elm-html": "4.0.1 <= v < 5.0.0", - "maxsnew/IO": "1.0.1 <= v < 2.0.0", - "evancz/elm-effects": "2.0.0 <= v < 3.0.0", - "evancz/start-app": "2.0.1 <= v < 3.0.0" - }, - "elm-version": "0.15.0 <= v < 0.16.0" -} diff --git a/src/vendor/elm-web-api/test/0.15/package.json b/src/vendor/elm-web-api/test/0.15/package.json deleted file mode 100644 index 691ec8f..0000000 --- a/src/vendor/elm-web-api/test/0.15/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "elm-web-api", - "version": "1.0.0", - "description": "Tests for elm-web-api", - "main": "elm.js", - "dependencies": { - "mocha": "^2.3.3", - "node-localstorage": "^0.6.0", - "elm": "^2.0.0" - }, - "devDependencies": {}, - "scripts": { - "test": "elm-make ../src/elm/Browser.elm --output elm.html && open elm.html;", - "ci": "sh ../ci.sh", - "console": "elm-make ../src/elm/CommandLine.elm --output elm.js && cat ../src/elm/shim.js >> elm.js && node elm.js;" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/rgrempel/elm-web-api.git" - }, - "author": "Ryan Rempel ", - "license": "MIT", - "bugs": { - "url": "https://github.com/rgrempel/elm-web-api/issues" - }, - "homepage": "https://github.com/rgrempel/elm-web-api#readme" -} diff --git a/src/vendor/elm-web-api/test/0.16/elm-package.json b/src/vendor/elm-web-api/test/0.16/elm-package.json deleted file mode 100644 index 9236117..0000000 --- a/src/vendor/elm-web-api/test/0.16/elm-package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "version": "1.0.0", - "summary": "Tests for the WebAPI module", - "repository": "https://github.com/rgrempel/elm-web-api.git", - "license": "BSD3", - "source-directories": [ - "src", - "../src", - "../../src" - ], - "exposed-modules": [ - ], - "native-modules": true, - "dependencies": { - "elm-lang/core": "3.0.0 <= v < 4.0.0", - "avh4/elm-test": "1.0.0 <= v < 2.0.0", - "laszlopandy/elm-console": "1.0.0 <= v < 2.0.0" - }, - "elm-version": "0.16.0 <= v < 0.17.0" -} diff --git a/src/vendor/elm-web-api/test/0.16/package.json b/src/vendor/elm-web-api/test/0.16/package.json deleted file mode 100644 index 99022e1..0000000 --- a/src/vendor/elm-web-api/test/0.16/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "elm-web-api", - "version": "1.0.0", - "description": "Tests for elm-web-api", - "main": "elm.js", - "dependencies": { - "node-localstorage": "^0.6.0" - }, - "devDependencies": {}, - "scripts": { - "test": "elm-make ../src/Browser.elm --output elm.html && open elm.html;", - "console": "elm-make ../src/CommandLine.elm --output elm.js && cat ../src/shim.js >> elm.js && node elm.js;" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/rgrempel/elm-web-api.git" - }, - "author": "Ryan Rempel ", - "license": "MIT", - "bugs": { - "url": "https://github.com/rgrempel/elm-web-api/issues" - }, - "homepage": "https://github.com/rgrempel/elm-web-api#readme" -} diff --git a/src/vendor/elm-web-api/test/ci.sh b/src/vendor/elm-web-api/test/ci.sh deleted file mode 100644 index 0d7d474..0000000 --- a/src/vendor/elm-web-api/test/ci.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -# Print commands as we go -set -x - -elm-make ../src/elm/CI.elm --output elm.html || exit 1 -elm-make ../../examples/src/WindowExample.elm --output window.html || exit 1 -elm-make ../../examples/src/LocationExample.elm --output location.html || exit 1 -elm-make ../../examples/src/StorageExample.elm --output storage.html || exit 1 - -# Always exit 0 if we get this far ... the SauceLabs matrix takes over -mocha --delay ../src/run.js || exit 0 diff --git a/src/vendor/elm-web-api/test/package.json b/src/vendor/elm-web-api/test/package.json deleted file mode 100644 index e97063a..0000000 --- a/src/vendor/elm-web-api/test/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "elm-web-api", - "version": "1.0.0", - "description": "Tests for elm-web-api", - "main": "elm.js", - "dependencies": { - "chai": "^3.4.0", - "colors": "^1.1.2", - "count-substring": "^1.0.2", - "extend": "^3.0.0", - "git-rev": "^0.2.1", - "html-to-text": "^1.3.2", - "http-server": "^0.8.5", - "q": "^1.4.1", - "sauce-connect-launcher": "^0.13.0", - "saucelabs": "^1.0.1", - "selenium-standalone": "^4.7.1", - "webdriverio": "^3.2.6" - }, - "devDependencies": {}, - "scripts": {}, - "repository": { - "type": "git", - "url": "git+https://github.com/rgrempel/elm-web-api.git" - }, - "author": "Ryan Rempel ", - "license": "MIT", - "bugs": { - "url": "https://github.com/rgrempel/elm-web-api/issues" - }, - "homepage": "https://github.com/rgrempel/elm-web-api#readme" -} diff --git a/src/vendor/elm-web-api/test/src/config.js b/src/vendor/elm-web-api/test/src/config.js deleted file mode 100644 index 4bef20c..0000000 --- a/src/vendor/elm-web-api/test/src/config.js +++ /dev/null @@ -1,33 +0,0 @@ -var sauceUserName = process.env.GEB_SAUCE_LABS_USER; -var sauceAccessKey = process.env.GEB_SAUCE_LABS_ACCESS_PASSWORD; - -module.exports = { - // Configuration options - quiet: false, // Silences the console output - - webdriver: { // Options for Selenium WebDriver (WebdriverIO) - user: sauceUserName, - key: sauceAccessKey - }, - - httpServer: { // Options for local http server (npmjs.org/package/http-server) - disable: false, - port: 8080 // Non-standard option; it is passed into the httpServer.listen() method - }, - - sauceLabs: { // Options for SauceLabs API wrapper (npmjs.org/package/saucelabs) - username: sauceUserName, - password: sauceAccessKey - }, - - sauceConnect: { // Options for SauceLabs Connect (npmjs.org/package/sauce-connect-launcher) - disable: false, - username: sauceUserName, - accessKey: sauceAccessKey - }, - - selenium: { // Options for Selenium Server (npmjs.org/package/selenium-standalone). Only used if you need Selenium running locally. - args: [] // options to pass to `java -jar selenium-server-standalone-X.XX.X.jar` - } -}; - diff --git a/src/vendor/elm-web-api/test/src/elm/Browser.elm b/src/vendor/elm-web-api/test/src/elm/Browser.elm deleted file mode 100644 index 6f66d3a..0000000 --- a/src/vendor/elm-web-api/test/src/elm/Browser.elm +++ /dev/null @@ -1,25 +0,0 @@ -module Main where - -import Signal exposing (Signal, Mailbox, mailbox, constant, send) -import Task exposing (Task, andThen, sequence) -import Graphics.Element exposing (Element, empty, flow, down) -import ElmTest.Runner.Element exposing (runDisplay) -import Tests - - -main : Signal Element -main = - let - update test element = - flow down - [ element - , runDisplay test - ] - - in - Signal.foldp update empty (.signal Tests.tests) - - -port task : Task () () -port task = Tests.task - diff --git a/src/vendor/elm-web-api/test/src/elm/CI.elm b/src/vendor/elm-web-api/test/src/elm/CI.elm deleted file mode 100644 index 37b8f18..0000000 --- a/src/vendor/elm-web-api/test/src/elm/CI.elm +++ /dev/null @@ -1,31 +0,0 @@ -module Main where - -import Signal exposing (Signal, Mailbox, mailbox, constant, send) -import Task exposing (Task, andThen, sequence) -import Graphics.Element exposing (Element, empty, flow, down, show) -import ElmTest.Runner.String exposing (runDisplay) -import Html exposing (Html, pre, text) -import Html.Attributes exposing (id) -import Tests - - -main : Signal Html -main = - let - update test result = - (runDisplay test) :: result - - models = - Signal.foldp update [] (.signal Tests.tests) - - view model = - pre [id "results"] <| - List.map text model - - in - Signal.map view models - - -port task : Task () () -port task = Tests.task - diff --git a/src/vendor/elm-web-api/test/src/elm/CommandLine.elm b/src/vendor/elm-web-api/test/src/elm/CommandLine.elm deleted file mode 100644 index af635ac..0000000 --- a/src/vendor/elm-web-api/test/src/elm/CommandLine.elm +++ /dev/null @@ -1,27 +0,0 @@ -module Main where - -import Task exposing (Task) -import Tests -import ElmTest.Test exposing (Test) -import ElmTest.Runner.String exposing (runDisplay) - - -test : Task () Test -test = Tests.test - - -port task : Task () () -port task = - Task.map runDisplay test - `Task.andThen` Signal.send results.address - - -results : Signal.Mailbox String -results = Signal.mailbox "" - - -port result : Signal String -port result = - results.signal - - diff --git a/src/vendor/elm-web-api/test/src/elm/Native/TestUtil.js b/src/vendor/elm-web-api/test/src/elm/Native/TestUtil.js deleted file mode 100644 index 93cfaed..0000000 --- a/src/vendor/elm-web-api/test/src/elm/Native/TestUtil.js +++ /dev/null @@ -1,40 +0,0 @@ -Elm.Native.TestUtil = {}; -Elm.Native.TestUtil.make = function (localRuntime) { - localRuntime.Native = localRuntime.Native || {}; - localRuntime.Native.TestUtil = localRuntime.Native.TestUtil || {}; - - if (!localRuntime.Native.TestUtil.values) { - var Task = Elm.Native.Task.make(localRuntime); - var Signal = Elm.Native.Signal.make(localRuntime); - - var sample = function (signal) { - // Use closure to track value - var val = signal.value; - - var handler = function (value) { - val = value; - }; - - // We construct a new "output" node, because otherwise the incoming - // signal may be pruned by trimDeadNodes() in Runtime.js - // (if trimDeadNodes() sees that it is not otherwise used). - var output = Signal.output("sample-" + signal.name, handler, signal); - - return Task.asyncFunction(function (callback) { - // Need to return the value inside setTimeout, because - // otherwise we can be called out-of-order ... that is, a - // previous `Task.andThen` which updated a Signal may not have - // actually completed yet unless we do this inside a timeout. - localRuntime.setTimeout(function () { - callback(Task.succeed(val)); - }, 0); - }); - }; - - localRuntime.Native.TestUtil.values = { - sample: sample - }; - } - - return localRuntime.Native.TestUtil.values; -}; diff --git a/src/vendor/elm-web-api/test/src/elm/TestMailbox.elm b/src/vendor/elm-web-api/test/src/elm/TestMailbox.elm deleted file mode 100644 index 5104081..0000000 --- a/src/vendor/elm-web-api/test/src/elm/TestMailbox.elm +++ /dev/null @@ -1,9 +0,0 @@ -module TestMailbox where - -import Signal exposing (Signal, Mailbox, mailbox, constant, send) -import ElmTest.Test exposing (Test, suite) - - -tests : Mailbox Test -tests = - mailbox (suite "Tests have not arrived yet" []) diff --git a/src/vendor/elm-web-api/test/src/elm/TestUtil.elm b/src/vendor/elm-web-api/test/src/elm/TestUtil.elm deleted file mode 100644 index 60fed52..0000000 --- a/src/vendor/elm-web-api/test/src/elm/TestUtil.elm +++ /dev/null @@ -1,17 +0,0 @@ -module TestUtil (sample) where - - -{-| Test utilities. - -@docs sample --} - - -import Task exposing (Task) -import Native.TestUtil - - -{-| Construct a task which, when performed, will return the current value of a Signal. -} -sample : Signal a -> Task x a -sample = - Native.TestUtil.sample diff --git a/src/vendor/elm-web-api/test/src/elm/Tests.elm b/src/vendor/elm-web-api/test/src/elm/Tests.elm deleted file mode 100644 index 383efd3..0000000 --- a/src/vendor/elm-web-api/test/src/elm/Tests.elm +++ /dev/null @@ -1,42 +0,0 @@ -module Tests where - -import Signal exposing (Signal, Mailbox, mailbox, constant, send) -import Task exposing (Task, andThen, sequence) -import ElmTest.Test exposing (Test, suite) - -import TestMailbox - -import WebAPI.MathTest -import WebAPI.NumberTest -import WebAPI.StorageTest -import WebAPI.ScreenTest -import WebAPI.LocationTest -import WebAPI.DateTest -import WebAPI.AnimationFrameTest -import WebAPI.CookieTest -import WebAPI.DocumentTest - - -test : Task () Test -test = - Task.map (suite "WebAPI tests") <| - sequence - [ WebAPI.DocumentTest.tests - , WebAPI.MathTest.tests - , WebAPI.NumberTest.tests - , WebAPI.StorageTest.tests - , WebAPI.ScreenTest.tests - , WebAPI.LocationTest.tests - , WebAPI.DateTest.tests - , WebAPI.AnimationFrameTest.tests - , WebAPI.CookieTest.tests - ] - - -task : Task () () -task = - test `andThen` send tests.address - - -tests : Mailbox Test -tests = TestMailbox.tests diff --git a/src/vendor/elm-web-api/test/src/elm/WebAPI/AnimationFrameTest.elm b/src/vendor/elm-web-api/test/src/elm/WebAPI/AnimationFrameTest.elm deleted file mode 100644 index 9ccec92..0000000 --- a/src/vendor/elm-web-api/test/src/elm/WebAPI/AnimationFrameTest.elm +++ /dev/null @@ -1,111 +0,0 @@ -module WebAPI.AnimationFrameTest where - -import ElmTest.Test exposing (..) -import ElmTest.Assertion exposing (..) -import Task exposing (Task, sequence, succeed, andThen) -import Time exposing (Time) -import Signal exposing (Signal, Mailbox, mailbox) -import String - -import WebAPI.AnimationFrame as AnimationFrame -import WebAPI.Date -import Debug -import TestUtil exposing (sample) - - --- When testing on SauceLabs, this is really slow ... much faster locally ... --- not sure why. So, we allow an absurd amount of time for a frame here ... --- when run locally, the frame rate is about right. So, this is not really --- testing performance at the moment, just that it works at all. -frame : Time -frame = Time.second - - -taskTest : Task () Test -taskTest = - WebAPI.Date.now `andThen` (\startTime -> - AnimationFrame.task `andThen` (\timestamp -> - AnimationFrame.task `andThen` (\timestamp2 -> - WebAPI.Date.now `andThen` (\endTime -> - let - wallTime = - endTime - startTime - - delta = - timestamp2 - timestamp - - in - Task.succeed <| - suite "task" - [ test - ( String.join " " - [ "wall time" - , toString wallTime - , "is less than" - , toString (frame * 2) - ] - ) <| - assert (wallTime < frame * 2) - , test - ( String.join " " - [ "callback time" - , toString delta - , "is less than" - , toString frame - ] - ) <| - assert (delta < frame) - ] - )))) - - -result : Mailbox Time -result = mailbox 0 - - -delay : Time -delay = 0.2 * Time.second - - -requestTest : Task () Test -requestTest = - let - task time = - Signal.send result.address time - - in - Task.map (\time -> - test ("request fired at " ++ (toString time)) <| - assert (time > 0) - ) <| Signal.send result.address 0 - `andThen` always (AnimationFrame.request task) - `andThen` always (Task.sleep delay) - `andThen` always (sample result.signal) - - -cancelTest : Task () Test -cancelTest = - let - task time = - Signal.send result.address time - - in - Task.map (\time -> - test "request should have been canceled" <| - assertEqual 0 time - ) <| Signal.send result.address 0 - `andThen` always (AnimationFrame.request task) - `andThen` AnimationFrame.cancel - `andThen` always (Task.sleep delay) - `andThen` always (sample result.signal) - - -tests : Task () Test -tests = - Task.map (suite "WebAPI.AnimationFrameTest") <| - sequence <| - [ taskTest - , requestTest - , cancelTest - ] - diff --git a/src/vendor/elm-web-api/test/src/elm/WebAPI/CookieTest.elm b/src/vendor/elm-web-api/test/src/elm/WebAPI/CookieTest.elm deleted file mode 100644 index 91dc926..0000000 --- a/src/vendor/elm-web-api/test/src/elm/WebAPI/CookieTest.elm +++ /dev/null @@ -1,158 +0,0 @@ -module WebAPI.CookieTest where - -import String -import ElmTest.Assertion exposing (..) -import ElmTest.Test exposing (..) -import ElmTest.Runner.Element exposing (runDisplay) - -import Task exposing (Task, andThen, sequence, map) -import Date exposing (fromTime) -import Time exposing (second) -import Dict - -import WebAPI.Cookie as Cookies - - -(>>>) = flip Task.map -(>>+) = Task.andThen - - -simpleSetGet : Task x Test -simpleSetGet = - (Cookies.set "bog" "joe" >>+ always Cookies.get) - >>> - (Dict.get "bog" >> assertEqual (Just "joe") >> test "simple set then get") - - --- Make sure we're actually *changing* the cookie -secondSetGet : Task x Test -secondSetGet = - (Cookies.set "bog" "frank" >>+ always Cookies.get) - >>> - (Dict.get "bog" >> assertEqual (Just "frank") >> test "repeated set/get, to make sure we can change the cookie") - - -multipleSetGet : Task x Test -multipleSetGet = - ( - Cookies.set "cookie1" "cookie 1 value" - >>+ - always (Cookies.set "cookie2" "cookie 2 value") - >>+ - always Cookies.get - ) - >>> - (\cookies -> - [Dict.get "cookie1" cookies, Dict.get "cookie2" cookies] |> - assertEqual [Just "cookie 1 value", Just "cookie 2 value"] |> - test "multiple cookies") - - -encodingTest : Task x Test -encodingTest = - (Cookies.set "encoded=" "value needs encoding ;" >>+ always Cookies.get) - >>> - (Dict.get "encoded=" >> assertEqual (Just "value needs encoding ;") >> test "key and value should be encoded") - - -setWithWrongPath : Task x Test -setWithWrongPath = - let - defaults = - Cookies.defaultOptions - - options = - -- { defaults | path = Just "/path" } - Cookies.Options (Just "/path") Nothing Nothing Nothing Nothing - - in - (Cookies.setWith options "wrong path cookie" "path cookie value" >>+ always Cookies.get) - >>> - (Dict.get "wrong path cookie" >> assertEqual Nothing >> test "test with path set to bad value") - - -setWithGoodPath : Task x Test -setWithGoodPath = - let - defaults = - Cookies.defaultOptions - - options = - -- { defaults | path = Just "" } - Cookies.Options (Just "") Nothing Nothing Nothing Nothing - - in - (Cookies.setWith options "good path cookie" "path cookie value" >>+ always Cookies.get) - >>> - (Dict.get "good path cookie" >> assertEqual (Just "path cookie value") >> test "test with path set to good value") - - -maxAgeFuture : Task x Test -maxAgeFuture = - let - defaults = - Cookies.defaultOptions - - options = - -- { defaults | maxAge = Just 1000 } - Cookies.Options Nothing Nothing (Just 1000) Nothing Nothing - - in - (Cookies.setWith options "max age future" "cookie value" >>+ always Cookies.get) - >>> - (Dict.get "max age future" >> assertEqual (Just "cookie value") >> test "test with maxAge in future") - - -expiresInFuture : Task x Test -expiresInFuture = - let - defaults = - Cookies.defaultOptions - - options = - -- Sets the date to September 26, 2028 conveniently - -- { defaults | expires = Just (fromTime (1853609409 * second)) } - Cookies.Options Nothing Nothing Nothing (Just (fromTime (1853609409 * second))) Nothing - - in - (Cookies.setWith options "expires" "cookie value" >>+ always Cookies.get) - >>> - (Dict.get "expires" >> assertEqual (Just "cookie value") >> test "test with expiry in future") - - -expiresInPast : Task x Test -expiresInPast = - let - defaults = - Cookies.defaultOptions - - options = - -- { defaults | expires = Just (fromTime 0) } - Cookies.Options Nothing Nothing Nothing (Just (fromTime 0)) Nothing - - in - (Cookies.setWith options "expires" "cookie value" >>+ always Cookies.get) - >>> - (Dict.get "expires" >> assertEqual (Nothing) >> test "test with expiry in past") - - -tests : Task () Test -tests = - Task.map (suite "WebAPI.CookieTest") <| - sequence - [ simpleSetGet - , secondSetGet - , multipleSetGet - , encodingTest - - -- TODO: setWithWrongPath is "failing" on Chrome, but it's not really a proper - -- test, since I'm just trying to retrieve it locally -- what the path really - -- controls is whether it's sent to the server. So, in principle I ought to - -- do a more sophisticated test of this ... - -- , setWithWrongPath - - , setWithGoodPath - , maxAgeFuture - , expiresInFuture - , expiresInPast - ] diff --git a/src/vendor/elm-web-api/test/src/elm/WebAPI/DateTest.elm b/src/vendor/elm-web-api/test/src/elm/WebAPI/DateTest.elm deleted file mode 100644 index 2658af7..0000000 --- a/src/vendor/elm-web-api/test/src/elm/WebAPI/DateTest.elm +++ /dev/null @@ -1,386 +0,0 @@ -module WebAPI.DateTest where - -import ElmTest.Test exposing (..) -import ElmTest.Assertion exposing (..) -import Task exposing (Task, sequence, succeed, andThen) - -import WebAPI.Date exposing (..) -import Date -import Time -import String - - -testCurrent : Task x Test -testCurrent = - current |> - Task.map (\c -> - test "current" <| - assert <| - (Date.year c) > 2014 - ) - - -testNow : Task x Test -testNow = - now |> - Task.map (\n -> - test "now" <| - assert <| - n > 1445463720748 - ) - - -testTimezoneOffset : Test -testTimezoneOffset = - let - date = - fromParts Local (Parts 2015 1 1 1 1 1 1) - - in - test "timezoneOffset" <| - assert <| - (abs (timezoneOffset date)) < (12 * Time.hour) - - -testFromPartsLocal : Test -testFromPartsLocal = - let - date = - fromParts Local (Parts 2015 1 2 3 4 5 6) - - tuple = - ( Date.year date - , Date.month date - , Date.day date - , Date.hour date - , Date.minute date - , Date.second date - , Date.millisecond date - ) - - in - test "fromParts Local" <| - assertEqual - (2015, Date.Feb, 2, 3, 4, 5, 6) - tuple - - -testFromAndToParts : String -> Timezone -> Test -testFromAndToParts title zone = - let - parts = - Parts 2015 1 2 3 4 5 6 - - date = - fromParts zone parts - - result = - toParts zone date - - back = - fromParts zone result - - in - test title <| - assertEqual - (Date.toTime date) - (Date.toTime back) - - -testDayOfWeek : String -> Timezone -> Test -testDayOfWeek title zone = - let - makeTest (string, expected, parts) = - test string <| - assertEqual expected <| - dayOfWeek zone (fromParts zone parts) - - in - suite title <| - List.map makeTest - [ ("Sunday", Date.Sun, Parts 2015 1 1 0 0 0 0) - , ("Monday", Date.Mon, Parts 2015 1 2 0 0 0 0) - , ("Tuesday", Date.Tue, Parts 2015 1 3 0 0 0 0) - , ("Wednesday", Date.Wed, Parts 2015 1 4 0 0 0 0) - , ("Thursday", Date.Thu, Parts 2015 1 5 0 0 0 0) - , ("Friday", Date.Fri, Parts 2015 1 6 0 0 0 0) - , ("Saturday", Date.Sat, Parts 2015 1 7 0 0 0 0) - ] - - -testToMonth : Test -testToMonth = - let - makeTest (input, expected) = - test (toString input) <| - assertEqual - expected - (toMonth input) - - in - suite "toMonth" <| - List.map makeTest - [ (-8, Date.May) - , (-7, Date.Jun) - , (-6, Date.Jul) - , (-5, Date.Aug) - , (-4, Date.Sep) - , (-3, Date.Oct) - , (-2, Date.Nov) - , (-1, Date.Dec) - , (0, Date.Jan) - , (1, Date.Feb) - , (2, Date.Mar) - , (3, Date.Apr) - , (4, Date.May) - , (5, Date.Jun) - , (6, Date.Jul) - , (7, Date.Aug) - , (8, Date.Sep) - , (9, Date.Oct) - , (10, Date.Nov) - , (11, Date.Dec) - , (12, Date.Jan) - , (13, Date.Feb) - ] - - -testFromMonth : Test -testFromMonth = - let - makeTest (expected, input) = - test (toString input) <| - assertEqual - expected - (fromMonth input) - - in - suite "fromMonth" <| - List.map makeTest - [ (0, Date.Jan) - , (1, Date.Feb) - , (2, Date.Mar) - , (3, Date.Apr) - , (4, Date.May) - , (5, Date.Jun) - , (6, Date.Jul) - , (7, Date.Aug) - , (8, Date.Sep) - , (9, Date.Oct) - , (10, Date.Nov) - , (11, Date.Dec) - ] - - -testToDay : Test -testToDay = - let - makeTest (input, expected) = - test (toString input) <| - assertEqual - expected - (toDay input) - - in - suite "toMonth" <| - List.map makeTest - [ (-8, Date.Sat) - , (-7, Date.Sun) - , (-6, Date.Mon) - , (-5, Date.Tue) - , (-4, Date.Wed) - , (-3, Date.Thu) - , (-2, Date.Fri) - , (-1, Date.Sat) - , (0, Date.Sun) - , (1, Date.Mon) - , (2, Date.Tue) - , (3, Date.Wed) - , (4, Date.Thu) - , (5, Date.Fri) - , (6, Date.Sat) - , (7, Date.Sun) - , (8, Date.Mon) - , (9, Date.Tue) - , (10, Date.Wed) - , (11, Date.Thu) - , (12, Date.Fri) - , (13, Date.Sat) - ] - - -testFromDay : Test -testFromDay = - let - makeTest (expected, input) = - test (toString input) <| - assertEqual - expected - (fromDay input) - - in - suite "fromMonth" <| - List.map makeTest - [ (0, Date.Sun) - , (1, Date.Mon) - , (2, Date.Tue) - , (3, Date.Wed) - , (4, Date.Thu) - , (5, Date.Fri) - , (6, Date.Sat) - ] - - -offsetTimeTest : Test -offsetTimeTest = - let - date = - fromParts Local (Parts 2015 1 2 3 4 5 6) - - forward = - fromParts Local (Parts 2015 1 2 3 5 5 6) - - backward = - fromParts Local (Parts 2015 1 2 3 3 5 6) - - offsetTimePositive = - test "positive" <| - assertEqual - (Date.toTime forward) - (Date.toTime (offsetTime (1 * Time.minute) date)) - - offsetTimeNegative = - test "negative" <| - assertEqual - (Date.toTime backward) - (Date.toTime (offsetTime (-1 * Time.minute) date)) - - in - suite "offsetTime" - [ offsetTimePositive - , offsetTimeNegative - ] - - -offsetYearTest : String -> Timezone -> Test -offsetYearTest title zone = - let - date = - fromParts zone (Parts 2015 1 2 3 4 5 6) - - forward = - fromParts zone (Parts 2016 1 2 3 4 5 6) - - backward = - fromParts zone (Parts 2014 1 2 3 4 5 6) - - offsetYearPositive = - test "positive" <| - assertEqual - (Date.toTime forward) - (Date.toTime (offsetYear zone 1 date)) - - offsetYearNegative = - test "negative" <| - assertEqual - (Date.toTime backward) - (Date.toTime (offsetYear zone -1 date)) - - in - suite title - [ offsetYearPositive - , offsetYearNegative - ] - - -offsetMonthTest : String -> Timezone -> Test -offsetMonthTest title zone = - let - date = - fromParts zone (Parts 2015 0 2 3 4 5 6) - - forward = - fromParts zone (Parts 2015 1 2 3 4 5 6) - - backward = - fromParts zone (Parts 2014 11 2 3 4 5 6) - - offsetPositive = - test "positive" <| - assertEqual - (Date.toTime forward) - (Date.toTime (offsetMonth zone 1 date)) - - offsetNegative = - test "negative" <| - assertEqual - (Date.toTime backward) - (Date.toTime (offsetMonth zone -1 date)) - - in - suite title - [ offsetPositive - , offsetNegative - ] - - -timescale : Test -timescale = - suite "timescale" - [ test "day" <| assertEqual 86400000 day - , test "inDays" <| assertEqual 1 (inDays 86400000) - , test "week" <| assertEqual 604800000 week - , test "inWeeks" <| assertEqual 1 (inWeeks 604800000) - ] - - -strings : Test -strings = - let - date = - fromParts UTC (Parts 2015 0 2 3 4 5 6) - - utc = - utcString date - - in - suite "Strings" - -- For dateString and timeString, we'd have to do some calculation - -- based on timezoneOffset to figure out the real expectation - [ test "dateString" <| assert <| String.length (dateString date) > 6 - , test "timeString" <| assert <| String.length (timeString date) > 6 - , test "isoString" <| assertEqual "2015-01-02T03:04:05.006Z" (isoString date) - , test ("utcString " ++ utc) <| - assert <| - "Fri, 02 Jan 2015 03:04:05 GMT" == utc || - -- This is from IE 10 - "Fri, 2 Jan 2015 03:04:05 UTC" == utc - ] - - -tests : Task () Test -tests = - Task.map (suite "WebAPI.DateTest") <| - sequence <| - [ testCurrent - , testNow - ] - ++ - List.map Task.succeed - [ testTimezoneOffset - , testFromPartsLocal - , testFromAndToParts "fromParts and toParts Local" Local - , testFromAndToParts "fromParts and toParts UTC" UTC - , testDayOfWeek "dayOfWeek Local" Local - , testDayOfWeek "dayOfWeek UTC" UTC - , testToMonth, testFromMonth - , testToDay, testFromDay - , offsetTimeTest - , offsetYearTest "offsetYear Local" Local - , offsetYearTest "offsetYear UTC" UTC - , offsetMonthTest "offsetMonth Local" Local - , offsetMonthTest "offsetMonth UTC" UTC - , timescale - , strings - ] - diff --git a/src/vendor/elm-web-api/test/src/elm/WebAPI/DocumentTest.elm b/src/vendor/elm-web-api/test/src/elm/WebAPI/DocumentTest.elm deleted file mode 100644 index 8061bd3..0000000 --- a/src/vendor/elm-web-api/test/src/elm/WebAPI/DocumentTest.elm +++ /dev/null @@ -1,77 +0,0 @@ -module WebAPI.DocumentTest where - -import String -import ElmTest.Assertion exposing (..) -import ElmTest.Test exposing (..) - -import Task exposing (Task, andThen, sequence, map) -import TestUtil exposing (sample) -import Signal.Extra exposing (foldp') -import Time - -import WebAPI.Document exposing (..) - - -(>>>) = flip Task.map -(>>+) = Task.andThen - - -(>>-) task func = - task `andThen` (always func) - - -getReadyStateTest : Task x Test -getReadyStateTest = - getReadyState >>> (\state -> - test ("getReadyState got " ++ (toString state)) <| - -- Basically, this succeeds if it doesn't throw an error - assert <| - state == Loading || - state == Interactive || - state == Complete - ) - - -readyStateTest : Task x Test -readyStateTest = - let - accumulator = - foldp' (::) (\s -> [s]) readyState - - in - Task.sleep (0.5 * Time.second) >>- - sample accumulator >>> (\list -> - test ("readyState signal: " ++ (toString list)) <| - assert <| - List.length list >= 2 - ) - - -getTitleTest : Task x Test -getTitleTest = - getTitle >>> (\title -> - test "getTitle should be 'Main'" <| - assertEqual "Main" title - ) - - -setTitleTest : Task x Test -setTitleTest = - setTitle "New title" >>+ (\setTitleResponse -> - getTitle >>> (\newTitle -> - test "setTitle should work" <| - assert <| - setTitleResponse == () && - newTitle == "New title" - ) - ) - - -tests : Task () Test -tests = - Task.map (suite "WebAPI.DocumentTest") <| - sequence - [ getReadyStateTest - , readyStateTest - , getTitleTest, setTitleTest - ] diff --git a/src/vendor/elm-web-api/test/src/elm/WebAPI/LocationTest.elm b/src/vendor/elm-web-api/test/src/elm/WebAPI/LocationTest.elm deleted file mode 100644 index 01f35db..0000000 --- a/src/vendor/elm-web-api/test/src/elm/WebAPI/LocationTest.elm +++ /dev/null @@ -1,45 +0,0 @@ -module WebAPI.LocationTest where - -import ElmTest.Test exposing (..) -import ElmTest.Assertion exposing (..) -import Task exposing (Task, sequence, succeed, andThen) -import String - -import WebAPI.Location exposing (..) - - -locationTest : Task () Test -locationTest = - location |> - Task.map (\loc -> - suite "location" - [ test "hash" <| assertEqual "" loc.hash - , test "host" <| assertEqual "localhost:8080" loc.host - , test "hostname" <| assertEqual "localhost" loc.hostname - , test "href" <| - assert <| - List.all identity - [ String.startsWith "http://" loc.href - , String.endsWith "elm.html" loc.href - ] - , test "origin" <| assertEqual "http://localhost:8080" loc.origin - , test "pathname" <| - assert <| - List.all identity - [ String.startsWith "/" loc.pathname - , String.endsWith "elm.html" loc.pathname - ] - , test "port'" <| assertEqual "8080" loc.port' - , test "protocol" <| assertEqual "http:" loc.protocol - , test "search" <| assertEqual "" loc.search - ] - ) - - -tests : Task () Test -tests = - Task.map (suite "WebAPI.LocationTest") <| - sequence <| - [ locationTest - ] - diff --git a/src/vendor/elm-web-api/test/src/elm/WebAPI/MathTest.elm b/src/vendor/elm-web-api/test/src/elm/WebAPI/MathTest.elm deleted file mode 100644 index 89c0500..0000000 --- a/src/vendor/elm-web-api/test/src/elm/WebAPI/MathTest.elm +++ /dev/null @@ -1,46 +0,0 @@ -module WebAPI.MathTest where - -import ElmTest.Test exposing (..) -import ElmTest.Assertion exposing (..) -import Task exposing (Task, sequence, succeed) - -import WebAPI.Math - - -within : Float -> Float -> Float -> Assertion -within tolerance value1 value2 = - assert <| - abs (value1 - value2) < tolerance - - -within001 : Float -> Float -> Assertion -within001 = within 0.001 - - -random : Task x Test -random = - WebAPI.Math.random |> - Task.map (\r -> - test "random" <| - assert <| - (r >= 0 && r <= 1) - ) - - -tests : Task x Test -tests = - Task.map (suite "WebAPI.Math") <| - sequence <| - List.map succeed - [ test "ln2" <| within001 WebAPI.Math.ln2 0.693 - , test "ln10" <| within001 WebAPI.Math.ln10 2.303 - , test "log2e" <| within001 WebAPI.Math.log2e 1.443 - , test "log10e" <| within001 WebAPI.Math.log10e 0.434 - , test "sqrt1_2" <| within001 WebAPI.Math.sqrt1_2 0.707 - , test "sqrt2" <| within001 WebAPI.Math.sqrt2 1.414 - , test "exp" <| within001 (WebAPI.Math.exp 2) (e ^ 2) - , test "log" <| within001 (WebAPI.Math.log 27) (logBase e 27) - ] - ++ - [ random - ] diff --git a/src/vendor/elm-web-api/test/src/elm/WebAPI/NumberTest.elm b/src/vendor/elm-web-api/test/src/elm/WebAPI/NumberTest.elm deleted file mode 100644 index 8dacad9..0000000 --- a/src/vendor/elm-web-api/test/src/elm/WebAPI/NumberTest.elm +++ /dev/null @@ -1,73 +0,0 @@ -module WebAPI.NumberTest where - -import ElmTest.Test exposing (..) -import ElmTest.Assertion exposing (..) -import Task exposing (Task, sequence, succeed) -import Result exposing (Result(..)) - -import WebAPI.Number - - -within : Float -> Float -> Float -> Assertion -within tolerance value1 value2 = - assert <| - abs (value1 - value2) < tolerance - - -within001 : Float -> Float -> Assertion -within001 = within 0.001 - - -isErr : Result a b -> Bool -isErr result = - case result of - Ok _ -> False - Err _ -> True - - -toFixedDigitsFailure : Test -toFixedDigitsFailure = - let - result = - WebAPI.Number.toFixedDigits -10 200 - - in - -- Firefox gives 0 for this, which I suppose is somewhat sensible - test ("toFixedDigits -10 200 should fail, or be 0: " ++ (toString result)) <| - assert <| - (isErr result) || result == (Ok "0") - - -tests : Task x Test -tests = - Task.map (suite "WebAPI.Number") <| - sequence <| - List.map succeed - [ test "maxValue" <| assert <| WebAPI.Number.maxValue > 1000 - , test "minValue" <| within001 WebAPI.Number.minValue 0 - , test "nan" <| assert <| isNaN WebAPI.Number.nan - , test "negativeInfinity" <| assert <| isInfinite WebAPI.Number.negativeInfinity - , test "positiveInfinity" <| assert <| isInfinite WebAPI.Number.positiveInfinity - , test "toExponential" <| assertEqual (WebAPI.Number.toExponential 200) "2e+2" - , test "toExponentialDigits success" <| assertEqual (WebAPI.Number.toExponentialDigits 1 200.0) (Ok "2.0e+2") - , test "toExponentialDigits failure" <| assert <| isErr (WebAPI.Number.toExponentialDigits -10 200) - , test "toExponentialDigits integer" <| assertEqual (WebAPI.Number.toExponentialDigits 1 200) (Ok "2.0e+2") - , test "safeExponentialDigits success" <| assertEqual (WebAPI.Number.safeExponentialDigits 1 200.0) "2.0e+2" - , test "safeExponentialDigits failure" <| assertEqual (WebAPI.Number.safeExponentialDigits -10 200.0) "2e+2" - , test "toFixed" <| assertEqual (WebAPI.Number.toFixed 200.1) "200" - , test "toFixedDigits success" <| assertEqual (WebAPI.Number.toFixedDigits 2 200.1) (Ok "200.10") - , toFixedDigitsFailure - , test "toFixedDigits integer" <| assertEqual (WebAPI.Number.toFixedDigits 2 200) (Ok "200.00") - , test "safeFixedDigits success" <| assertEqual (WebAPI.Number.safeFixedDigits 2 200.1) "200.10" - , test "safeFixedDigits failure" <| assertEqual (WebAPI.Number.safeFixedDigits -10 200.1) "200" - , test "toPrecisionDigits success" <| assertEqual (WebAPI.Number.toPrecisionDigits 5 200.1) (Ok "200.10") - , test "toPrecisionDigits failure" <| assert <| isErr (WebAPI.Number.toPrecisionDigits -10 200) - , test "toPrecisionDigits integer" <| assertEqual (WebAPI.Number.toPrecisionDigits 2 223) (Ok "2.2e+2") - , test "safePrecisionDigits success" <| assertEqual (WebAPI.Number.safePrecisionDigits 5 200.1) "200.10" - , test "safePrecisionDigits failure" <| assertEqual (WebAPI.Number.safePrecisionDigits -10 200.1) "2e+2" - , test "toStringUsingBase success" <| assertEqual (WebAPI.Number.toStringUsingBase 16 32.0) (Ok "20") - , test "toStringUsingBase failure" <| assert <| isErr (WebAPI.Number.toStringUsingBase -10 200) - , test "toStringUsingBase integer" <| assertEqual (WebAPI.Number.toStringUsingBase 16 32) (Ok "20") - , test "safeStringUsingBase success" <| assertEqual (WebAPI.Number.safeStringUsingBase 16 32.0) "20" - , test "safeStringUsingBase failure" <| assertEqual (WebAPI.Number.safeStringUsingBase -10 32) "100000" - ] diff --git a/src/vendor/elm-web-api/test/src/elm/WebAPI/ScreenTest.elm b/src/vendor/elm-web-api/test/src/elm/WebAPI/ScreenTest.elm deleted file mode 100644 index d40f9c2..0000000 --- a/src/vendor/elm-web-api/test/src/elm/WebAPI/ScreenTest.elm +++ /dev/null @@ -1,55 +0,0 @@ -module WebAPI.ScreenTest where - -import ElmTest.Test exposing (..) -import ElmTest.Assertion exposing (..) -import Task exposing (Task, sequence, succeed, andThen) -import String - -import WebAPI.Screen exposing (..) - - -screenTest : Task () Test -screenTest = - screen |> - Task.map (\s -> - test "screen" << - assert <| - List.all ((flip (>=)) 0) <| - List.map ((|>) s) - [ .availTop - , .availLeft - , .availHeight - , .availWidth - , .colorDepth - , .pixelDepth - , .height - , .width - ] - ) - - -screenXYTest : Task () Test -screenXYTest = - screenXY |> - Task.map (\(x, y) -> - test - (String.join "" - [ "screenXY (" - , toString x, ", " - , toString y, ")" - ] - ) <| - -- Note that in IE, you get (-8, -8), which I suppose actually - -- is meaningful. - assert (x >= -32 && y >= -32) - ) - - -tests : Task () Test -tests = - Task.map (suite "WebAPI.ScreenTest") <| - sequence <| - [ screenTest - , screenXYTest - ] - diff --git a/src/vendor/elm-web-api/test/src/elm/WebAPI/StorageTest.elm b/src/vendor/elm-web-api/test/src/elm/WebAPI/StorageTest.elm deleted file mode 100644 index 6a7bc1f..0000000 --- a/src/vendor/elm-web-api/test/src/elm/WebAPI/StorageTest.elm +++ /dev/null @@ -1,104 +0,0 @@ -module WebAPI.StorageTest where - -import ElmTest.Test exposing (..) -import ElmTest.Assertion exposing (..) -import Task exposing (Task, sequence, succeed, andThen) - -import WebAPI.Storage exposing (..) - - -length0Test : Storage -> Task () Test -length0Test storage = - clear storage `andThen` - always (length storage) |> - Task.map (assertEqual 0 >> test "length") - - -length1Test : Storage -> Task () Test -length1Test storage = - clear storage `andThen` - always (set storage "bob" "joe") `andThen` - always (length storage) |> - Task.map (assertEqual 1 >> test "length") |> - Task.mapError (always ()) - - -keyTestSuccess : Storage -> Task () Test -keyTestSuccess storage = - clear storage `andThen` - always (set storage "bob" "joe") `andThen` - always (key storage 0) |> - Task.map (assertEqual (Just "bob") >> test "keySuccess") |> - Task.mapError (always ()) - - -keyTestError : Storage -> Task () Test -keyTestError storage = - clear storage `andThen` - always (set storage "bob" "joe") `andThen` - always (key storage 5) |> - Task.map (assertEqual Nothing >> test "keyError") |> - Task.mapError (always ()) - - -getItemTestSuccess : Storage -> Task () Test -getItemTestSuccess storage = - clear storage `andThen` - always (set storage "bob" "joe") `andThen` - always (get storage "bob") |> - Task.map (assertEqual (Just "joe") >> test "getItemSuccess") |> - Task.mapError (always ()) - - -getItemTestError : Storage -> Task () Test -getItemTestError storage = - clear storage `andThen` - always (set storage "bob" "joe") `andThen` - always (get storage "wrong") |> - Task.map (assertEqual Nothing >> test "getItemError") |> - Task.mapError (always ()) - - -removeItemTest : Storage -> Task () Test -removeItemTest storage = - clear storage `andThen` - always (set storage "bob" "joe") `andThen` - always (remove storage "bob") `andThen` - always (length storage) |> - Task.map (assertEqual 0 >> test "removeItem") |> - Task.mapError (always ()) - - -removeItemTestError : Storage -> Task () Test -removeItemTestError storage = - clear storage `andThen` - always (set storage "bob" "joe") `andThen` - always (remove storage "not there") `andThen` - always (length storage) |> - Task.map (assertEqual 1 >> test "removeItem") |> - Task.mapError (always ()) - - -tests : Task () Test -tests = - Task.map (suite "Storage") <| - sequence <| - List.map makeSuite - [ (local, "localStorage") - , (session, "sessionStorage") - ] - - -makeSuite : (Storage, String) -> Task () Test -makeSuite (storage, label) = - Task.map (suite label) <| - sequence - [ length0Test storage - , length1Test storage - , keyTestSuccess storage - , keyTestError storage - , getItemTestSuccess storage - , getItemTestError storage - , removeItemTest storage - , removeItemTestError storage - ] diff --git a/src/vendor/elm-web-api/test/src/elm/shim.js b/src/vendor/elm-web-api/test/src/elm/shim.js deleted file mode 100644 index 0ffea98..0000000 --- a/src/vendor/elm-web-api/test/src/elm/shim.js +++ /dev/null @@ -1,14 +0,0 @@ -var LocalStorage = require('node-localstorage').LocalStorage; - -var window = { - localStorage: new LocalStorage('./localStorage.store'), - sessionStorage: new LocalStorage('./sessionStorage.store'), - - setTimeout: setTimeout -} - -var app = Elm.worker(Elm.Main); - -app.ports.result.subscribe(function (s) { - console.log(s); -}); diff --git a/src/vendor/elm-web-api/test/src/mocha/browser.js b/src/vendor/elm-web-api/test/src/mocha/browser.js deleted file mode 100644 index 8e397ec..0000000 --- a/src/vendor/elm-web-api/test/src/mocha/browser.js +++ /dev/null @@ -1,41 +0,0 @@ -var windowTest = require('./windowTest'); -var elmTest = require('./elmTest'); -var locationTest = require('./locationTest'); -var storageTest = require('./storageTest'); - -module.exports = function (browser) { - var title = - browser.desiredCapabilities.browserName + "-" + - browser.desiredCapabilities.version + "-" + - browser.desiredCapabilities.platform + " " - browser.desiredCapabilities.build; - - describe(title, function () { - this.timeout(600000); - this.slow(4000); - - var allPassed = true; - - // Before any tests run, initialize the browser. - before(function (done) { - browser.init(function (err) { - if (err) throw err; - done(); - }); - }); - - elmTest(browser); - windowTest(browser); - locationTest(browser); - storageTest(browser); - - afterEach(function() { - allPassed = allPassed && (this.currentTest.state === 'passed'); - }); - - after(function (done) { - console.log(title + (allPassed ? " PASSED" : " FAILED")); - browser.passed(allPassed, done); - }); - }); -}; diff --git a/src/vendor/elm-web-api/test/src/mocha/elmTest.js b/src/vendor/elm-web-api/test/src/mocha/elmTest.js deleted file mode 100644 index 92ba76f..0000000 --- a/src/vendor/elm-web-api/test/src/mocha/elmTest.js +++ /dev/null @@ -1,30 +0,0 @@ -var expect = require('chai').expect; -var count = require('count-substring'); -var Q = require('q'); - -module.exports = function (browser) { - describe("The tests written in Elm", function () { - var falsy = function () { - return Q.when(false); - }; - - it('should pass', function () { - return browser - .url('http://localhost:8080/elm.html') - .waitUntil(function () { - return this.getText("#results").then(function (text) { - return text.indexOf("suites run") > 0; - }, falsy); - }, 30000, 500) - .getText("#results") - .then(function (text) { - // Always log the test results - console.log(text); - - var failedCount = count(text, "FAILED"); - expect(failedCount).to.equal(0); - }); - }); - }); -}; - diff --git a/src/vendor/elm-web-api/test/src/mocha/locationTest.js b/src/vendor/elm-web-api/test/src/mocha/locationTest.js deleted file mode 100644 index d6e1020..0000000 --- a/src/vendor/elm-web-api/test/src/mocha/locationTest.js +++ /dev/null @@ -1,40 +0,0 @@ -var expect = require('chai').expect; -var Q = require('q'); - -module.exports = function (browser) { - describe("The Location example", function () { - beforeEach(function (done) { - browser.url('http://localhost:8080/location.html', done); - }); - - var falsy = function () { - return Q.when(false); - }; - - it("should reload from server", function () { - return browser - .setValue("#input", "This goes away on reload") - .click("#reload-force-button") - - // Wait for it not to have a value again - .waitUntil(function () { - return this.getValue("#input").then(function (value) { - return value === ""; - }, falsy); - }, 6000, 250); - }); - - it("should reload from cache", function () { - return browser - .setValue("#input", "This goes away on reload") - .click("#reload-cache-button") - - // Wait for it not to have a value again - .waitUntil(function () { - return this.getValue("#input").then(function (value) { - return value === ""; - }, falsy); - }, 6000, 250); - }); - }); -}; diff --git a/src/vendor/elm-web-api/test/src/mocha/storageTest.js b/src/vendor/elm-web-api/test/src/mocha/storageTest.js deleted file mode 100644 index 1538bea..0000000 --- a/src/vendor/elm-web-api/test/src/mocha/storageTest.js +++ /dev/null @@ -1,116 +0,0 @@ -var expect = require('chai').expect; -var Q = require('q'); - -module.exports = function (browser) { - var run; - - if ( - browser.desiredCapabilities.browserName == 'chrome' || - browser.desiredCapabilities.browserName == 'internet explorer' || - browser.desiredCapabilities.browserName == 'opera' - ) { - // Can't get the tab switching to work in these - run = describe.skip; - } else { - run = describe; - } - - run("The Storage example", function () { - var url = 'http://localhost:8080/storage.html'; - - before(function () { - return browser - .newWindow(url, "tab1") - .waitForExist("#select-area", 6000) - .selectByIndex("#select-area", 0) - .selectByIndex("#select-operation", 5) - .click("#perform-action") - .newWindow(url, "tab2") - .waitForExist("#select-area", 6000) - .selectByIndex("#select-area", 0) - .selectByIndex("#select-operation", 5) - .click("#perform-action"); - }); - - after(function () { - return browser - .switchTab("tab1") - .close() - .switchTab("tab2") - .close(); - }); - - it("first set should trigger add event", function () { - var expectedText = "LogEvent { area = Local, change = Add \"testKey\" \"testValue\", url = \"" + url + "\" }"; - - return browser - .switchTab("tab1") - .waitForExist("#select-area", 6000) - .selectByIndex("#select-area", 0) - .selectByIndex("#select-operation", 3) - .waitForExist("#select-set-key", 6000) - .setValue("#select-set-key", "testKey") - .setValue("#select-set-value", "testValue") - .click("#perform-action") - .switchTab("tab2") - .waitUntil(function () { - return this.getText("#log").then(function (text) { - return text.indexOf(expectedText) >= 0; - }); - }, 8000, 250); - }); - - it("second set should trigger modify event", function () { - var expectedText = "LogEvent { area = Local, change = Modify \"testKey\" \"testValue\" \"testValue2\", url = \"" + url + "\" }"; - - return browser - .switchTab("tab1") - .setValue("#select-set-key", "testKey") - .setValue("#select-set-value", "testValue2") - .click("#perform-action") - .switchTab("tab2") - .waitUntil(function () { - return this.getText("#log").then(function (text) { - return text.indexOf(expectedText) >= 0; - }); - }, 8000, 250); - }); - - it("remove should trigger remove event", function () { - var expectedText = "LogEvent { area = Local, change = Remove \"testKey\" \"testValue2\", url = \"" + url + "\" }"; - - return browser - .switchTab("tab1") - .selectByIndex("#select-operation", 4) - .waitForExist("#select-remove-key", 6000) - .setValue("#select-remove-key", "testKey") - .click("#perform-action") - .switchTab("tab2") - .waitUntil(function () { - return this.getText("#log").then(function (text) { - return text.indexOf(expectedText) >= 0; - }); - }, 8000, 250); - }); - - it("clear should trigger clear event", function () { - var expectedText = "LogEvent { area = Local, change = Clear, url = \"" + url + "\" }"; - - return browser - .switchTab("tab1") - .selectByIndex("#select-operation", 3) - .waitForExist("#select-set-key", 6000) - .setValue("#select-set-key", "testKey") - .setValue("#select-set-value", "testValue") - .click("#perform-action") - .selectByIndex("#select-operation", 5) - .click("#perform-action") - .switchTab("tab2") - .waitUntil(function () { - return this.getText("#log").then(function (text) { - return text.indexOf(expectedText) >= 0; - }); - }, 8000, 250); - }); - }); -}; diff --git a/src/vendor/elm-web-api/test/src/mocha/windowTest.js b/src/vendor/elm-web-api/test/src/mocha/windowTest.js deleted file mode 100644 index 4376e7f..0000000 --- a/src/vendor/elm-web-api/test/src/mocha/windowTest.js +++ /dev/null @@ -1,166 +0,0 @@ -var expect = require('chai').expect; -var Q = require('q'); - -module.exports = function (browser) { - describe("The Window example", function () { - beforeEach(function (done) { - browser.url('http://localhost:8080/window.html', done); - }); - - // Don't test alerts etc. under Safari, because Selenium can't - // manage alerts with Safari. - var describeAlert = ( - browser.desiredCapabilities.browserName == 'safari' || - browser.desiredCapabilities.browserName == 'opera' - ) ? describe.skip : describe; - - var truthy = function () { - return Q.when(true); - }; - - var falsy = function () { - return Q.when(false); - }; - - describeAlert("alert", function () { - it("should open", function () { - return browser - .click("#alert-button") - - .waitUntil(function () { - return this.alertText().then(truthy, falsy); - }, 30000, 500) - - .alertText().then(function (text) { - expect(text).to.equal("Hello world!"); - }) - - .alertAccept(); - }); - }); - - describeAlert("confirm", function () { - it("should recognize acceptance", function () { - return browser - .click("#confirm-button") - - .waitUntil(function () { - return this.alertText().then(truthy, falsy); - }, 30000, 500) - - .alertText().then(function (text) { - expect(text).to.equal("Do you agree?"); - }) - - .alertAccept() - - .waitUntil(function () { - return this.getText("#message").then(function (text) { - return text.indexOf("Pressed OK") >= 0; - }); - }, 30000, 500); - }); - - it("should recognize rejection", function () { - return browser - .click("#confirm-button") - - .waitUntil(function () { - return this.alertText().then(truthy, falsy); - }, 30000, 500) - - .alertText().then(function (text) { - expect(text).to.equal("Do you agree?"); - }) - - .alertDismiss() - - .waitUntil(function () { - return this.getText("#message").then(function (text) { - return text.indexOf("Pressed cancel") >= 0; - }); - }, 30000, 500); - }); - }); - - describeAlert("prompt", function () { - it("should recognize dismissal", function () { - return browser - .click("#prompt-button") - - .waitUntil(function () { - return this.alertText().then(truthy, falsy); - }, 30000, 500) - - .alertText().then(function (text) { - expect(text).to.equal("What is your favourite colour?"); - }) - - .alertDismiss() - - .waitUntil(function () { - return this.getText("#message").then(function (text) { - return text.indexOf("User canceled.") >= 0; - }); - }, 30000, 500); - }); - - it("should return default when accepted", function () { - return browser - .click("#prompt-button") - - .waitUntil(function () { - return this.alertText().then(truthy, falsy); - }, 30000, 500) - - .alertText().then(function (text) { - expect(text).to.equal("What is your favourite colour?"); - }) - - .alertAccept() - - .waitUntil(function () { - return this.getText("#message").then(function (text) { - return text.indexOf("Got response: Blue") >= 0; - }); - }, 30000, 500); - }); - - it("should interpret empty string as dismissal", function () { - return browser - .click("#prompt-button") - - .waitUntil(function () { - return this.alertText().then(truthy, falsy); - }, 30000, 500) - - .alertText("") - .alertAccept() - - .waitUntil(function () { - return this.getText("#message").then(function (text) { - return text.indexOf("User canceled.") >= 0; - }); - }, 30000, 500); - }); - - it("should return entered text if entered", function () { - return browser - .click("#prompt-button") - - .waitUntil(function () { - return this.alertText().then(truthy, falsy); - }, 30000, 500) - - .alertText("Red") - .alertAccept() - - .waitUntil(function () { - return this.getText("#message").then(function (text) { - return text.indexOf("Got response: Red") >= 0; - }); - }, 30000, 500); - }); - }); - }); -}; diff --git a/src/vendor/elm-web-api/test/src/remote.js b/src/vendor/elm-web-api/test/src/remote.js deleted file mode 100644 index 051aea4..0000000 --- a/src/vendor/elm-web-api/test/src/remote.js +++ /dev/null @@ -1,131 +0,0 @@ -// Definition of browsers to test remotely - -module.exports = function remote (rev) { - return [{ -/* Something about the iPhone setup on SauceLabs isn't working for me - browserName: 'iphone', - platform: 'OS X 10.10', - version: '9.0', - deviceName: 'iPhone 6', - deviceOrientation: 'portrait', - build: rev, - name: 'iphone 6 ' + rev - },{ -*/ browserName: 'safari', - version: '6.0', - platform: 'OS X 10.8', - build: rev, - name: 'Safari Mountain Lion ' + rev - },{ - browserName: 'safari', - version: '7.0', - platform: 'OS X 10.9', - build: rev, - name: 'Safari Mavericks ' + rev - },{ - browserName: 'safari', - version: '8.0', - platform: 'OS X 10.10', - build: rev, - name: 'Safari Yosemite ' + rev - },{ - browserName: 'safari', - version: '9.0', - platform: 'OS X 10.11', - build: rev, - name: 'Safari El Capitan ' + rev - },{ -/* browserName: 'android', - platform: 'Linux', - version: '5.1', - deviceName: 'Android Emulator', - deviceOrientation: 'portrait', - build: rev, - name: 'Android 5.1 ' + rev - },{ -*/ browserName: 'chrome', - version: '46.0', - platform: 'Windows 10', - build: rev, - name: 'Chrome Windows 10 46.0 ' + rev - },{ - browserName: 'chrome', - version: '45.0', - platform: 'Windows 10', - build: rev, - name: 'Chrome Windows 10 45.0 ' + rev - },{ - browserName: 'chrome', - version: '44.0', - platform: 'Windows 10', - build: rev, - name: 'Chrome Windows 10 44.0 ' + rev - },{ - browserName: 'chrome', - version: '43.0', - platform: 'Windows 10', - build: rev, - name: 'Chrome Windows 10 43.0 ' + rev - },{ - browserName: 'firefox', - version: '41.0', - platform: 'Linux', - build: rev, - name: 'Firefox Linux 41.0 ' + rev - },{ - browserName: 'firefox', - version: '40.0', - platform: 'Linux', - build: rev, - name: 'Firefox Linux 40.0 ' + rev - },{ - browserName: 'firefox', - version: '39.0', - platform: 'Linux', - build: rev, - name: 'Firefox Linux 39.0 ' + rev - },{ - browserName: 'firefox', - version: '38.0', - platform: 'Linux', - build: rev, - name: 'Firefox Linux 38.0 ' + rev - },{ -/* browserName: 'internet explorer', - version: '8.0', - platform: 'Windows 7', - build: rev, - name: 'Internet Explorer 8.0 ' + rev, - },{ -*/ browserName: 'internet explorer', - version: '9.0', - platform: 'Windows 7', - build: rev, - name: 'Internet Explorer 9.0 ' + rev, - },{ - browserName: 'opera', - version: '12.15', - platform: 'Linux', - build: rev, - name: 'Opera 12.15 ' + rev, - },{ - browserName: 'internet explorer', - version: '10.0', - platform: 'Windows 8', - build: rev, - name: 'Internet Explorer 10.0 ' + rev - },{ - browserName: 'internet explorer', - version: '11.0', - platform: 'Windows 10', - build: rev, - name: 'Internet Explorer 11.0 ' + rev -/* },{ - Looks like webdriver support for Edge isn't complete yet - browserName: 'microsoftedge', - version: '20.10240', - platform: 'Windows 10', - build: rev, - name: 'MS Edge 20.10240 ' + rev -*/ }]; -}; diff --git a/src/vendor/elm-web-api/test/src/run.js b/src/vendor/elm-web-api/test/src/run.js deleted file mode 100644 index 4293d25..0000000 --- a/src/vendor/elm-web-api/test/src/run.js +++ /dev/null @@ -1,23 +0,0 @@ -var SeSauce = require('./selenium-sauce'); -var git = require('git-rev'); - -var remote = require('./remote'); -var config = require('./config'); -var eachBrowser = require('./mocha/browser'); - -git.short(function (rev) { - // If SauceLabs environment variables are present, set up SauceLabs browsers - if (config.webdriver.user) { - config.webdriver.desiredCapabilities = remote(rev); - } else { - config.webdriver.desiredCapabilities = [{ - browserName: 'firefox' - }]; - }; - - new SeSauce(config, eachBrowser); - - // Need to call mocha with a --delay, since git.short is async - run(); -}); - diff --git a/src/vendor/elm-web-api/test/src/selenium-sauce.js b/src/vendor/elm-web-api/test/src/selenium-sauce.js deleted file mode 100644 index d28eaab..0000000 --- a/src/vendor/elm-web-api/test/src/selenium-sauce.js +++ /dev/null @@ -1,238 +0,0 @@ -var webdriverio = require('webdriverio'), - httpserver = require('http-server'), - selenium = require('selenium-standalone'), - sauceConnectLauncher = require('sauce-connect-launcher'), - extend = require('extend'), - colors = require('colors'), - SauceLabs = require('saucelabs'); - -/** - * Initializes Selenium Sauce using the specified options. - * 'doEachBrowser' is called once for each browser in options.webdriver.desiredCapabilities, passing in the webdriverio instance. - */ -var SeSauce = function(options, doEachBrowser) { - - extend(this, { - browsers: [], // Contains a list of webdriverio instances - _browserActions: [], - - _initialized: false, - _stopped: false, - - options: { - quiet: false, // Silences the console output - webdriver: { // Options for selenium webdriver (webdriverio) - host: 'ondemand.saucelabs.com', - port: 80, - user: null, - key: null, - logLevel: 'silent', - desiredCapabilities: [] // Non-standard option; An array of desired capabilities instead of a single object - }, - httpServer: { // Options for local http server (npmjs.org/package/http-server) - disable: false, // Non-standard option; used to skip launching the http server - port: 8080 // Non-standard option; it is passed into the httpServer.listen() method - }, - sauceLabs: { // Options for SauceLabs API wrapper (npmjs.org/package/saucelabs) - username: null, - password: null - }, - sauceConnect: { // Options for SauceLabs Connect (npmjs.org/package/sauce-connect-launcher) - disable: false, // Non-standard option; used to disable sauce connect - username: null, - accessKey: null - }, - selenium: { // Options for Selenium Server (npmjs.org/package/selenium-standalone). Only used if you need Selenium running locally. - args: [] // options to pass to `java -jar selenium-server-standalone-X.XX.X.jar` - } - } - }); - - - this._doEachBrowser = doEachBrowser; - this.options.quiet = options.quiet; - - extend(this.options.webdriver, options.webdriver || {}); - extend(this.options.httpServer, options.httpServer || {}); - extend(this.options.sauceLabs, options.sauceLabs || {}); - extend(this.options.sauceConnect, options.sauceConnect || {}); - extend(this.options.selenium, options.selenium || {}); - - if (this.options.webdriver.desiredCapabilities && this.options.webdriver.desiredCapabilities.constructor === Object) - this.options.webdriver.desiredCapabilities = [this.options.webdriver.desiredCapabilities]; - - if (!(this.options.webdriver.user && this.options.webdriver.key) && this.options.webdriver.host == 'ondemand.saucelabs.com') { - this.options.webdriver.host = 'localhost'; - this.options.webdriver.port = 4444; - } - - var self = this; - - for (var i = 0, len = this.options.webdriver.desiredCapabilities.length; i < len; i++) { - var wdOptions = extend({}, this.options.webdriver); - wdOptions.desiredCapabilities = this.options.webdriver.desiredCapabilities[i]; - var browser = webdriverio.remote(wdOptions); - this.browsers.push(browser); - - browser._oldInit = browser.init; - browser.init = function (complete) { - self._initOnce(function (err) { - if (err) - return complete(err); - this._oldInit(complete); - }.bind(this)); - }.bind(browser); - - browser._oldEnd = browser.end; - browser.end = function (complete) { - this._oldEnd(function () { - self.browsers.splice(self.browsers.indexOf(this), 1); - if (self.browsers.length == 0) - self._stop(complete); - else - complete(); - }.bind(this)); - }.bind(browser); - - browser.passed = function(success, complete) { - this.updateJob({ passed: success }, function() { - this.end(complete); - }.bind(this)); - }.bind(browser); - - browser.updateJob = function(data, complete) { - if (self.sauceLabs) - self.sauceLabs.updateJob(this.requestHandler.sessionID, data, complete); - else - complete(); - }.bind(browser); - - doEachBrowser.call(this, browser); - } - -}; - -extend(SeSauce.prototype, { - - /** - * Performs one-time initialization. Calls 'complete' when done, passing in an error message if necessary. - * @private - */ - _initOnce: function (complete) { - if (this._initialized) - return complete(); - - var self = this; - this._initialized = true; - - this.webdriver = webdriverio; - - if (!this.options.httpServer.disable) { - this._log("Launching local web server (http://localhost:" + this.options.httpServer.port + "/)..."); - this.httpServer = httpserver.createServer(this.options.httpServer); - this.httpServer.listen(this.options.httpServer.port); - this._log("Web server ready."); - } - - if (this.options.sauceLabs.username && this.options.sauceLabs.password) { - this._log("Initializing SauceLabs API."); - this.sauceLabs = new SauceLabs({ - username: this.options.sauceLabs.username, - password: this.options.sauceLabs.password - }); - } - - if (this.options.sauceConnect.username && this.options.sauceConnect.accessKey) { - if (this.options.sauceConnect.disable) - this._log("Sauce Connect disabled."); - else { - this._log("Launching Sauce Connect..."); - delete this.options.sauceConnect.disable; - sauceConnectLauncher(this.options.sauceConnect, function (errmsg, process) { - if (errmsg) { - if (process) process.close(); - return self._doError('Error launching Sauce Connect:\n' + errmsg, complete); - } - self.sauceConnect = process; - self._log("Sauce Connect ready."); - complete(); - }); - } - } - else { - this._log("No SauceLabs username/accessKey. Launching Selenium locally..."); - - selenium.install({}, function (err) { - if (err) { - self._doError(err, complete); - } else { - selenium.start({ - seleniumArgs: self.options.selenium.args - }, function (err, child) { - if (err) { - self._doError(err, complete); - } else { - self.selenium = child; - complete(); - } - }); - } - }); - } - }, - - /** - * Logs an error message, stops all services, and then calls the 'complete' callback, passing in the error message. - * @private - */ - _doError: function (msg, complete) { - this._err(msg); - this._stop(function () { - complete(msg); - }); - }, - - - /** - * @private - */ - _stop: function (complete) { - if (this._stopped) - return complete && complete(); - - this._stopped = true; - - if (this.httpServer) { - this.httpServer.close(); - this._log("Web server stopped."); - } - - if (this.selenium) { - this.selenium.kill(); - this._log("Local Selenium server stopped."); - } - - if (this.sauceConnect) { - var self = this; - this._log("Closing Sauce Connect..."); - this.sauceConnect.close(function () { - self._log("Sauce Connect closed."); - if (complete) - complete(); - }); - } - else if (complete) - complete(); - }, - - _log: function(str) { - if(!this.options.quiet) - console.log('SelSauce: '.blue + str); - }, - - _err: function(str) { - console.error('SelSauce: '.bgRed + str); - } -}); - -module.exports = SeSauce;