From a7db22556b91bc7c499e010b4c051f4442ad8ce2 Mon Sep 17 00:00:00 2001 From: Joris Date: Tue, 29 Dec 2015 22:38:42 +0100 Subject: Using persona to validate emails --- .gitignore | 2 +- README.md | 1 - config.txt | 1 - elm-package.json | 2 +- package.json | 12 +- public/javascripts/.gitkeep | 0 public/javascripts/client.js | 17024 +++++++++++++++++++++++ public/javascripts/elmLauncher.js | 13 - public/javascripts/main.js | 28 + sharedCost.cabal | 55 +- src/client/Main.elm | 101 - src/client/Model.elm | 32 - src/client/Model/Config.elm | 18 - src/client/Model/Date.elm | 15 - src/client/Model/Income.elm | 76 - src/client/Model/Payer.elm | 132 - src/client/Model/Payment.elm | 44 - src/client/Model/Translations.elm | 69 - src/client/Model/User.elm | 44 - src/client/Model/View.elm | 12 - src/client/Model/View/LoggedIn/Account.elm | 67 - src/client/Model/View/LoggedIn/Add.elm | 43 - src/client/Model/View/LoggedIn/Edition.elm | 7 - src/client/Model/View/LoggedIn/Monthly.elm | 17 - src/client/Model/View/LoggedInView.elm | 35 - src/client/Model/View/SignInView.elm | 15 - src/client/Native/Reads.js | 22 - src/client/Reads.elm | 10 - src/client/ServerCommunication.elm | 143 - src/client/Update.elm | 57 - src/client/Update/LoggedIn.elm | 68 - src/client/Update/LoggedIn/Account.elm | 64 - src/client/Update/LoggedIn/Add.elm | 29 - src/client/Update/LoggedIn/Monthly.elm | 27 - src/client/Update/SignIn.elm | 24 - src/client/Utils/Dict.elm | 11 - src/client/Utils/Either.elm | 9 - src/client/Utils/Maybe.elm | 27 - src/client/Utils/Validation.elm | 23 - src/client/View/Date.elm | 59 - src/client/View/Events.elm | 19 - src/client/View/Expand.elm | 25 - src/client/View/Header.elm | 36 - src/client/View/Icon.elm | 12 - src/client/View/Loading.elm | 8 - src/client/View/LoggedIn.elm | 30 - src/client/View/LoggedIn/Account.elm | 130 - src/client/View/LoggedIn/Add.elm | 122 - src/client/View/LoggedIn/Monthly.elm | 89 - src/client/View/LoggedIn/Paging.elm | 100 - src/client/View/LoggedIn/Table.elm | 97 - src/client/View/Page.elm | 31 - src/client/View/Price.elm | 38 - src/client/View/SignIn.elm | 57 - src/client/elm/InitViewAction.elm | 25 + src/client/elm/Main.elm | 89 + src/client/elm/Model.elm | 32 + src/client/elm/Model/Config.elm | 18 + src/client/elm/Model/Date.elm | 15 + src/client/elm/Model/Income.elm | 76 + src/client/elm/Model/Payer.elm | 132 + src/client/elm/Model/Payment.elm | 44 + src/client/elm/Model/Translations.elm | 69 + src/client/elm/Model/User.elm | 44 + src/client/elm/Model/View.elm | 12 + src/client/elm/Model/View/LoggedIn/Account.elm | 67 + src/client/elm/Model/View/LoggedIn/Add.elm | 43 + src/client/elm/Model/View/LoggedIn/Edition.elm | 7 + src/client/elm/Model/View/LoggedIn/Monthly.elm | 17 + src/client/elm/Model/View/LoggedInView.elm | 35 + src/client/elm/Model/View/SignInView.elm | 15 + src/client/elm/Native/Reads.js | 22 + src/client/elm/Persona.elm | 28 + src/client/elm/Reads.elm | 10 + src/client/elm/ServerCommunication.elm | 95 + src/client/elm/Sign.elm | 43 + src/client/elm/SimpleHTTP.elm | 41 + src/client/elm/Update.elm | 57 + src/client/elm/Update/LoggedIn.elm | 68 + src/client/elm/Update/LoggedIn/Account.elm | 64 + src/client/elm/Update/LoggedIn/Add.elm | 29 + src/client/elm/Update/LoggedIn/Monthly.elm | 27 + src/client/elm/Update/SignIn.elm | 15 + src/client/elm/Utils/Dict.elm | 11 + src/client/elm/Utils/Either.elm | 9 + src/client/elm/Utils/Maybe.elm | 27 + src/client/elm/Utils/Validation.elm | 23 + src/client/elm/View/Date.elm | 59 + src/client/elm/View/Events.elm | 19 + src/client/elm/View/Expand.elm | 25 + src/client/elm/View/Header.elm | 39 + src/client/elm/View/Icon.elm | 12 + src/client/elm/View/Loading.elm | 8 + src/client/elm/View/LoggedIn.elm | 30 + src/client/elm/View/LoggedIn/Account.elm | 130 + src/client/elm/View/LoggedIn/Add.elm | 122 + src/client/elm/View/LoggedIn/Monthly.elm | 89 + src/client/elm/View/LoggedIn/Paging.elm | 100 + src/client/elm/View/LoggedIn/Table.elm | 97 + src/client/elm/View/Page.elm | 31 + src/client/elm/View/Price.elm | 38 + src/client/elm/View/SignIn.elm | 46 + src/client/js/main.js | 28 + src/server/Config.hs | 2 - src/server/Controller/SignIn.hs | 84 +- src/server/Design/Header.hs | 2 +- src/server/Design/SignIn.hs | 20 - src/server/Main.hs | 12 +- src/server/Model/Database.hs | 1 - src/server/Model/Message/Key.hs | 6 +- src/server/Model/Message/Translations.hs | 26 +- src/server/Model/SignIn.hs | 17 +- src/server/Persona.hs | 42 + src/server/Secure.hs | 7 +- src/server/View/Page.hs | 4 +- 115 files changed, 19342 insertions(+), 2295 deletions(-) create mode 100644 public/javascripts/.gitkeep create mode 100644 public/javascripts/client.js delete mode 100644 public/javascripts/elmLauncher.js create mode 100644 public/javascripts/main.js delete mode 100644 src/client/Main.elm delete mode 100644 src/client/Model.elm delete mode 100644 src/client/Model/Config.elm delete mode 100644 src/client/Model/Date.elm delete mode 100644 src/client/Model/Income.elm delete mode 100644 src/client/Model/Payer.elm delete mode 100644 src/client/Model/Payment.elm delete mode 100644 src/client/Model/Translations.elm delete mode 100644 src/client/Model/User.elm delete mode 100644 src/client/Model/View.elm delete mode 100644 src/client/Model/View/LoggedIn/Account.elm delete mode 100644 src/client/Model/View/LoggedIn/Add.elm delete mode 100644 src/client/Model/View/LoggedIn/Edition.elm delete mode 100644 src/client/Model/View/LoggedIn/Monthly.elm delete mode 100644 src/client/Model/View/LoggedInView.elm delete mode 100644 src/client/Model/View/SignInView.elm delete mode 100644 src/client/Native/Reads.js delete mode 100644 src/client/Reads.elm delete mode 100644 src/client/ServerCommunication.elm delete mode 100644 src/client/Update.elm delete mode 100644 src/client/Update/LoggedIn.elm delete mode 100644 src/client/Update/LoggedIn/Account.elm delete mode 100644 src/client/Update/LoggedIn/Add.elm delete mode 100644 src/client/Update/LoggedIn/Monthly.elm delete mode 100644 src/client/Update/SignIn.elm delete mode 100644 src/client/Utils/Dict.elm delete mode 100644 src/client/Utils/Either.elm delete mode 100644 src/client/Utils/Maybe.elm delete mode 100644 src/client/Utils/Validation.elm delete mode 100644 src/client/View/Date.elm delete mode 100644 src/client/View/Events.elm delete mode 100644 src/client/View/Expand.elm delete mode 100644 src/client/View/Header.elm delete mode 100644 src/client/View/Icon.elm delete mode 100644 src/client/View/Loading.elm delete mode 100644 src/client/View/LoggedIn.elm delete mode 100644 src/client/View/LoggedIn/Account.elm delete mode 100644 src/client/View/LoggedIn/Add.elm delete mode 100644 src/client/View/LoggedIn/Monthly.elm delete mode 100644 src/client/View/LoggedIn/Paging.elm delete mode 100644 src/client/View/LoggedIn/Table.elm delete mode 100644 src/client/View/Page.elm delete mode 100644 src/client/View/Price.elm delete mode 100644 src/client/View/SignIn.elm create mode 100644 src/client/elm/InitViewAction.elm create mode 100644 src/client/elm/Main.elm create mode 100644 src/client/elm/Model.elm create mode 100644 src/client/elm/Model/Config.elm create mode 100644 src/client/elm/Model/Date.elm create mode 100644 src/client/elm/Model/Income.elm create mode 100644 src/client/elm/Model/Payer.elm create mode 100644 src/client/elm/Model/Payment.elm create mode 100644 src/client/elm/Model/Translations.elm create mode 100644 src/client/elm/Model/User.elm create mode 100644 src/client/elm/Model/View.elm create mode 100644 src/client/elm/Model/View/LoggedIn/Account.elm create mode 100644 src/client/elm/Model/View/LoggedIn/Add.elm create mode 100644 src/client/elm/Model/View/LoggedIn/Edition.elm create mode 100644 src/client/elm/Model/View/LoggedIn/Monthly.elm create mode 100644 src/client/elm/Model/View/LoggedInView.elm create mode 100644 src/client/elm/Model/View/SignInView.elm create mode 100644 src/client/elm/Native/Reads.js create mode 100644 src/client/elm/Persona.elm create mode 100644 src/client/elm/Reads.elm create mode 100644 src/client/elm/ServerCommunication.elm create mode 100644 src/client/elm/Sign.elm create mode 100644 src/client/elm/SimpleHTTP.elm create mode 100644 src/client/elm/Update.elm create mode 100644 src/client/elm/Update/LoggedIn.elm create mode 100644 src/client/elm/Update/LoggedIn/Account.elm create mode 100644 src/client/elm/Update/LoggedIn/Add.elm create mode 100644 src/client/elm/Update/LoggedIn/Monthly.elm create mode 100644 src/client/elm/Update/SignIn.elm create mode 100644 src/client/elm/Utils/Dict.elm create mode 100644 src/client/elm/Utils/Either.elm create mode 100644 src/client/elm/Utils/Maybe.elm create mode 100644 src/client/elm/Utils/Validation.elm create mode 100644 src/client/elm/View/Date.elm create mode 100644 src/client/elm/View/Events.elm create mode 100644 src/client/elm/View/Expand.elm create mode 100644 src/client/elm/View/Header.elm create mode 100644 src/client/elm/View/Icon.elm create mode 100644 src/client/elm/View/Loading.elm create mode 100644 src/client/elm/View/LoggedIn.elm create mode 100644 src/client/elm/View/LoggedIn/Account.elm create mode 100644 src/client/elm/View/LoggedIn/Add.elm create mode 100644 src/client/elm/View/LoggedIn/Monthly.elm create mode 100644 src/client/elm/View/LoggedIn/Paging.elm create mode 100644 src/client/elm/View/LoggedIn/Table.elm create mode 100644 src/client/elm/View/Page.elm create mode 100644 src/client/elm/View/Price.elm create mode 100644 src/client/elm/View/SignIn.elm create mode 100644 src/client/js/main.js create mode 100644 src/server/Persona.hs diff --git a/.gitignore b/.gitignore index 7540460..c391230 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ database database-shm database-wal elm-stuff/ -public/javascripts/client.js +./public/javascripts/*.js sessionKey deploy kimsufi.tar.gz diff --git a/README.md b/README.md index 7023624..631926e 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,5 @@ Here is an example configuration file: ``` hostname = localhost:3001 port = 3001 -sign-in-expiration-mn = 5 currency = € ``` diff --git a/config.txt b/config.txt index 1c9e0b0..0fbe5df 100644 --- a/config.txt +++ b/config.txt @@ -1,4 +1,3 @@ hostname = localhost:3001 port = 3001 -sign-in-expiration-mn = 5 currency = € diff --git a/elm-package.json b/elm-package.json index 1835a26..b433fb2 100644 --- a/elm-package.json +++ b/elm-package.json @@ -3,7 +3,7 @@ "summary": "SharedCost", "repository": "https://github.com/guyonvarch/sharedCost.git", "license": "BSD3", - "source-directories": ["src/client"], + "source-directories": ["src/client/elm"], "exposed-modules": [], "elm-version": "0.15.1 <= v < 0.16.0", "dependencies": { diff --git a/package.json b/package.json index a44c384..cf98877 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,17 @@ { "devDependencies": { - "watch": "0.16.0" + "watch": "0.17.1" }, "scripts": { - "watch": "npm run watch-server & npm run watch-client", + "watch": "npm run watch-server & npm run watch-elm & npm run watch-js", "watch-server": "watch 'npm run build-and-launch-server --silent' src/server", - "watch-client": "watch 'npm run build-client --silent' src/client", + "watch-elm": "watch 'npm run build-elm --silent' src/client/elm", + "watch-js": "watch 'npm run build-js --silent' src/client/js", - "build": "npm run build-server && npm run build-client", + "build": "npm run build-server && npm run build-elm && npm run build-js", "build-server": "cabal build", - "build-client": "elm make src/client/Main.elm --output public/javascripts/client.js", + "build-elm": "elm make src/client/elm/Main.elm --output public/javascripts/client.js", + "build-js": "cp src/client/js/main.js public/javascripts/main.js && echo 'javascript pasted.'", "build-and-launch-server": "npm run build-server && npm run kill-server && npm run launch-server", "launch-server": "./dist/build/sharedCost/sharedCost &", diff --git a/public/javascripts/.gitkeep b/public/javascripts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/public/javascripts/client.js b/public/javascripts/client.js new file mode 100644 index 0000000..59f8d69 --- /dev/null +++ b/public/javascripts/client.js @@ -0,0 +1,17024 @@ +var Elm = Elm || { Native: {} }; +Elm.Array = Elm.Array || {}; +Elm.Array.make = function (_elm) { + "use strict"; + _elm.Array = _elm.Array || {}; + if (_elm.Array.values) + return _elm.Array.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Array", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Native$Array = Elm.Native.Array.make(_elm); + var append = $Native$Array.append; + var length = $Native$Array.length; + var isEmpty = function (array) { + return _U.eq(length(array), + 0); + }; + var slice = $Native$Array.slice; + var set = $Native$Array.set; + var get = F2(function (i, + array) { + return _U.cmp(0, + i) < 1 && _U.cmp(i, + $Native$Array.length(array)) < 0 ? $Maybe.Just(A2($Native$Array.get, + i, + array)) : $Maybe.Nothing; + }); + var push = $Native$Array.push; + var empty = $Native$Array.empty; + var filter = F2(function (isOkay, + arr) { + var update = F2(function (x, + xs) { + return isOkay(x) ? A2($Native$Array.push, + x, + xs) : xs; + }); + return A3($Native$Array.foldl, + update, + $Native$Array.empty, + arr); + }); + var foldr = $Native$Array.foldr; + var foldl = $Native$Array.foldl; + var indexedMap = $Native$Array.indexedMap; + var map = $Native$Array.map; + var toIndexedList = function (array) { + return A3($List.map2, + F2(function (v0,v1) { + return {ctor: "_Tuple2" + ,_0: v0 + ,_1: v1}; + }), + _L.range(0, + $Native$Array.length(array) - 1), + $Native$Array.toList(array)); + }; + var toList = $Native$Array.toList; + var fromList = $Native$Array.fromList; + var initialize = $Native$Array.initialize; + var repeat = F2(function (n,e) { + return A2(initialize, + n, + $Basics.always(e)); + }); + var Array = {ctor: "Array"}; + _elm.Array.values = {_op: _op + ,empty: empty + ,repeat: repeat + ,initialize: initialize + ,fromList: fromList + ,isEmpty: isEmpty + ,length: length + ,push: push + ,append: append + ,get: get + ,set: set + ,slice: slice + ,toList: toList + ,toIndexedList: toIndexedList + ,map: map + ,indexedMap: indexedMap + ,filter: filter + ,foldl: foldl + ,foldr: foldr}; + return _elm.Array.values; +}; +Elm.Basics = Elm.Basics || {}; +Elm.Basics.make = function (_elm) { + "use strict"; + _elm.Basics = _elm.Basics || {}; + if (_elm.Basics.values) + return _elm.Basics.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Basics", + $Native$Basics = Elm.Native.Basics.make(_elm), + $Native$Show = Elm.Native.Show.make(_elm), + $Native$Utils = Elm.Native.Utils.make(_elm); + var uncurry = F2(function (f, + _v0) { + var $ = _v0, + a = $._0, + b = $._1; + return A2(f,a,b); + }); + var curry = F3(function (f, + a, + b) { + return f({ctor: "_Tuple2" + ,_0: a + ,_1: b}); + }); + var flip = F3(function (f,b,a) { + return A2(f,a,b); + }); + var snd = function (_v1) { + var _ = _v1; + var b = function () { + switch (_.ctor) + {case "_Tuple2": return _._1;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }(); + return b; + }; + var fst = function (_v5) { + var _ = _v5; + var a = function () { + switch (_.ctor) + {case "_Tuple2": return _._0;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }(); + return a; + }; + var always = F2(function (a, + _v9) { + var _ = _v9; + return a; + }); + var identity = function (x) { + return x; + }; + _op["<|"] = F2(function (f,x) { + return f(x); + }); + _op["|>"] = F2(function (x,f) { + return f(x); + }); + _op[">>"] = F3(function (f, + g, + x) { + return g(f(x)); + }); + _op["<<"] = F3(function (g, + f, + x) { + return g(f(x)); + }); + _op["++"] = $Native$Utils.append; + var toString = $Native$Show.toString; + var isInfinite = $Native$Basics.isInfinite; + var isNaN = $Native$Basics.isNaN; + var toFloat = $Native$Basics.toFloat; + var ceiling = $Native$Basics.ceiling; + var floor = $Native$Basics.floor; + var truncate = $Native$Basics.truncate; + var round = $Native$Basics.round; + var otherwise = true; + var not = $Native$Basics.not; + var xor = $Native$Basics.xor; + _op["||"] = $Native$Basics.or; + _op["&&"] = $Native$Basics.and; + var max = $Native$Basics.max; + var min = $Native$Basics.min; + var GT = {ctor: "GT"}; + var EQ = {ctor: "EQ"}; + var LT = {ctor: "LT"}; + var compare = $Native$Basics.compare; + _op[">="] = $Native$Basics.ge; + _op["<="] = $Native$Basics.le; + _op[">"] = $Native$Basics.gt; + _op["<"] = $Native$Basics.lt; + _op["/="] = $Native$Basics.neq; + _op["=="] = $Native$Basics.eq; + var e = $Native$Basics.e; + var pi = $Native$Basics.pi; + var clamp = $Native$Basics.clamp; + var logBase = $Native$Basics.logBase; + var abs = $Native$Basics.abs; + var negate = $Native$Basics.negate; + var sqrt = $Native$Basics.sqrt; + var atan2 = $Native$Basics.atan2; + var atan = $Native$Basics.atan; + var asin = $Native$Basics.asin; + var acos = $Native$Basics.acos; + var tan = $Native$Basics.tan; + var sin = $Native$Basics.sin; + var cos = $Native$Basics.cos; + _op["^"] = $Native$Basics.exp; + _op["%"] = $Native$Basics.mod; + var rem = $Native$Basics.rem; + _op["//"] = $Native$Basics.div; + _op["/"] = $Native$Basics.floatDiv; + _op["*"] = $Native$Basics.mul; + _op["-"] = $Native$Basics.sub; + _op["+"] = $Native$Basics.add; + var toPolar = $Native$Basics.toPolar; + var fromPolar = $Native$Basics.fromPolar; + var turns = $Native$Basics.turns; + var degrees = $Native$Basics.degrees; + var radians = function (t) { + return t; + }; + _elm.Basics.values = {_op: _op + ,max: max + ,min: min + ,compare: compare + ,not: not + ,xor: xor + ,otherwise: otherwise + ,rem: rem + ,negate: negate + ,abs: abs + ,sqrt: sqrt + ,clamp: clamp + ,logBase: logBase + ,e: e + ,pi: pi + ,cos: cos + ,sin: sin + ,tan: tan + ,acos: acos + ,asin: asin + ,atan: atan + ,atan2: atan2 + ,round: round + ,floor: floor + ,ceiling: ceiling + ,truncate: truncate + ,toFloat: toFloat + ,degrees: degrees + ,radians: radians + ,turns: turns + ,toPolar: toPolar + ,fromPolar: fromPolar + ,isNaN: isNaN + ,isInfinite: isInfinite + ,toString: toString + ,fst: fst + ,snd: snd + ,identity: identity + ,always: always + ,flip: flip + ,curry: curry + ,uncurry: uncurry + ,LT: LT + ,EQ: EQ + ,GT: GT}; + return _elm.Basics.values; +}; +Elm.Char = Elm.Char || {}; +Elm.Char.make = function (_elm) { + "use strict"; + _elm.Char = _elm.Char || {}; + if (_elm.Char.values) + return _elm.Char.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Char", + $Basics = Elm.Basics.make(_elm), + $Native$Char = Elm.Native.Char.make(_elm); + var fromCode = $Native$Char.fromCode; + var toCode = $Native$Char.toCode; + var toLocaleLower = $Native$Char.toLocaleLower; + var toLocaleUpper = $Native$Char.toLocaleUpper; + var toLower = $Native$Char.toLower; + var toUpper = $Native$Char.toUpper; + var isBetween = F3(function (low, + high, + $char) { + var code = toCode($char); + return _U.cmp(code, + toCode(low)) > -1 && _U.cmp(code, + toCode(high)) < 1; + }); + var isUpper = A2(isBetween, + _U.chr("A"), + _U.chr("Z")); + var isLower = A2(isBetween, + _U.chr("a"), + _U.chr("z")); + var isDigit = A2(isBetween, + _U.chr("0"), + _U.chr("9")); + var isOctDigit = A2(isBetween, + _U.chr("0"), + _U.chr("7")); + var isHexDigit = function ($char) { + return isDigit($char) || (A3(isBetween, + _U.chr("a"), + _U.chr("f"), + $char) || A3(isBetween, + _U.chr("A"), + _U.chr("F"), + $char)); + }; + _elm.Char.values = {_op: _op + ,isUpper: isUpper + ,isLower: isLower + ,isDigit: isDigit + ,isOctDigit: isOctDigit + ,isHexDigit: isHexDigit + ,toUpper: toUpper + ,toLower: toLower + ,toLocaleUpper: toLocaleUpper + ,toLocaleLower: toLocaleLower + ,toCode: toCode + ,fromCode: fromCode}; + return _elm.Char.values; +}; +Elm.Color = Elm.Color || {}; +Elm.Color.make = function (_elm) { + "use strict"; + _elm.Color = _elm.Color || {}; + if (_elm.Color.values) + return _elm.Color.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Color", + $Basics = Elm.Basics.make(_elm); + var Radial = F5(function (a, + b, + c, + d, + e) { + return {ctor: "Radial" + ,_0: a + ,_1: b + ,_2: c + ,_3: d + ,_4: e}; + }); + var radial = Radial; + var Linear = F3(function (a, + b, + c) { + return {ctor: "Linear" + ,_0: a + ,_1: b + ,_2: c}; + }); + var linear = Linear; + var fmod = F2(function (f,n) { + var integer = $Basics.floor(f); + return $Basics.toFloat(A2($Basics._op["%"], + integer, + n)) + f - $Basics.toFloat(integer); + }); + var rgbToHsl = F3(function (red, + green, + blue) { + var b = $Basics.toFloat(blue) / 255; + var g = $Basics.toFloat(green) / 255; + var r = $Basics.toFloat(red) / 255; + var cMax = A2($Basics.max, + A2($Basics.max,r,g), + b); + var cMin = A2($Basics.min, + A2($Basics.min,r,g), + b); + var c = cMax - cMin; + var lightness = (cMax + cMin) / 2; + var saturation = _U.eq(lightness, + 0) ? 0 : c / (1 - $Basics.abs(2 * lightness - 1)); + var hue = $Basics.degrees(60) * (_U.eq(cMax, + r) ? A2(fmod, + (g - b) / c, + 6) : _U.eq(cMax, + g) ? (b - r) / c + 2 : _U.eq(cMax, + b) ? (r - g) / c + 4 : _U.badIf($moduleName, + "between lines 150 and 152")); + return {ctor: "_Tuple3" + ,_0: hue + ,_1: saturation + ,_2: lightness}; + }); + var hslToRgb = F3(function (hue, + saturation, + lightness) { + var hue$ = hue / $Basics.degrees(60); + var chroma = (1 - $Basics.abs(2 * lightness - 1)) * saturation; + var x = chroma * (1 - $Basics.abs(A2(fmod, + hue$, + 2) - 1)); + var $ = _U.cmp(hue$, + 0) < 0 ? {ctor: "_Tuple3" + ,_0: 0 + ,_1: 0 + ,_2: 0} : _U.cmp(hue$, + 1) < 0 ? {ctor: "_Tuple3" + ,_0: chroma + ,_1: x + ,_2: 0} : _U.cmp(hue$, + 2) < 0 ? {ctor: "_Tuple3" + ,_0: x + ,_1: chroma + ,_2: 0} : _U.cmp(hue$, + 3) < 0 ? {ctor: "_Tuple3" + ,_0: 0 + ,_1: chroma + ,_2: x} : _U.cmp(hue$, + 4) < 0 ? {ctor: "_Tuple3" + ,_0: 0 + ,_1: x + ,_2: chroma} : _U.cmp(hue$, + 5) < 0 ? {ctor: "_Tuple3" + ,_0: x + ,_1: 0 + ,_2: chroma} : _U.cmp(hue$, + 6) < 0 ? {ctor: "_Tuple3" + ,_0: chroma + ,_1: 0 + ,_2: x} : {ctor: "_Tuple3" + ,_0: 0 + ,_1: 0 + ,_2: 0}, + r = $._0, + g = $._1, + b = $._2; + var m = lightness - chroma / 2; + return {ctor: "_Tuple3" + ,_0: r + m + ,_1: g + m + ,_2: b + m}; + }); + var toRgb = function (color) { + switch (color.ctor) + {case "HSLA": + var $ = A3(hslToRgb, + color._0, + color._1, + color._2), + r = $._0, + g = $._1, + b = $._2; + return {_: {} + ,alpha: color._3 + ,blue: $Basics.round(255 * b) + ,green: $Basics.round(255 * g) + ,red: $Basics.round(255 * r)}; + case "RGBA": return {_: {} + ,alpha: color._3 + ,blue: color._2 + ,green: color._1 + ,red: color._0};} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var toHsl = function (color) { + switch (color.ctor) + {case "HSLA": return {_: {} + ,alpha: color._3 + ,hue: color._0 + ,lightness: color._2 + ,saturation: color._1}; + case "RGBA": + var $ = A3(rgbToHsl, + color._0, + color._1, + color._2), + h = $._0, + s = $._1, + l = $._2; + return {_: {} + ,alpha: color._3 + ,hue: h + ,lightness: l + ,saturation: s};} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var HSLA = F4(function (a, + b, + c, + d) { + return {ctor: "HSLA" + ,_0: a + ,_1: b + ,_2: c + ,_3: d}; + }); + var hsla = F4(function (hue, + saturation, + lightness, + alpha) { + return A4(HSLA, + hue - $Basics.turns($Basics.toFloat($Basics.floor(hue / (2 * $Basics.pi)))), + saturation, + lightness, + alpha); + }); + var hsl = F3(function (hue, + saturation, + lightness) { + return A4(hsla, + hue, + saturation, + lightness, + 1); + }); + var complement = function (color) { + switch (color.ctor) + {case "HSLA": return A4(hsla, + color._0 + $Basics.degrees(180), + color._1, + color._2, + color._3); + case "RGBA": + var $ = A3(rgbToHsl, + color._0, + color._1, + color._2), + h = $._0, + s = $._1, + l = $._2; + return A4(hsla, + h + $Basics.degrees(180), + s, + l, + color._3);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var grayscale = function (p) { + return A4(HSLA,0,0,1 - p,1); + }; + var greyscale = function (p) { + return A4(HSLA,0,0,1 - p,1); + }; + var RGBA = F4(function (a, + b, + c, + d) { + return {ctor: "RGBA" + ,_0: a + ,_1: b + ,_2: c + ,_3: d}; + }); + var rgba = RGBA; + var rgb = F3(function (r,g,b) { + return A4(RGBA,r,g,b,1); + }); + var lightRed = A4(RGBA, + 239, + 41, + 41, + 1); + var red = A4(RGBA,204,0,0,1); + var darkRed = A4(RGBA, + 164, + 0, + 0, + 1); + var lightOrange = A4(RGBA, + 252, + 175, + 62, + 1); + var orange = A4(RGBA, + 245, + 121, + 0, + 1); + var darkOrange = A4(RGBA, + 206, + 92, + 0, + 1); + var lightYellow = A4(RGBA, + 255, + 233, + 79, + 1); + var yellow = A4(RGBA, + 237, + 212, + 0, + 1); + var darkYellow = A4(RGBA, + 196, + 160, + 0, + 1); + var lightGreen = A4(RGBA, + 138, + 226, + 52, + 1); + var green = A4(RGBA, + 115, + 210, + 22, + 1); + var darkGreen = A4(RGBA, + 78, + 154, + 6, + 1); + var lightBlue = A4(RGBA, + 114, + 159, + 207, + 1); + var blue = A4(RGBA, + 52, + 101, + 164, + 1); + var darkBlue = A4(RGBA, + 32, + 74, + 135, + 1); + var lightPurple = A4(RGBA, + 173, + 127, + 168, + 1); + var purple = A4(RGBA, + 117, + 80, + 123, + 1); + var darkPurple = A4(RGBA, + 92, + 53, + 102, + 1); + var lightBrown = A4(RGBA, + 233, + 185, + 110, + 1); + var brown = A4(RGBA, + 193, + 125, + 17, + 1); + var darkBrown = A4(RGBA, + 143, + 89, + 2, + 1); + var black = A4(RGBA,0,0,0,1); + var white = A4(RGBA, + 255, + 255, + 255, + 1); + var lightGrey = A4(RGBA, + 238, + 238, + 236, + 1); + var grey = A4(RGBA, + 211, + 215, + 207, + 1); + var darkGrey = A4(RGBA, + 186, + 189, + 182, + 1); + var lightGray = A4(RGBA, + 238, + 238, + 236, + 1); + var gray = A4(RGBA, + 211, + 215, + 207, + 1); + var darkGray = A4(RGBA, + 186, + 189, + 182, + 1); + var lightCharcoal = A4(RGBA, + 136, + 138, + 133, + 1); + var charcoal = A4(RGBA, + 85, + 87, + 83, + 1); + var darkCharcoal = A4(RGBA, + 46, + 52, + 54, + 1); + _elm.Color.values = {_op: _op + ,rgb: rgb + ,rgba: rgba + ,hsl: hsl + ,hsla: hsla + ,greyscale: greyscale + ,grayscale: grayscale + ,complement: complement + ,linear: linear + ,radial: radial + ,toRgb: toRgb + ,toHsl: toHsl + ,red: red + ,orange: orange + ,yellow: yellow + ,green: green + ,blue: blue + ,purple: purple + ,brown: brown + ,lightRed: lightRed + ,lightOrange: lightOrange + ,lightYellow: lightYellow + ,lightGreen: lightGreen + ,lightBlue: lightBlue + ,lightPurple: lightPurple + ,lightBrown: lightBrown + ,darkRed: darkRed + ,darkOrange: darkOrange + ,darkYellow: darkYellow + ,darkGreen: darkGreen + ,darkBlue: darkBlue + ,darkPurple: darkPurple + ,darkBrown: darkBrown + ,white: white + ,lightGrey: lightGrey + ,grey: grey + ,darkGrey: darkGrey + ,lightCharcoal: lightCharcoal + ,charcoal: charcoal + ,darkCharcoal: darkCharcoal + ,black: black + ,lightGray: lightGray + ,gray: gray + ,darkGray: darkGray}; + return _elm.Color.values; +}; +Elm.Date = Elm.Date || {}; +Elm.Date.make = function (_elm) { + "use strict"; + _elm.Date = _elm.Date || {}; + if (_elm.Date.values) + return _elm.Date.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Date", + $Native$Date = Elm.Native.Date.make(_elm), + $Result = Elm.Result.make(_elm), + $Time = Elm.Time.make(_elm); + var millisecond = $Native$Date.millisecond; + var second = $Native$Date.second; + var minute = $Native$Date.minute; + var hour = $Native$Date.hour; + var dayOfWeek = $Native$Date.dayOfWeek; + var day = $Native$Date.day; + var month = $Native$Date.month; + var year = $Native$Date.year; + var fromTime = $Native$Date.fromTime; + var toTime = $Native$Date.toTime; + var fromString = $Native$Date.read; + var Dec = {ctor: "Dec"}; + var Nov = {ctor: "Nov"}; + var Oct = {ctor: "Oct"}; + var Sep = {ctor: "Sep"}; + var Aug = {ctor: "Aug"}; + var Jul = {ctor: "Jul"}; + var Jun = {ctor: "Jun"}; + var May = {ctor: "May"}; + var Apr = {ctor: "Apr"}; + var Mar = {ctor: "Mar"}; + var Feb = {ctor: "Feb"}; + var Jan = {ctor: "Jan"}; + var Sun = {ctor: "Sun"}; + var Sat = {ctor: "Sat"}; + var Fri = {ctor: "Fri"}; + var Thu = {ctor: "Thu"}; + var Wed = {ctor: "Wed"}; + var Tue = {ctor: "Tue"}; + var Mon = {ctor: "Mon"}; + var Date = {ctor: "Date"}; + _elm.Date.values = {_op: _op + ,fromString: fromString + ,toTime: toTime + ,fromTime: fromTime + ,year: year + ,month: month + ,day: day + ,dayOfWeek: dayOfWeek + ,hour: hour + ,minute: minute + ,second: second + ,millisecond: millisecond + ,Jan: Jan + ,Feb: Feb + ,Mar: Mar + ,Apr: Apr + ,May: May + ,Jun: Jun + ,Jul: Jul + ,Aug: Aug + ,Sep: Sep + ,Oct: Oct + ,Nov: Nov + ,Dec: Dec + ,Mon: Mon + ,Tue: Tue + ,Wed: Wed + ,Thu: Thu + ,Fri: Fri + ,Sat: Sat + ,Sun: Sun}; + return _elm.Date.values; +}; +Elm.Debug = Elm.Debug || {}; +Elm.Debug.make = function (_elm) { + "use strict"; + _elm.Debug = _elm.Debug || {}; + if (_elm.Debug.values) + return _elm.Debug.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Debug", + $Graphics$Collage = Elm.Graphics.Collage.make(_elm), + $Native$Debug = Elm.Native.Debug.make(_elm); + var trace = $Native$Debug.tracePath; + var watchSummary = $Native$Debug.watchSummary; + var watch = $Native$Debug.watch; + var crash = $Native$Debug.crash; + var log = $Native$Debug.log; + _elm.Debug.values = {_op: _op + ,log: log + ,crash: crash + ,watch: watch + ,watchSummary: watchSummary + ,trace: trace}; + return _elm.Debug.values; +}; +Elm.Dict = Elm.Dict || {}; +Elm.Dict.make = function (_elm) { + "use strict"; + _elm.Dict = _elm.Dict || {}; + if (_elm.Dict.values) + return _elm.Dict.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Dict", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Native$Debug = Elm.Native.Debug.make(_elm), + $String = Elm.String.make(_elm); + var foldr = F3(function (f, + acc, + t) { + _v0: while (true) { + switch (t.ctor) + {case "RBEmpty": + switch (t._0.ctor) + {case "LBlack": return acc;} + break; + case "RBNode": var _v8 = f, + _v9 = A3(f, + t._1, + t._2, + A3(foldr,f,acc,t._4)), + _v10 = t._3; + f = _v8; + acc = _v9; + t = _v10; + continue _v0;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + } + }); + var keys = function (dict) { + return A3(foldr, + F3(function (key, + value, + keyList) { + return A2($List._op["::"], + key, + keyList); + }), + _L.fromArray([]), + dict); + }; + var values = function (dict) { + return A3(foldr, + F3(function (key, + value, + valueList) { + return A2($List._op["::"], + value, + valueList); + }), + _L.fromArray([]), + dict); + }; + var toList = function (dict) { + return A3(foldr, + F3(function (key,value,list) { + return A2($List._op["::"], + {ctor: "_Tuple2" + ,_0: key + ,_1: value}, + list); + }), + _L.fromArray([]), + dict); + }; + var foldl = F3(function (f, + acc, + dict) { + _v11: while (true) { + switch (dict.ctor) + {case "RBEmpty": + switch (dict._0.ctor) + {case "LBlack": return acc;} + break; + case "RBNode": var _v19 = f, + _v20 = A3(f, + dict._1, + dict._2, + A3(foldl,f,acc,dict._3)), + _v21 = dict._4; + f = _v19; + acc = _v20; + dict = _v21; + continue _v11;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + } + }); + var isBBlack = function (dict) { + switch (dict.ctor) + {case "RBEmpty": + switch (dict._0.ctor) + {case "LBBlack": return true;} + break; + case "RBNode": + switch (dict._0.ctor) + {case "BBlack": return true;} + break;} + return false; + }; + var showFlag = function (f) { + switch (f.ctor) + {case "Insert": return "Insert"; + case "Remove": return "Remove"; + case "Same": return "Same";} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var Same = {ctor: "Same"}; + var Remove = {ctor: "Remove"}; + var Insert = {ctor: "Insert"}; + var get = F2(function (targetKey, + dict) { + _v30: while (true) { + switch (dict.ctor) + {case "RBEmpty": + switch (dict._0.ctor) + {case "LBlack": + return $Maybe.Nothing;} + break; + case "RBNode": + var _v38 = A2($Basics.compare, + targetKey, + dict._1); + switch (_v38.ctor) + {case "EQ": + return $Maybe.Just(dict._2); + case "GT": var _v39 = targetKey, + _v40 = dict._4; + targetKey = _v39; + dict = _v40; + continue _v30; + case "LT": var _v41 = targetKey, + _v42 = dict._3; + targetKey = _v41; + dict = _v42; + continue _v30;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now");} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + } + }); + var member = F2(function (key, + dict) { + var _v43 = A2(get,key,dict); + switch (_v43.ctor) + {case "Just": return true; + case "Nothing": return false;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var max = function (dict) { + _v45: while (true) { + switch (dict.ctor) + {case "RBEmpty": + return $Native$Debug.crash("(max Empty) is not defined"); + case "RBNode": + switch (dict._4.ctor) + {case "RBEmpty": + return {ctor: "_Tuple2" + ,_0: dict._1 + ,_1: dict._2};} + var _v54 = dict._4; + dict = _v54; + continue _v45;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + } + }; + var min = function (dict) { + _v55: while (true) { + switch (dict.ctor) + {case "RBEmpty": + switch (dict._0.ctor) + {case "LBlack": + return $Native$Debug.crash("(min Empty) is not defined");} + break; + case "RBNode": + switch (dict._3.ctor) + {case "RBEmpty": + switch (dict._3._0.ctor) + {case "LBlack": + return {ctor: "_Tuple2" + ,_0: dict._1 + ,_1: dict._2};} + break;} + var _v64 = dict._3; + dict = _v64; + continue _v55;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + } + }; + var RBEmpty = function (a) { + return {ctor: "RBEmpty" + ,_0: a}; + }; + var RBNode = F5(function (a, + b, + c, + d, + e) { + return {ctor: "RBNode" + ,_0: a + ,_1: b + ,_2: c + ,_3: d + ,_4: e}; + }); + var showLColor = function (color) { + switch (color.ctor) + {case "LBBlack": + return "LBBlack"; + case "LBlack": return "LBlack";} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var LBBlack = {ctor: "LBBlack"}; + var LBlack = {ctor: "LBlack"}; + var empty = RBEmpty(LBlack); + var isEmpty = function (dict) { + return _U.eq(dict,empty); + }; + var map = F2(function (f,dict) { + switch (dict.ctor) + {case "RBEmpty": + switch (dict._0.ctor) + {case "LBlack": + return RBEmpty(LBlack);} + break; + case "RBNode": return A5(RBNode, + dict._0, + dict._1, + A2(f,dict._1,dict._2), + A2(map,f,dict._3), + A2(map,f,dict._4));} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var showNColor = function (c) { + switch (c.ctor) + {case "BBlack": return "BBlack"; + case "Black": return "Black"; + case "NBlack": return "NBlack"; + case "Red": return "Red";} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var reportRemBug = F4(function (msg, + c, + lgot, + rgot) { + return $Native$Debug.crash($String.concat(_L.fromArray(["Internal red-black tree invariant violated, expected " + ,msg + ," and got " + ,showNColor(c) + ,"/" + ,lgot + ,"/" + ,rgot + ,"\nPlease report this bug to "]))); + }); + var NBlack = {ctor: "NBlack"}; + var BBlack = {ctor: "BBlack"}; + var Black = {ctor: "Black"}; + var ensureBlackRoot = function (dict) { + switch (dict.ctor) + {case "RBEmpty": + switch (dict._0.ctor) + {case "LBlack": return dict;} + break; + case "RBNode": + switch (dict._0.ctor) + {case "Black": return dict; + case "Red": return A5(RBNode, + Black, + dict._1, + dict._2, + dict._3, + dict._4);} + break;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var blackish = function (t) { + switch (t.ctor) + {case "RBEmpty": return true; + case "RBNode": + return _U.eq(t._0, + Black) || _U.eq(t._0,BBlack);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var blacken = function (t) { + switch (t.ctor) + {case "RBEmpty": + return RBEmpty(LBlack); + case "RBNode": return A5(RBNode, + Black, + t._1, + t._2, + t._3, + t._4);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var Red = {ctor: "Red"}; + var moreBlack = function (color) { + switch (color.ctor) + {case "BBlack": + return $Native$Debug.crash("Can\'t make a double black node more black!"); + case "Black": return BBlack; + case "NBlack": return Red; + case "Red": return Black;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var lessBlack = function (color) { + switch (color.ctor) + {case "BBlack": return Black; + case "Black": return Red; + case "NBlack": + return $Native$Debug.crash("Can\'t make a negative black node less black!"); + case "Red": return NBlack;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var lessBlackTree = function (dict) { + switch (dict.ctor) + {case "RBEmpty": + switch (dict._0.ctor) + {case "LBBlack": + return RBEmpty(LBlack);} + break; + case "RBNode": return A5(RBNode, + lessBlack(dict._0), + dict._1, + dict._2, + dict._3, + dict._4);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var redden = function (t) { + switch (t.ctor) + {case "RBEmpty": + return $Native$Debug.crash("can\'t make a Leaf red"); + case "RBNode": return A5(RBNode, + Red, + t._1, + t._2, + t._3, + t._4);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var balance_node = function (t) { + var assemble = function (col) { + return function (xk) { + return function (xv) { + return function (yk) { + return function (yv) { + return function (zk) { + return function (zv) { + return function (a) { + return function (b) { + return function (c) { + return function (d) { + return A5(RBNode, + lessBlack(col), + yk, + yv, + A5(RBNode,Black,xk,xv,a,b), + A5(RBNode,Black,zk,zv,c,d)); + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; + if (blackish(t)) { + switch (t.ctor) + {case "RBNode": + switch (t._3.ctor) + {case "RBNode": + switch (t._3._0.ctor) + {case "Red": + switch (t._3._3.ctor) + {case "RBNode": + switch (t._3._3._0.ctor) + {case "Red": + return assemble(t._0)(t._3._3._1)(t._3._3._2)(t._3._1)(t._3._2)(t._1)(t._2)(t._3._3._3)(t._3._3._4)(t._3._4)(t._4);} + break;} + switch (t._3._4.ctor) + {case "RBNode": + switch (t._3._4._0.ctor) + {case "Red": + return assemble(t._0)(t._3._1)(t._3._2)(t._3._4._1)(t._3._4._2)(t._1)(t._2)(t._3._3)(t._3._4._3)(t._3._4._4)(t._4);} + break;} + break;} + break;} + switch (t._4.ctor) + {case "RBNode": + switch (t._4._0.ctor) + {case "Red": + switch (t._4._3.ctor) + {case "RBNode": + switch (t._4._3._0.ctor) + {case "Red": + return assemble(t._0)(t._1)(t._2)(t._4._3._1)(t._4._3._2)(t._4._1)(t._4._2)(t._3)(t._4._3._3)(t._4._3._4)(t._4._4);} + break;} + switch (t._4._4.ctor) + {case "RBNode": + switch (t._4._4._0.ctor) + {case "Red": + return assemble(t._0)(t._1)(t._2)(t._4._1)(t._4._2)(t._4._4._1)(t._4._4._2)(t._3)(t._4._3)(t._4._4._3)(t._4._4._4);} + break;} + break;} + break;} + switch (t._0.ctor) + {case "BBlack": + switch (t._4.ctor) + {case "RBNode": + switch (t._4._0.ctor) + {case "NBlack": + switch (t._4._3.ctor) + {case "RBNode": + switch (t._4._3._0.ctor) + {case "Black": + switch (t._4._4.ctor) + {case "RBNode": + switch (t._4._4._0.ctor) + {case "Black": return A5(RBNode, + Black, + t._4._3._1, + t._4._3._2, + A5(RBNode, + Black, + t._1, + t._2, + t._3, + t._4._3._3), + A5(balance, + Black, + t._4._1, + t._4._2, + t._4._3._4, + redden(t._4._4)));} + break;} + return t;} + break;} + break;} + break;} + switch (t._3.ctor) + {case "RBNode": + switch (t._3._0.ctor) + {case "NBlack": + switch (t._3._4.ctor) + {case "RBNode": + switch (t._3._4._0.ctor) + {case "Black": + switch (t._3._3.ctor) + {case "RBNode": + switch (t._3._3._0.ctor) + {case "Black": return A5(RBNode, + Black, + t._3._4._1, + t._3._4._2, + A5(balance, + Black, + t._3._1, + t._3._2, + redden(t._3._3), + t._3._4._3), + A5(RBNode, + Black, + t._1, + t._2, + t._3._4._4, + t._4));} + break;} + return t;} + break;} + break;} + break;} + break;} + break;} + return t; + } else return t; + }; + var balance = F5(function (c, + k, + v, + l, + r) { + return balance_node(A5(RBNode, + c, + k, + v, + l, + r)); + }); + var bubble = F5(function (c, + k, + v, + l, + r) { + return isBBlack(l) || isBBlack(r) ? A5(balance, + moreBlack(c), + k, + v, + lessBlackTree(l), + lessBlackTree(r)) : A5(RBNode, + c, + k, + v, + l, + r); + }); + var remove_max = F5(function (c, + k, + v, + l, + r) { + switch (r.ctor) + {case "RBEmpty": return A3(rem, + c, + l, + r); + case "RBNode": return A5(bubble, + c, + k, + v, + l, + A5(remove_max, + r._0, + r._1, + r._2, + r._3, + r._4));} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var rem = F3(function (c,l,r) { + var _v186 = {ctor: "_Tuple2" + ,_0: l + ,_1: r}; + switch (_v186.ctor) + {case "_Tuple2": + switch (_v186._0.ctor) + {case "RBEmpty": + switch (_v186._1.ctor) + {case "RBEmpty": switch (c.ctor) + {case "Black": + return RBEmpty(LBBlack); + case "Red": + return RBEmpty(LBlack);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + case "RBNode": + var _v208 = {ctor: "_Tuple3" + ,_0: c + ,_1: _v186._0._0 + ,_2: _v186._1._0}; + switch (_v208.ctor) + {case "_Tuple3": + switch (_v208._0.ctor) + {case "Black": + switch (_v208._1.ctor) + {case "LBlack": + switch (_v208._2.ctor) + {case "Red": return A5(RBNode, + Black, + _v186._1._1, + _v186._1._2, + _v186._1._3, + _v186._1._4);} + break;} + break;} + break;} + return A4(reportRemBug, + "Black/LBlack/Red", + c, + showLColor(_v186._0._0), + showNColor(_v186._1._0));} + break; + case "RBNode": + switch (_v186._1.ctor) + {case "RBEmpty": + var _v212 = {ctor: "_Tuple3" + ,_0: c + ,_1: _v186._0._0 + ,_2: _v186._1._0}; + switch (_v212.ctor) + {case "_Tuple3": + switch (_v212._0.ctor) + {case "Black": + switch (_v212._1.ctor) + {case "Red": + switch (_v212._2.ctor) + {case "LBlack": + return A5(RBNode, + Black, + _v186._0._1, + _v186._0._2, + _v186._0._3, + _v186._0._4);} + break;} + break;} + break;} + return A4(reportRemBug, + "Black/Red/LBlack", + c, + showNColor(_v186._0._0), + showLColor(_v186._1._0)); + case "RBNode": + var l$ = A5(remove_max, + _v186._0._0, + _v186._0._1, + _v186._0._2, + _v186._0._3, + _v186._0._4); + var r = A5(RBNode, + _v186._1._0, + _v186._1._1, + _v186._1._2, + _v186._1._3, + _v186._1._4); + var l = A5(RBNode, + _v186._0._0, + _v186._0._1, + _v186._0._2, + _v186._0._3, + _v186._0._4); + var $ = max(l), + k = $._0, + v = $._1; + return A5(bubble,c,k,v,l$,r);} + break;} + break;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var update = F3(function (k, + alter, + dict) { + var up = function (dict) { + switch (dict.ctor) + {case "RBEmpty": + switch (dict._0.ctor) + {case "LBlack": + var _v223 = alter($Maybe.Nothing); + switch (_v223.ctor) + {case "Just": + return {ctor: "_Tuple2" + ,_0: Insert + ,_1: A5(RBNode, + Red, + k, + _v223._0, + empty, + empty)}; + case "Nothing": + return {ctor: "_Tuple2" + ,_0: Same + ,_1: empty};} + _U.badCase($moduleName, + "bugs in reporting the exact location right now");} + break; + case "RBNode": + var _v225 = A2($Basics.compare, + k, + dict._1); + switch (_v225.ctor) + {case "EQ": + var _v226 = alter($Maybe.Just(dict._2)); + switch (_v226.ctor) + {case "Just": + return {ctor: "_Tuple2" + ,_0: Same + ,_1: A5(RBNode, + dict._0, + dict._1, + _v226._0, + dict._3, + dict._4)}; + case "Nothing": + return {ctor: "_Tuple2" + ,_0: Remove + ,_1: A3(rem, + dict._0, + dict._3, + dict._4)};} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + case "GT": var $ = up(dict._4), + flag = $._0, + newRight = $._1; + switch (flag.ctor) + {case "Insert": + return {ctor: "_Tuple2" + ,_0: Insert + ,_1: A5(balance, + dict._0, + dict._1, + dict._2, + dict._3, + newRight)}; + case "Remove": + return {ctor: "_Tuple2" + ,_0: Remove + ,_1: A5(bubble, + dict._0, + dict._1, + dict._2, + dict._3, + newRight)}; + case "Same": + return {ctor: "_Tuple2" + ,_0: Same + ,_1: A5(RBNode, + dict._0, + dict._1, + dict._2, + dict._3, + newRight)};} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + case "LT": var $ = up(dict._3), + flag = $._0, + newLeft = $._1; + switch (flag.ctor) + {case "Insert": + return {ctor: "_Tuple2" + ,_0: Insert + ,_1: A5(balance, + dict._0, + dict._1, + dict._2, + newLeft, + dict._4)}; + case "Remove": + return {ctor: "_Tuple2" + ,_0: Remove + ,_1: A5(bubble, + dict._0, + dict._1, + dict._2, + newLeft, + dict._4)}; + case "Same": + return {ctor: "_Tuple2" + ,_0: Same + ,_1: A5(RBNode, + dict._0, + dict._1, + dict._2, + newLeft, + dict._4)};} + _U.badCase($moduleName, + "bugs in reporting the exact location right now");} + _U.badCase($moduleName, + "bugs in reporting the exact location right now");} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var $ = up(dict), + flag = $._0, + updatedDict = $._1; + switch (flag.ctor) + {case "Insert": + return ensureBlackRoot(updatedDict); + case "Remove": + return blacken(updatedDict); + case "Same": + return updatedDict;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var insert = F3(function (key, + value, + dict) { + return A3(update, + key, + $Basics.always($Maybe.Just(value)), + dict); + }); + var singleton = F2(function (key, + value) { + return A3(insert, + key, + value, + empty); + }); + var union = F2(function (t1, + t2) { + return A3(foldl, + insert, + t2, + t1); + }); + var fromList = function (assocs) { + return A3($List.foldl, + F2(function (_v231,dict) { + var $ = _v231, + key = $._0, + value = $._1; + return A3(insert, + key, + value, + dict); + }), + empty, + assocs); + }; + var filter = F2(function (predicate, + dictionary) { + var add = F3(function (key, + value, + dict) { + return A2(predicate, + key, + value) ? A3(insert, + key, + value, + dict) : dict; + }); + return A3(foldl, + add, + empty, + dictionary); + }); + var intersect = F2(function (t1, + t2) { + return A2(filter, + F2(function (k,_v232) { + var _ = _v232; + return A2(member,k,t2); + }), + t1); + }); + var partition = F2(function (predicate, + dict) { + var add = F3(function (key, + value, + _v233) { + var $ = _v233, + t1 = $._0, + t2 = $._1; + return A2(predicate, + key, + value) ? {ctor: "_Tuple2" + ,_0: A3(insert,key,value,t1) + ,_1: t2} : {ctor: "_Tuple2" + ,_0: t1 + ,_1: A3(insert,key,value,t2)}; + }); + return A3(foldl, + add, + {ctor: "_Tuple2" + ,_0: empty + ,_1: empty}, + dict); + }); + var remove = F2(function (key, + dict) { + return A3(update, + key, + $Basics.always($Maybe.Nothing), + dict); + }); + var diff = F2(function (t1,t2) { + return A3(foldl, + F3(function (k,v,t) { + return A2(remove,k,t); + }), + t1, + t2); + }); + _elm.Dict.values = {_op: _op + ,empty: empty + ,singleton: singleton + ,insert: insert + ,update: update + ,isEmpty: isEmpty + ,get: get + ,remove: remove + ,member: member + ,filter: filter + ,partition: partition + ,foldl: foldl + ,foldr: foldr + ,map: map + ,union: union + ,intersect: intersect + ,diff: diff + ,keys: keys + ,values: values + ,toList: toList + ,fromList: fromList}; + return _elm.Dict.values; +}; +Elm.Graphics = Elm.Graphics || {}; +Elm.Graphics.Collage = Elm.Graphics.Collage || {}; +Elm.Graphics.Collage.make = function (_elm) { + "use strict"; + _elm.Graphics = _elm.Graphics || {}; + _elm.Graphics.Collage = _elm.Graphics.Collage || {}; + if (_elm.Graphics.Collage.values) + return _elm.Graphics.Collage.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Graphics.Collage", + $Basics = Elm.Basics.make(_elm), + $Color = Elm.Color.make(_elm), + $Graphics$Element = Elm.Graphics.Element.make(_elm), + $List = Elm.List.make(_elm), + $Native$Graphics$Collage = Elm.Native.Graphics.Collage.make(_elm), + $Text = Elm.Text.make(_elm), + $Transform2D = Elm.Transform2D.make(_elm); + var ngon = F2(function (n,r) { + var m = $Basics.toFloat(n); + var t = 2 * $Basics.pi / m; + var f = function (i) { + return {ctor: "_Tuple2" + ,_0: r * $Basics.cos(t * i) + ,_1: r * $Basics.sin(t * i)}; + }; + return A2($List.map, + f, + _L.range(0,m - 1)); + }); + var oval = F2(function (w,h) { + var hh = h / 2; + var hw = w / 2; + var n = 50; + var t = 2 * $Basics.pi / n; + var f = function (i) { + return {ctor: "_Tuple2" + ,_0: hw * $Basics.cos(t * i) + ,_1: hh * $Basics.sin(t * i)}; + }; + return A2($List.map, + f, + _L.range(0,n - 1)); + }); + var circle = function (r) { + return A2(oval,2 * r,2 * r); + }; + var rect = F2(function (w,h) { + var hh = h / 2; + var hw = w / 2; + return _L.fromArray([{ctor: "_Tuple2" + ,_0: 0 - hw + ,_1: 0 - hh} + ,{ctor: "_Tuple2" + ,_0: 0 - hw + ,_1: hh} + ,{ctor: "_Tuple2",_0: hw,_1: hh} + ,{ctor: "_Tuple2" + ,_0: hw + ,_1: 0 - hh}]); + }); + var square = function (n) { + return A2(rect,n,n); + }; + var polygon = function (points) { + return points; + }; + var segment = F2(function (p1, + p2) { + return _L.fromArray([p1,p2]); + }); + var path = function (ps) { + return ps; + }; + var collage = $Native$Graphics$Collage.collage; + var alpha = F2(function (a,f) { + return _U.replace([["alpha" + ,a]], + f); + }); + var rotate = F2(function (t,f) { + return _U.replace([["theta" + ,f.theta + t]], + f); + }); + var scale = F2(function (s,f) { + return _U.replace([["scale" + ,f.scale * s]], + f); + }); + var moveY = F2(function (y,f) { + return _U.replace([["y" + ,f.y + y]], + f); + }); + var moveX = F2(function (x,f) { + return _U.replace([["x" + ,f.x + x]], + f); + }); + var move = F2(function (_v0,f) { + var $ = _v0, + x = $._0, + y = $._1; + return _U.replace([["x",f.x + x] + ,["y",f.y + y]], + f); + }); + var form = function (f) { + return {_: {} + ,alpha: 1 + ,form: f + ,scale: 1 + ,theta: 0 + ,x: 0 + ,y: 0}; + }; + var Fill = function (a) { + return {ctor: "Fill",_0: a}; + }; + var Line = function (a) { + return {ctor: "Line",_0: a}; + }; + var FGroup = F2(function (a,b) { + return {ctor: "FGroup" + ,_0: a + ,_1: b}; + }); + var group = function (fs) { + return form(A2(FGroup, + $Transform2D.identity, + fs)); + }; + var groupTransform = F2(function (matrix, + fs) { + return form(A2(FGroup, + matrix, + fs)); + }); + var FElement = function (a) { + return {ctor: "FElement" + ,_0: a}; + }; + var toForm = function (e) { + return form(FElement(e)); + }; + var FImage = F4(function (a, + b, + c, + d) { + return {ctor: "FImage" + ,_0: a + ,_1: b + ,_2: c + ,_3: d}; + }); + var sprite = F4(function (w, + h, + pos, + src) { + return form(A4(FImage, + w, + h, + pos, + src)); + }); + var FText = function (a) { + return {ctor: "FText",_0: a}; + }; + var text = function (t) { + return form(FText(t)); + }; + var FOutlinedText = F2(function (a, + b) { + return {ctor: "FOutlinedText" + ,_0: a + ,_1: b}; + }); + var outlinedText = F2(function (ls, + t) { + return form(A2(FOutlinedText, + ls, + t)); + }); + var FShape = F2(function (a,b) { + return {ctor: "FShape" + ,_0: a + ,_1: b}; + }); + var fill = F2(function (style, + shape) { + return form(A2(FShape, + Fill(style), + shape)); + }); + var outlined = F2(function (style, + shape) { + return form(A2(FShape, + Line(style), + shape)); + }); + var FPath = F2(function (a,b) { + return {ctor: "FPath" + ,_0: a + ,_1: b}; + }); + var traced = F2(function (style, + path) { + return form(A2(FPath, + style, + path)); + }); + var LineStyle = F6(function (a, + b, + c, + d, + e, + f) { + return {_: {} + ,cap: c + ,color: a + ,dashOffset: f + ,dashing: e + ,join: d + ,width: b}; + }); + var Clipped = {ctor: "Clipped"}; + var Sharp = function (a) { + return {ctor: "Sharp",_0: a}; + }; + var Smooth = {ctor: "Smooth"}; + var Padded = {ctor: "Padded"}; + var Round = {ctor: "Round"}; + var Flat = {ctor: "Flat"}; + var defaultLine = {_: {} + ,cap: Flat + ,color: $Color.black + ,dashOffset: 0 + ,dashing: _L.fromArray([]) + ,join: Sharp(10) + ,width: 1}; + var solid = function (clr) { + return _U.replace([["color" + ,clr]], + defaultLine); + }; + var dashed = function (clr) { + return _U.replace([["color" + ,clr] + ,["dashing" + ,_L.fromArray([8,4])]], + defaultLine); + }; + var dotted = function (clr) { + return _U.replace([["color" + ,clr] + ,["dashing" + ,_L.fromArray([3,3])]], + defaultLine); + }; + var Grad = function (a) { + return {ctor: "Grad",_0: a}; + }; + var gradient = F2(function (grad, + shape) { + return A2(fill, + Grad(grad), + shape); + }); + var Texture = function (a) { + return {ctor: "Texture" + ,_0: a}; + }; + var textured = F2(function (src, + shape) { + return A2(fill, + Texture(src), + shape); + }); + var Solid = function (a) { + return {ctor: "Solid",_0: a}; + }; + var filled = F2(function (color, + shape) { + return A2(fill, + Solid(color), + shape); + }); + var Form = F6(function (a, + b, + c, + d, + e, + f) { + return {_: {} + ,alpha: e + ,form: f + ,scale: b + ,theta: a + ,x: c + ,y: d}; + }); + _elm.Graphics.Collage.values = {_op: _op + ,collage: collage + ,toForm: toForm + ,filled: filled + ,textured: textured + ,gradient: gradient + ,outlined: outlined + ,traced: traced + ,text: text + ,outlinedText: outlinedText + ,move: move + ,moveX: moveX + ,moveY: moveY + ,scale: scale + ,rotate: rotate + ,alpha: alpha + ,group: group + ,groupTransform: groupTransform + ,rect: rect + ,oval: oval + ,square: square + ,circle: circle + ,ngon: ngon + ,polygon: polygon + ,segment: segment + ,path: path + ,solid: solid + ,dashed: dashed + ,dotted: dotted + ,defaultLine: defaultLine + ,Form: Form + ,LineStyle: LineStyle + ,Flat: Flat + ,Round: Round + ,Padded: Padded + ,Smooth: Smooth + ,Sharp: Sharp + ,Clipped: Clipped}; + return _elm.Graphics.Collage.values; +}; +Elm.Graphics = Elm.Graphics || {}; +Elm.Graphics.Element = Elm.Graphics.Element || {}; +Elm.Graphics.Element.make = function (_elm) { + "use strict"; + _elm.Graphics = _elm.Graphics || {}; + _elm.Graphics.Element = _elm.Graphics.Element || {}; + if (_elm.Graphics.Element.values) + return _elm.Graphics.Element.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Graphics.Element", + $Basics = Elm.Basics.make(_elm), + $Color = Elm.Color.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Native$Graphics$Element = Elm.Native.Graphics.Element.make(_elm), + $Text = Elm.Text.make(_elm); + var DOut = {ctor: "DOut"}; + var outward = DOut; + var DIn = {ctor: "DIn"}; + var inward = DIn; + var DRight = {ctor: "DRight"}; + var right = DRight; + var DLeft = {ctor: "DLeft"}; + var left = DLeft; + var DDown = {ctor: "DDown"}; + var down = DDown; + var DUp = {ctor: "DUp"}; + var up = DUp; + var Position = F4(function (a, + b, + c, + d) { + return {_: {} + ,horizontal: a + ,vertical: b + ,x: c + ,y: d}; + }); + var Relative = function (a) { + return {ctor: "Relative" + ,_0: a}; + }; + var relative = Relative; + var Absolute = function (a) { + return {ctor: "Absolute" + ,_0: a}; + }; + var absolute = Absolute; + var N = {ctor: "N"}; + var bottomLeftAt = F2(function (x, + y) { + return {_: {} + ,horizontal: N + ,vertical: N + ,x: x + ,y: y}; + }); + var Z = {ctor: "Z"}; + var middle = {_: {} + ,horizontal: Z + ,vertical: Z + ,x: Relative(0.5) + ,y: Relative(0.5)}; + var midLeft = _U.replace([["horizontal" + ,N] + ,["x",Absolute(0)]], + middle); + var middleAt = F2(function (x, + y) { + return {_: {} + ,horizontal: Z + ,vertical: Z + ,x: x + ,y: y}; + }); + var midLeftAt = F2(function (x, + y) { + return {_: {} + ,horizontal: N + ,vertical: Z + ,x: x + ,y: y}; + }); + var midBottomAt = F2(function (x, + y) { + return {_: {} + ,horizontal: Z + ,vertical: N + ,x: x + ,y: y}; + }); + var P = {ctor: "P"}; + var topLeft = {_: {} + ,horizontal: N + ,vertical: P + ,x: Absolute(0) + ,y: Absolute(0)}; + var bottomLeft = _U.replace([["vertical" + ,N]], + topLeft); + var topRight = _U.replace([["horizontal" + ,P]], + topLeft); + var bottomRight = _U.replace([["horizontal" + ,P]], + bottomLeft); + var midRight = _U.replace([["horizontal" + ,P]], + midLeft); + var midTop = _U.replace([["vertical" + ,P] + ,["y",Absolute(0)]], + middle); + var midBottom = _U.replace([["vertical" + ,N]], + midTop); + var topLeftAt = F2(function (x, + y) { + return {_: {} + ,horizontal: N + ,vertical: P + ,x: x + ,y: y}; + }); + var topRightAt = F2(function (x, + y) { + return {_: {} + ,horizontal: P + ,vertical: P + ,x: x + ,y: y}; + }); + var bottomRightAt = F2(function (x, + y) { + return {_: {} + ,horizontal: P + ,vertical: N + ,x: x + ,y: y}; + }); + var midRightAt = F2(function (x, + y) { + return {_: {} + ,horizontal: P + ,vertical: Z + ,x: x + ,y: y}; + }); + var midTopAt = F2(function (x, + y) { + return {_: {} + ,horizontal: Z + ,vertical: P + ,x: x + ,y: y}; + }); + var justified = $Native$Graphics$Element.block("justify"); + var centered = $Native$Graphics$Element.block("center"); + var rightAligned = $Native$Graphics$Element.block("right"); + var leftAligned = $Native$Graphics$Element.block("left"); + var show = function (value) { + return leftAligned($Text.monospace($Text.fromString($Basics.toString(value)))); + }; + var Tiled = {ctor: "Tiled"}; + var Cropped = function (a) { + return {ctor: "Cropped" + ,_0: a}; + }; + var Fitted = {ctor: "Fitted"}; + var Plain = {ctor: "Plain"}; + var Custom = {ctor: "Custom"}; + var RawHtml = {ctor: "RawHtml"}; + var Spacer = {ctor: "Spacer"}; + var Flow = F2(function (a,b) { + return {ctor: "Flow" + ,_0: a + ,_1: b}; + }); + var Container = F2(function (a, + b) { + return {ctor: "Container" + ,_0: a + ,_1: b}; + }); + var Image = F4(function (a, + b, + c, + d) { + return {ctor: "Image" + ,_0: a + ,_1: b + ,_2: c + ,_3: d}; + }); + var newElement = $Native$Graphics$Element.newElement; + var image = F3(function (w, + h, + src) { + return A3(newElement, + w, + h, + A4(Image,Plain,w,h,src)); + }); + var fittedImage = F3(function (w, + h, + src) { + return A3(newElement, + w, + h, + A4(Image,Fitted,w,h,src)); + }); + var croppedImage = F4(function (pos, + w, + h, + src) { + return A3(newElement, + w, + h, + A4(Image,Cropped(pos),w,h,src)); + }); + var tiledImage = F3(function (w, + h, + src) { + return A3(newElement, + w, + h, + A4(Image,Tiled,w,h,src)); + }); + var container = F4(function (w, + h, + pos, + e) { + return A3(newElement, + w, + h, + A2(Container,pos,e)); + }); + var spacer = F2(function (w,h) { + return A3(newElement, + w, + h, + Spacer); + }); + var link = F2(function (href, + e) { + var p = e.props; + return {_: {} + ,element: e.element + ,props: _U.replace([["href" + ,href]], + p)}; + }); + var tag = F2(function (name,e) { + var p = e.props; + return {_: {} + ,element: e.element + ,props: _U.replace([["tag" + ,name]], + p)}; + }); + var color = F2(function (c,e) { + var p = e.props; + return {_: {} + ,element: e.element + ,props: _U.replace([["color" + ,$Maybe.Just(c)]], + p)}; + }); + var opacity = F2(function (o, + e) { + var p = e.props; + return {_: {} + ,element: e.element + ,props: _U.replace([["opacity" + ,o]], + p)}; + }); + var height = F2(function (nh, + e) { + var p = e.props; + var props = function () { + var _v0 = e.element; + switch (_v0.ctor) + {case "Image": + return _U.replace([["width" + ,$Basics.round($Basics.toFloat(_v0._1) / $Basics.toFloat(_v0._2) * $Basics.toFloat(nh))]], + p);} + return p; + }(); + return {_: {} + ,element: e.element + ,props: _U.replace([["height" + ,nh]], + p)}; + }); + var width = F2(function (nw,e) { + var p = e.props; + var props = function () { + var _v5 = e.element; + switch (_v5.ctor) + {case "Image": + return _U.replace([["height" + ,$Basics.round($Basics.toFloat(_v5._2) / $Basics.toFloat(_v5._1) * $Basics.toFloat(nw))]], + p); + case "RawHtml": + return _U.replace([["height" + ,$Basics.snd(A2($Native$Graphics$Element.htmlHeight, + nw, + e.element))]], + p);} + return p; + }(); + return {_: {} + ,element: e.element + ,props: _U.replace([["width" + ,nw]], + props)}; + }); + var size = F3(function (w,h,e) { + return A2(height, + h, + A2(width,w,e)); + }); + var sizeOf = function (e) { + return {ctor: "_Tuple2" + ,_0: e.props.width + ,_1: e.props.height}; + }; + var heightOf = function (e) { + return e.props.height; + }; + var widthOf = function (e) { + return e.props.width; + }; + var above = F2(function (hi, + lo) { + return A3(newElement, + A2($Basics.max, + widthOf(hi), + widthOf(lo)), + heightOf(hi) + heightOf(lo), + A2(Flow, + DDown, + _L.fromArray([hi,lo]))); + }); + var below = F2(function (lo, + hi) { + return A3(newElement, + A2($Basics.max, + widthOf(hi), + widthOf(lo)), + heightOf(hi) + heightOf(lo), + A2(Flow, + DDown, + _L.fromArray([hi,lo]))); + }); + var beside = F2(function (lft, + rht) { + return A3(newElement, + widthOf(lft) + widthOf(rht), + A2($Basics.max, + heightOf(lft), + heightOf(rht)), + A2(Flow, + right, + _L.fromArray([lft,rht]))); + }); + var layers = function (es) { + var hs = A2($List.map, + heightOf, + es); + var ws = A2($List.map, + widthOf, + es); + return A3(newElement, + A2($Maybe.withDefault, + 0, + $List.maximum(ws)), + A2($Maybe.withDefault, + 0, + $List.maximum(hs)), + A2(Flow,DOut,es)); + }; + var empty = A2(spacer,0,0); + var flow = F2(function (dir, + es) { + var newFlow = F2(function (w, + h) { + return A3(newElement, + w, + h, + A2(Flow,dir,es)); + }); + var maxOrZero = function (list) { + return A2($Maybe.withDefault, + 0, + $List.maximum(list)); + }; + var hs = A2($List.map, + heightOf, + es); + var ws = A2($List.map, + widthOf, + es); + if (_U.eq(es,_L.fromArray([]))) + return empty; else { + switch (dir.ctor) + {case "DDown": + return A2(newFlow, + maxOrZero(ws), + $List.sum(hs)); + case "DIn": return A2(newFlow, + maxOrZero(ws), + maxOrZero(hs)); + case "DLeft": return A2(newFlow, + $List.sum(ws), + maxOrZero(hs)); + case "DOut": return A2(newFlow, + maxOrZero(ws), + maxOrZero(hs)); + case "DRight": + return A2(newFlow, + $List.sum(ws), + maxOrZero(hs)); + case "DUp": return A2(newFlow, + maxOrZero(ws), + $List.sum(hs));} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + } + }); + var Properties = F9(function (a, + b, + c, + d, + e, + f, + g, + h, + i) { + return {_: {} + ,click: i + ,color: e + ,height: c + ,hover: h + ,href: f + ,id: a + ,opacity: d + ,tag: g + ,width: b}; + }); + var Element = F2(function (a, + b) { + return {_: {} + ,element: b + ,props: a}; + }); + _elm.Graphics.Element.values = {_op: _op + ,image: image + ,fittedImage: fittedImage + ,croppedImage: croppedImage + ,tiledImage: tiledImage + ,leftAligned: leftAligned + ,rightAligned: rightAligned + ,centered: centered + ,justified: justified + ,show: show + ,width: width + ,height: height + ,size: size + ,color: color + ,opacity: opacity + ,link: link + ,tag: tag + ,widthOf: widthOf + ,heightOf: heightOf + ,sizeOf: sizeOf + ,flow: flow + ,up: up + ,down: down + ,left: left + ,right: right + ,inward: inward + ,outward: outward + ,layers: layers + ,above: above + ,below: below + ,beside: beside + ,empty: empty + ,spacer: spacer + ,container: container + ,middle: middle + ,midTop: midTop + ,midBottom: midBottom + ,midLeft: midLeft + ,midRight: midRight + ,topLeft: topLeft + ,topRight: topRight + ,bottomLeft: bottomLeft + ,bottomRight: bottomRight + ,absolute: absolute + ,relative: relative + ,middleAt: middleAt + ,midTopAt: midTopAt + ,midBottomAt: midBottomAt + ,midLeftAt: midLeftAt + ,midRightAt: midRightAt + ,topLeftAt: topLeftAt + ,topRightAt: topRightAt + ,bottomLeftAt: bottomLeftAt + ,bottomRightAt: bottomRightAt + ,Element: Element + ,Position: Position}; + return _elm.Graphics.Element.values; +}; +Elm.Html = Elm.Html || {}; +Elm.Html.make = function (_elm) { + "use strict"; + _elm.Html = _elm.Html || {}; + if (_elm.Html.values) + return _elm.Html.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Html", + $Basics = Elm.Basics.make(_elm), + $Graphics$Element = Elm.Graphics.Element.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $VirtualDom = Elm.VirtualDom.make(_elm); + var fromElement = $VirtualDom.fromElement; + var toElement = $VirtualDom.toElement; + var text = $VirtualDom.text; + var node = $VirtualDom.node; + var body = node("body"); + var section = node("section"); + var nav = node("nav"); + var article = node("article"); + var aside = node("aside"); + var h1 = node("h1"); + var h2 = node("h2"); + var h3 = node("h3"); + var h4 = node("h4"); + var h5 = node("h5"); + var h6 = node("h6"); + var header = node("header"); + var footer = node("footer"); + var address = node("address"); + var main$ = node("main"); + var p = node("p"); + var hr = node("hr"); + var pre = node("pre"); + var blockquote = node("blockquote"); + var ol = node("ol"); + var ul = node("ul"); + var li = node("li"); + var dl = node("dl"); + var dt = node("dt"); + var dd = node("dd"); + var figure = node("figure"); + var figcaption = node("figcaption"); + var div = node("div"); + var a = node("a"); + var em = node("em"); + var strong = node("strong"); + var small = node("small"); + var s = node("s"); + var cite = node("cite"); + var q = node("q"); + var dfn = node("dfn"); + var abbr = node("abbr"); + var time = node("time"); + var code = node("code"); + var $var = node("var"); + var samp = node("samp"); + var kbd = node("kbd"); + var sub = node("sub"); + var sup = node("sup"); + var i = node("i"); + var b = node("b"); + var u = node("u"); + var mark = node("mark"); + var ruby = node("ruby"); + var rt = node("rt"); + var rp = node("rp"); + var bdi = node("bdi"); + var bdo = node("bdo"); + var span = node("span"); + var br = node("br"); + var wbr = node("wbr"); + var ins = node("ins"); + var del = node("del"); + var img = node("img"); + var iframe = node("iframe"); + var embed = node("embed"); + var object = node("object"); + var param = node("param"); + var video = node("video"); + var audio = node("audio"); + var source = node("source"); + var track = node("track"); + var canvas = node("canvas"); + var svg = node("svg"); + var math = node("math"); + var table = node("table"); + var caption = node("caption"); + var colgroup = node("colgroup"); + var col = node("col"); + var tbody = node("tbody"); + var thead = node("thead"); + var tfoot = node("tfoot"); + var tr = node("tr"); + var td = node("td"); + var th = node("th"); + var form = node("form"); + var fieldset = node("fieldset"); + var legend = node("legend"); + var label = node("label"); + var input = node("input"); + var button = node("button"); + var select = node("select"); + var datalist = node("datalist"); + var optgroup = node("optgroup"); + var option = node("option"); + var textarea = node("textarea"); + var keygen = node("keygen"); + var output = node("output"); + var progress = node("progress"); + var meter = node("meter"); + var details = node("details"); + var summary = node("summary"); + var menuitem = node("menuitem"); + var menu = node("menu"); + _elm.Html.values = {_op: _op + ,node: node + ,text: text + ,toElement: toElement + ,fromElement: fromElement + ,body: body + ,section: section + ,nav: nav + ,article: article + ,aside: aside + ,h1: h1 + ,h2: h2 + ,h3: h3 + ,h4: h4 + ,h5: h5 + ,h6: h6 + ,header: header + ,footer: footer + ,address: address + ,main$: main$ + ,p: p + ,hr: hr + ,pre: pre + ,blockquote: blockquote + ,ol: ol + ,ul: ul + ,li: li + ,dl: dl + ,dt: dt + ,dd: dd + ,figure: figure + ,figcaption: figcaption + ,div: div + ,a: a + ,em: em + ,strong: strong + ,small: small + ,s: s + ,cite: cite + ,q: q + ,dfn: dfn + ,abbr: abbr + ,time: time + ,code: code + ,$var: $var + ,samp: samp + ,kbd: kbd + ,sub: sub + ,sup: sup + ,i: i + ,b: b + ,u: u + ,mark: mark + ,ruby: ruby + ,rt: rt + ,rp: rp + ,bdi: bdi + ,bdo: bdo + ,span: span + ,br: br + ,wbr: wbr + ,ins: ins + ,del: del + ,img: img + ,iframe: iframe + ,embed: embed + ,object: object + ,param: param + ,video: video + ,audio: audio + ,source: source + ,track: track + ,canvas: canvas + ,svg: svg + ,math: math + ,table: table + ,caption: caption + ,colgroup: colgroup + ,col: col + ,tbody: tbody + ,thead: thead + ,tfoot: tfoot + ,tr: tr + ,td: td + ,th: th + ,form: form + ,fieldset: fieldset + ,legend: legend + ,label: label + ,input: input + ,button: button + ,select: select + ,datalist: datalist + ,optgroup: optgroup + ,option: option + ,textarea: textarea + ,keygen: keygen + ,output: output + ,progress: progress + ,meter: meter + ,details: details + ,summary: summary + ,menuitem: menuitem + ,menu: menu}; + return _elm.Html.values; +}; +Elm.Html = Elm.Html || {}; +Elm.Html.Attributes = Elm.Html.Attributes || {}; +Elm.Html.Attributes.make = function (_elm) { + "use strict"; + _elm.Html = _elm.Html || {}; + _elm.Html.Attributes = _elm.Html.Attributes || {}; + if (_elm.Html.Attributes.values) + return _elm.Html.Attributes.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Html.Attributes", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $Json$Encode = Elm.Json.Encode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $String = Elm.String.make(_elm), + $VirtualDom = Elm.VirtualDom.make(_elm); + var attribute = $VirtualDom.attribute; + var property = $VirtualDom.property; + var stringProperty = F2(function (name, + string) { + return A2(property, + name, + $Json$Encode.string(string)); + }); + var $class = function (name) { + return A2(stringProperty, + "className", + name); + }; + var id = function (name) { + return A2(stringProperty, + "id", + name); + }; + var title = function (name) { + return A2(stringProperty, + "title", + name); + }; + var accesskey = function ($char) { + return A2(stringProperty, + "accesskey", + $String.fromList(_L.fromArray([$char]))); + }; + var contextmenu = function (value) { + return A2(stringProperty, + "contextmenu", + value); + }; + var dir = function (value) { + return A2(stringProperty, + "dir", + value); + }; + var draggable = function (value) { + return A2(stringProperty, + "draggable", + value); + }; + var dropzone = function (value) { + return A2(stringProperty, + "dropzone", + value); + }; + var itemprop = function (value) { + return A2(stringProperty, + "itemprop", + value); + }; + var lang = function (value) { + return A2(stringProperty, + "lang", + value); + }; + var tabindex = function (n) { + return A2(stringProperty, + "tabIndex", + $Basics.toString(n)); + }; + var charset = function (value) { + return A2(stringProperty, + "charset", + value); + }; + var content = function (value) { + return A2(stringProperty, + "content", + value); + }; + var httpEquiv = function (value) { + return A2(stringProperty, + "httpEquiv", + value); + }; + var language = function (value) { + return A2(stringProperty, + "language", + value); + }; + var src = function (value) { + return A2(stringProperty, + "src", + value); + }; + var height = function (value) { + return A2(stringProperty, + "height", + $Basics.toString(value)); + }; + var width = function (value) { + return A2(stringProperty, + "width", + $Basics.toString(value)); + }; + var alt = function (value) { + return A2(stringProperty, + "alt", + value); + }; + var preload = function (value) { + return A2(stringProperty, + "preload", + value); + }; + var poster = function (value) { + return A2(stringProperty, + "poster", + value); + }; + var kind = function (value) { + return A2(stringProperty, + "kind", + value); + }; + var srclang = function (value) { + return A2(stringProperty, + "srclang", + value); + }; + var sandbox = function (value) { + return A2(stringProperty, + "sandbox", + value); + }; + var srcdoc = function (value) { + return A2(stringProperty, + "srcdoc", + value); + }; + var type$ = function (value) { + return A2(stringProperty, + "type", + value); + }; + var value = function (value) { + return A2(stringProperty, + "value", + value); + }; + var placeholder = function (value) { + return A2(stringProperty, + "placeholder", + value); + }; + var accept = function (value) { + return A2(stringProperty, + "accept", + value); + }; + var acceptCharset = function (value) { + return A2(stringProperty, + "acceptCharset", + value); + }; + var action = function (value) { + return A2(stringProperty, + "action", + value); + }; + var autocomplete = function (bool) { + return A2(stringProperty, + "autocomplete", + bool ? "on" : "off"); + }; + var autosave = function (value) { + return A2(stringProperty, + "autosave", + value); + }; + var enctype = function (value) { + return A2(stringProperty, + "enctype", + value); + }; + var formaction = function (value) { + return A2(stringProperty, + "formaction", + value); + }; + var list = function (value) { + return A2(stringProperty, + "list", + value); + }; + var minlength = function (n) { + return A2(stringProperty, + "minLength", + $Basics.toString(n)); + }; + var maxlength = function (n) { + return A2(stringProperty, + "maxLength", + $Basics.toString(n)); + }; + var method = function (value) { + return A2(stringProperty, + "method", + value); + }; + var name = function (value) { + return A2(stringProperty, + "name", + value); + }; + var pattern = function (value) { + return A2(stringProperty, + "pattern", + value); + }; + var size = function (n) { + return A2(stringProperty, + "size", + $Basics.toString(n)); + }; + var $for = function (value) { + return A2(stringProperty, + "htmlFor", + value); + }; + var form = function (value) { + return A2(stringProperty, + "form", + value); + }; + var max = function (value) { + return A2(stringProperty, + "max", + value); + }; + var min = function (value) { + return A2(stringProperty, + "min", + value); + }; + var step = function (n) { + return A2(stringProperty, + "step", + n); + }; + var cols = function (n) { + return A2(stringProperty, + "cols", + $Basics.toString(n)); + }; + var rows = function (n) { + return A2(stringProperty, + "rows", + $Basics.toString(n)); + }; + var wrap = function (value) { + return A2(stringProperty, + "wrap", + value); + }; + var usemap = function (value) { + return A2(stringProperty, + "useMap", + value); + }; + var shape = function (value) { + return A2(stringProperty, + "shape", + value); + }; + var coords = function (value) { + return A2(stringProperty, + "coords", + value); + }; + var challenge = function (value) { + return A2(stringProperty, + "challenge", + value); + }; + var keytype = function (value) { + return A2(stringProperty, + "keytype", + value); + }; + var align = function (value) { + return A2(stringProperty, + "align", + value); + }; + var cite = function (value) { + return A2(stringProperty, + "cite", + value); + }; + var href = function (value) { + return A2(stringProperty, + "href", + value); + }; + var target = function (value) { + return A2(stringProperty, + "target", + value); + }; + var downloadAs = function (value) { + return A2(stringProperty, + "download", + value); + }; + var hreflang = function (value) { + return A2(stringProperty, + "hreflang", + value); + }; + var media = function (value) { + return A2(stringProperty, + "media", + value); + }; + var ping = function (value) { + return A2(stringProperty, + "ping", + value); + }; + var rel = function (value) { + return A2(stringProperty, + "rel", + value); + }; + var datetime = function (value) { + return A2(stringProperty, + "datetime", + value); + }; + var pubdate = function (value) { + return A2(stringProperty, + "pubdate", + value); + }; + var start = function (n) { + return A2(stringProperty, + "start", + $Basics.toString(n)); + }; + var colspan = function (n) { + return A2(stringProperty, + "colSpan", + $Basics.toString(n)); + }; + var headers = function (value) { + return A2(stringProperty, + "headers", + value); + }; + var rowspan = function (n) { + return A2(stringProperty, + "rowSpan", + $Basics.toString(n)); + }; + var scope = function (value) { + return A2(stringProperty, + "scope", + value); + }; + var manifest = function (value) { + return A2(stringProperty, + "manifest", + value); + }; + var boolProperty = F2(function (name, + bool) { + return A2(property, + name, + $Json$Encode.bool(bool)); + }); + var hidden = function (bool) { + return A2(boolProperty, + "hidden", + bool); + }; + var contenteditable = function (bool) { + return A2(boolProperty, + "contentEditable", + bool); + }; + var spellcheck = function (bool) { + return A2(boolProperty, + "spellcheck", + bool); + }; + var async = function (bool) { + return A2(boolProperty, + "async", + bool); + }; + var defer = function (bool) { + return A2(boolProperty, + "defer", + bool); + }; + var scoped = function (bool) { + return A2(boolProperty, + "scoped", + bool); + }; + var autoplay = function (bool) { + return A2(boolProperty, + "autoplay", + bool); + }; + var controls = function (bool) { + return A2(boolProperty, + "controls", + bool); + }; + var loop = function (bool) { + return A2(boolProperty, + "loop", + bool); + }; + var $default = function (bool) { + return A2(boolProperty, + "default", + bool); + }; + var seamless = function (bool) { + return A2(boolProperty, + "seamless", + bool); + }; + var checked = function (bool) { + return A2(boolProperty, + "checked", + bool); + }; + var selected = function (bool) { + return A2(boolProperty, + "selected", + bool); + }; + var autofocus = function (bool) { + return A2(boolProperty, + "autofocus", + bool); + }; + var disabled = function (bool) { + return A2(boolProperty, + "disabled", + bool); + }; + var multiple = function (bool) { + return A2(boolProperty, + "multiple", + bool); + }; + var novalidate = function (bool) { + return A2(boolProperty, + "noValidate", + bool); + }; + var readonly = function (bool) { + return A2(boolProperty, + "readOnly", + bool); + }; + var required = function (bool) { + return A2(boolProperty, + "required", + bool); + }; + var ismap = function (value) { + return A2(boolProperty, + "isMap", + value); + }; + var download = function (bool) { + return A2(boolProperty, + "download", + bool); + }; + var reversed = function (bool) { + return A2(boolProperty, + "reversed", + bool); + }; + var classList = function (list) { + return $class($String.join(" ")($List.map($Basics.fst)($List.filter($Basics.snd)(list)))); + }; + var style = function (props) { + return property("style")($Json$Encode.object($List.map(function (_v0) { + var $ = _v0, + key = $._0, + value = $._1; + return {ctor: "_Tuple2" + ,_0: key + ,_1: $Json$Encode.string(value)}; + })(props))); + }; + var key = function (k) { + return A2(stringProperty, + "key", + k); + }; + _elm.Html.Attributes.values = {_op: _op + ,key: key + ,style: style + ,$class: $class + ,classList: classList + ,id: id + ,title: title + ,hidden: hidden + ,type$: type$ + ,value: value + ,checked: checked + ,placeholder: placeholder + ,selected: selected + ,accept: accept + ,acceptCharset: acceptCharset + ,action: action + ,autocomplete: autocomplete + ,autofocus: autofocus + ,autosave: autosave + ,disabled: disabled + ,enctype: enctype + ,formaction: formaction + ,list: list + ,maxlength: maxlength + ,minlength: minlength + ,method: method + ,multiple: multiple + ,name: name + ,novalidate: novalidate + ,pattern: pattern + ,readonly: readonly + ,required: required + ,size: size + ,$for: $for + ,form: form + ,max: max + ,min: min + ,step: step + ,cols: cols + ,rows: rows + ,wrap: wrap + ,href: href + ,target: target + ,download: download + ,downloadAs: downloadAs + ,hreflang: hreflang + ,media: media + ,ping: ping + ,rel: rel + ,ismap: ismap + ,usemap: usemap + ,shape: shape + ,coords: coords + ,src: src + ,height: height + ,width: width + ,alt: alt + ,autoplay: autoplay + ,controls: controls + ,loop: loop + ,preload: preload + ,poster: poster + ,$default: $default + ,kind: kind + ,srclang: srclang + ,sandbox: sandbox + ,seamless: seamless + ,srcdoc: srcdoc + ,reversed: reversed + ,start: start + ,align: align + ,colspan: colspan + ,rowspan: rowspan + ,headers: headers + ,scope: scope + ,async: async + ,charset: charset + ,content: content + ,defer: defer + ,httpEquiv: httpEquiv + ,language: language + ,scoped: scoped + ,accesskey: accesskey + ,contenteditable: contenteditable + ,contextmenu: contextmenu + ,dir: dir + ,draggable: draggable + ,dropzone: dropzone + ,itemprop: itemprop + ,lang: lang + ,spellcheck: spellcheck + ,tabindex: tabindex + ,challenge: challenge + ,keytype: keytype + ,cite: cite + ,datetime: datetime + ,pubdate: pubdate + ,manifest: manifest + ,property: property + ,attribute: attribute}; + return _elm.Html.Attributes.values; +}; +Elm.Html = Elm.Html || {}; +Elm.Html.Events = Elm.Html.Events || {}; +Elm.Html.Events.make = function (_elm) { + "use strict"; + _elm.Html = _elm.Html || {}; + _elm.Html.Events = _elm.Html.Events || {}; + if (_elm.Html.Events.values) + return _elm.Html.Events.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Html.Events", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $VirtualDom = Elm.VirtualDom.make(_elm); + var keyCode = A2($Json$Decode._op[":="], + "keyCode", + $Json$Decode.$int); + var targetChecked = A2($Json$Decode.at, + _L.fromArray(["target" + ,"checked"]), + $Json$Decode.bool); + var targetValue = A2($Json$Decode.at, + _L.fromArray(["target" + ,"value"]), + $Json$Decode.string); + var defaultOptions = $VirtualDom.defaultOptions; + var Options = F2(function (a, + b) { + return {_: {} + ,preventDefault: b + ,stopPropagation: a}; + }); + var onWithOptions = $VirtualDom.onWithOptions; + var on = $VirtualDom.on; + var messageOn = F3(function (name, + addr, + msg) { + return A3(on, + name, + $Json$Decode.value, + function (_v0) { + var _ = _v0; + return A2($Signal.message, + addr, + msg); + }); + }); + var onClick = messageOn("click"); + var onDoubleClick = messageOn("dblclick"); + var onMouseMove = messageOn("mousemove"); + var onMouseDown = messageOn("mousedown"); + var onMouseUp = messageOn("mouseup"); + var onMouseEnter = messageOn("mouseenter"); + var onMouseLeave = messageOn("mouseleave"); + var onMouseOver = messageOn("mouseover"); + var onMouseOut = messageOn("mouseout"); + var onBlur = messageOn("blur"); + var onFocus = messageOn("focus"); + var onSubmit = messageOn("submit"); + var onKey = F3(function (name, + addr, + handler) { + return A3(on, + name, + keyCode, + function (code) { + return A2($Signal.message, + addr, + handler(code)); + }); + }); + var onKeyUp = onKey("keyup"); + var onKeyDown = onKey("keydown"); + var onKeyPress = onKey("keypress"); + _elm.Html.Events.values = {_op: _op + ,onBlur: onBlur + ,onFocus: onFocus + ,onSubmit: onSubmit + ,onKeyUp: onKeyUp + ,onKeyDown: onKeyDown + ,onKeyPress: onKeyPress + ,onClick: onClick + ,onDoubleClick: onDoubleClick + ,onMouseMove: onMouseMove + ,onMouseDown: onMouseDown + ,onMouseUp: onMouseUp + ,onMouseEnter: onMouseEnter + ,onMouseLeave: onMouseLeave + ,onMouseOver: onMouseOver + ,onMouseOut: onMouseOut + ,on: on + ,onWithOptions: onWithOptions + ,defaultOptions: defaultOptions + ,targetValue: targetValue + ,targetChecked: targetChecked + ,keyCode: keyCode + ,Options: Options}; + return _elm.Html.Events.values; +}; +Elm.Http = Elm.Http || {}; +Elm.Http.make = function (_elm) { + "use strict"; + _elm.Http = _elm.Http || {}; + if (_elm.Http.values) + return _elm.Http.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Http", + $Basics = Elm.Basics.make(_elm), + $Dict = Elm.Dict.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Native$Http = Elm.Native.Http.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $String = Elm.String.make(_elm), + $Task = Elm.Task.make(_elm), + $Time = Elm.Time.make(_elm); + var send = $Native$Http.send; + var BadResponse = F2(function (a, + b) { + return {ctor: "BadResponse" + ,_0: a + ,_1: b}; + }); + var UnexpectedPayload = function (a) { + return {ctor: "UnexpectedPayload" + ,_0: a}; + }; + var handleResponse = F2(function (handle, + response) { + var _v0 = _U.cmp(200, + response.status) < 1 && _U.cmp(response.status, + 300) < 0; + switch (_v0) + {case false: + return $Task.fail(A2(BadResponse, + response.status, + response.statusText)); + case true: + var _v1 = response.value; + switch (_v1.ctor) + {case "Text": + return handle(_v1._0);} + return $Task.fail(UnexpectedPayload("Response body is a blob, expecting a string."));} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var NetworkError = {ctor: "NetworkError"}; + var Timeout = {ctor: "Timeout"}; + var promoteError = function (rawError) { + switch (rawError.ctor) + {case "RawNetworkError": + return NetworkError; + case "RawTimeout": + return Timeout;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var fromJson = F2(function (decoder, + response) { + var decode = function (str) { + var _v4 = A2($Json$Decode.decodeString, + decoder, + str); + switch (_v4.ctor) + {case "Err": + return $Task.fail(UnexpectedPayload(_v4._0)); + case "Ok": + return $Task.succeed(_v4._0);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + return A2($Task.andThen, + A2($Task.mapError, + promoteError, + response), + handleResponse(decode)); + }); + var RawNetworkError = {ctor: "RawNetworkError"}; + var RawTimeout = {ctor: "RawTimeout"}; + var Blob = function (a) { + return {ctor: "Blob",_0: a}; + }; + var Text = function (a) { + return {ctor: "Text",_0: a}; + }; + var Response = F5(function (a, + b, + c, + d, + e) { + return {_: {} + ,headers: c + ,status: a + ,statusText: b + ,url: d + ,value: e}; + }); + var defaultSettings = {_: {} + ,desiredResponseType: $Maybe.Nothing + ,onProgress: $Maybe.Nothing + ,onStart: $Maybe.Nothing + ,timeout: 0}; + var post = F3(function (decoder, + url, + body) { + var request = {_: {} + ,body: body + ,headers: _L.fromArray([]) + ,url: url + ,verb: "POST"}; + return A2(fromJson, + decoder, + A2(send, + defaultSettings, + request)); + }); + var Settings = F4(function (a, + b, + c, + d) { + return {_: {} + ,desiredResponseType: d + ,onProgress: c + ,onStart: b + ,timeout: a}; + }); + var multipart = $Native$Http.multipart; + var FileData = F3(function (a, + b, + c) { + return {ctor: "FileData" + ,_0: a + ,_1: b + ,_2: c}; + }); + var BlobData = F3(function (a, + b, + c) { + return {ctor: "BlobData" + ,_0: a + ,_1: b + ,_2: c}; + }); + var blobData = BlobData; + var StringData = F2(function (a, + b) { + return {ctor: "StringData" + ,_0: a + ,_1: b}; + }); + var stringData = StringData; + var BodyBlob = function (a) { + return {ctor: "BodyBlob" + ,_0: a}; + }; + var BodyFormData = {ctor: "BodyFormData"}; + var ArrayBuffer = {ctor: "ArrayBuffer"}; + var BodyString = function (a) { + return {ctor: "BodyString" + ,_0: a}; + }; + var string = BodyString; + var Empty = {ctor: "Empty"}; + var empty = Empty; + var getString = function (url) { + var request = {_: {} + ,body: empty + ,headers: _L.fromArray([]) + ,url: url + ,verb: "GET"}; + return A2($Task.andThen, + A2($Task.mapError, + promoteError, + A2(send, + defaultSettings, + request)), + handleResponse($Task.succeed)); + }; + var get = F2(function (decoder, + url) { + var request = {_: {} + ,body: empty + ,headers: _L.fromArray([]) + ,url: url + ,verb: "GET"}; + return A2(fromJson, + decoder, + A2(send, + defaultSettings, + request)); + }); + var Request = F4(function (a, + b, + c, + d) { + return {_: {} + ,body: d + ,headers: b + ,url: c + ,verb: a}; + }); + var uriDecode = $Native$Http.uriDecode; + var uriEncode = $Native$Http.uriEncode; + var queryEscape = function (string) { + return A2($String.join, + "+", + A2($String.split, + "%20", + uriEncode(string))); + }; + var queryPair = function (_v7) { + var $ = _v7, + key = $._0, + value = $._1; + return A2($Basics._op["++"], + queryEscape(key), + A2($Basics._op["++"], + "=", + queryEscape(value))); + }; + var url = F2(function (domain, + args) { + switch (args.ctor) + {case "[]": return domain;} + return A2($Basics._op["++"], + domain, + A2($Basics._op["++"], + "?", + A2($String.join, + "&", + A2($List.map,queryPair,args)))); + }); + var TODO_implement_file_in_another_library = {ctor: "TODO_implement_file_in_another_library"}; + var TODO_implement_blob_in_another_library = {ctor: "TODO_implement_blob_in_another_library"}; + _elm.Http.values = {_op: _op + ,getString: getString + ,get: get + ,post: post + ,send: send + ,url: url + ,uriEncode: uriEncode + ,uriDecode: uriDecode + ,empty: empty + ,string: string + ,multipart: multipart + ,stringData: stringData + ,blobData: blobData + ,defaultSettings: defaultSettings + ,fromJson: fromJson + ,Request: Request + ,Settings: Settings + ,Response: Response + ,Text: Text + ,Blob: Blob + ,Timeout: Timeout + ,NetworkError: NetworkError + ,UnexpectedPayload: UnexpectedPayload + ,BadResponse: BadResponse + ,RawTimeout: RawTimeout + ,RawNetworkError: RawNetworkError}; + return _elm.Http.values; +}; +Elm.InitViewAction = Elm.InitViewAction || {}; +Elm.InitViewAction.make = function (_elm) { + "use strict"; + _elm.InitViewAction = _elm.InitViewAction || {}; + if (_elm.InitViewAction.values) + return _elm.InitViewAction.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "InitViewAction", + $Basics = Elm.Basics.make(_elm), + $Http = Elm.Http.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Payer = Elm.Model.Payer.make(_elm), + $Model$Payment = Elm.Model.Payment.make(_elm), + $Model$User = Elm.Model.User.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Task = Elm.Task.make(_elm), + $Update = Elm.Update.make(_elm); + var loggedInView = A2($Task.andMap, + A2($Task.andMap, + A2($Task.andMap, + A2($Task.andMap, + A2($Task.andMap, + A2($Task.map, + $Update.GoLoggedInView, + A2($Http.get, + $Model$User.usersDecoder, + "/users")), + A2($Http.get, + A2($Json$Decode._op[":="], + "id", + $Model$User.userIdDecoder), + "/whoAmI")), + A2($Http.get, + $Model$Payment.paymentsDecoder, + "/monthlyPayments")), + A2($Http.get, + $Model$Payment.paymentsDecoder, + A2($Basics._op["++"], + "/payments?page=1&perPage=", + $Basics.toString($Model$Payment.perPage)))), + A2($Http.get, + A2($Json$Decode._op[":="], + "number", + $Json$Decode.$int), + "/payments/count")), + A2($Http.get, + $Model$Payer.payersDecoder, + "/payers")); + var initViewAction = A2($Task.onError, + loggedInView, + $Basics.always($Task.succeed($Update.GoSignInView))); + _elm.InitViewAction.values = {_op: _op + ,initViewAction: initViewAction}; + return _elm.InitViewAction.values; +}; +Elm.Json = Elm.Json || {}; +Elm.Json.Decode = Elm.Json.Decode || {}; +Elm.Json.Decode.make = function (_elm) { + "use strict"; + _elm.Json = _elm.Json || {}; + _elm.Json.Decode = _elm.Json.Decode || {}; + if (_elm.Json.Decode.values) + return _elm.Json.Decode.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Json.Decode", + $Array = Elm.Array.make(_elm), + $Dict = Elm.Dict.make(_elm), + $Json$Encode = Elm.Json.Encode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Native$Json = Elm.Native.Json.make(_elm), + $Result = Elm.Result.make(_elm); + var tuple8 = $Native$Json.decodeTuple8; + var tuple7 = $Native$Json.decodeTuple7; + var tuple6 = $Native$Json.decodeTuple6; + var tuple5 = $Native$Json.decodeTuple5; + var tuple4 = $Native$Json.decodeTuple4; + var tuple3 = $Native$Json.decodeTuple3; + var tuple2 = $Native$Json.decodeTuple2; + var tuple1 = $Native$Json.decodeTuple1; + var succeed = $Native$Json.succeed; + var fail = $Native$Json.fail; + var andThen = $Native$Json.andThen; + var customDecoder = $Native$Json.customDecoder; + var decodeValue = $Native$Json.runDecoderValue; + var value = $Native$Json.decodeValue; + var maybe = $Native$Json.decodeMaybe; + var $null = $Native$Json.decodeNull; + var array = $Native$Json.decodeArray; + var list = $Native$Json.decodeList; + var bool = $Native$Json.decodeBool; + var $int = $Native$Json.decodeInt; + var $float = $Native$Json.decodeFloat; + var string = $Native$Json.decodeString; + var oneOf = $Native$Json.oneOf; + var keyValuePairs = $Native$Json.decodeKeyValuePairs; + var object8 = $Native$Json.decodeObject8; + var object7 = $Native$Json.decodeObject7; + var object6 = $Native$Json.decodeObject6; + var object5 = $Native$Json.decodeObject5; + var object4 = $Native$Json.decodeObject4; + var object3 = $Native$Json.decodeObject3; + var object2 = $Native$Json.decodeObject2; + var object1 = $Native$Json.decodeObject1; + _op[":="] = $Native$Json.decodeField; + var at = F2(function (fields, + decoder) { + return A3($List.foldr, + F2(function (x,y) { + return A2(_op[":="],x,y); + }), + decoder, + fields); + }); + var decodeString = $Native$Json.runDecoderString; + var map = $Native$Json.decodeObject1; + var dict = function (decoder) { + return A2(map, + $Dict.fromList, + keyValuePairs(decoder)); + }; + var Decoder = {ctor: "Decoder"}; + _elm.Json.Decode.values = {_op: _op + ,decodeString: decodeString + ,decodeValue: decodeValue + ,string: string + ,$int: $int + ,$float: $float + ,bool: bool + ,$null: $null + ,list: list + ,array: array + ,tuple1: tuple1 + ,tuple2: tuple2 + ,tuple3: tuple3 + ,tuple4: tuple4 + ,tuple5: tuple5 + ,tuple6: tuple6 + ,tuple7: tuple7 + ,tuple8: tuple8 + ,at: at + ,object1: object1 + ,object2: object2 + ,object3: object3 + ,object4: object4 + ,object5: object5 + ,object6: object6 + ,object7: object7 + ,object8: object8 + ,keyValuePairs: keyValuePairs + ,dict: dict + ,maybe: maybe + ,oneOf: oneOf + ,map: map + ,fail: fail + ,succeed: succeed + ,andThen: andThen + ,value: value + ,customDecoder: customDecoder + ,Decoder: Decoder}; + return _elm.Json.Decode.values; +}; +Elm.Json = Elm.Json || {}; +Elm.Json.Encode = Elm.Json.Encode || {}; +Elm.Json.Encode.make = function (_elm) { + "use strict"; + _elm.Json = _elm.Json || {}; + _elm.Json.Encode = _elm.Json.Encode || {}; + if (_elm.Json.Encode.values) + return _elm.Json.Encode.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Json.Encode", + $Array = Elm.Array.make(_elm), + $Native$Json = Elm.Native.Json.make(_elm); + var list = $Native$Json.encodeList; + var array = $Native$Json.encodeArray; + var object = $Native$Json.encodeObject; + var $null = $Native$Json.encodeNull; + var bool = $Native$Json.identity; + var $float = $Native$Json.identity; + var $int = $Native$Json.identity; + var string = $Native$Json.identity; + var encode = $Native$Json.encode; + var Value = {ctor: "Value"}; + _elm.Json.Encode.values = {_op: _op + ,encode: encode + ,string: string + ,$int: $int + ,$float: $float + ,bool: bool + ,$null: $null + ,list: list + ,array: array + ,object: object + ,Value: Value}; + return _elm.Json.Encode.values; +}; +Elm.List = Elm.List || {}; +Elm.List.make = function (_elm) { + "use strict"; + _elm.List = _elm.List || {}; + if (_elm.List.values) + return _elm.List.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "List", + $Basics = Elm.Basics.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Native$List = Elm.Native.List.make(_elm); + var sortWith = $Native$List.sortWith; + var sortBy = $Native$List.sortBy; + var sort = function (xs) { + return A2(sortBy, + $Basics.identity, + xs); + }; + var repeat = $Native$List.repeat; + var drop = $Native$List.drop; + var take = $Native$List.take; + var map5 = $Native$List.map5; + var map4 = $Native$List.map4; + var map3 = $Native$List.map3; + var map2 = $Native$List.map2; + var any = $Native$List.any; + var all = F2(function (pred, + xs) { + return $Basics.not(A2(any, + function ($) { + return $Basics.not(pred($)); + }, + xs)); + }); + var foldr = $Native$List.foldr; + var foldl = $Native$List.foldl; + var length = function (xs) { + return A3(foldl, + F2(function (_v0,i) { + var _ = _v0; + return i + 1; + }), + 0, + xs); + }; + var sum = function (numbers) { + return A3(foldl, + F2(function (x,y) { + return x + y; + }), + 0, + numbers); + }; + var product = function (numbers) { + return A3(foldl, + F2(function (x,y) { + return x * y; + }), + 1, + numbers); + }; + var maximum = function (list) { + switch (list.ctor) + {case "::": + return $Maybe.Just(A3(foldl, + $Basics.max, + list._0, + list._1));} + return $Maybe.Nothing; + }; + var minimum = function (list) { + switch (list.ctor) + {case "::": + return $Maybe.Just(A3(foldl, + $Basics.min, + list._0, + list._1));} + return $Maybe.Nothing; + }; + var indexedMap = F2(function (f, + xs) { + return A3(map2, + f, + _L.range(0,length(xs) - 1), + xs); + }); + var member = F2(function (x, + xs) { + return A2(any, + function (a) { + return _U.eq(a,x); + }, + xs); + }); + var isEmpty = function (xs) { + switch (xs.ctor) + {case "[]": return true;} + return false; + }; + var tail = function (list) { + switch (list.ctor) + {case "::": + return $Maybe.Just(list._1); + case "[]": + return $Maybe.Nothing;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var head = function (list) { + switch (list.ctor) + {case "::": + return $Maybe.Just(list._0); + case "[]": + return $Maybe.Nothing;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + _op["::"] = $Native$List.cons; + var map = F2(function (f,xs) { + return A3(foldr, + F2(function (x,acc) { + return A2(_op["::"], + f(x), + acc); + }), + _L.fromArray([]), + xs); + }); + var filter = F2(function (pred, + xs) { + var conditionalCons = F2(function (x, + xs$) { + return pred(x) ? A2(_op["::"], + x, + xs$) : xs$; + }); + return A3(foldr, + conditionalCons, + _L.fromArray([]), + xs); + }); + var maybeCons = F3(function (f, + mx, + xs) { + var _v14 = f(mx); + switch (_v14.ctor) + {case "Just": + return A2(_op["::"],_v14._0,xs); + case "Nothing": return xs;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var filterMap = F2(function (f, + xs) { + return A3(foldr, + maybeCons(f), + _L.fromArray([]), + xs); + }); + var reverse = function (list) { + return A3(foldl, + F2(function (x,y) { + return A2(_op["::"],x,y); + }), + _L.fromArray([]), + list); + }; + var scanl = F3(function (f, + b, + xs) { + var scan1 = F2(function (x, + accAcc) { + switch (accAcc.ctor) + {case "::": return A2(_op["::"], + A2(f,x,accAcc._0), + accAcc); + case "[]": + return _L.fromArray([]);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + return reverse(A3(foldl, + scan1, + _L.fromArray([b]), + xs)); + }); + var append = F2(function (xs, + ys) { + switch (ys.ctor) + {case "[]": return xs;} + return A3(foldr, + F2(function (x,y) { + return A2(_op["::"],x,y); + }), + ys, + xs); + }); + var concat = function (lists) { + return A3(foldr, + append, + _L.fromArray([]), + lists); + }; + var concatMap = F2(function (f, + list) { + return concat(A2(map, + f, + list)); + }); + var partition = F2(function (pred, + list) { + var step = F2(function (x, + _v20) { + var $ = _v20, + trues = $._0, + falses = $._1; + return pred(x) ? {ctor: "_Tuple2" + ,_0: A2(_op["::"],x,trues) + ,_1: falses} : {ctor: "_Tuple2" + ,_0: trues + ,_1: A2(_op["::"],x,falses)}; + }); + return A3(foldr, + step, + {ctor: "_Tuple2" + ,_0: _L.fromArray([]) + ,_1: _L.fromArray([])}, + list); + }); + var unzip = function (pairs) { + var step = F2(function (_v21, + _v22) { + var $ = _v21, + x = $._0, + y = $._1; + var $ = _v22, + xs = $._0, + ys = $._1; + return {ctor: "_Tuple2" + ,_0: A2(_op["::"],x,xs) + ,_1: A2(_op["::"],y,ys)}; + }); + return A3(foldr, + step, + {ctor: "_Tuple2" + ,_0: _L.fromArray([]) + ,_1: _L.fromArray([])}, + pairs); + }; + var intersperse = F2(function (sep, + xs) { + switch (xs.ctor) + {case "::": + var step = F2(function (x, + rest) { + return A2(_op["::"], + sep, + A2(_op["::"],x,rest)); + }); + var spersed = A3(foldr, + step, + _L.fromArray([]), + xs._1); + return A2(_op["::"], + xs._0, + spersed); + case "[]": + return _L.fromArray([]);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + _elm.List.values = {_op: _op + ,isEmpty: isEmpty + ,length: length + ,reverse: reverse + ,member: member + ,head: head + ,tail: tail + ,filter: filter + ,take: take + ,drop: drop + ,repeat: repeat + ,append: append + ,concat: concat + ,intersperse: intersperse + ,partition: partition + ,unzip: unzip + ,map: map + ,map2: map2 + ,map3: map3 + ,map4: map4 + ,map5: map5 + ,filterMap: filterMap + ,concatMap: concatMap + ,indexedMap: indexedMap + ,foldr: foldr + ,foldl: foldl + ,sum: sum + ,product: product + ,maximum: maximum + ,minimum: minimum + ,all: all + ,any: any + ,scanl: scanl + ,sort: sort + ,sortBy: sortBy + ,sortWith: sortWith}; + return _elm.List.values; +}; +Elm.Main = Elm.Main || {}; +Elm.Main.make = function (_elm) { + "use strict"; + _elm.Main = _elm.Main || {}; + if (_elm.Main.values) + return _elm.Main.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Main", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $Http = Elm.Http.make(_elm), + $InitViewAction = Elm.InitViewAction.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model = Elm.Model.make(_elm), + $Persona = Elm.Persona.make(_elm), + $Result = Elm.Result.make(_elm), + $ServerCommunication = Elm.ServerCommunication.make(_elm), + $Sign = Elm.Sign.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Task = Elm.Task.make(_elm), + $Time = Elm.Time.make(_elm), + $Update = Elm.Update.make(_elm), + $View$Page = Elm.View.Page.make(_elm); + var sign = Elm.Native.Port.make(_elm).inboundSignal("sign", + "Json.Decode.Value", + function (v) { + return v; + }); + var signCommunication = A2($Signal.map, + function ($) { + return $Sign.toServerCommunication($Sign.decodeOperation($)); + }, + sign); + var persona = Elm.Native.Port.make(_elm).outboundSignal("persona", + function (v) { + return v; + }, + A2($Signal.map, + $Persona.toString, + $Persona.operations.signal)); + var serverCommunicationsPort = Elm.Native.Task.make(_elm).performSignal("serverCommunicationsPort", + A2($Signal.map, + function (comm) { + return A2($Basics.flip, + $Task.andThen, + function (action) { + return A2($Signal.send, + $Update.actions.address, + action); + })($ServerCommunication.sendRequest(comm)); + }, + A2($Signal.merge, + signCommunication, + $ServerCommunication.serverCommunications.signal))); + var initView = Elm.Native.Task.make(_elm).perform(A2($Task.andThen, + $InitViewAction.initViewAction, + $Signal.send($Update.actions.address))); + var ready = Elm.Native.Port.make(_elm).outboundSignal("ready", + function (v) { + return v; + }, + $Signal.constant("ready")); + var config = Elm.Native.Port.make(_elm).inbound("config", + "String", + function (v) { + return typeof v === "string" || typeof v === "object" && v instanceof String ? v : _U.badPort("a string", + v); + }); + var translations = Elm.Native.Port.make(_elm).inbound("translations", + "String", + function (v) { + return typeof v === "string" || typeof v === "object" && v instanceof String ? v : _U.badPort("a string", + v); + }); + var initialTime = Elm.Native.Port.make(_elm).inbound("initialTime", + "Time.Time", + function (v) { + return typeof v === "number" ? v : _U.badPort("a number", + v); + }); + var update = $Signal.mergeMany(_L.fromArray([A2($Signal.map, + $Update.UpdateTime, + $Time.every(1000)) + ,$Update.actions.signal])); + var model = A3($Signal.foldp, + $Update.updateModel, + A3($Model.initialModel, + initialTime, + translations, + config), + update); + var main = A2($Signal.map, + $View$Page.renderPage, + model); + _elm.Main.values = {_op: _op + ,main: main}; + return _elm.Main.values; +}; +Elm.Maybe = Elm.Maybe || {}; +Elm.Maybe.make = function (_elm) { + "use strict"; + _elm.Maybe = _elm.Maybe || {}; + if (_elm.Maybe.values) + return _elm.Maybe.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Maybe"; + var withDefault = F2(function ($default, + maybe) { + switch (maybe.ctor) + {case "Just": return maybe._0; + case "Nothing": + return $default;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var Nothing = {ctor: "Nothing"}; + var oneOf = function (maybes) { + _v2: while (true) { + switch (maybes.ctor) + {case "::": + switch (maybes._0.ctor) + {case "Just": return maybes._0; + case "Nothing": + var _v8 = maybes._1; + maybes = _v8; + continue _v2;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + case "[]": return Nothing;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + } + }; + var andThen = F2(function (maybeValue, + callback) { + switch (maybeValue.ctor) + {case "Just": + return callback(maybeValue._0); + case "Nothing": return Nothing;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var Just = function (a) { + return {ctor: "Just",_0: a}; + }; + var map = F2(function (f, + maybe) { + switch (maybe.ctor) + {case "Just": + return Just(f(maybe._0)); + case "Nothing": return Nothing;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + _elm.Maybe.values = {_op: _op + ,andThen: andThen + ,map: map + ,withDefault: withDefault + ,oneOf: oneOf + ,Just: Just + ,Nothing: Nothing}; + return _elm.Maybe.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + if (_elm.Model.values) + return _elm.Model.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model", + $Basics = Elm.Basics.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Config = Elm.Model.Config.make(_elm), + $Model$Translations = Elm.Model.Translations.make(_elm), + $Model$View = Elm.Model.View.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Time = Elm.Time.make(_elm); + var initialModel = F3(function (initialTime, + translationsValue, + configValue) { + return {_: {} + ,config: function () { + var _v3 = A2($Json$Decode.decodeString, + $Model$Config.configDecoder, + configValue); + switch (_v3.ctor) + {case "Err": return {_: {} + ,currency: ""}; + case "Ok": return _v3._0;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }() + ,currentTime: initialTime + ,translations: function () { + var _v0 = A2($Json$Decode.decodeString, + $Model$Translations.translationsDecoder, + translationsValue); + switch (_v0.ctor) + {case "Err": + return _L.fromArray([]); + case "Ok": return _v0._0;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }() + ,view: $Model$View.LoadingView}; + }); + var Model = F4(function (a, + b, + c, + d) { + return {_: {} + ,config: d + ,currentTime: b + ,translations: c + ,view: a}; + }); + _elm.Model.values = {_op: _op + ,initialModel: initialModel + ,Model: Model}; + return _elm.Model.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.Config = Elm.Model.Config || {}; +Elm.Model.Config.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + _elm.Model.Config = _elm.Model.Config || {}; + if (_elm.Model.Config.values) + return _elm.Model.Config.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model.Config", + $Basics = Elm.Basics.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var defaultConfig = {_: {} + ,currency: "€"}; + var Config = function (a) { + return {_: {},currency: a}; + }; + var configDecoder = A2($Json$Decode.object1, + Config, + A2($Json$Decode._op[":="], + "currency", + $Json$Decode.string)); + _elm.Model.Config.values = {_op: _op + ,configDecoder: configDecoder + ,Config: Config}; + return _elm.Model.Config.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.Date = Elm.Model.Date || {}; +Elm.Model.Date.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + _elm.Model.Date = _elm.Model.Date || {}; + if (_elm.Model.Date.values) + return _elm.Model.Date.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model.Date", + $Basics = Elm.Basics.make(_elm), + $Date = Elm.Date.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Time = Elm.Time.make(_elm); + var dateDecoder = A2($Json$Decode.customDecoder, + $Json$Decode.string, + $Date.fromString); + var timeDecoder = A2($Json$Decode.map, + $Date.toTime, + dateDecoder); + _elm.Model.Date.values = {_op: _op + ,timeDecoder: timeDecoder + ,dateDecoder: dateDecoder}; + return _elm.Model.Date.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.Income = Elm.Model.Income || {}; +Elm.Model.Income.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + _elm.Model.Income = _elm.Model.Income || {}; + if (_elm.Model.Income.values) + return _elm.Model.Income.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model.Income", + $Basics = Elm.Basics.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Date = Elm.Model.Date.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Time = Elm.Time.make(_elm), + $Utils$Maybe = Elm.Utils.Maybe.make(_elm); + var durationIncome = function (_v0) { + var $ = _v0, + duration = $._0, + income = $._1; + return $Basics.truncate(duration * $Basics.toFloat(income) / ($Time.hour * 24 * 365 / 12)); + }; + var getIncomesWithDuration = function (incomes) { + switch (incomes.ctor) + {case "::": + switch (incomes._1.ctor) + {case "::": + return A2($List._op["::"], + {ctor: "_Tuple2" + ,_0: incomes._1._0.creation - incomes._0.creation + ,_1: incomes._0.amount}, + getIncomesWithDuration(A2($List._op["::"], + incomes._1._0, + incomes._1._1)));} + break;} + return _L.fromArray([]); + }; + var cumulativeIncome = F2(function (currentTime, + incomes) { + return $List.sum($List.map(durationIncome)(getIncomesWithDuration(A2($Basics._op["++"], + incomes, + _L.fromArray([{_: {} + ,amount: 0 + ,creation: currentTime}]))))); + }); + var getIncomesAt = F2(function (time, + incomes) { + _v6: while (true) { + switch (incomes.ctor) + {case "::": + switch (incomes._1.ctor) + {case "::": + if (_U.cmp(incomes._0.creation, + time) < 0 && _U.cmp(incomes._1._0.creation, + time) > 0) + return $Maybe.Just({_: {} + ,amount: incomes._1._0.amount + ,creation: time}); else { + var _v12 = time, + _v13 = A2($List._op["::"], + incomes._1._0, + incomes._1._1); + time = _v12; + incomes = _v13; + continue _v6; + } + case "[]": + return _U.cmp(incomes._0.creation, + time) < 0 ? $Maybe.Just({_: {} + ,amount: incomes._0.amount + ,creation: time}) : $Maybe.Nothing;} + break; + case "[]": + return $Maybe.Nothing;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + } + }); + var getOrderedIncomesSince = F2(function (time, + incomes) { + var orderedIncomesSince = A2($List.filter, + function (income) { + return _U.cmp(income.creation, + time) > -1; + }, + incomes); + var mbStarterIncome = A2(getIncomesAt, + time, + incomes); + return A2($Basics._op["++"], + $Utils$Maybe.maybeToList(mbStarterIncome), + orderedIncomesSince); + }); + var cumulativeIncomesSince = F3(function (currentTime, + since, + incomes) { + return A2(cumulativeIncome, + currentTime, + A2(getOrderedIncomesSince, + since, + incomes)); + }); + var incomeDefinedForAll = function (usersIncomes) { + var firstIncomes = A2($List.map, + function ($) { + return $List.head($List.sortBy(function (_) { + return _.creation; + })($)); + }, + usersIncomes); + return A2($List.all, + $Utils$Maybe.isJust, + firstIncomes) ? function ($) { + return $List.head($List.reverse($List.sort($List.map(function (_) { + return _.creation; + })($Utils$Maybe.catMaybes($))))); + }(firstIncomes) : $Maybe.Nothing; + }; + var Income = F2(function (a,b) { + return {_: {} + ,amount: b + ,creation: a}; + }); + var incomeDecoder = A3($Json$Decode.object2, + Income, + A2($Json$Decode._op[":="], + "creation", + $Model$Date.timeDecoder), + A2($Json$Decode._op[":="], + "amount", + $Json$Decode.$int)); + _elm.Model.Income.values = {_op: _op + ,incomeDecoder: incomeDecoder + ,incomeDefinedForAll: incomeDefinedForAll + ,cumulativeIncomesSince: cumulativeIncomesSince + ,Income: Income}; + return _elm.Model.Income.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.Payer = Elm.Model.Payer || {}; +Elm.Model.Payer.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + _elm.Model.Payer = _elm.Model.Payer || {}; + if (_elm.Model.Payer.values) + return _elm.Model.Payer.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model.Payer", + $Basics = Elm.Basics.make(_elm), + $Dict = Elm.Dict.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Income = Elm.Model.Income.make(_elm), + $Model$User = Elm.Model.User.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Time = Elm.Time.make(_elm), + $Utils$Dict = Elm.Utils.Dict.make(_elm); + var getFinalDiff = F2(function (maxRatio, + payer) { + var postIncomeDiff = $Basics.truncate(-1 * (maxRatio - payer.ratio) * $Basics.toFloat(payer.cumulativeIncome)); + return postIncomeDiff + payer.preIncomePaymentSum; + }); + var getPostPaymentPayer = F3(function (currentTime, + since, + payer) { + var cumulativeIncome = A3($Model$Income.cumulativeIncomesSince, + currentTime, + since, + payer.incomes); + return {_: {} + ,cumulativeIncome: cumulativeIncome + ,preIncomePaymentSum: payer.preIncomePaymentSum + ,ratio: $Basics.toFloat(payer.postIncomePaymentSum) / $Basics.toFloat(cumulativeIncome)}; + }); + var PostPaymentPayer = F3(function (a, + b, + c) { + return {_: {} + ,cumulativeIncome: b + ,preIncomePaymentSum: a + ,ratio: c}; + }); + var exceedingPayersFromAmounts = function (userAmounts) { + var mbMinAmount = function ($) { + return $List.minimum($List.map($Basics.snd)($)); + }(userAmounts); + switch (mbMinAmount.ctor) + {case "Just": + return $List.filter(function (payer) { + return _U.cmp(payer.amount, + 0) > 0; + })($List.map(function (userAmount) { + return {_: {} + ,amount: $Basics.snd(userAmount) - mbMinAmount._0 + ,userId: $Basics.fst(userAmount)}; + })(userAmounts)); + case "Nothing": + return _L.fromArray([]);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var payersIncomeDefinedForAll = function (payers) { + return $Model$Income.incomeDefinedForAll(function ($) { + return $List.map(function ($) { + return function (_) { + return _.incomes; + }($Basics.snd($)); + })($Dict.toList($)); + }(payers)); + }; + var getOrderedExceedingPayers = F2(function (currentTime, + payers) { + var exceedingPayersOnPreIncome = exceedingPayersFromAmounts($Dict.toList($Utils$Dict.mapValues(function (_) { + return _.preIncomePaymentSum; + })(payers))); + var _v2 = payersIncomeDefinedForAll(payers); + switch (_v2.ctor) + {case "Just": + var postPaymentPayers = $Utils$Dict.mapValues(A2(getPostPaymentPayer, + currentTime, + _v2._0))(payers); + var mbMaxRatio = $List.maximum($List.map(function ($) { + return function (_) { + return _.ratio; + }($Basics.snd($)); + })($Dict.toList(postPaymentPayers))); + switch (mbMaxRatio.ctor) + {case "Just": + return exceedingPayersFromAmounts($Dict.toList($Utils$Dict.mapValues(getFinalDiff(mbMaxRatio._0))(postPaymentPayers))); + case "Nothing": + return exceedingPayersOnPreIncome;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + case "Nothing": + return exceedingPayersOnPreIncome;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var ExceedingPayer = F2(function (a, + b) { + return {_: {} + ,amount: b + ,userId: a}; + }); + var updatePayers = F4(function (payers, + userId, + creation, + amountDiff) { + return A2($Dict.update, + userId, + function (mbPayer) { + switch (mbPayer.ctor) + {case "Just": + var postIncome = $Maybe.withDefault(false)($Maybe.map(function (date) { + return _U.cmp(creation, + date) > 0; + })(payersIncomeDefinedForAll(payers))); + return postIncome ? $Maybe.Just(_U.replace([["postIncomePaymentSum" + ,mbPayer._0.postIncomePaymentSum + amountDiff]], + mbPayer._0)) : $Maybe.Just(_U.replace([["preIncomePaymentSum" + ,mbPayer._0.preIncomePaymentSum + amountDiff]], + mbPayer._0)); + case "Nothing": + return $Maybe.Nothing;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + })(payers); + }); + var Payer = F3(function (a, + b, + c) { + return {_: {} + ,incomes: c + ,postIncomePaymentSum: b + ,preIncomePaymentSum: a}; + }); + var payerDecoder = A3($Json$Decode.object2, + F2(function (v0,v1) { + return {ctor: "_Tuple2" + ,_0: v0 + ,_1: v1}; + }), + A2($Json$Decode._op[":="], + "userId", + $Model$User.userIdDecoder), + A4($Json$Decode.object3, + Payer, + A2($Json$Decode._op[":="], + "preIncomePaymentSum", + $Json$Decode.$int), + A2($Json$Decode._op[":="], + "postIncomePaymentSum", + $Json$Decode.$int), + A2($Json$Decode._op[":="], + "incomes", + $Json$Decode.list($Model$Income.incomeDecoder)))); + var payersDecoder = A2($Json$Decode.map, + $Dict.fromList, + $Json$Decode.list(payerDecoder)); + _elm.Model.Payer.values = {_op: _op + ,payersDecoder: payersDecoder + ,updatePayers: updatePayers + ,getOrderedExceedingPayers: getOrderedExceedingPayers + ,Payer: Payer + ,ExceedingPayer: ExceedingPayer}; + return _elm.Model.Payer.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.Payment = Elm.Model.Payment || {}; +Elm.Model.Payment.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + _elm.Model.Payment = _elm.Model.Payment || {}; + if (_elm.Model.Payment.values) + return _elm.Model.Payment.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model.Payment", + $Basics = Elm.Basics.make(_elm), + $Date = Elm.Date.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Date = Elm.Model.Date.make(_elm), + $Model$User = Elm.Model.User.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var paymentIdDecoder = $Json$Decode.$int; + var Payment = F5(function (a, + b, + c, + d, + e) { + return {_: {} + ,cost: d + ,creation: b + ,id: a + ,name: c + ,userId: e}; + }); + var paymentDecoder = A6($Json$Decode.object5, + Payment, + A2($Json$Decode._op[":="], + "id", + paymentIdDecoder), + A2($Json$Decode._op[":="], + "creation", + $Model$Date.dateDecoder), + A2($Json$Decode._op[":="], + "name", + $Json$Decode.string), + A2($Json$Decode._op[":="], + "cost", + $Json$Decode.$int), + A2($Json$Decode._op[":="], + "userId", + $Model$User.userIdDecoder)); + var paymentsDecoder = $Json$Decode.list(paymentDecoder); + var perPage = 8; + _elm.Model.Payment.values = {_op: _op + ,perPage: perPage + ,paymentsDecoder: paymentsDecoder + ,paymentIdDecoder: paymentIdDecoder + ,Payment: Payment}; + return _elm.Model.Payment.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.Translations = Elm.Model.Translations || {}; +Elm.Model.Translations.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + _elm.Model.Translations = _elm.Model.Translations || {}; + if (_elm.Model.Translations.values) + return _elm.Model.Translations.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model.Translations", + $Basics = Elm.Basics.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $String = Elm.String.make(_elm); + var replacePart = F2(function (values, + part) { + switch (part.ctor) + {case "Order": + return $Maybe.withDefault(A2($Basics._op["++"], + "{", + A2($Basics._op["++"], + $Basics.toString(part._0), + "}")))($List.head($List.drop(part._0 - 1)(values))); + case "Str": return part._0;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var Str = function (a) { + return {ctor: "Str",_0: a}; + }; + var Order = function (a) { + return {ctor: "Order",_0: a}; + }; + var partDecoderWithTag = function (tag) { + switch (tag) + {case "Order": + return A2($Json$Decode.object1, + Order, + A2($Json$Decode._op[":="], + "contents", + $Json$Decode.$int)); + case "Str": + return A2($Json$Decode.object1, + Str, + A2($Json$Decode._op[":="], + "contents", + $Json$Decode.string));} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var partDecoder = A2($Json$Decode.andThen, + A2($Json$Decode._op[":="], + "tag", + $Json$Decode.string), + partDecoderWithTag); + var getTranslation = F2(function (key, + translations) { + return $Maybe.map(function (_) { + return _.message; + })($List.head($List.filter(function (translation) { + return _U.eq(translation.key, + key); + })(translations))); + }); + var getParamMessage = F3(function (values, + key, + translations) { + return $Maybe.withDefault(key)($Maybe.map(function (parts) { + return $String.concat(A2($List.map, + replacePart(values), + parts)); + })(A2(getTranslation, + key, + translations))); + }); + var getMessage = getParamMessage(_L.fromArray([])); + var Translation = F2(function (a, + b) { + return {_: {} + ,key: a + ,message: b}; + }); + var translationDecoder = A3($Json$Decode.object2, + Translation, + A2($Json$Decode._op[":="], + "key", + $Json$Decode.string), + A2($Json$Decode._op[":="], + "message", + $Json$Decode.list(partDecoder))); + var translationsDecoder = $Json$Decode.list(translationDecoder); + _elm.Model.Translations.values = {_op: _op + ,translationsDecoder: translationsDecoder + ,getMessage: getMessage + ,getParamMessage: getParamMessage + ,Translation: Translation}; + return _elm.Model.Translations.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.User = Elm.Model.User || {}; +Elm.Model.User.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + _elm.Model.User = _elm.Model.User || {}; + if (_elm.Model.User.values) + return _elm.Model.User.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model.User", + $Basics = Elm.Basics.make(_elm), + $Dict = Elm.Dict.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var getUserName = F2(function (users, + userId) { + return $Maybe.map(function (_) { + return _.name; + })(A2($Dict.get,userId,users)); + }); + var userIdDecoder = $Json$Decode.$int; + var User = F2(function (a,b) { + return {_: {} + ,email: b + ,name: a}; + }); + var userDecoder = A3($Json$Decode.object2, + User, + A2($Json$Decode._op[":="], + "name", + $Json$Decode.string), + A2($Json$Decode._op[":="], + "email", + $Json$Decode.string)); + var userWithIdDecoder = A3($Json$Decode.object2, + F2(function (v0,v1) { + return {ctor: "_Tuple2" + ,_0: v0 + ,_1: v1}; + }), + A2($Json$Decode._op[":="], + "id", + userIdDecoder), + userDecoder); + var usersDecoder = A2($Json$Decode.map, + $Dict.fromList, + $Json$Decode.list(userWithIdDecoder)); + _elm.Model.User.values = {_op: _op + ,usersDecoder: usersDecoder + ,userDecoder: userDecoder + ,userIdDecoder: userIdDecoder + ,getUserName: getUserName + ,User: User}; + return _elm.Model.User.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.View = Elm.Model.View || {}; +Elm.Model.View.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + _elm.Model.View = _elm.Model.View || {}; + if (_elm.Model.View.values) + return _elm.Model.View.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model.View", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$View$LoggedInView = Elm.Model.View.LoggedInView.make(_elm), + $Model$View$SignInView = Elm.Model.View.SignInView.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var LoggedInView = function (a) { + return {ctor: "LoggedInView" + ,_0: a}; + }; + var SignInView = function (a) { + return {ctor: "SignInView" + ,_0: a}; + }; + var LoadingView = {ctor: "LoadingView"}; + _elm.Model.View.values = {_op: _op + ,LoadingView: LoadingView + ,SignInView: SignInView + ,LoggedInView: LoggedInView}; + return _elm.Model.View.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.View = Elm.Model.View || {}; +Elm.Model.View.LoggedIn = Elm.Model.View.LoggedIn || {}; +Elm.Model.View.LoggedIn.Account = Elm.Model.View.LoggedIn.Account || {}; +Elm.Model.View.LoggedIn.Account.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + _elm.Model.View = _elm.Model.View || {}; + _elm.Model.View.LoggedIn = _elm.Model.View.LoggedIn || {}; + _elm.Model.View.LoggedIn.Account = _elm.Model.View.LoggedIn.Account || {}; + if (_elm.Model.View.LoggedIn.Account.values) + return _elm.Model.View.LoggedIn.Account.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model.View.LoggedIn.Account", + $Basics = Elm.Basics.make(_elm), + $Dict = Elm.Dict.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Payer = Elm.Model.Payer.make(_elm), + $Model$Translations = Elm.Model.Translations.make(_elm), + $Model$User = Elm.Model.User.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Utils$Dict = Elm.Utils.Dict.make(_elm), + $Utils$Validation = Elm.Utils.Validation.make(_elm); + var validateIncome = F2(function (amount, + translations) { + return A2($Basics.flip, + $Result.andThen, + A2($Utils$Validation.validateNumber, + A2($Model$Translations.getMessage, + "IncomeMustBePositiveNumber", + translations), + function (number) { + return _U.cmp(number,0) > 0; + }))($Utils$Validation.validateNonEmpty(A2($Model$Translations.getMessage, + "IncomeRequired", + translations))(amount)); + }); + var initIncomeEdition = function (income) { + return {_: {} + ,error: $Maybe.Nothing + ,income: $Basics.toString(income)}; + }; + var IncomeEdition = F2(function (a, + b) { + return {_: {} + ,error: b + ,income: a}; + }); + var getCurrentIncome = function (account) { + var _v0 = A2($Dict.get, + account.me, + account.payers); + switch (_v0.ctor) + {case "Just": + return $Maybe.map(function (_) { + return _.amount; + })($List.head($List.reverse($List.sortBy(function (_) { + return _.creation; + })(_v0._0.incomes)))); + case "Nothing": + return $Maybe.Nothing;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var initAccount = F2(function (me, + payers) { + return {_: {} + ,incomeEdition: $Maybe.Nothing + ,me: me + ,payers: $Utils$Dict.mapValues(function (payer) { + return _U.replace([["incomes" + ,A2($List.sortBy, + function (_) { + return _.creation; + }, + payer.incomes)]], + payer); + })(payers) + ,visibleDetail: false}; + }); + var Account = F4(function (a, + b, + c, + d) { + return {_: {} + ,incomeEdition: d + ,me: a + ,payers: b + ,visibleDetail: c}; + }); + _elm.Model.View.LoggedIn.Account.values = {_op: _op + ,initAccount: initAccount + ,initIncomeEdition: initIncomeEdition + ,getCurrentIncome: getCurrentIncome + ,validateIncome: validateIncome + ,Account: Account + ,IncomeEdition: IncomeEdition}; + return _elm.Model.View.LoggedIn.Account.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.View = Elm.Model.View || {}; +Elm.Model.View.LoggedIn = Elm.Model.View.LoggedIn || {}; +Elm.Model.View.LoggedIn.Add = Elm.Model.View.LoggedIn.Add || {}; +Elm.Model.View.LoggedIn.Add.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + _elm.Model.View = _elm.Model.View || {}; + _elm.Model.View.LoggedIn = _elm.Model.View.LoggedIn || {}; + _elm.Model.View.LoggedIn.Add = _elm.Model.View.LoggedIn.Add || {}; + if (_elm.Model.View.LoggedIn.Add.values) + return _elm.Model.View.LoggedIn.Add.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model.View.LoggedIn.Add", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Translations = Elm.Model.Translations.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Utils$Validation = Elm.Utils.Validation.make(_elm); + var Monthly = {ctor: "Monthly"}; + var Punctual = {ctor: "Punctual"}; + var validateCost = F2(function (cost, + translations) { + return A2($Basics.flip, + $Result.andThen, + A2($Utils$Validation.validateNumber, + A2($Model$Translations.getMessage, + "CostMustBeNonNullNumber", + translations), + F2(function (x,y) { + return !_U.eq(x,y); + })(0)))($Utils$Validation.validateNonEmpty(A2($Model$Translations.getMessage, + "CostRequired", + translations))(cost)); + }); + var validateName = F2(function (name, + translations) { + return $Utils$Validation.validateNonEmpty(A2($Model$Translations.getMessage, + "CategoryRequired", + translations))(name); + }); + var initAddPayment = function (frequency) { + return {_: {} + ,cost: "" + ,costError: $Maybe.Nothing + ,frequency: frequency + ,name: "" + ,nameError: $Maybe.Nothing}; + }; + var AddPayment = F5(function (a, + b, + c, + d, + e) { + return {_: {} + ,cost: c + ,costError: d + ,frequency: e + ,name: a + ,nameError: b}; + }); + _elm.Model.View.LoggedIn.Add.values = {_op: _op + ,initAddPayment: initAddPayment + ,validateName: validateName + ,validateCost: validateCost + ,AddPayment: AddPayment + ,Punctual: Punctual + ,Monthly: Monthly}; + return _elm.Model.View.LoggedIn.Add.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.View = Elm.Model.View || {}; +Elm.Model.View.LoggedIn = Elm.Model.View.LoggedIn || {}; +Elm.Model.View.LoggedIn.Edition = Elm.Model.View.LoggedIn.Edition || {}; +Elm.Model.View.LoggedIn.Edition.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + _elm.Model.View = _elm.Model.View || {}; + _elm.Model.View.LoggedIn = _elm.Model.View.LoggedIn || {}; + _elm.Model.View.LoggedIn.Edition = _elm.Model.View.LoggedIn.Edition || {}; + if (_elm.Model.View.LoggedIn.Edition.values) + return _elm.Model.View.LoggedIn.Edition.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model.View.LoggedIn.Edition", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Payment = Elm.Model.Payment.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + _elm.Model.View.LoggedIn.Edition.values = {_op: _op}; + return _elm.Model.View.LoggedIn.Edition.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.View = Elm.Model.View || {}; +Elm.Model.View.LoggedIn = Elm.Model.View.LoggedIn || {}; +Elm.Model.View.LoggedIn.Monthly = Elm.Model.View.LoggedIn.Monthly || {}; +Elm.Model.View.LoggedIn.Monthly.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + _elm.Model.View = _elm.Model.View || {}; + _elm.Model.View.LoggedIn = _elm.Model.View.LoggedIn || {}; + _elm.Model.View.LoggedIn.Monthly = _elm.Model.View.LoggedIn.Monthly || {}; + if (_elm.Model.View.LoggedIn.Monthly.values) + return _elm.Model.View.LoggedIn.Monthly.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model.View.LoggedIn.Monthly", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Payment = Elm.Model.Payment.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var initMonthly = function (payments) { + return {_: {} + ,payments: payments + ,visibleDetail: false}; + }; + var Monthly = F2(function (a, + b) { + return {_: {} + ,payments: a + ,visibleDetail: b}; + }); + _elm.Model.View.LoggedIn.Monthly.values = {_op: _op + ,initMonthly: initMonthly + ,Monthly: Monthly}; + return _elm.Model.View.LoggedIn.Monthly.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.View = Elm.Model.View || {}; +Elm.Model.View.LoggedInView = Elm.Model.View.LoggedInView || {}; +Elm.Model.View.LoggedInView.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + _elm.Model.View = _elm.Model.View || {}; + _elm.Model.View.LoggedInView = _elm.Model.View.LoggedInView || {}; + if (_elm.Model.View.LoggedInView.values) + return _elm.Model.View.LoggedInView.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model.View.LoggedInView", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Payer = Elm.Model.Payer.make(_elm), + $Model$Payment = Elm.Model.Payment.make(_elm), + $Model$User = Elm.Model.User.make(_elm), + $Model$View$LoggedIn$Account = Elm.Model.View.LoggedIn.Account.make(_elm), + $Model$View$LoggedIn$Add = Elm.Model.View.LoggedIn.Add.make(_elm), + $Model$View$LoggedIn$Edition = Elm.Model.View.LoggedIn.Edition.make(_elm), + $Model$View$LoggedIn$Monthly = Elm.Model.View.LoggedIn.Monthly.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var initLoggedInView = F6(function (users, + me, + monthlyPayments, + payments, + paymentsCount, + payers) { + return {_: {} + ,account: A2($Model$View$LoggedIn$Account.initAccount, + me, + payers) + ,add: $Model$View$LoggedIn$Add.initAddPayment($Model$View$LoggedIn$Add.Punctual) + ,currentPage: 1 + ,monthly: $Model$View$LoggedIn$Monthly.initMonthly(monthlyPayments) + ,paymentEdition: $Maybe.Nothing + ,payments: payments + ,paymentsCount: paymentsCount + ,users: users}; + }); + var LoggedInView = F8(function (a, + b, + c, + d, + e, + f, + g, + h) { + return {_: {} + ,account: d + ,add: b + ,currentPage: h + ,monthly: c + ,paymentEdition: g + ,payments: e + ,paymentsCount: f + ,users: a}; + }); + _elm.Model.View.LoggedInView.values = {_op: _op + ,initLoggedInView: initLoggedInView + ,LoggedInView: LoggedInView}; + return _elm.Model.View.LoggedInView.values; +}; +Elm.Model = Elm.Model || {}; +Elm.Model.View = Elm.Model.View || {}; +Elm.Model.View.SignInView = Elm.Model.View.SignInView || {}; +Elm.Model.View.SignInView.make = function (_elm) { + "use strict"; + _elm.Model = _elm.Model || {}; + _elm.Model.View = _elm.Model.View || {}; + _elm.Model.View.SignInView = _elm.Model.View.SignInView || {}; + if (_elm.Model.View.SignInView.values) + return _elm.Model.View.SignInView.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Model.View.SignInView", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var initSignInView = {_: {} + ,login: "" + ,result: $Maybe.Nothing}; + var SignInView = F2(function (a, + b) { + return {_: {} + ,login: a + ,result: b}; + }); + _elm.Model.View.SignInView.values = {_op: _op + ,initSignInView: initSignInView + ,SignInView: SignInView}; + return _elm.Model.View.SignInView.values; +}; +Elm.Native.Array = {}; +Elm.Native.Array.make = function(localRuntime) { + + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Array = localRuntime.Native.Array || {}; + if (localRuntime.Native.Array.values) + { + return localRuntime.Native.Array.values; + } + if ('values' in Elm.Native.Array) + { + return localRuntime.Native.Array.values = Elm.Native.Array.values; + } + + var List = Elm.Native.List.make(localRuntime); + + // A RRB-Tree has two distinct data types. + // Leaf -> "height" is always 0 + // "table" is an array of elements + // Node -> "height" is always greater than 0 + // "table" is an array of child nodes + // "lengths" is an array of accumulated lengths of the child nodes + + // M is the maximal table size. 32 seems fast. E is the allowed increase + // of search steps when concatting to find an index. Lower values will + // decrease balancing, but will increase search steps. + var M = 32; + var E = 2; + + // An empty array. + var empty = { + ctor: "_Array", + height: 0, + table: new Array() + }; + + + function get(i, array) + { + if (i < 0 || i >= length(array)) + { + throw new Error( + "Index " + i + " is out of range. Check the length of " + + "your array first or use getMaybe or getWithDefault."); + } + return unsafeGet(i, array); + } + + + function unsafeGet(i, array) + { + for (var x = array.height; x > 0; x--) + { + var slot = i >> (x * 5); + while (array.lengths[slot] <= i) + { + slot++; + } + if (slot > 0) + { + i -= array.lengths[slot - 1]; + } + array = array.table[slot]; + } + return array.table[i]; + } + + + // Sets the value at the index i. Only the nodes leading to i will get + // copied and updated. + function set(i, item, array) + { + if (i < 0 || length(array) <= i) + { + return array; + } + return unsafeSet(i, item, array); + } + + + function unsafeSet(i, item, array) + { + array = nodeCopy(array); + + if (array.height == 0) + { + array.table[i] = item; + } + else + { + var slot = getSlot(i, array); + if (slot > 0) + { + i -= array.lengths[slot - 1]; + } + array.table[slot] = unsafeSet(i, item, array.table[slot]); + } + return array; + } + + + function initialize(len, f) + { + if (len == 0) + { + return empty; + } + var h = Math.floor( Math.log(len) / Math.log(M) ); + return initialize_(f, h, 0, len); + } + + function initialize_(f, h, from, to) + { + if (h == 0) + { + var table = new Array((to - from) % (M + 1)); + for (var i = 0; i < table.length; i++) + { + table[i] = f(from + i); + } + return { + ctor: "_Array", + height: 0, + table: table + }; + } + + var step = Math.pow(M, h); + var table = new Array(Math.ceil((to - from) / step)); + var lengths = new Array(table.length); + for (var i = 0; i < table.length; i++) + { + table[i] = initialize_(f, h - 1, from + (i * step), Math.min(from + ((i + 1) * step), to)); + lengths[i] = length(table[i]) + (i > 0 ? lengths[i-1] : 0); + } + return { + ctor: "_Array", + height: h, + table: table, + lengths: lengths + }; + } + + function fromList(list) + { + if (list == List.Nil) + { + return empty; + } + + // Allocate M sized blocks (table) and write list elements to it. + var table = new Array(M); + var nodes = new Array(); + var i = 0; + + while (list.ctor !== '[]') + { + table[i] = list._0; + list = list._1; + i++; + + // table is full, so we can push a leaf containing it into the + // next node. + if (i == M) + { + var leaf = { + ctor: "_Array", + height: 0, + table: table + }; + fromListPush(leaf, nodes); + table = new Array(M); + i = 0; + } + } + + // Maybe there is something left on the table. + if (i > 0) + { + var leaf = { + ctor: "_Array", + height: 0, + table: table.splice(0,i) + }; + fromListPush(leaf, nodes); + } + + // Go through all of the nodes and eventually push them into higher nodes. + for (var h = 0; h < nodes.length - 1; h++) + { + if (nodes[h].table.length > 0) + { + fromListPush(nodes[h], nodes); + } + } + + var head = nodes[nodes.length - 1]; + if (head.height > 0 && head.table.length == 1) + { + return head.table[0]; + } + else + { + return head; + } + } + + // Push a node into a higher node as a child. + function fromListPush(toPush, nodes) + { + var h = toPush.height; + + // Maybe the node on this height does not exist. + if (nodes.length == h) + { + var node = { + ctor: "_Array", + height: h + 1, + table: new Array(), + lengths: new Array() + }; + nodes.push(node); + } + + nodes[h].table.push(toPush); + var len = length(toPush); + if (nodes[h].lengths.length > 0) + { + len += nodes[h].lengths[nodes[h].lengths.length - 1]; + } + nodes[h].lengths.push(len); + + if (nodes[h].table.length == M) + { + fromListPush(nodes[h], nodes); + nodes[h] = { + ctor: "_Array", + height: h + 1, + table: new Array(), + lengths: new Array() + }; + } + } + + // Pushes an item via push_ to the bottom right of a tree. + function push(item, a) + { + var pushed = push_(item, a); + if (pushed !== null) + { + return pushed; + } + + var newTree = create(item, a.height); + return siblise(a, newTree); + } + + // Recursively tries to push an item to the bottom-right most + // tree possible. If there is no space left for the item, + // null will be returned. + function push_(item, a) + { + // Handle resursion stop at leaf level. + if (a.height == 0) + { + if (a.table.length < M) + { + var newA = { + ctor: "_Array", + height: 0, + table: a.table.slice() + }; + newA.table.push(item); + return newA; + } + else + { + return null; + } + } + + // Recursively push + var pushed = push_(item, botRight(a)); + + // There was space in the bottom right tree, so the slot will + // be updated. + if (pushed != null) + { + var newA = nodeCopy(a); + newA.table[newA.table.length - 1] = pushed; + newA.lengths[newA.lengths.length - 1]++; + return newA; + } + + // When there was no space left, check if there is space left + // for a new slot with a tree which contains only the item + // at the bottom. + if (a.table.length < M) + { + var newSlot = create(item, a.height - 1); + var newA = nodeCopy(a); + newA.table.push(newSlot); + newA.lengths.push(newA.lengths[newA.lengths.length - 1] + length(newSlot)); + return newA; + } + else + { + return null; + } + } + + // Converts an array into a list of elements. + function toList(a) + { + return toList_(List.Nil, a); + } + + function toList_(list, a) + { + for (var i = a.table.length - 1; i >= 0; i--) + { + list = + a.height == 0 + ? List.Cons(a.table[i], list) + : toList_(list, a.table[i]); + } + return list; + } + + // Maps a function over the elements of an array. + function map(f, a) + { + var newA = { + ctor: "_Array", + height: a.height, + table: new Array(a.table.length) + }; + if (a.height > 0) + { + newA.lengths = a.lengths; + } + for (var i = 0; i < a.table.length; i++) + { + newA.table[i] = + a.height == 0 + ? f(a.table[i]) + : map(f, a.table[i]); + } + return newA; + } + + // Maps a function over the elements with their index as first argument. + function indexedMap(f, a) + { + return indexedMap_(f, a, 0); + } + + function indexedMap_(f, a, from) + { + var newA = { + ctor: "_Array", + height: a.height, + table: new Array(a.table.length) + }; + if (a.height > 0) + { + newA.lengths = a.lengths; + } + for (var i = 0; i < a.table.length; i++) + { + newA.table[i] = + a.height == 0 + ? A2(f, from + i, a.table[i]) + : indexedMap_(f, a.table[i], i == 0 ? 0 : a.lengths[i - 1]); + } + return newA; + } + + function foldl(f, b, a) + { + if (a.height == 0) + { + for (var i = 0; i < a.table.length; i++) + { + b = A2(f, a.table[i], b); + } + } + else + { + for (var i = 0; i < a.table.length; i++) + { + b = foldl(f, b, a.table[i]); + } + } + return b; + } + + function foldr(f, b, a) + { + if (a.height == 0) + { + for (var i = a.table.length; i--; ) + { + b = A2(f, a.table[i], b); + } + } + else + { + for (var i = a.table.length; i--; ) + { + b = foldr(f, b, a.table[i]); + } + } + return b; + } + + // TODO: currently, it slices the right, then the left. This can be + // optimized. + function slice(from, to, a) + { + if (from < 0) + { + from += length(a); + } + if (to < 0) + { + to += length(a); + } + return sliceLeft(from, sliceRight(to, a)); + } + + function sliceRight(to, a) + { + if (to == length(a)) + { + return a; + } + + // Handle leaf level. + if (a.height == 0) + { + var newA = { ctor:"_Array", height:0 }; + newA.table = a.table.slice(0, to); + return newA; + } + + // Slice the right recursively. + var right = getSlot(to, a); + var sliced = sliceRight(to - (right > 0 ? a.lengths[right - 1] : 0), a.table[right]); + + // Maybe the a node is not even needed, as sliced contains the whole slice. + if (right == 0) + { + return sliced; + } + + // Create new node. + var newA = { + ctor: "_Array", + height: a.height, + table: a.table.slice(0, right), + lengths: a.lengths.slice(0, right) + }; + if (sliced.table.length > 0) + { + newA.table[right] = sliced; + newA.lengths[right] = length(sliced) + (right > 0 ? newA.lengths[right - 1] : 0); + } + return newA; + } + + function sliceLeft(from, a) + { + if (from == 0) + { + return a; + } + + // Handle leaf level. + if (a.height == 0) + { + var newA = { ctor:"_Array", height:0 }; + newA.table = a.table.slice(from, a.table.length + 1); + return newA; + } + + // Slice the left recursively. + var left = getSlot(from, a); + var sliced = sliceLeft(from - (left > 0 ? a.lengths[left - 1] : 0), a.table[left]); + + // Maybe the a node is not even needed, as sliced contains the whole slice. + if (left == a.table.length - 1) + { + return sliced; + } + + // Create new node. + var newA = { + ctor: "_Array", + height: a.height, + table: a.table.slice(left, a.table.length + 1), + lengths: new Array(a.table.length - left) + }; + newA.table[0] = sliced; + var len = 0; + for (var i = 0; i < newA.table.length; i++) + { + len += length(newA.table[i]); + newA.lengths[i] = len; + } + + return newA; + } + + // Appends two trees. + function append(a,b) + { + if (a.table.length === 0) + { + return b; + } + if (b.table.length === 0) + { + return a; + } + + var c = append_(a, b); + + // Check if both nodes can be crunshed together. + if (c[0].table.length + c[1].table.length <= M) + { + if (c[0].table.length === 0) + { + return c[1]; + } + if (c[1].table.length === 0) + { + return c[0]; + } + + // Adjust .table and .lengths + c[0].table = c[0].table.concat(c[1].table); + if (c[0].height > 0) + { + var len = length(c[0]); + for (var i = 0; i < c[1].lengths.length; i++) + { + c[1].lengths[i] += len; + } + c[0].lengths = c[0].lengths.concat(c[1].lengths); + } + + return c[0]; + } + + if (c[0].height > 0) + { + var toRemove = calcToRemove(a, b); + if (toRemove > E) + { + c = shuffle(c[0], c[1], toRemove); + } + } + + return siblise(c[0], c[1]); + } + + // Returns an array of two nodes; right and left. One node _may_ be empty. + function append_(a, b) + { + if (a.height === 0 && b.height === 0) + { + return [a, b]; + } + + if (a.height !== 1 || b.height !== 1) + { + if (a.height === b.height) + { + a = nodeCopy(a); + b = nodeCopy(b); + var appended = append_(botRight(a), botLeft(b)); + + insertRight(a, appended[1]); + insertLeft(b, appended[0]); + } + else if (a.height > b.height) + { + a = nodeCopy(a); + var appended = append_(botRight(a), b); + + insertRight(a, appended[0]); + b = parentise(appended[1], appended[1].height + 1); + } + else + { + b = nodeCopy(b); + var appended = append_(a, botLeft(b)); + + var left = appended[0].table.length === 0 ? 0 : 1; + var right = left === 0 ? 1 : 0; + insertLeft(b, appended[left]); + a = parentise(appended[right], appended[right].height + 1); + } + } + + // Check if balancing is needed and return based on that. + if (a.table.length === 0 || b.table.length === 0) + { + return [a,b]; + } + + var toRemove = calcToRemove(a, b); + if (toRemove <= E) + { + return [a,b]; + } + return shuffle(a, b, toRemove); + } + + // Helperfunctions for append_. Replaces a child node at the side of the parent. + function insertRight(parent, node) + { + var index = parent.table.length - 1; + parent.table[index] = node; + parent.lengths[index] = length(node) + parent.lengths[index] += index > 0 ? parent.lengths[index - 1] : 0; + } + + function insertLeft(parent, node) + { + if (node.table.length > 0) + { + parent.table[0] = node; + parent.lengths[0] = length(node); + + var len = length(parent.table[0]); + for (var i = 1; i < parent.lengths.length; i++) + { + len += length(parent.table[i]); + parent.lengths[i] = len; + } + } + else + { + parent.table.shift(); + for (var i = 1; i < parent.lengths.length; i++) + { + parent.lengths[i] = parent.lengths[i] - parent.lengths[0]; + } + parent.lengths.shift(); + } + } + + // Returns the extra search steps for E. Refer to the paper. + function calcToRemove(a, b) + { + var subLengths = 0; + for (var i = 0; i < a.table.length; i++) + { + subLengths += a.table[i].table.length; + } + for (var i = 0; i < b.table.length; i++) + { + subLengths += b.table[i].table.length; + } + + var toRemove = a.table.length + b.table.length + return toRemove - (Math.floor((subLengths - 1) / M) + 1); + } + + // get2, set2 and saveSlot are helpers for accessing elements over two arrays. + function get2(a, b, index) + { + return index < a.length + ? a[index] + : b[index - a.length]; + } + + function set2(a, b, index, value) + { + if (index < a.length) + { + a[index] = value; + } + else + { + b[index - a.length] = value; + } + } + + function saveSlot(a, b, index, slot) + { + set2(a.table, b.table, index, slot); + + var l = (index == 0 || index == a.lengths.length) + ? 0 + : get2(a.lengths, a.lengths, index - 1); + + set2(a.lengths, b.lengths, index, l + length(slot)); + } + + // Creates a node or leaf with a given length at their arrays for perfomance. + // Is only used by shuffle. + function createNode(h, length) + { + if (length < 0) + { + length = 0; + } + var a = { + ctor: "_Array", + height: h, + table: new Array(length) + }; + if (h > 0) + { + a.lengths = new Array(length); + } + return a; + } + + // Returns an array of two balanced nodes. + function shuffle(a, b, toRemove) + { + var newA = createNode(a.height, Math.min(M, a.table.length + b.table.length - toRemove)); + var newB = createNode(a.height, newA.table.length - (a.table.length + b.table.length - toRemove)); + + // Skip the slots with size M. More precise: copy the slot references + // to the new node + var read = 0; + while (get2(a.table, b.table, read).table.length % M == 0) + { + set2(newA.table, newB.table, read, get2(a.table, b.table, read)); + set2(newA.lengths, newB.lengths, read, get2(a.lengths, b.lengths, read)); + read++; + } + + // Pulling items from left to right, caching in a slot before writing + // it into the new nodes. + var write = read; + var slot = new createNode(a.height - 1, 0); + var from = 0; + + // If the current slot is still containing data, then there will be at + // least one more write, so we do not break this loop yet. + while (read - write - (slot.table.length > 0 ? 1 : 0) < toRemove) + { + // Find out the max possible items for copying. + var source = get2(a.table, b.table, read); + var to = Math.min(M - slot.table.length, source.table.length) + + // Copy and adjust size table. + slot.table = slot.table.concat(source.table.slice(from, to)); + if (slot.height > 0) + { + var len = slot.lengths.length; + for (var i = len; i < len + to - from; i++) + { + slot.lengths[i] = length(slot.table[i]); + slot.lengths[i] += (i > 0 ? slot.lengths[i - 1] : 0); + } + } + + from += to; + + // Only proceed to next slots[i] if the current one was + // fully copied. + if (source.table.length <= to) + { + read++; from = 0; + } + + // Only create a new slot if the current one is filled up. + if (slot.table.length == M) + { + saveSlot(newA, newB, write, slot); + slot = createNode(a.height - 1,0); + write++; + } + } + + // Cleanup after the loop. Copy the last slot into the new nodes. + if (slot.table.length > 0) + { + saveSlot(newA, newB, write, slot); + write++; + } + + // Shift the untouched slots to the left + while (read < a.table.length + b.table.length ) + { + saveSlot(newA, newB, write, get2(a.table, b.table, read)); + read++; + write++; + } + + return [newA, newB]; + } + + // Navigation functions + function botRight(a) + { + return a.table[a.table.length - 1]; + } + function botLeft(a) + { + return a.table[0]; + } + + // Copies a node for updating. Note that you should not use this if + // only updating only one of "table" or "lengths" for performance reasons. + function nodeCopy(a) + { + var newA = { + ctor: "_Array", + height: a.height, + table: a.table.slice() + }; + if (a.height > 0) + { + newA.lengths = a.lengths.slice(); + } + return newA; + } + + // Returns how many items are in the tree. + function length(array) + { + if (array.height == 0) + { + return array.table.length; + } + else + { + return array.lengths[array.lengths.length - 1]; + } + } + + // Calculates in which slot of "table" the item probably is, then + // find the exact slot via forward searching in "lengths". Returns the index. + function getSlot(i, a) + { + var slot = i >> (5 * a.height); + while (a.lengths[slot] <= i) + { + slot++; + } + return slot; + } + + // Recursively creates a tree with a given height containing + // only the given item. + function create(item, h) + { + if (h == 0) + { + return { + ctor: "_Array", + height: 0, + table: [item] + }; + } + return { + ctor: "_Array", + height: h, + table: [create(item, h - 1)], + lengths: [1] + }; + } + + // Recursively creates a tree that contains the given tree. + function parentise(tree, h) + { + if (h == tree.height) + { + return tree; + } + + return { + ctor: "_Array", + height: h, + table: [parentise(tree, h - 1)], + lengths: [length(tree)] + }; + } + + // Emphasizes blood brotherhood beneath two trees. + function siblise(a, b) + { + return { + ctor: "_Array", + height: a.height + 1, + table: [a, b], + lengths: [length(a), length(a) + length(b)] + }; + } + + function toJSArray(a) + { + var jsArray = new Array(length(a)); + toJSArray_(jsArray, 0, a); + return jsArray; + } + + function toJSArray_(jsArray, i, a) + { + for (var t = 0; t < a.table.length; t++) + { + if (a.height == 0) + { + jsArray[i + t] = a.table[t]; + } + else + { + var inc = t == 0 ? 0 : a.lengths[t - 1]; + toJSArray_(jsArray, i + inc, a.table[t]); + } + } + } + + function fromJSArray(jsArray) + { + if (jsArray.length == 0) + { + return empty; + } + var h = Math.floor(Math.log(jsArray.length) / Math.log(M)); + return fromJSArray_(jsArray, h, 0, jsArray.length); + } + + function fromJSArray_(jsArray, h, from, to) + { + if (h == 0) + { + return { + ctor: "_Array", + height: 0, + table: jsArray.slice(from, to) + }; + } + + var step = Math.pow(M, h); + var table = new Array(Math.ceil((to - from) / step)); + var lengths = new Array(table.length); + for (var i = 0; i < table.length; i++) + { + table[i] = fromJSArray_(jsArray, h - 1, from + (i * step), Math.min(from + ((i + 1) * step), to)); + lengths[i] = length(table[i]) + (i > 0 ? lengths[i-1] : 0); + } + return { + ctor: "_Array", + height: h, + table: table, + lengths: lengths + }; + } + + Elm.Native.Array.values = { + empty: empty, + fromList: fromList, + toList: toList, + initialize: F2(initialize), + append: F2(append), + push: F2(push), + slice: F3(slice), + get: F2(get), + set: F3(set), + map: F2(map), + indexedMap: F2(indexedMap), + foldl: F3(foldl), + foldr: F3(foldr), + length: length, + + toJSArray:toJSArray, + fromJSArray:fromJSArray + }; + + return localRuntime.Native.Array.values = Elm.Native.Array.values; + +} + +Elm.Native.Basics = {}; +Elm.Native.Basics.make = function(localRuntime) { + + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Basics = localRuntime.Native.Basics || {}; + if (localRuntime.Native.Basics.values) + { + return localRuntime.Native.Basics.values; + } + + var Utils = Elm.Native.Utils.make(localRuntime); + + function div(a, b) + { + return (a/b)|0; + } + function rem(a, b) + { + return a % b; + } + function mod(a, b) + { + if (b === 0) + { + throw new Error("Cannot perform mod 0. Division by zero error."); + } + var r = a % b; + var m = a === 0 ? 0 : (b > 0 ? (a >= 0 ? r : r+b) : -mod(-a,-b)); + + return m === b ? 0 : m; + } + function logBase(base, n) + { + return Math.log(n) / Math.log(base); + } + function negate(n) + { + return -n; + } + function abs(n) + { + return n < 0 ? -n : n; + } + + function min(a, b) + { + return Utils.cmp(a,b) < 0 ? a : b; + } + function max(a, b) + { + return Utils.cmp(a,b) > 0 ? a : b; + } + function clamp(lo, hi, n) + { + return Utils.cmp(n,lo) < 0 ? lo : Utils.cmp(n,hi) > 0 ? hi : n; + } + + function xor(a, b) + { + return a !== b; + } + function not(b) + { + return !b; + } + function isInfinite(n) + { + return n === Infinity || n === -Infinity + } + + function truncate(n) + { + return n|0; + } + + function degrees(d) + { + return d * Math.PI / 180; + } + function turns(t) + { + return 2 * Math.PI * t; + } + function fromPolar(point) + { + var r = point._0; + var t = point._1; + return Utils.Tuple2(r * Math.cos(t), r * Math.sin(t)); + } + function toPolar(point) + { + var x = point._0; + var y = point._1; + return Utils.Tuple2(Math.sqrt(x * x + y * y), Math.atan2(y,x)); + } + + return localRuntime.Native.Basics.values = { + div: F2(div), + rem: F2(rem), + mod: F2(mod), + + pi: Math.PI, + e: Math.E, + cos: Math.cos, + sin: Math.sin, + tan: Math.tan, + acos: Math.acos, + asin: Math.asin, + atan: Math.atan, + atan2: F2(Math.atan2), + + degrees: degrees, + turns: turns, + fromPolar: fromPolar, + toPolar: toPolar, + + sqrt: Math.sqrt, + logBase: F2(logBase), + negate: negate, + abs: abs, + min: F2(min), + max: F2(max), + clamp: F3(clamp), + compare: Utils.compare, + + xor: F2(xor), + not: not, + + truncate: truncate, + ceiling: Math.ceil, + floor: Math.floor, + round: Math.round, + toFloat: function(x) { return x; }, + isNaN: isNaN, + isInfinite: isInfinite + }; +}; + +Elm.Native.Char = {}; +Elm.Native.Char.make = function(localRuntime) { + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Char = localRuntime.Native.Char || {}; + if (localRuntime.Native.Char.values) + { + return localRuntime.Native.Char.values; + } + + var Utils = Elm.Native.Utils.make(localRuntime); + + return localRuntime.Native.Char.values = { + fromCode : function(c) { return Utils.chr(String.fromCharCode(c)); }, + toCode : function(c) { return c.charCodeAt(0); }, + toUpper : function(c) { return Utils.chr(c.toUpperCase()); }, + toLower : function(c) { return Utils.chr(c.toLowerCase()); }, + toLocaleUpper : function(c) { return Utils.chr(c.toLocaleUpperCase()); }, + toLocaleLower : function(c) { return Utils.chr(c.toLocaleLowerCase()); }, + }; +}; + +Elm.Native.Color = {}; +Elm.Native.Color.make = function(localRuntime) { + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Color = localRuntime.Native.Color || {}; + if (localRuntime.Native.Color.values) + { + return localRuntime.Native.Color.values; + } + + function toCss(c) + { + var format = ''; + var colors = ''; + if (c.ctor === 'RGBA') + { + format = 'rgb'; + colors = c._0 + ', ' + c._1 + ', ' + c._2; + } + else + { + format = 'hsl'; + colors = (c._0 * 180 / Math.PI) + ', ' + + (c._1 * 100) + '%, ' + + (c._2 * 100) + '%'; + } + if (c._3 === 1) + { + return format + '(' + colors + ')'; + } + else + { + return format + 'a(' + colors + ', ' + c._3 + ')'; + } + } + + return localRuntime.Native.Color.values = { + toCss: toCss + }; + +}; + +Elm.Native.Date = {}; +Elm.Native.Date.make = function(localRuntime) { + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Date = localRuntime.Native.Date || {}; + if (localRuntime.Native.Date.values) + { + return localRuntime.Native.Date.values; + } + + var Result = Elm.Result.make(localRuntime); + + function dateNow() + { + return new window.Date; + } + + function readDate(str) + { + var date = new window.Date(str); + return isNaN(date.getTime()) + ? Result.Err("unable to parse '" + str + "' as a date") + : Result.Ok(date); + } + + var dayTable = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + var monthTable = + ["Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; + + + return localRuntime.Native.Date.values = { + read : readDate, + year : function(d) { return d.getFullYear(); }, + month : function(d) { return { ctor:monthTable[d.getMonth()] }; }, + day : function(d) { return d.getDate(); }, + hour : function(d) { return d.getHours(); }, + minute : function(d) { return d.getMinutes(); }, + second : function(d) { return d.getSeconds(); }, + millisecond: function (d) { return d.getMilliseconds(); }, + toTime : function(d) { return d.getTime(); }, + fromTime: function(t) { return new window.Date(t); }, + dayOfWeek : function(d) { return { ctor:dayTable[d.getDay()] }; } + }; + +}; + +Elm.Native.Debug = {}; +Elm.Native.Debug.make = function(localRuntime) { + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Debug = localRuntime.Native.Debug || {}; + if (localRuntime.Native.Debug.values) + { + return localRuntime.Native.Debug.values; + } + + var toString = Elm.Native.Show.make(localRuntime).toString; + + function log(tag, value) + { + var msg = tag + ': ' + toString(value); + var process = process || {}; + if (process.stdout) + { + process.stdout.write(msg); + } + else + { + console.log(msg); + } + return value; + } + + function crash(message) + { + throw new Error(message); + } + + function tracePath(tag, form) + { + if (localRuntime.debug) + { + return localRuntime.debug.trace(tag, form); + } + return form; + } + + function watch(tag, value) + { + if (localRuntime.debug) + { + localRuntime.debug.watch(tag, value); + } + return value; + } + + function watchSummary(tag, summarize, value) + { + if (localRuntime.debug) + { + localRuntime.debug.watch(tag, summarize(value)); + } + return value; + } + + return localRuntime.Native.Debug.values = { + crash: crash, + tracePath: F2(tracePath), + log: F2(log), + watch: F2(watch), + watchSummary:F3(watchSummary), + }; +}; + + +// setup +Elm.Native = Elm.Native || {}; +Elm.Native.Graphics = Elm.Native.Graphics || {}; +Elm.Native.Graphics.Collage = Elm.Native.Graphics.Collage || {}; + +// definition +Elm.Native.Graphics.Collage.make = function(localRuntime) { + 'use strict'; + + // attempt to short-circuit + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Graphics = localRuntime.Native.Graphics || {}; + localRuntime.Native.Graphics.Collage = localRuntime.Native.Graphics.Collage || {}; + if ('values' in localRuntime.Native.Graphics.Collage) + { + return localRuntime.Native.Graphics.Collage.values; + } + + // okay, we cannot short-ciruit, so now we define everything + var Color = Elm.Native.Color.make(localRuntime); + var List = Elm.Native.List.make(localRuntime); + var NativeElement = Elm.Native.Graphics.Element.make(localRuntime); + var Transform = Elm.Transform2D.make(localRuntime); + var Utils = Elm.Native.Utils.make(localRuntime); + + function setStrokeStyle(ctx, style) + { + ctx.lineWidth = style.width; + + var cap = style.cap.ctor; + ctx.lineCap = cap === 'Flat' + ? 'butt' + : cap === 'Round' + ? 'round' + : 'square'; + + var join = style.join.ctor; + ctx.lineJoin = join === 'Smooth' + ? 'round' + : join === 'Sharp' + ? 'miter' + : 'bevel'; + + ctx.miterLimit = style.join._0 || 10; + ctx.strokeStyle = Color.toCss(style.color); + } + + function setFillStyle(ctx, style) + { + var sty = style.ctor; + ctx.fillStyle = sty === 'Solid' + ? Color.toCss(style._0) + : sty === 'Texture' + ? texture(redo, ctx, style._0) + : gradient(ctx, style._0); + } + + function trace(ctx, path) + { + var points = List.toArray(path); + var i = points.length - 1; + if (i <= 0) + { + return; + } + ctx.moveTo(points[i]._0, points[i]._1); + while (i--) + { + ctx.lineTo(points[i]._0, points[i]._1); + } + if (path.closed) + { + i = points.length - 1; + ctx.lineTo(points[i]._0, points[i]._1); + } + } + + function line(ctx,style,path) + { + (style.dashing.ctor === '[]') + ? trace(ctx, path) + : customLineHelp(ctx, style, path); + ctx.scale(1,-1); + ctx.stroke(); + } + + function customLineHelp(ctx, style, path) + { + var points = List.toArray(path); + if (path.closed) + { + points.push(points[0]); + } + var pattern = List.toArray(style.dashing); + var i = points.length - 1; + if (i <= 0) + { + return; + } + var x0 = points[i]._0, y0 = points[i]._1; + var x1=0, y1=0, dx=0, dy=0, remaining=0, nx=0, ny=0; + var pindex = 0, plen = pattern.length; + var draw = true, segmentLength = pattern[0]; + ctx.moveTo(x0,y0); + while (i--) + { + x1 = points[i]._0; + y1 = points[i]._1; + dx = x1 - x0; + dy = y1 - y0; + remaining = Math.sqrt(dx * dx + dy * dy); + while (segmentLength <= remaining) + { + x0 += dx * segmentLength / remaining; + y0 += dy * segmentLength / remaining; + ctx[draw ? 'lineTo' : 'moveTo'](x0, y0); + // update starting position + dx = x1 - x0; + dy = y1 - y0; + remaining = Math.sqrt(dx * dx + dy * dy); + // update pattern + draw = !draw; + pindex = (pindex + 1) % plen; + segmentLength = pattern[pindex]; + } + if (remaining > 0) + { + ctx[draw ? 'lineTo' : 'moveTo'](x1, y1); + segmentLength -= remaining; + } + x0 = x1; + y0 = y1; + } + } + + function drawLine(ctx, style, path) + { + setStrokeStyle(ctx, style); + return line(ctx, style, path); + } + + function texture(redo, ctx, src) + { + var img = new Image(); + img.src = src; + img.onload = redo; + return ctx.createPattern(img, 'repeat'); + } + + function gradient(ctx, grad) + { + var g; + var stops = []; + if (grad.ctor === 'Linear') + { + var p0 = grad._0, p1 = grad._1; + g = ctx.createLinearGradient(p0._0, -p0._1, p1._0, -p1._1); + stops = List.toArray(grad._2); + } + else + { + var p0 = grad._0, p2 = grad._2; + g = ctx.createRadialGradient(p0._0, -p0._1, grad._1, p2._0, -p2._1, grad._3); + stops = List.toArray(grad._4); + } + var len = stops.length; + for (var i = 0; i < len; ++i) + { + var stop = stops[i]; + g.addColorStop(stop._0, Color.toCss(stop._1)); + } + return g; + } + + function drawShape(redo, ctx, style, path) + { + trace(ctx, path); + setFillStyle(ctx, style); + ctx.scale(1,-1); + ctx.fill(); + } + + + // TEXT RENDERING + + function fillText(redo, ctx, text) + { + drawText(ctx, text, ctx.fillText); + } + + function strokeText(redo, ctx, style, text) + { + setStrokeStyle(ctx, style); + // Use native canvas API for dashes only for text for now + // Degrades to non-dashed on IE 9 + 10 + if (style.dashing.ctor !== '[]' && ctx.setLineDash) + { + var pattern = List.toArray(style.dashing); + ctx.setLineDash(pattern); + } + drawText(ctx, text, ctx.strokeText); + } + + function drawText(ctx, text, canvasDrawFn) + { + var textChunks = chunkText(defaultContext, text); + + var totalWidth = 0; + var maxHeight = 0; + var numChunks = textChunks.length; + + ctx.scale(1,-1); + + for (var i = numChunks; i--; ) + { + var chunk = textChunks[i]; + ctx.font = chunk.font; + var metrics = ctx.measureText(chunk.text); + chunk.width = metrics.width; + totalWidth += chunk.width; + if (chunk.height > maxHeight) + { + maxHeight = chunk.height; + } + } + + var x = -totalWidth / 2.0; + for (var i = 0; i < numChunks; ++i) + { + var chunk = textChunks[i]; + ctx.font = chunk.font; + ctx.fillStyle = chunk.color; + canvasDrawFn.call(ctx, chunk.text, x, maxHeight / 2); + x += chunk.width; + } + } + + function toFont(props) + { + return [ + props['font-style'], + props['font-variant'], + props['font-weight'], + props['font-size'], + props['font-family'] + ].join(' '); + } + + + // Convert the object returned by the text module + // into something we can use for styling canvas text + function chunkText(context, text) + { + var tag = text.ctor; + if (tag === 'Text:Append') + { + var leftChunks = chunkText(context, text._0); + var rightChunks = chunkText(context, text._1); + return leftChunks.concat(rightChunks); + } + if (tag === 'Text:Text') + { + return [{ + text: text._0, + color: context.color, + height: context['font-size'].slice(0,-2) | 0, + font: toFont(context) + }]; + } + if (tag === 'Text:Meta') + { + var newContext = freshContext(text._0, context); + return chunkText(newContext, text._1); + } + } + + function freshContext(props, ctx) + { + return { + 'font-style': props['font-style'] || ctx['font-style'], + 'font-variant': props['font-variant'] || ctx['font-variant'], + 'font-weight': props['font-weight'] || ctx['font-weight'], + 'font-size': props['font-size'] || ctx['font-size'], + 'font-family': props['font-family'] || ctx['font-family'], + 'color': props['color'] || ctx['color'] + }; + } + + var defaultContext = { + 'font-style': 'normal', + 'font-variant': 'normal', + 'font-weight': 'normal', + 'font-size': '12px', + 'font-family': 'sans-serif', + 'color': 'black' + }; + + + // IMAGES + + function drawImage(redo, ctx, form) + { + var img = new Image(); + img.onload = redo; + img.src = form._3; + var w = form._0, + h = form._1, + pos = form._2, + srcX = pos._0, + srcY = pos._1, + srcW = w, + srcH = h, + destX = -w/2, + destY = -h/2, + destW = w, + destH = h; + + ctx.scale(1,-1); + ctx.drawImage(img, srcX, srcY, srcW, srcH, destX, destY, destW, destH); + } + + function renderForm(redo, ctx, form) + { + ctx.save(); + + var x = form.x, + y = form.y, + theta = form.theta, + scale = form.scale; + + if (x !== 0 || y !== 0) + { + ctx.translate(x, y); + } + if (theta !== 0) + { + ctx.rotate(theta); + } + if (scale !== 1) + { + ctx.scale(scale,scale); + } + if (form.alpha !== 1) + { + ctx.globalAlpha = ctx.globalAlpha * form.alpha; + } + + ctx.beginPath(); + var f = form.form; + switch (f.ctor) + { + case 'FPath': + drawLine(ctx, f._0, f._1); + break; + + case 'FImage': + drawImage(redo, ctx, f); + break; + + case 'FShape': + if (f._0.ctor === 'Line') + { + f._1.closed = true; + drawLine(ctx, f._0._0, f._1); + } + else + { + drawShape(redo, ctx, f._0._0, f._1); + } + break; + + case 'FText': + fillText(redo, ctx, f._0); + break; + + case 'FOutlinedText': + strokeText(redo, ctx, f._0, f._1); + break; + } + ctx.restore(); + } + + function formToMatrix(form) + { + var scale = form.scale; + var matrix = A6( Transform.matrix, scale, 0, 0, scale, form.x, form.y ); + + var theta = form.theta + if (theta !== 0) + { + matrix = A2( Transform.multiply, matrix, Transform.rotation(theta) ); + } + + return matrix; + } + + function str(n) + { + if (n < 0.00001 && n > -0.00001) + { + return 0; + } + return n; + } + + function makeTransform(w, h, form, matrices) + { + var props = form.form._0.props; + var m = A6( Transform.matrix, 1, 0, 0, -1, + (w - props.width ) / 2, + (h - props.height) / 2 ); + var len = matrices.length; + for (var i = 0; i < len; ++i) + { + m = A2( Transform.multiply, m, matrices[i] ); + } + m = A2( Transform.multiply, m, formToMatrix(form) ); + + return 'matrix(' + + str( m[0]) + ', ' + str( m[3]) + ', ' + + str(-m[1]) + ', ' + str(-m[4]) + ', ' + + str( m[2]) + ', ' + str( m[5]) + ')'; + } + + function stepperHelp(list) + { + var arr = List.toArray(list); + var i = 0; + function peekNext() + { + return i < arr.length ? arr[i].form.ctor : ''; + } + // assumes that there is a next element + function next() + { + var out = arr[i]; + ++i; + return out; + } + return { + peekNext: peekNext, + next: next + }; + } + + function formStepper(forms) + { + var ps = [stepperHelp(forms)]; + var matrices = []; + var alphas = []; + function peekNext() + { + var len = ps.length; + var formType = ''; + for (var i = 0; i < len; ++i ) + { + if (formType = ps[i].peekNext()) return formType; + } + return ''; + } + // assumes that there is a next element + function next(ctx) + { + while (!ps[0].peekNext()) + { + ps.shift(); + matrices.pop(); + alphas.shift(); + if (ctx) + { + ctx.restore(); + } + } + var out = ps[0].next(); + var f = out.form; + if (f.ctor === 'FGroup') + { + ps.unshift(stepperHelp(f._1)); + var m = A2(Transform.multiply, f._0, formToMatrix(out)); + ctx.save(); + ctx.transform(m[0], m[3], m[1], m[4], m[2], m[5]); + matrices.push(m); + + var alpha = (alphas[0] || 1) * out.alpha; + alphas.unshift(alpha); + ctx.globalAlpha = alpha; + } + return out; + } + function transforms() + { + return matrices; + } + function alpha() + { + return alphas[0] || 1; + } + return { + peekNext: peekNext, + next: next, + transforms: transforms, + alpha: alpha + }; + } + + function makeCanvas(w,h) + { + var canvas = NativeElement.createNode('canvas'); + canvas.style.width = w + 'px'; + canvas.style.height = h + 'px'; + canvas.style.display = "block"; + canvas.style.position = "absolute"; + var ratio = window.devicePixelRatio || 1; + canvas.width = w * ratio; + canvas.height = h * ratio; + return canvas; + } + + function render(model) + { + var div = NativeElement.createNode('div'); + div.style.overflow = 'hidden'; + div.style.position = 'relative'; + update(div, model, model); + return div; + } + + function nodeStepper(w,h,div) + { + var kids = div.childNodes; + var i = 0; + var ratio = window.devicePixelRatio || 1; + + function transform(transforms, ctx) + { + ctx.translate( w / 2 * ratio, h / 2 * ratio ); + ctx.scale( ratio, -ratio ); + var len = transforms.length; + for (var i = 0; i < len; ++i) + { + var m = transforms[i]; + ctx.save(); + ctx.transform(m[0], m[3], m[1], m[4], m[2], m[5]); + } + return ctx; + } + function nextContext(transforms) + { + while (i < kids.length) + { + var node = kids[i]; + if (node.getContext) + { + node.width = w * ratio; + node.height = h * ratio; + node.style.width = w + 'px'; + node.style.height = h + 'px'; + ++i; + return transform(transforms, node.getContext('2d')); + } + div.removeChild(node); + } + var canvas = makeCanvas(w,h); + div.appendChild(canvas); + // we have added a new node, so we must step our position + ++i; + return transform(transforms, canvas.getContext('2d')); + } + function addElement(matrices, alpha, form) + { + var kid = kids[i]; + var elem = form.form._0; + + var node = (!kid || kid.getContext) + ? NativeElement.render(elem) + : NativeElement.update(kid, kid.oldElement, elem); + + node.style.position = 'absolute'; + node.style.opacity = alpha * form.alpha * elem.props.opacity; + NativeElement.addTransform(node.style, makeTransform(w, h, form, matrices)); + node.oldElement = elem; + ++i; + if (!kid) + { + div.appendChild(node); + } + else + { + div.insertBefore(node, kid); + } + } + function clearRest() + { + while (i < kids.length) + { + div.removeChild(kids[i]); + } + } + return { + nextContext: nextContext, + addElement: addElement, + clearRest: clearRest + }; + } + + + function update(div, _, model) + { + var w = model.w; + var h = model.h; + + var forms = formStepper(model.forms); + var nodes = nodeStepper(w,h,div); + var ctx = null; + var formType = ''; + + while (formType = forms.peekNext()) + { + // make sure we have context if we need it + if (ctx === null && formType !== 'FElement') + { + ctx = nodes.nextContext(forms.transforms()); + ctx.globalAlpha = forms.alpha(); + } + + var form = forms.next(ctx); + // if it is FGroup, all updates are made within formStepper when next is called. + if (formType === 'FElement') + { + // update or insert an element, get a new context + nodes.addElement(forms.transforms(), forms.alpha(), form); + ctx = null; + } + else if (formType !== 'FGroup') + { + renderForm(function() { update(div, model, model); }, ctx, form); + } + } + nodes.clearRest(); + return div; + } + + + function collage(w,h,forms) + { + return A3(NativeElement.newElement, w, h, { + ctor: 'Custom', + type: 'Collage', + render: render, + update: update, + model: {w:w, h:h, forms:forms} + }); + } + + return localRuntime.Native.Graphics.Collage.values = { + collage: F3(collage) + }; + +}; + + +// setup +Elm.Native = Elm.Native || {}; +Elm.Native.Graphics = Elm.Native.Graphics || {}; +Elm.Native.Graphics.Element = Elm.Native.Graphics.Element || {}; + +// definition +Elm.Native.Graphics.Element.make = function(localRuntime) { + 'use strict'; + + // attempt to short-circuit + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Graphics = localRuntime.Native.Graphics || {}; + localRuntime.Native.Graphics.Element = localRuntime.Native.Graphics.Element || {}; + if ('values' in localRuntime.Native.Graphics.Element) + { + return localRuntime.Native.Graphics.Element.values; + } + + var Color = Elm.Native.Color.make(localRuntime); + var List = Elm.Native.List.make(localRuntime); + var Maybe = Elm.Maybe.make(localRuntime); + var Text = Elm.Native.Text.make(localRuntime); + var Utils = Elm.Native.Utils.make(localRuntime); + + + // CREATION + + function createNode(elementType) + { + var node = document.createElement(elementType); + node.style.padding = "0"; + node.style.margin = "0"; + return node; + } + + + function newElement(width, height, elementPrim) + { + return { + _: {}, + element: elementPrim, + props: { + _: {}, + id: Utils.guid(), + width: width, + height: height, + opacity: 1, + color: Maybe.Nothing, + href: "", + tag: "", + hover: Utils.Tuple0, + click: Utils.Tuple0 + } + }; + } + + + // PROPERTIES + + function setProps(elem, node) + { + var props = elem.props; + + var element = elem.element; + var width = props.width - (element.adjustWidth || 0); + var height = props.height - (element.adjustHeight || 0); + node.style.width = (width |0) + 'px'; + node.style.height = (height|0) + 'px'; + + if (props.opacity !== 1) + { + node.style.opacity = props.opacity; + } + + if (props.color.ctor === 'Just') + { + node.style.backgroundColor = Color.toCss(props.color._0); + } + + if (props.tag !== '') + { + node.id = props.tag; + } + + if (props.hover.ctor !== '_Tuple0') + { + addHover(node, props.hover); + } + + if (props.click.ctor !== '_Tuple0') + { + addClick(node, props.click); + } + + if (props.href !== '') + { + var anchor = createNode('a'); + anchor.href = props.href; + anchor.style.display = 'block'; + anchor.style.pointerEvents = 'auto'; + anchor.appendChild(node); + node = anchor; + } + + return node; + } + + function addClick(e, handler) + { + e.style.pointerEvents = 'auto'; + e.elm_click_handler = handler; + function trigger(ev) + { + e.elm_click_handler(Utils.Tuple0); + ev.stopPropagation(); + } + e.elm_click_trigger = trigger; + e.addEventListener('click', trigger); + } + + function removeClick(e, handler) + { + if (e.elm_click_trigger) + { + e.removeEventListener('click', e.elm_click_trigger); + e.elm_click_trigger = null; + e.elm_click_handler = null; + } + } + + function addHover(e, handler) + { + e.style.pointerEvents = 'auto'; + e.elm_hover_handler = handler; + e.elm_hover_count = 0; + + function over(evt) + { + if (e.elm_hover_count++ > 0) return; + e.elm_hover_handler(true); + evt.stopPropagation(); + } + function out(evt) + { + if (e.contains(evt.toElement || evt.relatedTarget)) return; + e.elm_hover_count = 0; + e.elm_hover_handler(false); + evt.stopPropagation(); + } + e.elm_hover_over = over; + e.elm_hover_out = out; + e.addEventListener('mouseover', over); + e.addEventListener('mouseout', out); + } + + function removeHover(e) + { + e.elm_hover_handler = null; + if (e.elm_hover_over) + { + e.removeEventListener('mouseover', e.elm_hover_over); + e.elm_hover_over = null; + } + if (e.elm_hover_out) + { + e.removeEventListener('mouseout', e.elm_hover_out); + e.elm_hover_out = null; + } + } + + + // IMAGES + + function image(props, img) + { + switch (img._0.ctor) + { + case 'Plain': + return plainImage(img._3); + + case 'Fitted': + return fittedImage(props.width, props.height, img._3); + + case 'Cropped': + return croppedImage(img,props.width,props.height,img._3); + + case 'Tiled': + return tiledImage(img._3); + } + } + + function plainImage(src) + { + var img = createNode('img'); + img.src = src; + img.name = src; + img.style.display = "block"; + return img; + } + + function tiledImage(src) + { + var div = createNode('div'); + div.style.backgroundImage = 'url(' + src + ')'; + return div; + } + + function fittedImage(w, h, src) + { + var div = createNode('div'); + div.style.background = 'url(' + src + ') no-repeat center'; + div.style.webkitBackgroundSize = 'cover'; + div.style.MozBackgroundSize = 'cover'; + div.style.OBackgroundSize = 'cover'; + div.style.backgroundSize = 'cover'; + return div; + } + + function croppedImage(elem, w, h, src) + { + var pos = elem._0._0; + var e = createNode('div'); + e.style.overflow = "hidden"; + + var img = createNode('img'); + img.onload = function() { + var sw = w / elem._1, sh = h / elem._2; + img.style.width = ((this.width * sw)|0) + 'px'; + img.style.height = ((this.height * sh)|0) + 'px'; + img.style.marginLeft = ((- pos._0 * sw)|0) + 'px'; + img.style.marginTop = ((- pos._1 * sh)|0) + 'px'; + }; + img.src = src; + img.name = src; + e.appendChild(img); + return e; + } + + + // FLOW + + function goOut(node) + { + node.style.position = 'absolute'; + return node; + } + function goDown(node) + { + return node; + } + function goRight(node) + { + node.style.styleFloat = 'left'; + node.style.cssFloat = 'left'; + return node; + } + + var directionTable = { + DUp : goDown, + DDown : goDown, + DLeft : goRight, + DRight : goRight, + DIn : goOut, + DOut : goOut + }; + function needsReversal(dir) + { + return dir == 'DUp' || dir == 'DLeft' || dir == 'DIn'; + } + + function flow(dir,elist) + { + var array = List.toArray(elist); + var container = createNode('div'); + var goDir = directionTable[dir]; + if (goDir == goOut) + { + container.style.pointerEvents = 'none'; + } + if (needsReversal(dir)) + { + array.reverse(); + } + var len = array.length; + for (var i = 0; i < len; ++i) + { + container.appendChild(goDir(render(array[i]))); + } + return container; + } + + + // CONTAINER + + function toPos(pos) + { + return pos.ctor === "Absolute" + ? pos._0 + "px" + : (pos._0 * 100) + "%"; + } + + // must clear right, left, top, bottom, and transform + // before calling this function + function setPos(pos,elem,e) + { + var element = elem.element; + var props = elem.props; + var w = props.width + (element.adjustWidth ? element.adjustWidth : 0); + var h = props.height + (element.adjustHeight ? element.adjustHeight : 0); + + e.style.position = 'absolute'; + e.style.margin = 'auto'; + var transform = ''; + + switch (pos.horizontal.ctor) + { + case 'P': + e.style.right = toPos(pos.x); + e.style.removeProperty('left'); + break; + + case 'Z': + transform = 'translateX(' + ((-w/2)|0) + 'px) '; + + case 'N': + e.style.left = toPos(pos.x); + e.style.removeProperty('right'); + break; + } + switch (pos.vertical.ctor) + { + case 'N': + e.style.bottom = toPos(pos.y); + e.style.removeProperty('top'); + break; + + case 'Z': + transform += 'translateY(' + ((-h/2)|0) + 'px)'; + + case 'P': + e.style.top = toPos(pos.y); + e.style.removeProperty('bottom'); + break; + } + if (transform !== '') + { + addTransform(e.style, transform); + } + return e; + } + + function addTransform(style, transform) + { + style.transform = transform; + style.msTransform = transform; + style.MozTransform = transform; + style.webkitTransform = transform; + style.OTransform = transform; + } + + function container(pos,elem) + { + var e = render(elem); + setPos(pos, elem, e); + var div = createNode('div'); + div.style.position = 'relative'; + div.style.overflow = 'hidden'; + div.appendChild(e); + return div; + } + + + function rawHtml(elem) + { + var html = elem.html; + var guid = elem.guid; + var align = elem.align; + + var div = createNode('div'); + div.innerHTML = html; + div.style.visibility = "hidden"; + if (align) + { + div.style.textAlign = align; + } + div.style.visibility = 'visible'; + div.style.pointerEvents = 'auto'; + return div; + } + + + // RENDER + + function render(elem) + { + return setProps(elem, makeElement(elem)); + } + function makeElement(e) + { + var elem = e.element; + switch(elem.ctor) + { + case 'Image': + return image(e.props, elem); + + case 'Flow': + return flow(elem._0.ctor, elem._1); + + case 'Container': + return container(elem._0, elem._1); + + case 'Spacer': + return createNode('div'); + + case 'RawHtml': + return rawHtml(elem); + + case 'Custom': + return elem.render(elem.model); + } + } + + function updateAndReplace(node, curr, next) + { + var newNode = update(node, curr, next); + if (newNode !== node) + { + node.parentNode.replaceChild(newNode, node); + } + return newNode; + } + + + // UPDATE + + function update(node, curr, next) + { + var rootNode = node; + if (node.tagName === 'A') + { + node = node.firstChild; + } + if (curr.props.id === next.props.id) + { + updateProps(node, curr, next); + return rootNode; + } + if (curr.element.ctor !== next.element.ctor) + { + return render(next); + } + var nextE = next.element; + var currE = curr.element; + switch(nextE.ctor) + { + case "Spacer": + updateProps(node, curr, next); + return rootNode; + + case "RawHtml": + if(currE.html.valueOf() !== nextE.html.valueOf()) + { + node.innerHTML = nextE.html; + } + updateProps(node, curr, next); + return rootNode; + + case "Image": + if (nextE._0.ctor === 'Plain') + { + if (nextE._3 !== currE._3) + { + node.src = nextE._3; + } + } + else if (!Utils.eq(nextE,currE) + || next.props.width !== curr.props.width + || next.props.height !== curr.props.height) + { + return render(next); + } + updateProps(node, curr, next); + return rootNode; + + case "Flow": + var arr = List.toArray(nextE._1); + for (var i = arr.length; i--; ) + { + arr[i] = arr[i].element.ctor; + } + if (nextE._0.ctor !== currE._0.ctor) + { + return render(next); + } + var nexts = List.toArray(nextE._1); + var kids = node.childNodes; + if (nexts.length !== kids.length) + { + return render(next); + } + var currs = List.toArray(currE._1); + var dir = nextE._0.ctor; + var goDir = directionTable[dir]; + var toReverse = needsReversal(dir); + var len = kids.length; + for (var i = len; i-- ;) + { + var subNode = kids[toReverse ? len - i - 1 : i]; + goDir(updateAndReplace(subNode, currs[i], nexts[i])); + } + updateProps(node, curr, next); + return rootNode; + + case "Container": + var subNode = node.firstChild; + var newSubNode = updateAndReplace(subNode, currE._1, nextE._1); + setPos(nextE._0, nextE._1, newSubNode); + updateProps(node, curr, next); + return rootNode; + + case "Custom": + if (currE.type === nextE.type) + { + var updatedNode = nextE.update(node, currE.model, nextE.model); + updateProps(updatedNode, curr, next); + return updatedNode; + } + return render(next); + } + } + + function updateProps(node, curr, next) + { + var nextProps = next.props; + var currProps = curr.props; + + var element = next.element; + var width = nextProps.width - (element.adjustWidth || 0); + var height = nextProps.height - (element.adjustHeight || 0); + if (width !== currProps.width) + { + node.style.width = (width|0) + 'px'; + } + if (height !== currProps.height) + { + node.style.height = (height|0) + 'px'; + } + + if (nextProps.opacity !== currProps.opacity) + { + node.style.opacity = nextProps.opacity; + } + + var nextColor = nextProps.color.ctor === 'Just' + ? Color.toCss(nextProps.color._0) + : ''; + if (node.style.backgroundColor !== nextColor) + { + node.style.backgroundColor = nextColor; + } + + if (nextProps.tag !== currProps.tag) + { + node.id = nextProps.tag; + } + + if (nextProps.href !== currProps.href) + { + if (currProps.href === '') + { + // add a surrounding href + var anchor = createNode('a'); + anchor.href = nextProps.href; + anchor.style.display = 'block'; + anchor.style.pointerEvents = 'auto'; + + node.parentNode.replaceChild(anchor, node); + anchor.appendChild(node); + } + else if (nextProps.href === '') + { + // remove the surrounding href + var anchor = node.parentNode; + anchor.parentNode.replaceChild(node, anchor); + } + else + { + // just update the link + node.parentNode.href = nextProps.href; + } + } + + // update click and hover handlers + var removed = false; + + // update hover handlers + if (currProps.hover.ctor === '_Tuple0') + { + if (nextProps.hover.ctor !== '_Tuple0') + { + addHover(node, nextProps.hover); + } + } + else + { + if (nextProps.hover.ctor === '_Tuple0') + { + removed = true; + removeHover(node); + } + else + { + node.elm_hover_handler = nextProps.hover; + } + } + + // update click handlers + if (currProps.click.ctor === '_Tuple0') + { + if (nextProps.click.ctor !== '_Tuple0') + { + addClick(node, nextProps.click); + } + } + else + { + if (nextProps.click.ctor === '_Tuple0') + { + removed = true; + removeClick(node); + } + else + { + node.elm_click_handler = nextProps.click; + } + } + + // stop capturing clicks if + if (removed + && nextProps.hover.ctor === '_Tuple0' + && nextProps.click.ctor === '_Tuple0') + { + node.style.pointerEvents = 'none'; + } + } + + + // TEXT + + function block(align) + { + return function(text) + { + var raw = { + ctor :'RawHtml', + html : Text.renderHtml(text), + align: align + }; + var pos = htmlHeight(0, raw); + return newElement(pos._0, pos._1, raw); + } + } + + function markdown(text) + { + var raw = { + ctor:'RawHtml', + html: text, + align: null + }; + var pos = htmlHeight(0, raw); + return newElement(pos._0, pos._1, raw); + } + + function htmlHeight(width, rawHtml) + { + // create dummy node + var temp = document.createElement('div'); + temp.innerHTML = rawHtml.html; + if (width > 0) + { + temp.style.width = width + "px"; + } + temp.style.visibility = "hidden"; + temp.style.styleFloat = "left"; + temp.style.cssFloat = "left"; + + document.body.appendChild(temp); + + // get dimensions + var style = window.getComputedStyle(temp, null); + var w = Math.ceil(style.getPropertyValue("width").slice(0,-2) - 0); + var h = Math.ceil(style.getPropertyValue("height").slice(0,-2) - 0); + document.body.removeChild(temp); + return Utils.Tuple2(w,h); + } + + + return localRuntime.Native.Graphics.Element.values = { + render: render, + update: update, + updateAndReplace: updateAndReplace, + + createNode: createNode, + newElement: F3(newElement), + addTransform: addTransform, + htmlHeight: F2(htmlHeight), + guid: Utils.guid, + + block: block, + markdown: markdown + }; + +}; + +Elm.Native.Http = {}; +Elm.Native.Http.make = function(localRuntime) { + + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Http = localRuntime.Native.Http || {}; + if (localRuntime.Native.Http.values) + { + return localRuntime.Native.Http.values; + } + + var Dict = Elm.Dict.make(localRuntime); + var List = Elm.List.make(localRuntime); + var Maybe = Elm.Maybe.make(localRuntime); + var Task = Elm.Native.Task.make(localRuntime); + + + function send(settings, request) + { + return Task.asyncFunction(function(callback) { + var req = new XMLHttpRequest(); + + // start + if (settings.onStart.ctor === 'Just') + { + req.addEventListener('loadStart', function() { + var task = settings.onStart._0; + Task.spawn(task); + }); + } + + // progress + if (settings.onProgress.ctor === 'Just') + { + req.addEventListener('progress', function(event) { + var progress = !event.lengthComputable + ? Maybe.Nothing + : Maybe.Just({ + _: {}, + loaded: event.loaded, + total: event.total + }); + var task = settings.onProgress._0(progress); + Task.spawn(task); + }); + } + + // end + req.addEventListener('error', function() { + return callback(Task.fail({ ctor: 'RawNetworkError' })); + }); + + req.addEventListener('timeout', function() { + return callback(Task.fail({ ctor: 'RawTimeout' })); + }); + + req.addEventListener('load', function() { + return callback(Task.succeed(toResponse(req))); + }); + + req.open(request.verb, request.url, true); + + // set all the headers + function setHeader(pair) { + req.setRequestHeader(pair._0, pair._1); + } + A2(List.map, setHeader, request.headers); + + // set the timeout + req.timeout = settings.timeout; + + // ask for a specific MIME type for the response + if (settings.desiredResponseType.ctor === 'Just') + { + req.overrideMimeType(settings.desiredResponseType._0); + } + + req.send(request.body._0); + }); + } + + + // deal with responses + + function toResponse(req) + { + var tag = typeof req.response === 'string' ? 'Text' : 'Blob'; + return { + _: {}, + status: req.status, + statusText: req.statusText, + headers: parseHeaders(req.getAllResponseHeaders()), + url: req.responseURL, + value: { ctor: tag, _0: req.response } + }; + } + + + function parseHeaders(rawHeaders) + { + var headers = Dict.empty; + + if (!rawHeaders) + { + return headers; + } + + var headerPairs = rawHeaders.split('\u000d\u000a'); + for (var i = headerPairs.length; i--; ) + { + var headerPair = headerPairs[i]; + var index = headerPair.indexOf('\u003a\u0020'); + if (index > 0) + { + var key = headerPair.substring(0, index); + var value = headerPair.substring(index + 2); + + headers = A3(Dict.update, key, function(oldValue) { + if (oldValue.ctor === 'Just') + { + return Maybe.Just(value + ', ' + oldValue._0); + } + return Maybe.Just(value); + }, headers); + } + } + + return headers; + } + + + function multipart(dataList) + { + var formData = new FormData(); + + while (dataList.ctor !== '[]') + { + var data = dataList._0; + if (type === 'StringData') + { + formData.append(data._0, data._1); + } + else + { + var fileName = data._1.ctor === 'Nothing' + ? undefined + : data._1._0; + formData.append(data._0, data._2, fileName); + } + dataList = dataList._1; + } + + return { ctor: 'FormData', formData: formData }; + } + + + function uriEncode(string) + { + return encodeURIComponent(string); + } + + function uriDecode(string) + { + return decodeURIComponent(string); + } + + return localRuntime.Native.Http.values = { + send: F2(send), + multipart: multipart, + uriEncode: uriEncode, + uriDecode: uriDecode + }; +}; + +Elm.Native.Json = {}; +Elm.Native.Json.make = function(localRuntime) { + + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Json = localRuntime.Native.Json || {}; + if (localRuntime.Native.Json.values) { + return localRuntime.Native.Json.values; + } + + var ElmArray = Elm.Native.Array.make(localRuntime); + var List = Elm.Native.List.make(localRuntime); + var Maybe = Elm.Maybe.make(localRuntime); + var Result = Elm.Result.make(localRuntime); + var Utils = Elm.Native.Utils.make(localRuntime); + + + function crash(expected, actual) { + throw new Error( + 'expecting ' + expected + ' but got ' + JSON.stringify(actual) + ); + } + + + // PRIMITIVE VALUES + + function decodeNull(successValue) { + return function(value) { + if (value === null) { + return successValue; + } + crash('null', value); + }; + } + + + function decodeString(value) { + if (typeof value === 'string' || value instanceof String) { + return value; + } + crash('a String', value); + } + + + function decodeFloat(value) { + if (typeof value === 'number') { + return value; + } + crash('a Float', value); + } + + + function decodeInt(value) { + if (typeof value === 'number' && (value|0) === value) { + return value; + } + crash('an Int', value); + } + + + function decodeBool(value) { + if (typeof value === 'boolean') { + return value; + } + crash('a Bool', value); + } + + + // ARRAY + + function decodeArray(decoder) { + return function(value) { + if (value instanceof Array) { + var len = value.length; + var array = new Array(len); + for (var i = len; i-- ; ) { + array[i] = decoder(value[i]); + } + return ElmArray.fromJSArray(array); + } + crash('an Array', value); + }; + } + + + // LIST + + function decodeList(decoder) { + return function(value) { + if (value instanceof Array) { + var len = value.length; + var list = List.Nil; + for (var i = len; i-- ; ) { + list = List.Cons( decoder(value[i]), list ); + } + return list; + } + crash('a List', value); + }; + } + + + // MAYBE + + function decodeMaybe(decoder) { + return function(value) { + try { + return Maybe.Just(decoder(value)); + } catch(e) { + return Maybe.Nothing; + } + }; + } + + + // FIELDS + + function decodeField(field, decoder) { + return function(value) { + var subValue = value[field]; + if (subValue !== undefined) { + return decoder(subValue); + } + crash("an object with field '" + field + "'", value); + }; + } + + + // OBJECTS + + function decodeKeyValuePairs(decoder) { + return function(value) { + var isObject = + typeof value === 'object' + && value !== null + && !(value instanceof Array); + + if (isObject) { + var keyValuePairs = List.Nil; + for (var key in value) { + var elmValue = decoder(value[key]); + var pair = Utils.Tuple2(key, elmValue); + keyValuePairs = List.Cons(pair, keyValuePairs); + } + return keyValuePairs; + } + + crash("an object", value); + }; + } + + function decodeObject1(f, d1) { + return function(value) { + return f(d1(value)); + }; + } + + function decodeObject2(f, d1, d2) { + return function(value) { + return A2( f, d1(value), d2(value) ); + }; + } + + function decodeObject3(f, d1, d2, d3) { + return function(value) { + return A3( f, d1(value), d2(value), d3(value) ); + }; + } + + function decodeObject4(f, d1, d2, d3, d4) { + return function(value) { + return A4( f, d1(value), d2(value), d3(value), d4(value) ); + }; + } + + function decodeObject5(f, d1, d2, d3, d4, d5) { + return function(value) { + return A5( f, d1(value), d2(value), d3(value), d4(value), d5(value) ); + }; + } + + function decodeObject6(f, d1, d2, d3, d4, d5, d6) { + return function(value) { + return A6( f, + d1(value), + d2(value), + d3(value), + d4(value), + d5(value), + d6(value) + ); + }; + } + + function decodeObject7(f, d1, d2, d3, d4, d5, d6, d7) { + return function(value) { + return A7( f, + d1(value), + d2(value), + d3(value), + d4(value), + d5(value), + d6(value), + d7(value) + ); + }; + } + + function decodeObject8(f, d1, d2, d3, d4, d5, d6, d7, d8) { + return function(value) { + return A8( f, + d1(value), + d2(value), + d3(value), + d4(value), + d5(value), + d6(value), + d7(value), + d8(value) + ); + }; + } + + + // TUPLES + + function decodeTuple1(f, d1) { + return function(value) { + if ( !(value instanceof Array) || value.length !== 1 ) { + crash('a Tuple of length 1', value); + } + return f( d1(value[0]) ); + }; + } + + function decodeTuple2(f, d1, d2) { + return function(value) { + if ( !(value instanceof Array) || value.length !== 2 ) { + crash('a Tuple of length 2', value); + } + return A2( f, d1(value[0]), d2(value[1]) ); + }; + } + + function decodeTuple3(f, d1, d2, d3) { + return function(value) { + if ( !(value instanceof Array) || value.length !== 3 ) { + crash('a Tuple of length 3', value); + } + return A3( f, d1(value[0]), d2(value[1]), d3(value[2]) ); + }; + } + + + function decodeTuple4(f, d1, d2, d3, d4) { + return function(value) { + if ( !(value instanceof Array) || value.length !== 4 ) { + crash('a Tuple of length 4', value); + } + return A4( f, d1(value[0]), d2(value[1]), d3(value[2]), d4(value[3]) ); + }; + } + + + function decodeTuple5(f, d1, d2, d3, d4, d5) { + return function(value) { + if ( !(value instanceof Array) || value.length !== 5 ) { + crash('a Tuple of length 5', value); + } + return A5( f, + d1(value[0]), + d2(value[1]), + d3(value[2]), + d4(value[3]), + d5(value[4]) + ); + }; + } + + + function decodeTuple6(f, d1, d2, d3, d4, d5, d6) { + return function(value) { + if ( !(value instanceof Array) || value.length !== 6 ) { + crash('a Tuple of length 6', value); + } + return A6( f, + d1(value[0]), + d2(value[1]), + d3(value[2]), + d4(value[3]), + d5(value[4]), + d6(value[5]) + ); + }; + } + + function decodeTuple7(f, d1, d2, d3, d4, d5, d6, d7) { + return function(value) { + if ( !(value instanceof Array) || value.length !== 7 ) { + crash('a Tuple of length 7', value); + } + return A7( f, + d1(value[0]), + d2(value[1]), + d3(value[2]), + d4(value[3]), + d5(value[4]), + d6(value[5]), + d7(value[6]) + ); + }; + } + + + function decodeTuple8(f, d1, d2, d3, d4, d5, d6, d7, d8) { + return function(value) { + if ( !(value instanceof Array) || value.length !== 8 ) { + crash('a Tuple of length 8', value); + } + return A8( f, + d1(value[0]), + d2(value[1]), + d3(value[2]), + d4(value[3]), + d5(value[4]), + d6(value[5]), + d7(value[6]), + d8(value[7]) + ); + }; + } + + + // CUSTOM DECODERS + + function decodeValue(value) { + return value; + } + + function runDecoderValue(decoder, value) { + try { + return Result.Ok(decoder(value)); + } catch(e) { + return Result.Err(e.message); + } + } + + function customDecoder(decoder, callback) { + return function(value) { + var result = callback(decoder(value)); + if (result.ctor === 'Err') { + throw new Error('custom decoder failed: ' + result._0); + } + return result._0; + } + } + + function andThen(decode, callback) { + return function(value) { + var result = decode(value); + return callback(result)(value); + } + } + + function fail(msg) { + return function(value) { + throw new Error(msg); + } + } + + function succeed(successValue) { + return function(value) { + return successValue; + } + } + + + // ONE OF MANY + + function oneOf(decoders) { + return function(value) { + var errors = []; + var temp = decoders; + while (temp.ctor !== '[]') { + try { + return temp._0(value); + } catch(e) { + errors.push(e.message); + } + temp = temp._1; + } + throw new Error('expecting one of the following:\n ' + errors.join('\n ')); + } + } + + function get(decoder, value) { + try { + return Result.Ok(decoder(value)); + } catch(e) { + return Result.Err(e.message); + } + } + + + // ENCODE / DECODE + + function runDecoderString(decoder, string) { + try { + return Result.Ok(decoder(JSON.parse(string))); + } catch(e) { + return Result.Err(e.message); + } + } + + function encode(indentLevel, value) { + return JSON.stringify(value, null, indentLevel); + } + + function identity(value) { + return value; + } + + function encodeObject(keyValuePairs) { + var obj = {}; + while (keyValuePairs.ctor !== '[]') { + var pair = keyValuePairs._0; + obj[pair._0] = pair._1; + keyValuePairs = keyValuePairs._1; + } + return obj; + } + + return localRuntime.Native.Json.values = { + encode: F2(encode), + runDecoderString: F2(runDecoderString), + runDecoderValue: F2(runDecoderValue), + + get: F2(get), + oneOf: oneOf, + + decodeNull: decodeNull, + decodeInt: decodeInt, + decodeFloat: decodeFloat, + decodeString: decodeString, + decodeBool: decodeBool, + + decodeMaybe: decodeMaybe, + + decodeList: decodeList, + decodeArray: decodeArray, + + decodeField: F2(decodeField), + + decodeObject1: F2(decodeObject1), + decodeObject2: F3(decodeObject2), + decodeObject3: F4(decodeObject3), + decodeObject4: F5(decodeObject4), + decodeObject5: F6(decodeObject5), + decodeObject6: F7(decodeObject6), + decodeObject7: F8(decodeObject7), + decodeObject8: F9(decodeObject8), + decodeKeyValuePairs: decodeKeyValuePairs, + + decodeTuple1: F2(decodeTuple1), + decodeTuple2: F3(decodeTuple2), + decodeTuple3: F4(decodeTuple3), + decodeTuple4: F5(decodeTuple4), + decodeTuple5: F6(decodeTuple5), + decodeTuple6: F7(decodeTuple6), + decodeTuple7: F8(decodeTuple7), + decodeTuple8: F9(decodeTuple8), + + andThen: F2(andThen), + decodeValue: decodeValue, + customDecoder: F2(customDecoder), + fail: fail, + succeed: succeed, + + identity: identity, + encodeNull: null, + encodeArray: ElmArray.toJSArray, + encodeList: List.toArray, + encodeObject: encodeObject + + }; + +}; + +Elm.Native.List = {}; +Elm.Native.List.make = function(localRuntime) { + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.List = localRuntime.Native.List || {}; + if (localRuntime.Native.List.values) + { + return localRuntime.Native.List.values; + } + if ('values' in Elm.Native.List) + { + return localRuntime.Native.List.values = Elm.Native.List.values; + } + + var Utils = Elm.Native.Utils.make(localRuntime); + + var Nil = Utils.Nil; + var Cons = Utils.Cons; + + function toArray(xs) + { + var out = []; + while (xs.ctor !== '[]') + { + out.push(xs._0); + xs = xs._1; + } + return out; + } + + function fromArray(arr) + { + var out = Nil; + for (var i = arr.length; i--; ) + { + out = Cons(arr[i], out); + } + return out; + } + + function range(lo,hi) + { + var lst = Nil; + if (lo <= hi) + { + do { lst = Cons(hi,lst) } while (hi-->lo); + } + return lst + } + + // f defined similarly for both foldl and foldr (NB: different from Haskell) + // ie, foldl : (a -> b -> b) -> b -> [a] -> b + function foldl(f, b, xs) + { + var acc = b; + while (xs.ctor !== '[]') + { + acc = A2(f, xs._0, acc); + xs = xs._1; + } + return acc; + } + + function foldr(f, b, xs) + { + var arr = toArray(xs); + var acc = b; + for (var i = arr.length; i--; ) + { + acc = A2(f, arr[i], acc); + } + return acc; + } + + function any(pred, xs) + { + while (xs.ctor !== '[]') + { + if (pred(xs._0)) + { + return true; + } + xs = xs._1; + } + return false; + } + + function map2(f, xs, ys) + { + var arr = []; + while (xs.ctor !== '[]' && ys.ctor !== '[]') + { + arr.push(A2(f, xs._0, ys._0)); + xs = xs._1; + ys = ys._1; + } + return fromArray(arr); + } + + function map3(f, xs, ys, zs) + { + var arr = []; + while (xs.ctor !== '[]' && ys.ctor !== '[]' && zs.ctor !== '[]') + { + arr.push(A3(f, xs._0, ys._0, zs._0)); + xs = xs._1; + ys = ys._1; + zs = zs._1; + } + return fromArray(arr); + } + + function map4(f, ws, xs, ys, zs) + { + var arr = []; + while ( ws.ctor !== '[]' + && xs.ctor !== '[]' + && ys.ctor !== '[]' + && zs.ctor !== '[]') + { + arr.push(A4(f, ws._0, xs._0, ys._0, zs._0)); + ws = ws._1; + xs = xs._1; + ys = ys._1; + zs = zs._1; + } + return fromArray(arr); + } + + function map5(f, vs, ws, xs, ys, zs) + { + var arr = []; + while ( vs.ctor !== '[]' + && ws.ctor !== '[]' + && xs.ctor !== '[]' + && ys.ctor !== '[]' + && zs.ctor !== '[]') + { + arr.push(A5(f, vs._0, ws._0, xs._0, ys._0, zs._0)); + vs = vs._1; + ws = ws._1; + xs = xs._1; + ys = ys._1; + zs = zs._1; + } + return fromArray(arr); + } + + function sortBy(f, xs) + { + return fromArray(toArray(xs).sort(function(a,b){ + return Utils.cmp(f(a), f(b)); + })); + } + + function sortWith(f, xs) + { + return fromArray(toArray(xs).sort(function(a,b){ + var ord = f(a)(b).ctor; + return ord === 'EQ' ? 0 : ord === 'LT' ? -1 : 1; + })); + } + + function take(n, xs) + { + var arr = []; + while (xs.ctor !== '[]' && n > 0) + { + arr.push(xs._0); + xs = xs._1; + --n; + } + return fromArray(arr); + } + + function drop(n, xs) + { + while (xs.ctor !== '[]' && n > 0) + { + xs = xs._1; + --n; + } + return xs; + } + + function repeat(n, x) + { + var arr = []; + var pattern = [x]; + while (n > 0) + { + if (n & 1) + { + arr = arr.concat(pattern); + } + n >>= 1, pattern = pattern.concat(pattern); + } + return fromArray(arr); + } + + + Elm.Native.List.values = { + Nil:Nil, + Cons:Cons, + cons:F2(Cons), + toArray:toArray, + fromArray:fromArray, + range:range, + + foldl:F3(foldl), + foldr:F3(foldr), + + any:F2(any), + map2:F3(map2), + map3:F4(map3), + map4:F5(map4), + map5:F6(map5), + sortBy:F2(sortBy), + sortWith:F2(sortWith), + take:F2(take), + drop:F2(drop), + repeat:F2(repeat) + }; + return localRuntime.Native.List.values = Elm.Native.List.values; + +}; + +Elm.Native.Port = {}; +Elm.Native.Port.make = function(localRuntime) { + + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Port = localRuntime.Native.Port || {}; + if (localRuntime.Native.Port.values) + { + return localRuntime.Native.Port.values; + } + + var NS; + var Utils = Elm.Native.Utils.make(localRuntime); + + + // INBOUND + + function inbound(name, type, converter) + { + if (!localRuntime.argsTracker[name]) + { + throw new Error( + "Port Error:\n" + + "No argument was given for the port named '" + name + "' with type:\n\n" + + " " + type.split('\n').join('\n ') + "\n\n" + + "You need to provide an initial value!\n\n" + + "Find out more about ports here " + ); + } + var arg = localRuntime.argsTracker[name]; + arg.used = true; + + return jsToElm(name, type, converter, arg.value); + } + + + function inboundSignal(name, type, converter) + { + var initialValue = inbound(name, type, converter); + + if (!NS) + { + NS = Elm.Native.Signal.make(localRuntime); + } + var signal = NS.input('inbound-port-' + name, initialValue); + + function send(jsValue) + { + var elmValue = jsToElm(name, type, converter, jsValue); + setTimeout(function() { + localRuntime.notify(signal.id, elmValue); + }, 0); + } + + localRuntime.ports[name] = { send: send }; + + return signal; + } + + + function jsToElm(name, type, converter, value) + { + try + { + return converter(value); + } + catch(e) + { + throw new Error( + "Port Error:\n" + + "Regarding the port named '" + name + "' with type:\n\n" + + " " + type.split('\n').join('\n ') + "\n\n" + + "You just sent the value:\n\n" + + " " + JSON.stringify(value) + "\n\n" + + "but it cannot be converted to the necessary type.\n" + + e.message + ); + } + } + + + // OUTBOUND + + function outbound(name, converter, elmValue) + { + localRuntime.ports[name] = converter(elmValue); + } + + + function outboundSignal(name, converter, signal) + { + var subscribers = []; + + function subscribe(handler) + { + subscribers.push(handler); + } + function unsubscribe(handler) + { + subscribers.pop(subscribers.indexOf(handler)); + } + + function notify(elmValue) + { + var jsValue = converter(elmValue); + var len = subscribers.length; + for (var i = 0; i < len; ++i) + { + subscribers[i](jsValue); + } + } + + if (!NS) + { + NS = Elm.Native.Signal.make(localRuntime); + } + NS.output('outbound-port-' + name, notify, signal); + + localRuntime.ports[name] = { + subscribe: subscribe, + unsubscribe: unsubscribe + }; + + return signal; + } + + + return localRuntime.Native.Port.values = { + inbound: inbound, + outbound: outbound, + inboundSignal: inboundSignal, + outboundSignal: outboundSignal + }; +}; + +Elm.Native.Reads = {}; +Elm.Native.Reads.make = function(localRuntime) { + + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Reads = localRuntime.Native.Reads || {}; + if(localRuntime.Native.Reads.values) { + return localRuntime.Native.Reads.values; + } + + var Maybe = Elm.Maybe.make(localRuntime); + + function readInt(str) { + var number = Number(str); + return isNaN(number) || str === '' + ? Maybe.Nothing + : Maybe.Just(number); + } + + return localRuntime.Native.Reads.values = { + readInt: readInt + }; +}; + + +if (!Elm.fullscreen) { + + (function() { + 'use strict'; + + var Display = { + FULLSCREEN: 0, + COMPONENT: 1, + NONE: 2 + }; + + Elm.fullscreen = function(module, args) + { + var container = document.createElement('div'); + document.body.appendChild(container); + return init(Display.FULLSCREEN, container, module, args || {}); + }; + + Elm.embed = function(module, container, args) + { + var tag = container.tagName; + if (tag !== 'DIV') + { + throw new Error('Elm.node must be given a DIV, not a ' + tag + '.'); + } + return init(Display.COMPONENT, container, module, args || {}); + }; + + Elm.worker = function(module, args) + { + return init(Display.NONE, {}, module, args || {}); + }; + + function init(display, container, module, args, moduleToReplace) + { + // defining state needed for an instance of the Elm RTS + var inputs = []; + + /* OFFSET + * Elm's time traveling debugger lets you pause time. This means + * "now" may be shifted a bit into the past. By wrapping Date.now() + * we can manage this. + */ + var timer = { + programStart: Date.now(), + now: function() + { + return Date.now(); + } + }; + + var updateInProgress = false; + function notify(id, v) + { + if (updateInProgress) + { + throw new Error( + 'The notify function has been called synchronously!\n' + + 'This can lead to frames being dropped.\n' + + 'Definitely report this to \n'); + } + updateInProgress = true; + var timestep = timer.now(); + for (var i = inputs.length; i--; ) + { + inputs[i].notify(timestep, id, v); + } + updateInProgress = false; + } + function setTimeout(func, delay) + { + return window.setTimeout(func, delay); + } + + var listeners = []; + function addListener(relevantInputs, domNode, eventName, func) + { + domNode.addEventListener(eventName, func); + var listener = { + relevantInputs: relevantInputs, + domNode: domNode, + eventName: eventName, + func: func + }; + listeners.push(listener); + } + + var argsTracker = {}; + for (var name in args) + { + argsTracker[name] = { + value: args[name], + used: false + }; + } + + // create the actual RTS. Any impure modules will attach themselves to this + // object. This permits many Elm programs to be embedded per document. + var elm = { + notify: notify, + setTimeout: setTimeout, + node: container, + addListener: addListener, + inputs: inputs, + timer: timer, + argsTracker: argsTracker, + ports: {}, + + isFullscreen: function() { return display === Display.FULLSCREEN; }, + isEmbed: function() { return display === Display.COMPONENT; }, + isWorker: function() { return display === Display.NONE; } + }; + + function swap(newModule) + { + removeListeners(listeners); + var div = document.createElement('div'); + var newElm = init(display, div, newModule, args, elm); + inputs = []; + // elm.swap = newElm.swap; + return newElm; + } + + function dispose() + { + removeListeners(listeners); + inputs = []; + } + + var Module = {}; + try + { + Module = module.make(elm); + checkInputs(elm); + } + catch (error) + { + if (typeof container.appendChild == 'undefined') + { + console.log(error.message); + } + else + { + container.appendChild(errorNode(error.message)); + } + throw error; + } + + if (display !== Display.NONE) + { + var graphicsNode = initGraphics(elm, Module); + } + + var rootNode = { kids: inputs }; + trimDeadNodes(rootNode); + inputs = rootNode.kids; + filterListeners(inputs, listeners); + + addReceivers(elm.ports); + + if (typeof moduleToReplace !== 'undefined') + { + hotSwap(moduleToReplace, elm); + + // rerender scene if graphics are enabled. + if (typeof graphicsNode !== 'undefined') + { + graphicsNode.notify(0, true, 0); + } + } + + return { + swap: swap, + ports: elm.ports, + dispose: dispose + }; + }; + + function checkInputs(elm) + { + var argsTracker = elm.argsTracker; + for (var name in argsTracker) + { + if (!argsTracker[name].used) + { + throw new Error( + "Port Error:\nYou provided an argument named '" + name + + "' but there is no corresponding port!\n\n" + + "Maybe add a port '" + name + "' to your Elm module?\n" + + "Maybe remove the '" + name + "' argument from your initialization code in JS?" + ); + } + } + } + + function errorNode(message) + { + var code = document.createElement('code'); + + var lines = message.split('\n'); + code.appendChild(document.createTextNode(lines[0])); + code.appendChild(document.createElement('br')); + code.appendChild(document.createElement('br')); + for (var i = 1; i < lines.length; ++i) + { + code.appendChild(document.createTextNode('\u00A0 \u00A0 ' + lines[i].replace(/ /g, '\u00A0 '))); + code.appendChild(document.createElement('br')); + } + code.appendChild(document.createElement('br')); + code.appendChild(document.createTextNode("Open the developer console for more details.")); + return code; + } + + + //// FILTER SIGNALS //// + + // TODO: move this code into the signal module and create a function + // Signal.initializeGraph that actually instantiates everything. + + function filterListeners(inputs, listeners) + { + loop: + for (var i = listeners.length; i--; ) + { + var listener = listeners[i]; + for (var j = inputs.length; j--; ) + { + if (listener.relevantInputs.indexOf(inputs[j].id) >= 0) + { + continue loop; + } + } + listener.domNode.removeEventListener(listener.eventName, listener.func); + } + } + + function removeListeners(listeners) + { + for (var i = listeners.length; i--; ) + { + var listener = listeners[i]; + listener.domNode.removeEventListener(listener.eventName, listener.func); + } + } + + // add receivers for built-in ports if they are defined + function addReceivers(ports) + { + if ('title' in ports) + { + if (typeof ports.title === 'string') + { + document.title = ports.title; + } + else + { + ports.title.subscribe(function(v) { document.title = v; }); + } + } + if ('redirect' in ports) + { + ports.redirect.subscribe(function(v) { + if (v.length > 0) + { + window.location = v; + } + }); + } + } + + + // returns a boolean representing whether the node is alive or not. + function trimDeadNodes(node) + { + if (node.isOutput) + { + return true; + } + + var liveKids = []; + for (var i = node.kids.length; i--; ) + { + var kid = node.kids[i]; + if (trimDeadNodes(kid)) + { + liveKids.push(kid); + } + } + node.kids = liveKids; + + return liveKids.length > 0; + } + + + //// RENDERING //// + + function initGraphics(elm, Module) + { + if (!('main' in Module)) + { + throw new Error("'main' is missing! What do I display?!"); + } + + var signalGraph = Module.main; + + // make sure the signal graph is actually a signal & extract the visual model + if (!('notify' in signalGraph)) + { + signalGraph = Elm.Signal.make(elm).constant(signalGraph); + } + var initialScene = signalGraph.value; + + // Figure out what the render functions should be + var render; + var update; + if (initialScene.props) + { + var Element = Elm.Native.Graphics.Element.make(elm); + render = Element.render; + update = Element.updateAndReplace; + } + else + { + var VirtualDom = Elm.Native.VirtualDom.make(elm); + render = VirtualDom.render; + update = VirtualDom.updateAndReplace; + } + + // Add the initialScene to the DOM + var container = elm.node; + var node = render(initialScene); + while (container.firstChild) + { + container.removeChild(container.firstChild); + } + container.appendChild(node); + + var _requestAnimationFrame = + typeof requestAnimationFrame !== 'undefined' + ? requestAnimationFrame + : function(cb) { setTimeout(cb, 1000/60); } + ; + + // domUpdate is called whenever the main Signal changes. + // + // domUpdate and drawCallback implement a small state machine in order + // to schedule only 1 draw per animation frame. This enforces that + // once draw has been called, it will not be called again until the + // next frame. + // + // drawCallback is scheduled whenever + // 1. The state transitions from PENDING_REQUEST to EXTRA_REQUEST, or + // 2. The state transitions from NO_REQUEST to PENDING_REQUEST + // + // Invariants: + // 1. In the NO_REQUEST state, there is never a scheduled drawCallback. + // 2. In the PENDING_REQUEST and EXTRA_REQUEST states, there is always exactly 1 + // scheduled drawCallback. + var NO_REQUEST = 0; + var PENDING_REQUEST = 1; + var EXTRA_REQUEST = 2; + var state = NO_REQUEST; + var savedScene = initialScene; + var scheduledScene = initialScene; + + function domUpdate(newScene) + { + scheduledScene = newScene; + + switch (state) + { + case NO_REQUEST: + _requestAnimationFrame(drawCallback); + state = PENDING_REQUEST; + return; + case PENDING_REQUEST: + state = PENDING_REQUEST; + return; + case EXTRA_REQUEST: + state = PENDING_REQUEST; + return; + } + } + + function drawCallback() + { + switch (state) + { + case NO_REQUEST: + // This state should not be possible. How can there be no + // request, yet somehow we are actively fulfilling a + // request? + throw new Error( + "Unexpected draw callback.\n" + + "Please report this to ." + ); + + case PENDING_REQUEST: + // At this point, we do not *know* that another frame is + // needed, but we make an extra request to rAF just in + // case. It's possible to drop a frame if rAF is called + // too late, so we just do it preemptively. + _requestAnimationFrame(drawCallback); + state = EXTRA_REQUEST; + + // There's also stuff we definitely need to draw. + draw(); + return; + + case EXTRA_REQUEST: + // Turns out the extra request was not needed, so we will + // stop calling rAF. No reason to call it all the time if + // no one needs it. + state = NO_REQUEST; + return; + } + } + + function draw() + { + update(elm.node.firstChild, savedScene, scheduledScene); + if (elm.Native.Window) + { + elm.Native.Window.values.resizeIfNeeded(); + } + savedScene = scheduledScene; + } + + var renderer = Elm.Native.Signal.make(elm).output('main', domUpdate, signalGraph); + + // must check for resize after 'renderer' is created so + // that changes show up. + if (elm.Native.Window) + { + elm.Native.Window.values.resizeIfNeeded(); + } + + return renderer; + } + + //// HOT SWAPPING //// + + // Returns boolean indicating if the swap was successful. + // Requires that the two signal graphs have exactly the same + // structure. + function hotSwap(from, to) + { + function similar(nodeOld,nodeNew) + { + if (nodeOld.id !== nodeNew.id) + { + return false; + } + if (nodeOld.isOutput) + { + return nodeNew.isOutput; + } + return nodeOld.kids.length === nodeNew.kids.length; + } + function swap(nodeOld,nodeNew) + { + nodeNew.value = nodeOld.value; + return true; + } + var canSwap = depthFirstTraversals(similar, from.inputs, to.inputs); + if (canSwap) + { + depthFirstTraversals(swap, from.inputs, to.inputs); + } + from.node.parentNode.replaceChild(to.node, from.node); + + return canSwap; + } + + // Returns false if the node operation f ever fails. + function depthFirstTraversals(f, queueOld, queueNew) + { + if (queueOld.length !== queueNew.length) + { + return false; + } + queueOld = queueOld.slice(0); + queueNew = queueNew.slice(0); + + var seen = []; + while (queueOld.length > 0 && queueNew.length > 0) + { + var nodeOld = queueOld.pop(); + var nodeNew = queueNew.pop(); + if (seen.indexOf(nodeOld.id) < 0) + { + if (!f(nodeOld, nodeNew)) + { + return false; + } + queueOld = queueOld.concat(nodeOld.kids || []); + queueNew = queueNew.concat(nodeNew.kids || []); + seen.push(nodeOld.id); + } + } + return true; + } + }()); + + function F2(fun) + { + function wrapper(a) { return function(b) { return fun(a,b) } } + wrapper.arity = 2; + wrapper.func = fun; + return wrapper; + } + + function F3(fun) + { + function wrapper(a) { + return function(b) { return function(c) { return fun(a,b,c) }} + } + wrapper.arity = 3; + wrapper.func = fun; + return wrapper; + } + + function F4(fun) + { + function wrapper(a) { return function(b) { return function(c) { + return function(d) { return fun(a,b,c,d) }}} + } + wrapper.arity = 4; + wrapper.func = fun; + return wrapper; + } + + function F5(fun) + { + function wrapper(a) { return function(b) { return function(c) { + return function(d) { return function(e) { return fun(a,b,c,d,e) }}}} + } + wrapper.arity = 5; + wrapper.func = fun; + return wrapper; + } + + function F6(fun) + { + function wrapper(a) { return function(b) { return function(c) { + return function(d) { return function(e) { return function(f) { + return fun(a,b,c,d,e,f) }}}}} + } + wrapper.arity = 6; + wrapper.func = fun; + return wrapper; + } + + function F7(fun) + { + function wrapper(a) { return function(b) { return function(c) { + return function(d) { return function(e) { return function(f) { + return function(g) { return fun(a,b,c,d,e,f,g) }}}}}} + } + wrapper.arity = 7; + wrapper.func = fun; + return wrapper; + } + + function F8(fun) + { + function wrapper(a) { return function(b) { return function(c) { + return function(d) { return function(e) { return function(f) { + return function(g) { return function(h) { + return fun(a,b,c,d,e,f,g,h)}}}}}}} + } + wrapper.arity = 8; + wrapper.func = fun; + return wrapper; + } + + function F9(fun) + { + function wrapper(a) { return function(b) { return function(c) { + return function(d) { return function(e) { return function(f) { + return function(g) { return function(h) { return function(i) { + return fun(a,b,c,d,e,f,g,h,i) }}}}}}}} + } + wrapper.arity = 9; + wrapper.func = fun; + return wrapper; + } + + function A2(fun,a,b) + { + return fun.arity === 2 + ? fun.func(a,b) + : fun(a)(b); + } + function A3(fun,a,b,c) + { + return fun.arity === 3 + ? fun.func(a,b,c) + : fun(a)(b)(c); + } + function A4(fun,a,b,c,d) + { + return fun.arity === 4 + ? fun.func(a,b,c,d) + : fun(a)(b)(c)(d); + } + function A5(fun,a,b,c,d,e) + { + return fun.arity === 5 + ? fun.func(a,b,c,d,e) + : fun(a)(b)(c)(d)(e); + } + function A6(fun,a,b,c,d,e,f) + { + return fun.arity === 6 + ? fun.func(a,b,c,d,e,f) + : fun(a)(b)(c)(d)(e)(f); + } + function A7(fun,a,b,c,d,e,f,g) + { + return fun.arity === 7 + ? fun.func(a,b,c,d,e,f,g) + : fun(a)(b)(c)(d)(e)(f)(g); + } + function A8(fun,a,b,c,d,e,f,g,h) + { + return fun.arity === 8 + ? fun.func(a,b,c,d,e,f,g,h) + : fun(a)(b)(c)(d)(e)(f)(g)(h); + } + function A9(fun,a,b,c,d,e,f,g,h,i) + { + return fun.arity === 9 + ? fun.func(a,b,c,d,e,f,g,h,i) + : fun(a)(b)(c)(d)(e)(f)(g)(h)(i); + } +} + +Elm.Native.Show = {}; +Elm.Native.Show.make = function(localRuntime) { + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Show = localRuntime.Native.Show || {}; + if (localRuntime.Native.Show.values) + { + return localRuntime.Native.Show.values; + } + + var _Array; + var Dict; + var List; + var Utils = Elm.Native.Utils.make(localRuntime); + + var toString = function(v) + { + var type = typeof v; + if (type === "function") + { + var name = v.func ? v.func.name : v.name; + return ''; + } + else if (type === "boolean") + { + return v ? "True" : "False"; + } + else if (type === "number") + { + return v + ""; + } + else if ((v instanceof String) && v.isChar) + { + return "'" + addSlashes(v, true) + "'"; + } + else if (type === "string") + { + return '"' + addSlashes(v, false) + '"'; + } + else if (type === "object" && '_' in v && probablyPublic(v)) + { + var output = []; + for (var k in v._) + { + for (var i = v._[k].length; i--; ) + { + output.push(k + " = " + toString(v._[k][i])); + } + } + for (var k in v) + { + if (k === '_') continue; + output.push(k + " = " + toString(v[k])); + } + if (output.length === 0) + { + return "{}"; + } + return "{ " + output.join(", ") + " }"; + } + else if (type === "object" && 'ctor' in v) + { + if (v.ctor.substring(0,6) === "_Tuple") + { + var output = []; + for (var k in v) + { + if (k === 'ctor') continue; + output.push(toString(v[k])); + } + return "(" + output.join(",") + ")"; + } + else if (v.ctor === "_Array") + { + if (!_Array) + { + _Array = Elm.Array.make(localRuntime); + } + var list = _Array.toList(v); + return "Array.fromList " + toString(list); + } + else if (v.ctor === "::") + { + var output = '[' + toString(v._0); + v = v._1; + while (v.ctor === "::") + { + output += "," + toString(v._0); + v = v._1; + } + return output + ']'; + } + else if (v.ctor === "[]") + { + return "[]"; + } + else if (v.ctor === "RBNode" || v.ctor === "RBEmpty") + { + if (!Dict) + { + Dict = Elm.Dict.make(localRuntime); + } + if (!List) + { + List = Elm.List.make(localRuntime); + } + var list = Dict.toList(v); + var name = "Dict"; + if (list.ctor === "::" && list._0._1.ctor === "_Tuple0") + { + name = "Set"; + list = A2(List.map, function(x){return x._0}, list); + } + return name + ".fromList " + toString(list); + } + else if (v.ctor.slice(0,5) === "Text:") + { + return '' + } + else + { + var output = ""; + for (var i in v) + { + if (i === 'ctor') continue; + var str = toString(v[i]); + var parenless = str[0] === '{' || str[0] === '<' || str.indexOf(' ') < 0; + output += ' ' + (parenless ? str : '(' + str + ')'); + } + return v.ctor + output; + } + } + if (type === 'object' && 'notify' in v && 'id' in v) + { + return ''; + } + return ""; + }; + + function addSlashes(str, isChar) + { + var s = str.replace(/\\/g, '\\\\') + .replace(/\n/g, '\\n') + .replace(/\t/g, '\\t') + .replace(/\r/g, '\\r') + .replace(/\v/g, '\\v') + .replace(/\0/g, '\\0'); + if (isChar) + { + return s.replace(/\'/g, "\\'") + } + else + { + return s.replace(/\"/g, '\\"'); + } + } + + function probablyPublic(v) + { + var keys = Object.keys(v); + var len = keys.length; + if (len === 3 + && 'props' in v + && 'element' in v) + { + return false; + } + else if (len === 5 + && 'horizontal' in v + && 'vertical' in v + && 'x' in v + && 'y' in v) + { + return false; + } + else if (len === 7 + && 'theta' in v + && 'scale' in v + && 'x' in v + && 'y' in v + && 'alpha' in v + && 'form' in v) + { + return false; + } + return true; + } + + return localRuntime.Native.Show.values = { + toString: toString + }; +}; + +Elm.Native.Signal = {}; +Elm.Native.Signal.make = function(localRuntime) { + + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Signal = localRuntime.Native.Signal || {}; + if (localRuntime.Native.Signal.values) + { + return localRuntime.Native.Signal.values; + } + + + var Task = Elm.Native.Task.make(localRuntime); + var Utils = Elm.Native.Utils.make(localRuntime); + + + function broadcastToKids(node, timestamp, update) + { + var kids = node.kids; + for (var i = kids.length; i--; ) + { + kids[i].notify(timestamp, update, node.id); + } + } + + + // INPUT + + function input(name, base) + { + var node = { + id: Utils.guid(), + name: 'input-' + name, + value: base, + parents: [], + kids: [] + }; + + node.notify = function(timestamp, targetId, value) { + var update = targetId === node.id; + if (update) + { + node.value = value; + } + broadcastToKids(node, timestamp, update); + return update; + }; + + localRuntime.inputs.push(node); + + return node; + } + + function constant(value) + { + return input('constant', value); + } + + + // MAILBOX + + function mailbox(base) + { + var signal = input('mailbox', base); + + function send(value) { + return Task.asyncFunction(function(callback) { + localRuntime.setTimeout(function() { + localRuntime.notify(signal.id, value); + }, 0); + callback(Task.succeed(Utils.Tuple0)); + }); + } + + return { + _: {}, + signal: signal, + address: { + ctor: 'Address', + _0: send + } + }; + } + + function sendMessage(message) + { + Task.perform(message._0); + } + + + // OUTPUT + + function output(name, handler, parent) + { + var node = { + id: Utils.guid(), + name: 'output-' + name, + parents: [parent], + isOutput: true + }; + + node.notify = function(timestamp, parentUpdate, parentID) + { + if (parentUpdate) + { + handler(parent.value); + } + }; + + parent.kids.push(node); + + return node; + } + + + // MAP + + function mapMany(refreshValue, args) + { + var node = { + id: Utils.guid(), + name: 'map' + args.length, + value: refreshValue(), + parents: args, + kids: [] + }; + + var numberOfParents = args.length; + var count = 0; + var update = false; + + node.notify = function(timestamp, parentUpdate, parentID) + { + ++count; + + update = update || parentUpdate; + + if (count === numberOfParents) + { + if (update) + { + node.value = refreshValue(); + } + broadcastToKids(node, timestamp, update); + update = false; + count = 0; + } + }; + + for (var i = numberOfParents; i--; ) + { + args[i].kids.push(node); + } + + return node; + } + + + function map(func, a) + { + function refreshValue() + { + return func(a.value); + } + return mapMany(refreshValue, [a]); + } + + + function map2(func, a, b) + { + function refreshValue() + { + return A2( func, a.value, b.value ); + } + return mapMany(refreshValue, [a,b]); + } + + + function map3(func, a, b, c) + { + function refreshValue() + { + return A3( func, a.value, b.value, c.value ); + } + return mapMany(refreshValue, [a,b,c]); + } + + + function map4(func, a, b, c, d) + { + function refreshValue() + { + return A4( func, a.value, b.value, c.value, d.value ); + } + return mapMany(refreshValue, [a,b,c,d]); + } + + + function map5(func, a, b, c, d, e) + { + function refreshValue() + { + return A5( func, a.value, b.value, c.value, d.value, e.value ); + } + return mapMany(refreshValue, [a,b,c,d,e]); + } + + + + // FOLD + + function foldp(update, state, signal) + { + var node = { + id: Utils.guid(), + name: 'foldp', + parents: [signal], + kids: [], + value: state + }; + + node.notify = function(timestamp, parentUpdate, parentID) + { + if (parentUpdate) + { + node.value = A2( update, signal.value, node.value ); + } + broadcastToKids(node, timestamp, parentUpdate); + }; + + signal.kids.push(node); + + return node; + } + + + // TIME + + function timestamp(signal) + { + var node = { + id: Utils.guid(), + name: 'timestamp', + value: Utils.Tuple2(localRuntime.timer.programStart, signal.value), + parents: [signal], + kids: [] + }; + + node.notify = function(timestamp, parentUpdate, parentID) + { + if (parentUpdate) + { + node.value = Utils.Tuple2(timestamp, signal.value); + } + broadcastToKids(node, timestamp, parentUpdate); + }; + + signal.kids.push(node); + + return node; + } + + + function delay(time, signal) + { + var delayed = input('delay-input-' + time, signal.value); + + function handler(value) + { + setTimeout(function() { + localRuntime.notify(delayed.id, value); + }, time); + } + + output('delay-output-' + time, handler, signal); + + return delayed; + } + + + // MERGING + + function genericMerge(tieBreaker, leftStream, rightStream) + { + var node = { + id: Utils.guid(), + name: 'merge', + value: A2(tieBreaker, leftStream.value, rightStream.value), + parents: [leftStream, rightStream], + kids: [] + }; + + var left = { touched: false, update: false, value: null }; + var right = { touched: false, update: false, value: null }; + + node.notify = function(timestamp, parentUpdate, parentID) + { + if (parentID === leftStream.id) + { + left.touched = true; + left.update = parentUpdate; + left.value = leftStream.value; + } + if (parentID === rightStream.id) + { + right.touched = true; + right.update = parentUpdate; + right.value = rightStream.value; + } + + if (left.touched && right.touched) + { + var update = false; + if (left.update && right.update) + { + node.value = A2(tieBreaker, left.value, right.value); + update = true; + } + else if (left.update) + { + node.value = left.value; + update = true; + } + else if (right.update) + { + node.value = right.value; + update = true; + } + left.touched = false; + right.touched = false; + + broadcastToKids(node, timestamp, update); + } + }; + + leftStream.kids.push(node); + rightStream.kids.push(node); + + return node; + } + + + // FILTERING + + function filterMap(toMaybe, base, signal) + { + var maybe = toMaybe(signal.value); + var node = { + id: Utils.guid(), + name: 'filterMap', + value: maybe.ctor === 'Nothing' ? base : maybe._0, + parents: [signal], + kids: [] + }; + + node.notify = function(timestamp, parentUpdate, parentID) + { + var update = false; + if (parentUpdate) + { + var maybe = toMaybe(signal.value); + if (maybe.ctor === 'Just') + { + update = true; + node.value = maybe._0; + } + } + broadcastToKids(node, timestamp, update); + }; + + signal.kids.push(node); + + return node; + } + + + // SAMPLING + + function sampleOn(ticker, signal) + { + var node = { + id: Utils.guid(), + name: 'sampleOn', + value: signal.value, + parents: [ticker, signal], + kids: [] + }; + + var signalTouch = false; + var tickerTouch = false; + var tickerUpdate = false; + + node.notify = function(timestamp, parentUpdate, parentID) + { + if (parentID === ticker.id) + { + tickerTouch = true; + tickerUpdate = parentUpdate; + } + if (parentID === signal.id) + { + signalTouch = true; + } + + if (tickerTouch && signalTouch) + { + if (tickerUpdate) + { + node.value = signal.value; + } + tickerTouch = false; + signalTouch = false; + + broadcastToKids(node, timestamp, tickerUpdate); + } + }; + + ticker.kids.push(node); + signal.kids.push(node); + + return node; + } + + + // DROP REPEATS + + function dropRepeats(signal) + { + var node = { + id: Utils.guid(), + name: 'dropRepeats', + value: signal.value, + parents: [signal], + kids: [] + }; + + node.notify = function(timestamp, parentUpdate, parentID) + { + var update = false; + if (parentUpdate && !Utils.eq(node.value, signal.value)) + { + node.value = signal.value; + update = true; + } + broadcastToKids(node, timestamp, update); + }; + + signal.kids.push(node); + + return node; + } + + + return localRuntime.Native.Signal.values = { + input: input, + constant: constant, + mailbox: mailbox, + sendMessage: sendMessage, + output: output, + map: F2(map), + map2: F3(map2), + map3: F4(map3), + map4: F5(map4), + map5: F6(map5), + foldp: F3(foldp), + genericMerge: F3(genericMerge), + filterMap: F3(filterMap), + sampleOn: F2(sampleOn), + dropRepeats: dropRepeats, + timestamp: timestamp, + delay: F2(delay) + }; +}; + +Elm.Native.String = {}; +Elm.Native.String.make = function(localRuntime) { + + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.String = localRuntime.Native.String || {}; + if (localRuntime.Native.String.values) + { + return localRuntime.Native.String.values; + } + if ('values' in Elm.Native.String) + { + return localRuntime.Native.String.values = Elm.Native.String.values; + } + + + var Char = Elm.Char.make(localRuntime); + var List = Elm.Native.List.make(localRuntime); + var Maybe = Elm.Maybe.make(localRuntime); + var Result = Elm.Result.make(localRuntime); + var Utils = Elm.Native.Utils.make(localRuntime); + + function isEmpty(str) + { + return str.length === 0; + } + function cons(chr,str) + { + return chr + str; + } + function uncons(str) + { + var hd; + return (hd = str[0]) + ? Maybe.Just(Utils.Tuple2(Utils.chr(hd), str.slice(1))) + : Maybe.Nothing; + } + function append(a,b) + { + return a + b; + } + function concat(strs) + { + return List.toArray(strs).join(''); + } + function length(str) + { + return str.length; + } + function map(f,str) + { + var out = str.split(''); + for (var i = out.length; i--; ) + { + out[i] = f(Utils.chr(out[i])); + } + return out.join(''); + } + function filter(pred,str) + { + return str.split('').map(Utils.chr).filter(pred).join(''); + } + function reverse(str) + { + return str.split('').reverse().join(''); + } + function foldl(f,b,str) + { + var len = str.length; + for (var i = 0; i < len; ++i) + { + b = A2(f, Utils.chr(str[i]), b); + } + return b; + } + function foldr(f,b,str) + { + for (var i = str.length; i--; ) + { + b = A2(f, Utils.chr(str[i]), b); + } + return b; + } + + function split(sep, str) + { + return List.fromArray(str.split(sep)); + } + function join(sep, strs) + { + return List.toArray(strs).join(sep); + } + function repeat(n, str) + { + var result = ''; + while (n > 0) + { + if (n & 1) + { + result += str; + } + n >>= 1, str += str; + } + return result; + } + + function slice(start, end, str) + { + return str.slice(start,end); + } + function left(n, str) + { + return n < 1 ? "" : str.slice(0,n); + } + function right(n, str) + { + return n < 1 ? "" : str.slice(-n); + } + function dropLeft(n, str) + { + return n < 1 ? str : str.slice(n); + } + function dropRight(n, str) + { + return n < 1 ? str : str.slice(0,-n); + } + + function pad(n,chr,str) + { + var half = (n - str.length) / 2; + return repeat(Math.ceil(half),chr) + str + repeat(half|0,chr); + } + function padRight(n,chr,str) + { + return str + repeat(n - str.length, chr); + } + function padLeft(n,chr,str) + { + return repeat(n - str.length, chr) + str; + } + + function trim(str) + { + return str.trim(); + } + function trimLeft(str) + { + return str.trimLeft(); + } + function trimRight(str) + { + return str.trimRight(); + } + + function words(str) + { + return List.fromArray(str.trim().split(/\s+/g)); + } + function lines(str) + { + return List.fromArray(str.split(/\r\n|\r|\n/g)); + } + + function toUpper(str) + { + return str.toUpperCase(); + } + function toLower(str) + { + return str.toLowerCase(); + } + + function any(pred, str) + { + for (var i = str.length; i--; ) + { + if (pred(Utils.chr(str[i]))) + { + return true; + } + } + return false; + } + function all(pred, str) + { + for (var i = str.length; i--; ) + { + if (!pred(Utils.chr(str[i]))) + { + return false; + } + } + return true; + } + + function contains(sub, str) + { + return str.indexOf(sub) > -1; + } + function startsWith(sub, str) + { + return str.indexOf(sub) === 0; + } + function endsWith(sub, str) + { + return str.length >= sub.length && + str.lastIndexOf(sub) === str.length - sub.length; + } + function indexes(sub, str) + { + var subLen = sub.length; + var i = 0; + var is = []; + while ((i = str.indexOf(sub, i)) > -1) + { + is.push(i); + i = i + subLen; + } + return List.fromArray(is); + } + + function toInt(s) + { + var len = s.length; + if (len === 0) + { + return Result.Err("could not convert string '" + s + "' to an Int" ); + } + var start = 0; + if (s[0] == '-') + { + if (len === 1) + { + return Result.Err("could not convert string '" + s + "' to an Int" ); + } + start = 1; + } + for (var i = start; i < len; ++i) + { + if (!Char.isDigit(s[i])) + { + return Result.Err("could not convert string '" + s + "' to an Int" ); + } + } + return Result.Ok(parseInt(s, 10)); + } + + function toFloat(s) + { + var len = s.length; + if (len === 0) + { + return Result.Err("could not convert string '" + s + "' to a Float" ); + } + var start = 0; + if (s[0] == '-') + { + if (len === 1) + { + return Result.Err("could not convert string '" + s + "' to a Float" ); + } + start = 1; + } + var dotCount = 0; + for (var i = start; i < len; ++i) + { + if (Char.isDigit(s[i])) + { + continue; + } + if (s[i] === '.') + { + dotCount += 1; + if (dotCount <= 1) + { + continue; + } + } + return Result.Err("could not convert string '" + s + "' to a Float" ); + } + return Result.Ok(parseFloat(s)); + } + + function toList(str) + { + return List.fromArray(str.split('').map(Utils.chr)); + } + function fromList(chars) + { + return List.toArray(chars).join(''); + } + + return Elm.Native.String.values = { + isEmpty: isEmpty, + cons: F2(cons), + uncons: uncons, + append: F2(append), + concat: concat, + length: length, + map: F2(map), + filter: F2(filter), + reverse: reverse, + foldl: F3(foldl), + foldr: F3(foldr), + + split: F2(split), + join: F2(join), + repeat: F2(repeat), + + slice: F3(slice), + left: F2(left), + right: F2(right), + dropLeft: F2(dropLeft), + dropRight: F2(dropRight), + + pad: F3(pad), + padLeft: F3(padLeft), + padRight: F3(padRight), + + trim: trim, + trimLeft: trimLeft, + trimRight: trimRight, + + words: words, + lines: lines, + + toUpper: toUpper, + toLower: toLower, + + any: F2(any), + all: F2(all), + + contains: F2(contains), + startsWith: F2(startsWith), + endsWith: F2(endsWith), + indexes: F2(indexes), + + toInt: toInt, + toFloat: toFloat, + toList: toList, + fromList: fromList + }; +}; + +Elm.Native.Task = {}; +Elm.Native.Task.make = function(localRuntime) { + + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Task = localRuntime.Native.Task || {}; + if (localRuntime.Native.Task.values) + { + return localRuntime.Native.Task.values; + } + + var Result = Elm.Result.make(localRuntime); + var Signal; + var Utils = Elm.Native.Utils.make(localRuntime); + + + // CONSTRUCTORS + + function succeed(value) + { + return { + tag: 'Succeed', + value: value + }; + } + + function fail(error) + { + return { + tag: 'Fail', + value: error + }; + } + + function asyncFunction(func) + { + return { + tag: 'Async', + asyncFunction: func + }; + } + + function andThen(task, callback) + { + return { + tag: 'AndThen', + task: task, + callback: callback + }; + } + + function catch_(task, callback) + { + return { + tag: 'Catch', + task: task, + callback: callback + }; + } + + + // RUNNER + + function perform(task) { + runTask({ task: task }, function() {}); + } + + function performSignal(name, signal) + { + var workQueue = []; + + function onComplete() + { + workQueue.shift(); + + setTimeout(function() { + if (workQueue.length > 0) + { + runTask(workQueue[0], onComplete); + } + }, 0); + } + + function register(task) + { + var root = { task: task }; + workQueue.push(root); + if (workQueue.length === 1) + { + runTask(root, onComplete); + } + } + + if (!Signal) + { + Signal = Elm.Native.Signal.make(localRuntime); + } + Signal.output('perform-tasks-' + name, register, signal); + + register(signal.value); + + return signal; + } + + function mark(status, task) + { + return { status: status, task: task }; + } + + function runTask(root, onComplete) + { + var result = mark('runnable', root.task); + while (result.status === 'runnable') + { + result = stepTask(onComplete, root, result.task); + } + + if (result.status === 'done') + { + root.task = result.task; + onComplete(); + } + + if (result.status === 'blocked') + { + root.task = result.task; + } + } + + function stepTask(onComplete, root, task) + { + var tag = task.tag; + + if (tag === 'Succeed' || tag === 'Fail') + { + return mark('done', task); + } + + if (tag === 'Async') + { + var placeHolder = {}; + var couldBeSync = true; + var wasSync = false; + + task.asyncFunction(function(result) { + placeHolder.tag = result.tag; + placeHolder.value = result.value; + if (couldBeSync) + { + wasSync = true; + } + else + { + runTask(root, onComplete); + } + }); + couldBeSync = false; + return mark(wasSync ? 'done' : 'blocked', placeHolder); + } + + if (tag === 'AndThen' || tag === 'Catch') + { + var result = mark('runnable', task.task); + while (result.status === 'runnable') + { + result = stepTask(onComplete, root, result.task); + } + + if (result.status === 'done') + { + var activeTask = result.task; + var activeTag = activeTask.tag; + + var succeedChain = activeTag === 'Succeed' && tag === 'AndThen'; + var failChain = activeTag === 'Fail' && tag === 'Catch'; + + return (succeedChain || failChain) + ? mark('runnable', task.callback(activeTask.value)) + : mark('runnable', activeTask); + } + if (result.status === 'blocked') + { + return mark('blocked', { + tag: tag, + task: result.task, + callback: task.callback + }); + } + } + } + + + // THREADS + + function sleep(time) { + return asyncFunction(function(callback) { + setTimeout(function() { + callback(succeed(Utils.Tuple0)); + }, time); + }); + } + + function spawn(task) { + return asyncFunction(function(callback) { + var id = setTimeout(function() { + perform(task); + }, 0); + callback(succeed(id)); + }); + } + + + return localRuntime.Native.Task.values = { + succeed: succeed, + fail: fail, + asyncFunction: asyncFunction, + andThen: F2(andThen), + catch_: F2(catch_), + perform: perform, + performSignal: performSignal, + spawn: spawn, + sleep: sleep + }; +}; + +Elm.Native.Text = {}; +Elm.Native.Text.make = function(localRuntime) { + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Text = localRuntime.Native.Text || {}; + if (localRuntime.Native.Text.values) + { + return localRuntime.Native.Text.values; + } + + var toCss = Elm.Native.Color.make(localRuntime).toCss; + var List = Elm.Native.List.make(localRuntime); + + + // CONSTRUCTORS + + function fromString(str) + { + return { + ctor: 'Text:Text', + _0: str + }; + } + + function append(a, b) + { + return { + ctor: 'Text:Append', + _0: a, + _1: b + }; + } + + function addMeta(field, value, text) + { + var newProps = {}; + var newText = { + ctor: 'Text:Meta', + _0: newProps, + _1: text + }; + + if (text.ctor === 'Text:Meta') + { + newText._1 = text._1; + var props = text._0; + for (var i = metaKeys.length; i--; ) + { + var key = metaKeys[i]; + var val = props[key]; + if (val) + { + newProps[key] = val; + } + } + } + newProps[field] = value; + return newText; + } + + var metaKeys = [ + 'font-size', + 'font-family', + 'font-style', + 'font-weight', + 'href', + 'text-decoration', + 'color' + ]; + + + // conversions from Elm values to CSS + + function toTypefaces(list) + { + var typefaces = List.toArray(list); + for (var i = typefaces.length; i--; ) + { + var typeface = typefaces[i]; + if (typeface.indexOf(' ') > -1) + { + typefaces[i] = "'" + typeface + "'"; + } + } + return typefaces.join(','); + } + + function toLine(line) + { + var ctor = line.ctor; + return ctor === 'Under' + ? 'underline' + : ctor === 'Over' + ? 'overline' + : 'line-through'; + } + + // setting styles of Text + + function style(style, text) + { + var newText = addMeta('color', toCss(style.color), text); + var props = newText._0; + + if (style.typeface.ctor !== '[]') + { + props['font-family'] = toTypefaces(style.typeface); + } + if (style.height.ctor !== "Nothing") + { + props['font-size'] = style.height._0 + 'px'; + } + if (style.bold) + { + props['font-weight'] = 'bold'; + } + if (style.italic) + { + props['font-style'] = 'italic'; + } + if (style.line.ctor !== 'Nothing') + { + props['text-decoration'] = toLine(style.line._0); + } + return newText; + } + + function height(px, text) + { + return addMeta('font-size', px + 'px', text); + } + + function typeface(names, text) + { + return addMeta('font-family', toTypefaces(names), text); + } + + function monospace(text) + { + return addMeta('font-family', 'monospace', text); + } + + function italic(text) + { + return addMeta('font-style', 'italic', text); + } + + function bold(text) + { + return addMeta('font-weight', 'bold', text); + } + + function link(href, text) + { + return addMeta('href', href, text); + } + + function line(line, text) + { + return addMeta('text-decoration', toLine(line), text); + } + + function color(color, text) + { + return addMeta('color', toCss(color), text);; + } + + + // RENDER + + function renderHtml(text) + { + var tag = text.ctor; + if (tag === 'Text:Append') + { + return renderHtml(text._0) + renderHtml(text._1); + } + if (tag === 'Text:Text') + { + return properEscape(text._0); + } + if (tag === 'Text:Meta') + { + return renderMeta(text._0, renderHtml(text._1)); + } + } + + function renderMeta(metas, string) + { + var href = metas['href']; + if (href) + { + string = '' + string + ''; + } + var styles = ''; + for (var key in metas) + { + if (key === 'href') + { + continue; + } + styles += key + ':' + metas[key] + ';'; + } + if (styles) + { + string = '' + string + ''; + } + return string; + } + + function properEscape(str) + { + if (str.length == 0) + { + return str; + } + str = str //.replace(/&/g, "&") + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(//g, ">"); + var arr = str.split('\n'); + for (var i = arr.length; i--; ) + { + arr[i] = makeSpaces(arr[i]); + } + return arr.join('
'); + } + + function makeSpaces(s) + { + if (s.length == 0) + { + return s; + } + var arr = s.split(''); + if (arr[0] == ' ') + { + arr[0] = " " + } + for (var i = arr.length; --i; ) + { + if (arr[i][0] == ' ' && arr[i-1] == ' ') + { + arr[i-1] = arr[i-1] + arr[i]; + arr[i] = ''; + } + } + for (var i = arr.length; i--; ) + { + if (arr[i].length > 1 && arr[i][0] == ' ') + { + var spaces = arr[i].split(''); + for (var j = spaces.length - 2; j >= 0; j -= 2) + { + spaces[j] = ' '; + } + arr[i] = spaces.join(''); + } + } + arr = arr.join(''); + if (arr[arr.length-1] === " ") + { + return arr.slice(0,-1) + ' '; + } + return arr; + } + + + return localRuntime.Native.Text.values = { + fromString: fromString, + append: F2(append), + + height: F2(height), + italic: italic, + bold: bold, + line: F2(line), + monospace: monospace, + typeface: F2(typeface), + color: F2(color), + link: F2(link), + style: F2(style), + + toTypefaces: toTypefaces, + toLine: toLine, + renderHtml: renderHtml + }; +}; + +Elm.Native.Time = {}; +Elm.Native.Time.make = function(localRuntime) +{ + + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Time = localRuntime.Native.Time || {}; + if (localRuntime.Native.Time.values) + { + return localRuntime.Native.Time.values; + } + + var NS = Elm.Native.Signal.make(localRuntime); + var Maybe = Elm.Maybe.make(localRuntime); + + + // FRAMES PER SECOND + + function fpsWhen(desiredFPS, isOn) + { + var msPerFrame = 1000 / desiredFPS; + var ticker = NS.input('fps-' + desiredFPS, null); + + function notifyTicker() + { + localRuntime.notify(ticker.id, null); + } + + function firstArg(x, y) + { + return x; + } + + // input fires either when isOn changes, or when ticker fires. + // Its value is a tuple with the current timestamp, and the state of isOn + var input = NS.timestamp(A3(NS.map2, F2(firstArg), NS.dropRepeats(isOn), ticker)); + + var initialState = { + isOn: false, + time: localRuntime.timer.programStart, + delta: 0 + }; + + var timeoutId; + + function update(input,state) + { + var currentTime = input._0; + var isOn = input._1; + var wasOn = state.isOn; + var previousTime = state.time; + + if (isOn) + { + timeoutId = localRuntime.setTimeout(notifyTicker, msPerFrame); + } + else if (wasOn) + { + clearTimeout(timeoutId); + } + + return { + isOn: isOn, + time: currentTime, + delta: (isOn && !wasOn) ? 0 : currentTime - previousTime + }; + } + + return A2( + NS.map, + function(state) { return state.delta; }, + A3(NS.foldp, F2(update), update(input.value,initialState), input) + ); + } + + + // EVERY + + function every(t) + { + var ticker = NS.input('every-' + t, null); + function tellTime() + { + localRuntime.notify(ticker.id, null); + } + var clock = A2( NS.map, fst, NS.timestamp(ticker) ); + setInterval(tellTime, t); + return clock; + } + + + function fst(pair) + { + return pair._0; + } + + + function read(s) + { + var t = Date.parse(s); + return isNaN(t) ? Maybe.Nothing : Maybe.Just(t); + } + + return localRuntime.Native.Time.values = { + fpsWhen: F2(fpsWhen), + every: every, + toDate: function(t) { return new window.Date(t); }, + read: read + }; + +}; + +Elm.Native.Transform2D = {}; +Elm.Native.Transform2D.make = function(localRuntime) { + + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Transform2D = localRuntime.Native.Transform2D || {}; + if (localRuntime.Native.Transform2D.values) + { + return localRuntime.Native.Transform2D.values; + } + + var A; + if (typeof Float32Array === 'undefined') + { + A = function(arr) + { + this.length = arr.length; + this[0] = arr[0]; + this[1] = arr[1]; + this[2] = arr[2]; + this[3] = arr[3]; + this[4] = arr[4]; + this[5] = arr[5]; + }; + } + else + { + A = Float32Array; + } + + // layout of matrix in an array is + // + // | m11 m12 dx | + // | m21 m22 dy | + // | 0 0 1 | + // + // new A([ m11, m12, dx, m21, m22, dy ]) + + var identity = new A([1,0,0,0,1,0]); + function matrix(m11, m12, m21, m22, dx, dy) + { + return new A([m11, m12, dx, m21, m22, dy]); + } + + function rotation(t) + { + var c = Math.cos(t); + var s = Math.sin(t); + return new A([c, -s, 0, s, c, 0]); + } + + function rotate(t,m) + { + var c = Math.cos(t); + var s = Math.sin(t); + var m11 = m[0], m12 = m[1], m21 = m[3], m22 = m[4]; + return new A([m11*c + m12*s, -m11*s + m12*c, m[2], + m21*c + m22*s, -m21*s + m22*c, m[5]]); + } + /* + function move(xy,m) { + var x = xy._0; + var y = xy._1; + var m11 = m[0], m12 = m[1], m21 = m[3], m22 = m[4]; + return new A([m11, m12, m11*x + m12*y + m[2], + m21, m22, m21*x + m22*y + m[5]]); + } + function scale(s,m) { return new A([m[0]*s, m[1]*s, m[2], m[3]*s, m[4]*s, m[5]]); } + function scaleX(x,m) { return new A([m[0]*x, m[1], m[2], m[3]*x, m[4], m[5]]); } + function scaleY(y,m) { return new A([m[0], m[1]*y, m[2], m[3], m[4]*y, m[5]]); } + function reflectX(m) { return new A([-m[0], m[1], m[2], -m[3], m[4], m[5]]); } + function reflectY(m) { return new A([m[0], -m[1], m[2], m[3], -m[4], m[5]]); } + + function transform(m11, m21, m12, m22, mdx, mdy, n) { + var n11 = n[0], n12 = n[1], n21 = n[3], n22 = n[4], ndx = n[2], ndy = n[5]; + return new A([m11*n11 + m12*n21, + m11*n12 + m12*n22, + m11*ndx + m12*ndy + mdx, + m21*n11 + m22*n21, + m21*n12 + m22*n22, + m21*ndx + m22*ndy + mdy]); + } + */ + function multiply(m, n) + { + var m11 = m[0], m12 = m[1], m21 = m[3], m22 = m[4], mdx = m[2], mdy = m[5]; + var n11 = n[0], n12 = n[1], n21 = n[3], n22 = n[4], ndx = n[2], ndy = n[5]; + return new A([m11*n11 + m12*n21, + m11*n12 + m12*n22, + m11*ndx + m12*ndy + mdx, + m21*n11 + m22*n21, + m21*n12 + m22*n22, + m21*ndx + m22*ndy + mdy]); + } + + return localRuntime.Native.Transform2D.values = { + identity:identity, + matrix:F6(matrix), + rotation:rotation, + multiply:F2(multiply) + /* + transform:F7(transform), + rotate:F2(rotate), + move:F2(move), + scale:F2(scale), + scaleX:F2(scaleX), + scaleY:F2(scaleY), + reflectX:reflectX, + reflectY:reflectY + */ + }; + +}; + +Elm.Native = Elm.Native || {}; +Elm.Native.Utils = {}; +Elm.Native.Utils.make = function(localRuntime) { + + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Utils = localRuntime.Native.Utils || {}; + if (localRuntime.Native.Utils.values) + { + return localRuntime.Native.Utils.values; + } + + function eq(l,r) + { + var stack = [{'x': l, 'y': r}] + while (stack.length > 0) + { + var front = stack.pop(); + var x = front.x; + var y = front.y; + if (x === y) + { + continue; + } + if (typeof x === "object") + { + var c = 0; + for (var i in x) + { + ++c; + if (i in y) + { + if (i !== 'ctor') + { + stack.push({ 'x': x[i], 'y': y[i] }); + } + } + else + { + return false; + } + } + if ('ctor' in x) + { + stack.push({'x': x.ctor, 'y': y.ctor}); + } + if (c !== Object.keys(y).length) + { + return false; + } + } + else if (typeof x === 'function') + { + throw new Error('Equality error: general function equality is ' + + 'undecidable, and therefore, unsupported'); + } + else + { + return false; + } + } + return true; + } + + // code in Generate/JavaScript.hs depends on the particular + // integer values assigned to LT, EQ, and GT + var LT = -1, EQ = 0, GT = 1, ord = ['LT','EQ','GT']; + + function compare(x,y) + { + return { + ctor: ord[cmp(x,y)+1] + }; + } + + function cmp(x,y) { + var ord; + if (typeof x !== 'object') + { + return x === y ? EQ : x < y ? LT : GT; + } + else if (x.isChar) + { + var a = x.toString(); + var b = y.toString(); + return a === b + ? EQ + : a < b + ? LT + : GT; + } + else if (x.ctor === "::" || x.ctor === "[]") + { + while (true) + { + if (x.ctor === "[]" && y.ctor === "[]") + { + return EQ; + } + if (x.ctor !== y.ctor) + { + return x.ctor === '[]' ? LT : GT; + } + ord = cmp(x._0, y._0); + if (ord !== EQ) + { + return ord; + } + x = x._1; + y = y._1; + } + } + else if (x.ctor.slice(0,6) === '_Tuple') + { + var n = x.ctor.slice(6) - 0; + var err = 'cannot compare tuples with more than 6 elements.'; + if (n === 0) return EQ; + if (n >= 1) { ord = cmp(x._0, y._0); if (ord !== EQ) return ord; + if (n >= 2) { ord = cmp(x._1, y._1); if (ord !== EQ) return ord; + if (n >= 3) { ord = cmp(x._2, y._2); if (ord !== EQ) return ord; + if (n >= 4) { ord = cmp(x._3, y._3); if (ord !== EQ) return ord; + if (n >= 5) { ord = cmp(x._4, y._4); if (ord !== EQ) return ord; + if (n >= 6) { ord = cmp(x._5, y._5); if (ord !== EQ) return ord; + if (n >= 7) throw new Error('Comparison error: ' + err); } } } } } } + return EQ; + } + else + { + throw new Error('Comparison error: comparison is only defined on ints, ' + + 'floats, times, chars, strings, lists of comparable values, ' + + 'and tuples of comparable values.'); + } + } + + + var Tuple0 = { + ctor: "_Tuple0" + }; + + function Tuple2(x,y) + { + return { + ctor: "_Tuple2", + _0: x, + _1: y + }; + } + + function chr(c) + { + var x = new String(c); + x.isChar = true; + return x; + } + + function txt(str) + { + var t = new String(str); + t.text = true; + return t; + } + + var count = 0; + function guid(_) + { + return count++ + } + + function copy(oldRecord) + { + var newRecord = {}; + for (var key in oldRecord) + { + var value = key === '_' + ? copy(oldRecord._) + : oldRecord[key]; + newRecord[key] = value; + } + return newRecord; + } + + function remove(key, oldRecord) + { + var record = copy(oldRecord); + if (key in record._) + { + record[key] = record._[key][0]; + record._[key] = record._[key].slice(1); + if (record._[key].length === 0) + { + delete record._[key]; + } + } + else + { + delete record[key]; + } + return record; + } + + function replace(keyValuePairs, oldRecord) + { + var record = copy(oldRecord); + for (var i = keyValuePairs.length; i--; ) + { + var pair = keyValuePairs[i]; + record[pair[0]] = pair[1]; + } + return record; + } + + function insert(key, value, oldRecord) + { + var newRecord = copy(oldRecord); + if (key in newRecord) + { + var values = newRecord._[key]; + var copiedValues = values ? values.slice(0) : []; + newRecord._[key] = [newRecord[key]].concat(copiedValues); + } + newRecord[key] = value; + return newRecord; + } + + function getXY(e) + { + var posx = 0; + var posy = 0; + if (e.pageX || e.pageY) + { + posx = e.pageX; + posy = e.pageY; + } + else if (e.clientX || e.clientY) + { + posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; + posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; + } + + if (localRuntime.isEmbed()) + { + var rect = localRuntime.node.getBoundingClientRect(); + var relx = rect.left + document.body.scrollLeft + document.documentElement.scrollLeft; + var rely = rect.top + document.body.scrollTop + document.documentElement.scrollTop; + // TODO: figure out if there is a way to avoid rounding here + posx = posx - Math.round(relx) - localRuntime.node.clientLeft; + posy = posy - Math.round(rely) - localRuntime.node.clientTop; + } + return Tuple2(posx, posy); + } + + + //// LIST STUFF //// + + var Nil = { ctor:'[]' }; + + function Cons(hd,tl) + { + return { + ctor: "::", + _0: hd, + _1: tl + }; + } + + function append(xs,ys) + { + // append Strings + if (typeof xs === "string") + { + return xs + ys; + } + + // append Text + if (xs.ctor.slice(0,5) === 'Text:') + { + return { + ctor: 'Text:Append', + _0: xs, + _1: ys + }; + } + + + + // append Lists + if (xs.ctor === '[]') + { + return ys; + } + var root = Cons(xs._0, Nil); + var curr = root; + xs = xs._1; + while (xs.ctor !== '[]') + { + curr._1 = Cons(xs._0, Nil); + xs = xs._1; + curr = curr._1; + } + curr._1 = ys; + return root; + } + + //// RUNTIME ERRORS //// + + function indent(lines) + { + return '\n' + lines.join('\n'); + } + + function badCase(moduleName, span) + { + var msg = indent([ + 'Non-exhaustive pattern match in case-expression.', + 'Make sure your patterns cover every case!' + ]); + throw new Error('Runtime error in module ' + moduleName + ' (' + span + ')' + msg); + } + + function badIf(moduleName, span) + { + var msg = indent([ + 'Non-exhaustive pattern match in multi-way-if expression.', + 'It is best to use \'otherwise\' as the last branch of multi-way-if.' + ]); + throw new Error('Runtime error in module ' + moduleName + ' (' + span + ')' + msg); + } + + + function badPort(expected, received) + { + var msg = indent([ + 'Expecting ' + expected + ' but was given ', + JSON.stringify(received) + ]); + throw new Error('Runtime error when sending values through a port.' + msg); + } + + + return localRuntime.Native.Utils.values = { + eq: eq, + cmp: cmp, + compare: F2(compare), + Tuple0: Tuple0, + Tuple2: Tuple2, + chr: chr, + txt: txt, + copy: copy, + remove: remove, + replace: replace, + insert: insert, + guid: guid, + getXY: getXY, + + Nil: Nil, + Cons: Cons, + append: F2(append), + + badCase: badCase, + badIf: badIf, + badPort: badPort + }; +}; + +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o> 0 + currentItem = indices[currentIndex] + + if (minIndex === maxIndex) { + return currentItem >= left && currentItem <= right + } else if (currentItem < left) { + minIndex = currentIndex + 1 + } else if (currentItem > right) { + maxIndex = currentIndex - 1 + } else { + return true + } + } + + return false; +} + +function ascending(a, b) { + return a > b ? 1 : -1 +} + +},{}],8:[function(require,module,exports){ +var applyProperties = require("./apply-properties") + +var isWidget = require("../vnode/is-widget.js") +var VPatch = require("../vnode/vpatch.js") + +var render = require("./create-element") +var updateWidget = require("./update-widget") + +module.exports = applyPatch + +function applyPatch(vpatch, domNode, renderOptions) { + var type = vpatch.type + var vNode = vpatch.vNode + var patch = vpatch.patch + + switch (type) { + case VPatch.REMOVE: + return removeNode(domNode, vNode) + case VPatch.INSERT: + return insertNode(domNode, patch, renderOptions) + case VPatch.VTEXT: + return stringPatch(domNode, vNode, patch, renderOptions) + case VPatch.WIDGET: + return widgetPatch(domNode, vNode, patch, renderOptions) + case VPatch.VNODE: + return vNodePatch(domNode, vNode, patch, renderOptions) + case VPatch.ORDER: + reorderChildren(domNode, patch) + return domNode + case VPatch.PROPS: + applyProperties(domNode, patch, vNode.properties) + return domNode + case VPatch.THUNK: + return replaceRoot(domNode, + renderOptions.patch(domNode, patch, renderOptions)) + default: + return domNode + } +} + +function removeNode(domNode, vNode) { + var parentNode = domNode.parentNode + + if (parentNode) { + parentNode.removeChild(domNode) + } + + destroyWidget(domNode, vNode); + + return null +} + +function insertNode(parentNode, vNode, renderOptions) { + var newNode = render(vNode, renderOptions) + + if (parentNode) { + parentNode.appendChild(newNode) + } + + return parentNode +} + +function stringPatch(domNode, leftVNode, vText, renderOptions) { + var newNode + + if (domNode.nodeType === 3) { + domNode.replaceData(0, domNode.length, vText.text) + newNode = domNode + } else { + var parentNode = domNode.parentNode + newNode = render(vText, renderOptions) + + if (parentNode && newNode !== domNode) { + parentNode.replaceChild(newNode, domNode) + } + } + + return newNode +} + +function widgetPatch(domNode, leftVNode, widget, renderOptions) { + var updating = updateWidget(leftVNode, widget) + var newNode + + if (updating) { + newNode = widget.update(leftVNode, domNode) || domNode + } else { + newNode = render(widget, renderOptions) + } + + var parentNode = domNode.parentNode + + if (parentNode && newNode !== domNode) { + parentNode.replaceChild(newNode, domNode) + } + + if (!updating) { + destroyWidget(domNode, leftVNode) + } + + return newNode +} + +function vNodePatch(domNode, leftVNode, vNode, renderOptions) { + var parentNode = domNode.parentNode + var newNode = render(vNode, renderOptions) + + if (parentNode && newNode !== domNode) { + parentNode.replaceChild(newNode, domNode) + } + + return newNode +} + +function destroyWidget(domNode, w) { + if (typeof w.destroy === "function" && isWidget(w)) { + w.destroy(domNode) + } +} + +function reorderChildren(domNode, moves) { + var childNodes = domNode.childNodes + var keyMap = {} + var node + var remove + var insert + + for (var i = 0; i < moves.removes.length; i++) { + remove = moves.removes[i] + node = childNodes[remove.from] + if (remove.key) { + keyMap[remove.key] = node + } + domNode.removeChild(node) + } + + var length = childNodes.length + for (var j = 0; j < moves.inserts.length; j++) { + insert = moves.inserts[j] + node = keyMap[insert.key] + // this is the weirdest bug i've ever seen in webkit + domNode.insertBefore(node, insert.to >= length++ ? null : childNodes[insert.to]) + } +} + +function replaceRoot(oldRoot, newRoot) { + if (oldRoot && newRoot && oldRoot !== newRoot && oldRoot.parentNode) { + oldRoot.parentNode.replaceChild(newRoot, oldRoot) + } + + return newRoot; +} + +},{"../vnode/is-widget.js":16,"../vnode/vpatch.js":19,"./apply-properties":5,"./create-element":6,"./update-widget":10}],9:[function(require,module,exports){ +var document = require("global/document") +var isArray = require("x-is-array") + +var domIndex = require("./dom-index") +var patchOp = require("./patch-op") +module.exports = patch + +function patch(rootNode, patches) { + return patchRecursive(rootNode, patches) +} + +function patchRecursive(rootNode, patches, renderOptions) { + var indices = patchIndices(patches) + + if (indices.length === 0) { + return rootNode + } + + var index = domIndex(rootNode, patches.a, indices) + var ownerDocument = rootNode.ownerDocument + + if (!renderOptions) { + renderOptions = { patch: patchRecursive } + if (ownerDocument !== document) { + renderOptions.document = ownerDocument + } + } + + for (var i = 0; i < indices.length; i++) { + var nodeIndex = indices[i] + rootNode = applyPatch(rootNode, + index[nodeIndex], + patches[nodeIndex], + renderOptions) + } + + return rootNode +} + +function applyPatch(rootNode, domNode, patchList, renderOptions) { + if (!domNode) { + return rootNode + } + + var newNode + + if (isArray(patchList)) { + for (var i = 0; i < patchList.length; i++) { + newNode = patchOp(patchList[i], domNode, renderOptions) + + if (domNode === rootNode) { + rootNode = newNode + } + } + } else { + newNode = patchOp(patchList, domNode, renderOptions) + + if (domNode === rootNode) { + rootNode = newNode + } + } + + return rootNode +} + +function patchIndices(patches) { + var indices = [] + + for (var key in patches) { + if (key !== "a") { + indices.push(Number(key)) + } + } + + return indices +} + +},{"./dom-index":7,"./patch-op":8,"global/document":2,"x-is-array":4}],10:[function(require,module,exports){ +var isWidget = require("../vnode/is-widget.js") + +module.exports = updateWidget + +function updateWidget(a, b) { + if (isWidget(a) && isWidget(b)) { + if ("name" in a && "name" in b) { + return a.id === b.id + } else { + return a.init === b.init + } + } + + return false +} + +},{"../vnode/is-widget.js":16}],11:[function(require,module,exports){ +var isVNode = require("./is-vnode") +var isVText = require("./is-vtext") +var isWidget = require("./is-widget") +var isThunk = require("./is-thunk") + +module.exports = handleThunk + +function handleThunk(a, b) { + var renderedA = a + var renderedB = b + + if (isThunk(b)) { + renderedB = renderThunk(b, a) + } + + if (isThunk(a)) { + renderedA = renderThunk(a, null) + } + + return { + a: renderedA, + b: renderedB + } +} + +function renderThunk(thunk, previous) { + var renderedThunk = thunk.vnode + + if (!renderedThunk) { + renderedThunk = thunk.vnode = thunk.render(previous) + } + + if (!(isVNode(renderedThunk) || + isVText(renderedThunk) || + isWidget(renderedThunk))) { + throw new Error("thunk did not return a valid node"); + } + + return renderedThunk +} + +},{"./is-thunk":12,"./is-vnode":14,"./is-vtext":15,"./is-widget":16}],12:[function(require,module,exports){ +module.exports = isThunk + +function isThunk(t) { + return t && t.type === "Thunk" +} + +},{}],13:[function(require,module,exports){ +module.exports = isHook + +function isHook(hook) { + return hook && + (typeof hook.hook === "function" && !hook.hasOwnProperty("hook") || + typeof hook.unhook === "function" && !hook.hasOwnProperty("unhook")) +} + +},{}],14:[function(require,module,exports){ +var version = require("./version") + +module.exports = isVirtualNode + +function isVirtualNode(x) { + return x && x.type === "VirtualNode" && x.version === version +} + +},{"./version":17}],15:[function(require,module,exports){ +var version = require("./version") + +module.exports = isVirtualText + +function isVirtualText(x) { + return x && x.type === "VirtualText" && x.version === version +} + +},{"./version":17}],16:[function(require,module,exports){ +module.exports = isWidget + +function isWidget(w) { + return w && w.type === "Widget" +} + +},{}],17:[function(require,module,exports){ +module.exports = "2" + +},{}],18:[function(require,module,exports){ +var version = require("./version") +var isVNode = require("./is-vnode") +var isWidget = require("./is-widget") +var isThunk = require("./is-thunk") +var isVHook = require("./is-vhook") + +module.exports = VirtualNode + +var noProperties = {} +var noChildren = [] + +function VirtualNode(tagName, properties, children, key, namespace) { + this.tagName = tagName + this.properties = properties || noProperties + this.children = children || noChildren + this.key = key != null ? String(key) : undefined + this.namespace = (typeof namespace === "string") ? namespace : null + + var count = (children && children.length) || 0 + var descendants = 0 + var hasWidgets = false + var hasThunks = false + var descendantHooks = false + var hooks + + for (var propName in properties) { + if (properties.hasOwnProperty(propName)) { + var property = properties[propName] + if (isVHook(property) && property.unhook) { + if (!hooks) { + hooks = {} + } + + hooks[propName] = property + } + } + } + + for (var i = 0; i < count; i++) { + var child = children[i] + if (isVNode(child)) { + descendants += child.count || 0 + + if (!hasWidgets && child.hasWidgets) { + hasWidgets = true + } + + if (!hasThunks && child.hasThunks) { + hasThunks = true + } + + if (!descendantHooks && (child.hooks || child.descendantHooks)) { + descendantHooks = true + } + } else if (!hasWidgets && isWidget(child)) { + if (typeof child.destroy === "function") { + hasWidgets = true + } + } else if (!hasThunks && isThunk(child)) { + hasThunks = true; + } + } + + this.count = count + descendants + this.hasWidgets = hasWidgets + this.hasThunks = hasThunks + this.hooks = hooks + this.descendantHooks = descendantHooks +} + +VirtualNode.prototype.version = version +VirtualNode.prototype.type = "VirtualNode" + +},{"./is-thunk":12,"./is-vhook":13,"./is-vnode":14,"./is-widget":16,"./version":17}],19:[function(require,module,exports){ +var version = require("./version") + +VirtualPatch.NONE = 0 +VirtualPatch.VTEXT = 1 +VirtualPatch.VNODE = 2 +VirtualPatch.WIDGET = 3 +VirtualPatch.PROPS = 4 +VirtualPatch.ORDER = 5 +VirtualPatch.INSERT = 6 +VirtualPatch.REMOVE = 7 +VirtualPatch.THUNK = 8 + +module.exports = VirtualPatch + +function VirtualPatch(type, vNode, patch) { + this.type = Number(type) + this.vNode = vNode + this.patch = patch +} + +VirtualPatch.prototype.version = version +VirtualPatch.prototype.type = "VirtualPatch" + +},{"./version":17}],20:[function(require,module,exports){ +var version = require("./version") + +module.exports = VirtualText + +function VirtualText(text) { + this.text = String(text) +} + +VirtualText.prototype.version = version +VirtualText.prototype.type = "VirtualText" + +},{"./version":17}],21:[function(require,module,exports){ +var isObject = require("is-object") +var isHook = require("../vnode/is-vhook") + +module.exports = diffProps + +function diffProps(a, b) { + var diff + + for (var aKey in a) { + if (!(aKey in b)) { + diff = diff || {} + diff[aKey] = undefined + } + + var aValue = a[aKey] + var bValue = b[aKey] + + if (aValue === bValue) { + continue + } else if (isObject(aValue) && isObject(bValue)) { + if (getPrototype(bValue) !== getPrototype(aValue)) { + diff = diff || {} + diff[aKey] = bValue + } else if (isHook(bValue)) { + diff = diff || {} + diff[aKey] = bValue + } else { + var objectDiff = diffProps(aValue, bValue) + if (objectDiff) { + diff = diff || {} + diff[aKey] = objectDiff + } + } + } else { + diff = diff || {} + diff[aKey] = bValue + } + } + + for (var bKey in b) { + if (!(bKey in a)) { + diff = diff || {} + diff[bKey] = b[bKey] + } + } + + return diff +} + +function getPrototype(value) { + if (Object.getPrototypeOf) { + return Object.getPrototypeOf(value) + } else if (value.__proto__) { + return value.__proto__ + } else if (value.constructor) { + return value.constructor.prototype + } +} + +},{"../vnode/is-vhook":13,"is-object":3}],22:[function(require,module,exports){ +var isArray = require("x-is-array") + +var VPatch = require("../vnode/vpatch") +var isVNode = require("../vnode/is-vnode") +var isVText = require("../vnode/is-vtext") +var isWidget = require("../vnode/is-widget") +var isThunk = require("../vnode/is-thunk") +var handleThunk = require("../vnode/handle-thunk") + +var diffProps = require("./diff-props") + +module.exports = diff + +function diff(a, b) { + var patch = { a: a } + walk(a, b, patch, 0) + return patch +} + +function walk(a, b, patch, index) { + if (a === b) { + return + } + + var apply = patch[index] + var applyClear = false + + if (isThunk(a) || isThunk(b)) { + thunks(a, b, patch, index) + } else if (b == null) { + + // If a is a widget we will add a remove patch for it + // Otherwise any child widgets/hooks must be destroyed. + // This prevents adding two remove patches for a widget. + if (!isWidget(a)) { + clearState(a, patch, index) + apply = patch[index] + } + + apply = appendPatch(apply, new VPatch(VPatch.REMOVE, a, b)) + } else if (isVNode(b)) { + if (isVNode(a)) { + if (a.tagName === b.tagName && + a.namespace === b.namespace && + a.key === b.key) { + var propsPatch = diffProps(a.properties, b.properties) + if (propsPatch) { + apply = appendPatch(apply, + new VPatch(VPatch.PROPS, a, propsPatch)) + } + apply = diffChildren(a, b, patch, apply, index) + } else { + apply = appendPatch(apply, new VPatch(VPatch.VNODE, a, b)) + applyClear = true + } + } else { + apply = appendPatch(apply, new VPatch(VPatch.VNODE, a, b)) + applyClear = true + } + } else if (isVText(b)) { + if (!isVText(a)) { + apply = appendPatch(apply, new VPatch(VPatch.VTEXT, a, b)) + applyClear = true + } else if (a.text !== b.text) { + apply = appendPatch(apply, new VPatch(VPatch.VTEXT, a, b)) + } + } else if (isWidget(b)) { + if (!isWidget(a)) { + applyClear = true + } + + apply = appendPatch(apply, new VPatch(VPatch.WIDGET, a, b)) + } + + if (apply) { + patch[index] = apply + } + + if (applyClear) { + clearState(a, patch, index) + } +} + +function diffChildren(a, b, patch, apply, index) { + var aChildren = a.children + var orderedSet = reorder(aChildren, b.children) + var bChildren = orderedSet.children + + var aLen = aChildren.length + var bLen = bChildren.length + var len = aLen > bLen ? aLen : bLen + + for (var i = 0; i < len; i++) { + var leftNode = aChildren[i] + var rightNode = bChildren[i] + index += 1 + + if (!leftNode) { + if (rightNode) { + // Excess nodes in b need to be added + apply = appendPatch(apply, + new VPatch(VPatch.INSERT, null, rightNode)) + } + } else { + walk(leftNode, rightNode, patch, index) + } + + if (isVNode(leftNode) && leftNode.count) { + index += leftNode.count + } + } + + if (orderedSet.moves) { + // Reorder nodes last + apply = appendPatch(apply, new VPatch( + VPatch.ORDER, + a, + orderedSet.moves + )) + } + + return apply +} + +function clearState(vNode, patch, index) { + // TODO: Make this a single walk, not two + unhook(vNode, patch, index) + destroyWidgets(vNode, patch, index) +} + +// Patch records for all destroyed widgets must be added because we need +// a DOM node reference for the destroy function +function destroyWidgets(vNode, patch, index) { + if (isWidget(vNode)) { + if (typeof vNode.destroy === "function") { + patch[index] = appendPatch( + patch[index], + new VPatch(VPatch.REMOVE, vNode, null) + ) + } + } else if (isVNode(vNode) && (vNode.hasWidgets || vNode.hasThunks)) { + var children = vNode.children + var len = children.length + for (var i = 0; i < len; i++) { + var child = children[i] + index += 1 + + destroyWidgets(child, patch, index) + + if (isVNode(child) && child.count) { + index += child.count + } + } + } else if (isThunk(vNode)) { + thunks(vNode, null, patch, index) + } +} + +// Create a sub-patch for thunks +function thunks(a, b, patch, index) { + var nodes = handleThunk(a, b) + var thunkPatch = diff(nodes.a, nodes.b) + if (hasPatches(thunkPatch)) { + patch[index] = new VPatch(VPatch.THUNK, null, thunkPatch) + } +} + +function hasPatches(patch) { + for (var index in patch) { + if (index !== "a") { + return true + } + } + + return false +} + +// Execute hooks when two nodes are identical +function unhook(vNode, patch, index) { + if (isVNode(vNode)) { + if (vNode.hooks) { + patch[index] = appendPatch( + patch[index], + new VPatch( + VPatch.PROPS, + vNode, + undefinedKeys(vNode.hooks) + ) + ) + } + + if (vNode.descendantHooks || vNode.hasThunks) { + var children = vNode.children + var len = children.length + for (var i = 0; i < len; i++) { + var child = children[i] + index += 1 + + unhook(child, patch, index) + + if (isVNode(child) && child.count) { + index += child.count + } + } + } + } else if (isThunk(vNode)) { + thunks(vNode, null, patch, index) + } +} + +function undefinedKeys(obj) { + var result = {} + + for (var key in obj) { + result[key] = undefined + } + + return result +} + +// List diff, naive left to right reordering +function reorder(aChildren, bChildren) { + // O(M) time, O(M) memory + var bChildIndex = keyIndex(bChildren) + var bKeys = bChildIndex.keys + var bFree = bChildIndex.free + + if (bFree.length === bChildren.length) { + return { + children: bChildren, + moves: null + } + } + + // O(N) time, O(N) memory + var aChildIndex = keyIndex(aChildren) + var aKeys = aChildIndex.keys + var aFree = aChildIndex.free + + if (aFree.length === aChildren.length) { + return { + children: bChildren, + moves: null + } + } + + // O(MAX(N, M)) memory + var newChildren = [] + + var freeIndex = 0 + var freeCount = bFree.length + var deletedItems = 0 + + // Iterate through a and match a node in b + // O(N) time, + for (var i = 0 ; i < aChildren.length; i++) { + var aItem = aChildren[i] + var itemIndex + + if (aItem.key) { + if (bKeys.hasOwnProperty(aItem.key)) { + // Match up the old keys + itemIndex = bKeys[aItem.key] + newChildren.push(bChildren[itemIndex]) + + } else { + // Remove old keyed items + itemIndex = i - deletedItems++ + newChildren.push(null) + } + } else { + // Match the item in a with the next free item in b + if (freeIndex < freeCount) { + itemIndex = bFree[freeIndex++] + newChildren.push(bChildren[itemIndex]) + } else { + // There are no free items in b to match with + // the free items in a, so the extra free nodes + // are deleted. + itemIndex = i - deletedItems++ + newChildren.push(null) + } + } + } + + var lastFreeIndex = freeIndex >= bFree.length ? + bChildren.length : + bFree[freeIndex] + + // Iterate through b and append any new keys + // O(M) time + for (var j = 0; j < bChildren.length; j++) { + var newItem = bChildren[j] + + if (newItem.key) { + if (!aKeys.hasOwnProperty(newItem.key)) { + // Add any new keyed items + // We are adding new items to the end and then sorting them + // in place. In future we should insert new items in place. + newChildren.push(newItem) + } + } else if (j >= lastFreeIndex) { + // Add any leftover non-keyed items + newChildren.push(newItem) + } + } + + var simulate = newChildren.slice() + var simulateIndex = 0 + var removes = [] + var inserts = [] + var simulateItem + + for (var k = 0; k < bChildren.length;) { + var wantedItem = bChildren[k] + simulateItem = simulate[simulateIndex] + + // remove items + while (simulateItem === null && simulate.length) { + removes.push(remove(simulate, simulateIndex, null)) + simulateItem = simulate[simulateIndex] + } + + if (!simulateItem || simulateItem.key !== wantedItem.key) { + // if we need a key in this position... + if (wantedItem.key) { + if (simulateItem && simulateItem.key) { + // if an insert doesn't put this key in place, it needs to move + if (bKeys[simulateItem.key] !== k + 1) { + removes.push(remove(simulate, simulateIndex, simulateItem.key)) + simulateItem = simulate[simulateIndex] + // if the remove didn't put the wanted item in place, we need to insert it + if (!simulateItem || simulateItem.key !== wantedItem.key) { + inserts.push({key: wantedItem.key, to: k}) + } + // items are matching, so skip ahead + else { + simulateIndex++ + } + } + else { + inserts.push({key: wantedItem.key, to: k}) + } + } + else { + inserts.push({key: wantedItem.key, to: k}) + } + k++ + } + // a key in simulate has no matching wanted key, remove it + else if (simulateItem && simulateItem.key) { + removes.push(remove(simulate, simulateIndex, simulateItem.key)) + } + } + else { + simulateIndex++ + k++ + } + } + + // remove all the remaining nodes from simulate + while(simulateIndex < simulate.length) { + simulateItem = simulate[simulateIndex] + removes.push(remove(simulate, simulateIndex, simulateItem && simulateItem.key)) + } + + // If the only moves we have are deletes then we can just + // let the delete patch remove these items. + if (removes.length === deletedItems && !inserts.length) { + return { + children: newChildren, + moves: null + } + } + + return { + children: newChildren, + moves: { + removes: removes, + inserts: inserts + } + } +} + +function remove(arr, index, key) { + arr.splice(index, 1) + + return { + from: index, + key: key + } +} + +function keyIndex(children) { + var keys = {} + var free = [] + var length = children.length + + for (var i = 0; i < length; i++) { + var child = children[i] + + if (child.key) { + keys[child.key] = i + } else { + free.push(i) + } + } + + return { + keys: keys, // A hash of key name to index + free: free, // An array of unkeyed item indices + } +} + +function appendPatch(apply, patch) { + if (apply) { + if (isArray(apply)) { + apply.push(patch) + } else { + apply = [apply, patch] + } + + return apply + } else { + return patch + } +} + +},{"../vnode/handle-thunk":11,"../vnode/is-thunk":12,"../vnode/is-vnode":14,"../vnode/is-vtext":15,"../vnode/is-widget":16,"../vnode/vpatch":19,"./diff-props":21,"x-is-array":4}],23:[function(require,module,exports){ +var VNode = require('virtual-dom/vnode/vnode'); +var VText = require('virtual-dom/vnode/vtext'); +var diff = require('virtual-dom/vtree/diff'); +var patch = require('virtual-dom/vdom/patch'); +var createElement = require('virtual-dom/create-element'); +var isHook = require("virtual-dom/vnode/is-vhook"); + + +Elm.Native.VirtualDom = {}; +Elm.Native.VirtualDom.make = function(elm) +{ + elm.Native = elm.Native || {}; + elm.Native.VirtualDom = elm.Native.VirtualDom || {}; + if (elm.Native.VirtualDom.values) + { + return elm.Native.VirtualDom.values; + } + + var Element = Elm.Native.Graphics.Element.make(elm); + var Json = Elm.Native.Json.make(elm); + var List = Elm.Native.List.make(elm); + var Signal = Elm.Native.Signal.make(elm); + var Utils = Elm.Native.Utils.make(elm); + + var ATTRIBUTE_KEY = 'UniqueNameThatOthersAreVeryUnlikelyToUse'; + + function listToProperties(list) + { + var object = {}; + while (list.ctor !== '[]') + { + var entry = list._0; + if (entry.key === ATTRIBUTE_KEY) + { + object.attributes = object.attributes || {}; + object.attributes[entry.value.attrKey] = entry.value.attrValue; + } + else + { + object[entry.key] = entry.value; + } + list = list._1; + } + return object; + } + + function node(name) + { + return F2(function(propertyList, contents) { + return makeNode(name, propertyList, contents); + }); + } + + function makeNode(name, propertyList, contents) + { + var props = listToProperties(propertyList); + + var key, namespace; + // support keys + if (props.key !== undefined) + { + key = props.key; + props.key = undefined; + } + + // support namespace + if (props.namespace !== undefined) + { + namespace = props.namespace; + props.namespace = undefined; + } + + // ensure that setting text of an input does not move the cursor + var useSoftSet = + name === 'input' + && props.value !== undefined + && !isHook(props.value); + + if (useSoftSet) + { + props.value = SoftSetHook(props.value); + } + + return new VNode(name, props, List.toArray(contents), key, namespace); + } + + function property(key, value) + { + return { + key: key, + value: value + }; + } + + function attribute(key, value) + { + return { + key: ATTRIBUTE_KEY, + value: { + attrKey: key, + attrValue: value + } + }; + } + + function on(name, options, decoder, createMessage) + { + function eventHandler(event) + { + var value = A2(Json.runDecoderValue, decoder, event); + if (value.ctor === 'Ok') + { + if (options.stopPropagation) + { + event.stopPropagation(); + } + if (options.preventDefault) + { + event.preventDefault(); + } + Signal.sendMessage(createMessage(value._0)); + } + } + return property('on' + name, eventHandler); + } + + function SoftSetHook(value) + { + if (!(this instanceof SoftSetHook)) + { + return new SoftSetHook(value); + } + + this.value = value; + } + + SoftSetHook.prototype.hook = function (node, propertyName) + { + if (node[propertyName] !== this.value) + { + node[propertyName] = this.value; + } + }; + + function text(string) + { + return new VText(string); + } + + function ElementWidget(element) + { + this.element = element; + } + + ElementWidget.prototype.type = "Widget"; + + ElementWidget.prototype.init = function init() + { + return Element.render(this.element); + }; + + ElementWidget.prototype.update = function update(previous, node) + { + return Element.update(node, previous.element, this.element); + }; + + function fromElement(element) + { + return new ElementWidget(element); + } + + function toElement(width, height, html) + { + return A3(Element.newElement, width, height, { + ctor: 'Custom', + type: 'evancz/elm-html', + render: render, + update: update, + model: html + }); + } + + function render(model) + { + var element = Element.createNode('div'); + element.appendChild(createElement(model)); + return element; + } + + function update(node, oldModel, newModel) + { + updateAndReplace(node.firstChild, oldModel, newModel); + return node; + } + + function updateAndReplace(node, oldModel, newModel) + { + var patches = diff(oldModel, newModel); + var newNode = patch(node, patches); + return newNode; + } + + function lazyRef(fn, a) + { + function thunk() + { + return fn(a); + } + return new Thunk(fn, [a], thunk); + } + + function lazyRef2(fn, a, b) + { + function thunk() + { + return A2(fn, a, b); + } + return new Thunk(fn, [a,b], thunk); + } + + function lazyRef3(fn, a, b, c) + { + function thunk() + { + return A3(fn, a, b, c); + } + return new Thunk(fn, [a,b,c], thunk); + } + + function Thunk(fn, args, thunk) + { + this.fn = fn; + this.args = args; + this.vnode = null; + this.key = undefined; + this.thunk = thunk; + } + + Thunk.prototype.type = "Thunk"; + Thunk.prototype.update = updateThunk; + Thunk.prototype.render = renderThunk; + + function shouldUpdate(current, previous) + { + if (current.fn !== previous.fn) + { + return true; + } + + // if it's the same function, we know the number of args must match + var cargs = current.args; + var pargs = previous.args; + + for (var i = cargs.length; i--; ) + { + if (cargs[i] !== pargs[i]) + { + return true; + } + } + + return false; + } + + function updateThunk(previous, domNode) + { + if (!shouldUpdate(this, previous)) + { + this.vnode = previous.vnode; + return; + } + + if (!this.vnode) + { + this.vnode = this.thunk(); + } + + var patches = diff(previous.vnode, this.vnode); + patch(domNode, patches); + } + + function renderThunk() + { + return this.thunk(); + } + + return Elm.Native.VirtualDom.values = { + node: node, + text: text, + on: F4(on), + + property: F2(property), + attribute: F2(attribute), + + lazy: F2(lazyRef), + lazy2: F3(lazyRef2), + lazy3: F4(lazyRef3), + + toElement: F3(toElement), + fromElement: fromElement, + + render: createElement, + updateAndReplace: updateAndReplace + }; +}; + +},{"virtual-dom/create-element":1,"virtual-dom/vdom/patch":9,"virtual-dom/vnode/is-vhook":13,"virtual-dom/vnode/vnode":18,"virtual-dom/vnode/vtext":20,"virtual-dom/vtree/diff":22}],24:[function(require,module,exports){ + +},{}]},{},[23]); + +Elm.Persona = Elm.Persona || {}; +Elm.Persona.make = function (_elm) { + "use strict"; + _elm.Persona = _elm.Persona || {}; + if (_elm.Persona.values) + return _elm.Persona.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Persona", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var toString = function (operation) { + switch (operation.ctor) + {case "SignIn": return "SignIn"; + case "SignOut": + return "SignOut";} + return "NoOp"; + }; + var SignOut = {ctor: "SignOut"}; + var SignIn = {ctor: "SignIn"}; + var NoOp = {ctor: "NoOp"}; + var operations = $Signal.mailbox(NoOp); + var fromString = function (str) { + switch (str) + {case "SignIn": return SignIn; + case "SignOut": return SignOut;} + return NoOp; + }; + _elm.Persona.values = {_op: _op + ,operations: operations + ,fromString: fromString + ,toString: toString + ,NoOp: NoOp + ,SignIn: SignIn + ,SignOut: SignOut}; + return _elm.Persona.values; +}; +Elm.Reads = Elm.Reads || {}; +Elm.Reads.make = function (_elm) { + "use strict"; + _elm.Reads = _elm.Reads || {}; + if (_elm.Reads.values) + return _elm.Reads.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Reads", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Native$Reads = Elm.Native.Reads.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var readInt = $Native$Reads.readInt; + _elm.Reads.values = {_op: _op + ,readInt: readInt}; + return _elm.Reads.values; +}; +Elm.Result = Elm.Result || {}; +Elm.Result.make = function (_elm) { + "use strict"; + _elm.Result = _elm.Result || {}; + if (_elm.Result.values) + return _elm.Result.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Result", + $Maybe = Elm.Maybe.make(_elm); + var toMaybe = function (result) { + switch (result.ctor) + {case "Err": + return $Maybe.Nothing; + case "Ok": + return $Maybe.Just(result._0);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var Err = function (a) { + return {ctor: "Err",_0: a}; + }; + var andThen = F2(function (result, + callback) { + switch (result.ctor) + {case "Err": + return Err(result._0); + case "Ok": + return callback(result._0);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var Ok = function (a) { + return {ctor: "Ok",_0: a}; + }; + var map = F2(function (func, + ra) { + switch (ra.ctor) + {case "Err": return Err(ra._0); + case "Ok": + return Ok(func(ra._0));} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var map2 = F3(function (func, + ra, + rb) { + var _v9 = {ctor: "_Tuple2" + ,_0: ra + ,_1: rb}; + switch (_v9.ctor) + {case "_Tuple2": + switch (_v9._0.ctor) + {case "Err": + return Err(_v9._0._0); + case "Ok": switch (_v9._1.ctor) + {case "Ok": return Ok(A2(func, + _v9._0._0, + _v9._1._0));} + break;} + switch (_v9._1.ctor) + {case "Err": + return Err(_v9._1._0);} + break;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var map3 = F4(function (func, + ra, + rb, + rc) { + var _v16 = {ctor: "_Tuple3" + ,_0: ra + ,_1: rb + ,_2: rc}; + switch (_v16.ctor) + {case "_Tuple3": + switch (_v16._0.ctor) + {case "Err": + return Err(_v16._0._0); + case "Ok": switch (_v16._1.ctor) + {case "Ok": + switch (_v16._2.ctor) + {case "Ok": return Ok(A3(func, + _v16._0._0, + _v16._1._0, + _v16._2._0));} + break;} + break;} + switch (_v16._1.ctor) + {case "Err": + return Err(_v16._1._0);} + switch (_v16._2.ctor) + {case "Err": + return Err(_v16._2._0);} + break;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var map4 = F5(function (func, + ra, + rb, + rc, + rd) { + var _v26 = {ctor: "_Tuple4" + ,_0: ra + ,_1: rb + ,_2: rc + ,_3: rd}; + switch (_v26.ctor) + {case "_Tuple4": + switch (_v26._0.ctor) + {case "Err": + return Err(_v26._0._0); + case "Ok": switch (_v26._1.ctor) + {case "Ok": + switch (_v26._2.ctor) + {case "Ok": + switch (_v26._3.ctor) + {case "Ok": return Ok(A4(func, + _v26._0._0, + _v26._1._0, + _v26._2._0, + _v26._3._0));} + break;} + break;} + break;} + switch (_v26._1.ctor) + {case "Err": + return Err(_v26._1._0);} + switch (_v26._2.ctor) + {case "Err": + return Err(_v26._2._0);} + switch (_v26._3.ctor) + {case "Err": + return Err(_v26._3._0);} + break;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var map5 = F6(function (func, + ra, + rb, + rc, + rd, + re) { + var _v39 = {ctor: "_Tuple5" + ,_0: ra + ,_1: rb + ,_2: rc + ,_3: rd + ,_4: re}; + switch (_v39.ctor) + {case "_Tuple5": + switch (_v39._0.ctor) + {case "Err": + return Err(_v39._0._0); + case "Ok": switch (_v39._1.ctor) + {case "Ok": + switch (_v39._2.ctor) + {case "Ok": + switch (_v39._3.ctor) + {case "Ok": + switch (_v39._4.ctor) + {case "Ok": return Ok(A5(func, + _v39._0._0, + _v39._1._0, + _v39._2._0, + _v39._3._0, + _v39._4._0));} + break;} + break;} + break;} + break;} + switch (_v39._1.ctor) + {case "Err": + return Err(_v39._1._0);} + switch (_v39._2.ctor) + {case "Err": + return Err(_v39._2._0);} + switch (_v39._3.ctor) + {case "Err": + return Err(_v39._3._0);} + switch (_v39._4.ctor) + {case "Err": + return Err(_v39._4._0);} + break;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var formatError = F2(function (f, + result) { + switch (result.ctor) + {case "Err": + return Err(f(result._0)); + case "Ok": + return Ok(result._0);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var fromMaybe = F2(function (err, + maybe) { + switch (maybe.ctor) + {case "Just": + return Ok(maybe._0); + case "Nothing": + return Err(err);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + _elm.Result.values = {_op: _op + ,map: map + ,map2: map2 + ,map3: map3 + ,map4: map4 + ,map5: map5 + ,andThen: andThen + ,toMaybe: toMaybe + ,fromMaybe: fromMaybe + ,formatError: formatError + ,Ok: Ok + ,Err: Err}; + return _elm.Result.values; +}; +Elm.ServerCommunication = Elm.ServerCommunication || {}; +Elm.ServerCommunication.make = function (_elm) { + "use strict"; + _elm.ServerCommunication = _elm.ServerCommunication || {}; + if (_elm.ServerCommunication.values) + return _elm.ServerCommunication.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "ServerCommunication", + $Basics = Elm.Basics.make(_elm), + $Http = Elm.Http.make(_elm), + $InitViewAction = Elm.InitViewAction.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Payment = Elm.Model.Payment.make(_elm), + $Model$User = Elm.Model.User.make(_elm), + $Model$View$LoggedIn$Add = Elm.Model.View.LoggedIn.Add.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $SimpleHTTP = Elm.SimpleHTTP.make(_elm), + $Task = Elm.Task.make(_elm), + $Time = Elm.Time.make(_elm), + $Update = Elm.Update.make(_elm), + $Update$LoggedIn = Elm.Update.LoggedIn.make(_elm), + $Update$LoggedIn$Account = Elm.Update.LoggedIn.Account.make(_elm), + $Update$LoggedIn$Monthly = Elm.Update.LoggedIn.Monthly.make(_elm); + var deletePaymentURL = function (id) { + return A2($Basics._op["++"], + "payment/delete?id=", + $Basics.toString(id)); + }; + var addPaymentURL = F3(function (name, + cost, + frequency) { + return A2($Basics._op["++"], + "/payment/add?name=", + A2($Basics._op["++"], + name, + A2($Basics._op["++"], + "&cost=", + A2($Basics._op["++"], + $Basics.toString(cost), + A2($Basics._op["++"], + "&frequency=", + $Basics.toString(frequency)))))); + }); + var getPaymentsAtPage = function (page) { + return A2($Http.get, + $Model$Payment.paymentsDecoder, + A2($Basics._op["++"], + "payments?page=", + A2($Basics._op["++"], + $Basics.toString(page), + A2($Basics._op["++"], + "&perPage=", + $Basics.toString($Model$Payment.perPage))))); + }; + var sendRequest = function (communication) { + switch (communication.ctor) + {case "AddMonthlyPayment": + return $Task.map(function (id) { + return $Update.UpdateLoggedIn(A3($Update$LoggedIn.AddMonthlyPayment, + id, + communication._0, + communication._1)); + })(A2($Basics.flip, + $Task.andThen, + $SimpleHTTP.decodeHttpValue(A2($Json$Decode._op[":="], + "id", + $Model$Payment.paymentIdDecoder)))($SimpleHTTP.post(A3(addPaymentURL, + communication._0, + communication._1, + $Model$View$LoggedIn$Add.Monthly)))); + case "AddPayment": + return $Task.map(function (payments) { + return $Update.UpdateLoggedIn(A4($Update$LoggedIn.AddPayment, + communication._0, + communication._1, + communication._2, + payments)); + })(A2($Basics.flip, + $Task.andThen, + $Basics.always(getPaymentsAtPage(1)))($SimpleHTTP.post(A3(addPaymentURL, + communication._1, + communication._2, + $Model$View$LoggedIn$Add.Punctual)))); + case "DeleteMonthlyPayment": + return $Task.map($Basics.always($Update.UpdateLoggedIn($Update$LoggedIn.UpdateMonthly($Update$LoggedIn$Monthly.DeletePayment(communication._0)))))($SimpleHTTP.post(deletePaymentURL(communication._0))); + case "DeletePayment": + return $Task.map(function (payments) { + return $Update.UpdateLoggedIn(A2($Update$LoggedIn.DeletePayment, + communication._0, + payments)); + })(A2($Basics.flip, + $Task.andThen, + $Basics.always(getPaymentsAtPage(communication._1)))($SimpleHTTP.post(deletePaymentURL(communication._0.id)))); + case "NoCommunication": + return $Task.succeed($Update.NoOp); + case "SetIncome": + return $Task.map($Basics.always($Update.UpdateLoggedIn($Update$LoggedIn.UpdateAccount(A2($Update$LoggedIn$Account.UpdateIncome, + communication._0, + communication._1)))))($SimpleHTTP.post(A2($Basics._op["++"], + "/income?amount=", + $Basics.toString(communication._1)))); + case "SignIn": + return A2($Basics.flip, + $Task.andThen, + $Basics.always($InitViewAction.initViewAction))($SimpleHTTP.post(A2($Basics._op["++"], + "/signIn?assertion=", + communication._0))); + case "SignOut": + return $Task.map($Basics.always($Update.GoSignInView))($SimpleHTTP.post("/signOut")); + case "UpdatePage": + return A2($Basics.flip, + $Task.andThen, + function ($) { + return $Task.succeed($Update.UpdateLoggedIn($Update$LoggedIn.UpdatePage(communication._0)($))); + })(getPaymentsAtPage(communication._0));} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var SignOut = {ctor: "SignOut"}; + var UpdatePage = function (a) { + return {ctor: "UpdatePage" + ,_0: a}; + }; + var DeleteMonthlyPayment = function (a) { + return {ctor: "DeleteMonthlyPayment" + ,_0: a}; + }; + var DeletePayment = F2(function (a, + b) { + return {ctor: "DeletePayment" + ,_0: a + ,_1: b}; + }); + var SetIncome = F2(function (a, + b) { + return {ctor: "SetIncome" + ,_0: a + ,_1: b}; + }); + var AddMonthlyPayment = F2(function (a, + b) { + return {ctor: "AddMonthlyPayment" + ,_0: a + ,_1: b}; + }); + var AddPayment = F3(function (a, + b, + c) { + return {ctor: "AddPayment" + ,_0: a + ,_1: b + ,_2: c}; + }); + var SignIn = function (a) { + return {ctor: "SignIn" + ,_0: a}; + }; + var NoCommunication = {ctor: "NoCommunication"}; + var serverCommunications = $Signal.mailbox(NoCommunication); + _elm.ServerCommunication.values = {_op: _op + ,sendRequest: sendRequest + ,serverCommunications: serverCommunications + ,NoCommunication: NoCommunication + ,SignIn: SignIn + ,AddPayment: AddPayment + ,AddMonthlyPayment: AddMonthlyPayment + ,SetIncome: SetIncome + ,DeletePayment: DeletePayment + ,DeleteMonthlyPayment: DeleteMonthlyPayment + ,UpdatePage: UpdatePage + ,SignOut: SignOut}; + return _elm.ServerCommunication.values; +}; +Elm.Sign = Elm.Sign || {}; +Elm.Sign.make = function (_elm) { + "use strict"; + _elm.Sign = _elm.Sign || {}; + if (_elm.Sign.values) + return _elm.Sign.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Sign", + $Basics = Elm.Basics.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $ServerCommunication = Elm.ServerCommunication.make(_elm), + $Signal = Elm.Signal.make(_elm); + var toServerCommunication = function (operation) { + switch (operation.ctor) + {case "NoOp": + return $ServerCommunication.NoCommunication; + case "SignIn": + return $ServerCommunication.SignIn(operation._0); + case "SignOut": + return $ServerCommunication.SignOut;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var SignOut = {ctor: "SignOut"}; + var SignIn = function (a) { + return {ctor: "SignIn" + ,_0: a}; + }; + var NoOp = {ctor: "NoOp"}; + var operationDecoderWithTag = function (operation) { + switch (operation) + {case "SignIn": + return A2($Json$Decode.map, + SignIn, + A2($Json$Decode._op[":="], + "assertion", + $Json$Decode.string)); + case "SignOut": + return $Json$Decode.succeed(SignOut);} + return $Json$Decode.succeed(NoOp); + }; + var operationDecoder = A2($Json$Decode.andThen, + A2($Json$Decode._op[":="], + "operation", + $Json$Decode.string), + operationDecoderWithTag); + var decodeOperation = function (value) { + return $Maybe.withDefault(NoOp)($Result.toMaybe(A2($Json$Decode.decodeValue, + operationDecoder, + value))); + }; + _elm.Sign.values = {_op: _op + ,decodeOperation: decodeOperation + ,toServerCommunication: toServerCommunication + ,NoOp: NoOp + ,SignIn: SignIn + ,SignOut: SignOut}; + return _elm.Sign.values; +}; +Elm.Signal = Elm.Signal || {}; +Elm.Signal.make = function (_elm) { + "use strict"; + _elm.Signal = _elm.Signal || {}; + if (_elm.Signal.values) + return _elm.Signal.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Signal", + $Basics = Elm.Basics.make(_elm), + $Debug = Elm.Debug.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Native$Signal = Elm.Native.Signal.make(_elm), + $Task = Elm.Task.make(_elm); + var send = F2(function (_v0, + value) { + var _raw = _v0, + $ = _raw.ctor === "Address" ? _raw : _U.badCase($moduleName, + "bugs in reporting the exact location right now"), + actuallySend = $._0; + return A2($Task.onError, + actuallySend(value), + function (_v1) { + var _ = _v1; + return $Task.succeed({ctor: "_Tuple0"}); + }); + }); + var Message = function (a) { + return {ctor: "Message" + ,_0: a}; + }; + var message = F2(function (_v2, + value) { + var _raw = _v2, + $ = _raw.ctor === "Address" ? _raw : _U.badCase($moduleName, + "bugs in reporting the exact location right now"), + send = $._0; + return Message(send(value)); + }); + var mailbox = $Native$Signal.mailbox; + var Address = function (a) { + return {ctor: "Address" + ,_0: a}; + }; + var forwardTo = F2(function (_v3, + f) { + var _raw = _v3, + $ = _raw.ctor === "Address" ? _raw : _U.badCase($moduleName, + "bugs in reporting the exact location right now"), + send = $._0; + return Address(function (x) { + return send(f(x)); + }); + }); + var Mailbox = F2(function (a, + b) { + return {_: {} + ,address: a + ,signal: b}; + }); + var sampleOn = $Native$Signal.sampleOn; + var dropRepeats = $Native$Signal.dropRepeats; + var filterMap = $Native$Signal.filterMap; + var filter = F3(function (isOk, + base, + signal) { + return A3(filterMap, + function (value) { + return isOk(value) ? $Maybe.Just(value) : $Maybe.Nothing; + }, + base, + signal); + }); + var merge = F2(function (left, + right) { + return A3($Native$Signal.genericMerge, + $Basics.always, + left, + right); + }); + var mergeMany = function (signalList) { + var _v4 = $List.reverse(signalList); + switch (_v4.ctor) + {case "::": + return A3($List.foldl, + merge, + _v4._0, + _v4._1); + case "[]": + return $Debug.crash("mergeMany was given an empty list!");} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var foldp = $Native$Signal.foldp; + var map5 = $Native$Signal.map5; + var map4 = $Native$Signal.map4; + var map3 = $Native$Signal.map3; + var map2 = $Native$Signal.map2; + _op["~"] = F2(function (funcs, + args) { + return A3(map2, + F2(function (f,v) { + return f(v); + }), + funcs, + args); + }); + var map = $Native$Signal.map; + _op["<~"] = map; + var constant = $Native$Signal.constant; + var Signal = {ctor: "Signal"}; + _elm.Signal.values = {_op: _op + ,merge: merge + ,mergeMany: mergeMany + ,map: map + ,map2: map2 + ,map3: map3 + ,map4: map4 + ,map5: map5 + ,constant: constant + ,dropRepeats: dropRepeats + ,filter: filter + ,filterMap: filterMap + ,sampleOn: sampleOn + ,foldp: foldp + ,mailbox: mailbox + ,send: send + ,message: message + ,forwardTo: forwardTo + ,Mailbox: Mailbox}; + return _elm.Signal.values; +}; +Elm.SimpleHTTP = Elm.SimpleHTTP || {}; +Elm.SimpleHTTP.make = function (_elm) { + "use strict"; + _elm.SimpleHTTP = _elm.SimpleHTTP || {}; + if (_elm.SimpleHTTP.values) + return _elm.SimpleHTTP.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "SimpleHTTP", + $Basics = Elm.Basics.make(_elm), + $Http = Elm.Http.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Task = Elm.Task.make(_elm); + var decodeHttpValue = F2(function (decoder, + value) { + switch (value.ctor) + {case "Text": + var _v2 = A2($Json$Decode.decodeString, + decoder, + value._0); + switch (_v2.ctor) + {case "Err": + return $Task.fail($Http.UnexpectedPayload(_v2._0)); + case "Ok": + return $Task.succeed(_v2._0);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now");} + return $Task.fail($Http.UnexpectedPayload("Response body is a blob, expecting a string.")); + }); + var promoteError = function (rawError) { + switch (rawError.ctor) + {case "RawNetworkError": + return $Http.NetworkError; + case "RawTimeout": + return $Http.Timeout;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var handleResponse = function (response) { + return _U.cmp(200, + response.status) < 1 && _U.cmp(response.status, + 300) < 0 ? $Task.succeed(response.value) : $Task.fail(A2($Http.BadResponse, + response.status, + response.statusText)); + }; + var post = function (url) { + return A2($Basics.flip, + $Task.andThen, + handleResponse)($Task.mapError(promoteError)($Http.send($Http.defaultSettings)({_: {} + ,body: $Http.empty + ,headers: _L.fromArray([]) + ,url: url + ,verb: "POST"}))); + }; + _elm.SimpleHTTP.values = {_op: _op + ,post: post + ,decodeHttpValue: decodeHttpValue}; + return _elm.SimpleHTTP.values; +}; +Elm.String = Elm.String || {}; +Elm.String.make = function (_elm) { + "use strict"; + _elm.String = _elm.String || {}; + if (_elm.String.values) + return _elm.String.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "String", + $Maybe = Elm.Maybe.make(_elm), + $Native$String = Elm.Native.String.make(_elm), + $Result = Elm.Result.make(_elm); + var fromList = $Native$String.fromList; + var toList = $Native$String.toList; + var toFloat = $Native$String.toFloat; + var toInt = $Native$String.toInt; + var indices = $Native$String.indexes; + var indexes = $Native$String.indexes; + var endsWith = $Native$String.endsWith; + var startsWith = $Native$String.startsWith; + var contains = $Native$String.contains; + var all = $Native$String.all; + var any = $Native$String.any; + var toLower = $Native$String.toLower; + var toUpper = $Native$String.toUpper; + var lines = $Native$String.lines; + var words = $Native$String.words; + var trimRight = $Native$String.trimRight; + var trimLeft = $Native$String.trimLeft; + var trim = $Native$String.trim; + var padRight = $Native$String.padRight; + var padLeft = $Native$String.padLeft; + var pad = $Native$String.pad; + var dropRight = $Native$String.dropRight; + var dropLeft = $Native$String.dropLeft; + var right = $Native$String.right; + var left = $Native$String.left; + var slice = $Native$String.slice; + var repeat = $Native$String.repeat; + var join = $Native$String.join; + var split = $Native$String.split; + var foldr = $Native$String.foldr; + var foldl = $Native$String.foldl; + var reverse = $Native$String.reverse; + var filter = $Native$String.filter; + var map = $Native$String.map; + var length = $Native$String.length; + var concat = $Native$String.concat; + var append = $Native$String.append; + var uncons = $Native$String.uncons; + var cons = $Native$String.cons; + var fromChar = function ($char) { + return A2(cons,$char,""); + }; + var isEmpty = $Native$String.isEmpty; + _elm.String.values = {_op: _op + ,isEmpty: isEmpty + ,length: length + ,reverse: reverse + ,repeat: repeat + ,cons: cons + ,uncons: uncons + ,fromChar: fromChar + ,append: append + ,concat: concat + ,split: split + ,join: join + ,words: words + ,lines: lines + ,slice: slice + ,left: left + ,right: right + ,dropLeft: dropLeft + ,dropRight: dropRight + ,contains: contains + ,startsWith: startsWith + ,endsWith: endsWith + ,indexes: indexes + ,indices: indices + ,toInt: toInt + ,toFloat: toFloat + ,toList: toList + ,fromList: fromList + ,toUpper: toUpper + ,toLower: toLower + ,pad: pad + ,padLeft: padLeft + ,padRight: padRight + ,trim: trim + ,trimLeft: trimLeft + ,trimRight: trimRight + ,map: map + ,filter: filter + ,foldl: foldl + ,foldr: foldr + ,any: any + ,all: all}; + return _elm.String.values; +}; +Elm.Task = Elm.Task || {}; +Elm.Task.make = function (_elm) { + "use strict"; + _elm.Task = _elm.Task || {}; + if (_elm.Task.values) + return _elm.Task.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Task", + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Native$Task = Elm.Native.Task.make(_elm), + $Result = Elm.Result.make(_elm); + var sleep = $Native$Task.sleep; + var spawn = $Native$Task.spawn; + var ThreadID = function (a) { + return {ctor: "ThreadID" + ,_0: a}; + }; + var onError = $Native$Task.catch_; + var andThen = $Native$Task.andThen; + var fail = $Native$Task.fail; + var mapError = F2(function (f, + promise) { + return A2(onError, + promise, + function (err) { + return fail(f(err)); + }); + }); + var succeed = $Native$Task.succeed; + var map = F2(function (func, + promiseA) { + return A2(andThen, + promiseA, + function (a) { + return succeed(func(a)); + }); + }); + var map2 = F3(function (func, + promiseA, + promiseB) { + return A2(andThen, + promiseA, + function (a) { + return A2(andThen, + promiseB, + function (b) { + return succeed(A2(func,a,b)); + }); + }); + }); + var map3 = F4(function (func, + promiseA, + promiseB, + promiseC) { + return A2(andThen, + promiseA, + function (a) { + return A2(andThen, + promiseB, + function (b) { + return A2(andThen, + promiseC, + function (c) { + return succeed(A3(func, + a, + b, + c)); + }); + }); + }); + }); + var map4 = F5(function (func, + promiseA, + promiseB, + promiseC, + promiseD) { + return A2(andThen, + promiseA, + function (a) { + return A2(andThen, + promiseB, + function (b) { + return A2(andThen, + promiseC, + function (c) { + return A2(andThen, + promiseD, + function (d) { + return succeed(A4(func, + a, + b, + c, + d)); + }); + }); + }); + }); + }); + var map5 = F6(function (func, + promiseA, + promiseB, + promiseC, + promiseD, + promiseE) { + return A2(andThen, + promiseA, + function (a) { + return A2(andThen, + promiseB, + function (b) { + return A2(andThen, + promiseC, + function (c) { + return A2(andThen, + promiseD, + function (d) { + return A2(andThen, + promiseE, + function (e) { + return succeed(A5(func, + a, + b, + c, + d, + e)); + }); + }); + }); + }); + }); + }); + var andMap = F2(function (promiseFunc, + promiseValue) { + return A2(andThen, + promiseFunc, + function (func) { + return A2(andThen, + promiseValue, + function (value) { + return succeed(func(value)); + }); + }); + }); + var sequence = function (promises) { + switch (promises.ctor) + {case "::": return A3(map2, + F2(function (x,y) { + return A2($List._op["::"], + x, + y); + }), + promises._0, + sequence(promises._1)); + case "[]": + return succeed(_L.fromArray([]));} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var toMaybe = function (task) { + return A2(onError, + A2(map,$Maybe.Just,task), + function (_v3) { + var _ = _v3; + return succeed($Maybe.Nothing); + }); + }; + var fromMaybe = F2(function ($default, + maybe) { + switch (maybe.ctor) + {case "Just": + return succeed(maybe._0); + case "Nothing": + return fail($default);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var toResult = function (task) { + return A2(onError, + A2(map,$Result.Ok,task), + function (msg) { + return succeed($Result.Err(msg)); + }); + }; + var fromResult = function (result) { + switch (result.ctor) + {case "Err": + return fail(result._0); + case "Ok": + return succeed(result._0);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var Task = {ctor: "Task"}; + _elm.Task.values = {_op: _op + ,succeed: succeed + ,fail: fail + ,map: map + ,map2: map2 + ,map3: map3 + ,map4: map4 + ,map5: map5 + ,andMap: andMap + ,sequence: sequence + ,andThen: andThen + ,onError: onError + ,mapError: mapError + ,toMaybe: toMaybe + ,fromMaybe: fromMaybe + ,toResult: toResult + ,fromResult: fromResult + ,spawn: spawn + ,sleep: sleep}; + return _elm.Task.values; +}; +Elm.Text = Elm.Text || {}; +Elm.Text.make = function (_elm) { + "use strict"; + _elm.Text = _elm.Text || {}; + if (_elm.Text.values) + return _elm.Text.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Text", + $Color = Elm.Color.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Native$Text = Elm.Native.Text.make(_elm); + var line = $Native$Text.line; + var italic = $Native$Text.italic; + var bold = $Native$Text.bold; + var color = $Native$Text.color; + var height = $Native$Text.height; + var link = $Native$Text.link; + var monospace = $Native$Text.monospace; + var typeface = $Native$Text.typeface; + var style = $Native$Text.style; + var append = $Native$Text.append; + var fromString = $Native$Text.fromString; + var empty = fromString(""); + var concat = function (texts) { + return A3($List.foldr, + append, + empty, + texts); + }; + var join = F2(function (seperator, + texts) { + return concat(A2($List.intersperse, + seperator, + texts)); + }); + var defaultStyle = {_: {} + ,bold: false + ,color: $Color.black + ,height: $Maybe.Nothing + ,italic: false + ,line: $Maybe.Nothing + ,typeface: _L.fromArray([])}; + var Style = F6(function (a, + b, + c, + d, + e, + f) { + return {_: {} + ,bold: d + ,color: c + ,height: b + ,italic: e + ,line: f + ,typeface: a}; + }); + var Through = {ctor: "Through"}; + var Over = {ctor: "Over"}; + var Under = {ctor: "Under"}; + var Text = {ctor: "Text"}; + _elm.Text.values = {_op: _op + ,fromString: fromString + ,empty: empty + ,append: append + ,concat: concat + ,join: join + ,link: link + ,style: style + ,defaultStyle: defaultStyle + ,typeface: typeface + ,monospace: monospace + ,height: height + ,color: color + ,bold: bold + ,italic: italic + ,line: line + ,Style: Style + ,Under: Under + ,Over: Over + ,Through: Through}; + return _elm.Text.values; +}; +Elm.Time = Elm.Time || {}; +Elm.Time.make = function (_elm) { + "use strict"; + _elm.Time = _elm.Time || {}; + if (_elm.Time.values) + return _elm.Time.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Time", + $Basics = Elm.Basics.make(_elm), + $Native$Signal = Elm.Native.Signal.make(_elm), + $Native$Time = Elm.Native.Time.make(_elm), + $Signal = Elm.Signal.make(_elm); + var delay = $Native$Signal.delay; + var since = F2(function (time, + signal) { + var stop = A2($Signal.map, + $Basics.always(-1), + A2(delay,time,signal)); + var start = A2($Signal.map, + $Basics.always(1), + signal); + var delaydiff = A3($Signal.foldp, + F2(function (x,y) { + return x + y; + }), + 0, + A2($Signal.merge,start,stop)); + return A2($Signal.map, + F2(function (x,y) { + return !_U.eq(x,y); + })(0), + delaydiff); + }); + var timestamp = $Native$Signal.timestamp; + var every = $Native$Time.every; + var fpsWhen = $Native$Time.fpsWhen; + var fps = function (targetFrames) { + return A2(fpsWhen, + targetFrames, + $Signal.constant(true)); + }; + var inMilliseconds = function (t) { + return t; + }; + var millisecond = 1; + var second = 1000 * millisecond; + var minute = 60 * second; + var hour = 60 * minute; + var inHours = function (t) { + return t / hour; + }; + var inMinutes = function (t) { + return t / minute; + }; + var inSeconds = function (t) { + return t / second; + }; + _elm.Time.values = {_op: _op + ,millisecond: millisecond + ,second: second + ,minute: minute + ,hour: hour + ,inMilliseconds: inMilliseconds + ,inSeconds: inSeconds + ,inMinutes: inMinutes + ,inHours: inHours + ,fps: fps + ,fpsWhen: fpsWhen + ,every: every + ,timestamp: timestamp + ,delay: delay + ,since: since}; + return _elm.Time.values; +}; +Elm.Transform2D = Elm.Transform2D || {}; +Elm.Transform2D.make = function (_elm) { + "use strict"; + _elm.Transform2D = _elm.Transform2D || {}; + if (_elm.Transform2D.values) + return _elm.Transform2D.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Transform2D", + $Native$Transform2D = Elm.Native.Transform2D.make(_elm); + var multiply = $Native$Transform2D.multiply; + var rotation = $Native$Transform2D.rotation; + var matrix = $Native$Transform2D.matrix; + var translation = F2(function (x, + y) { + return A6(matrix, + 1, + 0, + 0, + 1, + x, + y); + }); + var scale = function (s) { + return A6(matrix, + s, + 0, + 0, + s, + 0, + 0); + }; + var scaleX = function (x) { + return A6(matrix, + x, + 0, + 0, + 1, + 0, + 0); + }; + var scaleY = function (y) { + return A6(matrix, + 1, + 0, + 0, + y, + 0, + 0); + }; + var identity = $Native$Transform2D.identity; + var Transform2D = {ctor: "Transform2D"}; + _elm.Transform2D.values = {_op: _op + ,identity: identity + ,matrix: matrix + ,multiply: multiply + ,rotation: rotation + ,translation: translation + ,scale: scale + ,scaleX: scaleX + ,scaleY: scaleY}; + return _elm.Transform2D.values; +}; +Elm.Update = Elm.Update || {}; +Elm.Update.make = function (_elm) { + "use strict"; + _elm.Update = _elm.Update || {}; + if (_elm.Update.values) + return _elm.Update.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Update", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model = Elm.Model.make(_elm), + $Model$Payer = Elm.Model.Payer.make(_elm), + $Model$Payment = Elm.Model.Payment.make(_elm), + $Model$User = Elm.Model.User.make(_elm), + $Model$View = Elm.Model.View.make(_elm), + $Model$View$LoggedInView = Elm.Model.View.LoggedInView.make(_elm), + $Model$View$SignInView = Elm.Model.View.SignInView.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Time = Elm.Time.make(_elm), + $Update$LoggedIn = Elm.Update.LoggedIn.make(_elm), + $Update$SignIn = Elm.Update.SignIn.make(_elm); + var updateModel = F2(function (action, + model) { + switch (action.ctor) + {case "GoLoggedInView": + return _U.replace([["view" + ,$Model$View.LoggedInView(A6($Model$View$LoggedInView.initLoggedInView, + action._0, + action._1, + action._2, + action._3, + action._4, + action._5))]], + model); + case "GoSignInView": + return _U.replace([["view" + ,$Model$View.SignInView($Model$View$SignInView.initSignInView)]], + model); + case "NoOp": return model; + case "SignInError": + var signInView = _U.replace([["result" + ,$Maybe.Just($Result.Err(action._0))]], + $Model$View$SignInView.initSignInView); + return _U.replace([["view" + ,$Model$View.SignInView(signInView)]], + model); + case "UpdateLoggedIn": + var _v11 = model.view; + switch (_v11.ctor) + {case "LoggedInView": + return _U.replace([["view" + ,$Model$View.LoggedInView(A3($Update$LoggedIn.updateLoggedIn, + model, + action._0, + _v11._0))]], + model);} + return model; + case "UpdateSignIn": + var _v13 = model.view; + switch (_v13.ctor) + {case "SignInView": + return _U.replace([["view" + ,$Model$View.SignInView(A2($Update$SignIn.updateSignIn, + action._0, + _v13._0))]], + model);} + return model; + case "UpdateTime": + return _U.replace([["currentTime" + ,action._0]], + model);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var UpdateLoggedIn = function (a) { + return {ctor: "UpdateLoggedIn" + ,_0: a}; + }; + var GoLoggedInView = F6(function (a, + b, + c, + d, + e, + f) { + return {ctor: "GoLoggedInView" + ,_0: a + ,_1: b + ,_2: c + ,_3: d + ,_4: e + ,_5: f}; + }); + var UpdateSignIn = function (a) { + return {ctor: "UpdateSignIn" + ,_0: a}; + }; + var SignInError = function (a) { + return {ctor: "SignInError" + ,_0: a}; + }; + var GoSignInView = {ctor: "GoSignInView"}; + var UpdateTime = function (a) { + return {ctor: "UpdateTime" + ,_0: a}; + }; + var NoOp = {ctor: "NoOp"}; + var actions = $Signal.mailbox(NoOp); + _elm.Update.values = {_op: _op + ,actions: actions + ,updateModel: updateModel + ,NoOp: NoOp + ,UpdateTime: UpdateTime + ,GoSignInView: GoSignInView + ,SignInError: SignInError + ,UpdateSignIn: UpdateSignIn + ,GoLoggedInView: GoLoggedInView + ,UpdateLoggedIn: UpdateLoggedIn}; + return _elm.Update.values; +}; +Elm.Update = Elm.Update || {}; +Elm.Update.LoggedIn = Elm.Update.LoggedIn || {}; +Elm.Update.LoggedIn.make = function (_elm) { + "use strict"; + _elm.Update = _elm.Update || {}; + _elm.Update.LoggedIn = _elm.Update.LoggedIn || {}; + if (_elm.Update.LoggedIn.values) + return _elm.Update.LoggedIn.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Update.LoggedIn", + $Basics = Elm.Basics.make(_elm), + $Date = Elm.Date.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model = Elm.Model.make(_elm), + $Model$Payment = Elm.Model.Payment.make(_elm), + $Model$User = Elm.Model.User.make(_elm), + $Model$View$LoggedIn$Add = Elm.Model.View.LoggedIn.Add.make(_elm), + $Model$View$LoggedInView = Elm.Model.View.LoggedInView.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Update$LoggedIn$Account = Elm.Update.LoggedIn.Account.make(_elm), + $Update$LoggedIn$Add = Elm.Update.LoggedIn.Add.make(_elm), + $Update$LoggedIn$Monthly = Elm.Update.LoggedIn.Monthly.make(_elm); + var updateLoggedIn = F3(function (model, + action, + loggedInView) { + switch (action.ctor) + {case "AddMonthlyPayment": + return _U.replace([["add" + ,$Model$View$LoggedIn$Add.initAddPayment($Model$View$LoggedIn$Add.Monthly)] + ,["monthly" + ,function () { + var payment = A5($Model$Payment.Payment, + action._0, + $Date.fromTime(model.currentTime), + action._1, + action._2, + loggedInView.account.me); + return A2($Update$LoggedIn$Monthly.updateMonthly, + $Update$LoggedIn$Monthly.AddPayment(payment), + loggedInView.monthly); + }()]], + loggedInView); + case "AddPayment": + return _U.replace([["payments" + ,action._3] + ,["currentPage",1] + ,["add" + ,$Model$View$LoggedIn$Add.initAddPayment($Model$View$LoggedIn$Add.Punctual)] + ,["account" + ,A2($Update$LoggedIn$Account.updateAccount, + A3($Update$LoggedIn$Account.UpdatePayer, + action._0, + model.currentTime, + action._2), + loggedInView.account)] + ,["paymentsCount" + ,loggedInView.paymentsCount + 1]], + loggedInView); + case "DeletePayment": + return _U.replace([["payments" + ,action._1] + ,["account" + ,A2($Update$LoggedIn$Account.updateAccount, + A3($Update$LoggedIn$Account.UpdatePayer, + action._0.userId, + $Date.toTime(action._0.creation), + 0 - action._0.cost), + loggedInView.account)] + ,["paymentsCount" + ,loggedInView.paymentsCount - 1]], + loggedInView); + case "ToggleEdit": + return _U.replace([["paymentEdition" + ,_U.eq(loggedInView.paymentEdition, + $Maybe.Just(action._0)) ? $Maybe.Nothing : $Maybe.Just(action._0)]], + loggedInView); + case "UpdateAccount": + return _U.replace([["account" + ,A2($Update$LoggedIn$Account.updateAccount, + action._0, + loggedInView.account)]], + loggedInView); + case "UpdateAdd": + return _U.replace([["add" + ,A2($Update$LoggedIn$Add.updateAddPayment, + action._0, + loggedInView.add)]], + loggedInView); + case "UpdateMonthly": + return _U.replace([["monthly" + ,A2($Update$LoggedIn$Monthly.updateMonthly, + action._0, + loggedInView.monthly)]], + loggedInView); + case "UpdatePage": + return _U.replace([["currentPage" + ,action._0] + ,["payments",action._1]], + loggedInView); + case "UpdatePayments": + return _U.replace([["payments" + ,action._0]], + loggedInView);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var UpdateAccount = function (a) { + return {ctor: "UpdateAccount" + ,_0: a}; + }; + var UpdateMonthly = function (a) { + return {ctor: "UpdateMonthly" + ,_0: a}; + }; + var UpdatePage = F2(function (a, + b) { + return {ctor: "UpdatePage" + ,_0: a + ,_1: b}; + }); + var DeletePayment = F2(function (a, + b) { + return {ctor: "DeletePayment" + ,_0: a + ,_1: b}; + }); + var ToggleEdit = function (a) { + return {ctor: "ToggleEdit" + ,_0: a}; + }; + var AddMonthlyPayment = F3(function (a, + b, + c) { + return {ctor: "AddMonthlyPayment" + ,_0: a + ,_1: b + ,_2: c}; + }); + var AddPayment = F4(function (a, + b, + c, + d) { + return {ctor: "AddPayment" + ,_0: a + ,_1: b + ,_2: c + ,_3: d}; + }); + var UpdatePayments = function (a) { + return {ctor: "UpdatePayments" + ,_0: a}; + }; + var UpdateAdd = function (a) { + return {ctor: "UpdateAdd" + ,_0: a}; + }; + _elm.Update.LoggedIn.values = {_op: _op + ,updateLoggedIn: updateLoggedIn + ,UpdateAdd: UpdateAdd + ,UpdatePayments: UpdatePayments + ,AddPayment: AddPayment + ,AddMonthlyPayment: AddMonthlyPayment + ,ToggleEdit: ToggleEdit + ,DeletePayment: DeletePayment + ,UpdatePage: UpdatePage + ,UpdateMonthly: UpdateMonthly + ,UpdateAccount: UpdateAccount}; + return _elm.Update.LoggedIn.values; +}; +Elm.Update = Elm.Update || {}; +Elm.Update.LoggedIn = Elm.Update.LoggedIn || {}; +Elm.Update.LoggedIn.Account = Elm.Update.LoggedIn.Account || {}; +Elm.Update.LoggedIn.Account.make = function (_elm) { + "use strict"; + _elm.Update = _elm.Update || {}; + _elm.Update.LoggedIn = _elm.Update.LoggedIn || {}; + _elm.Update.LoggedIn.Account = _elm.Update.LoggedIn.Account || {}; + if (_elm.Update.LoggedIn.Account.values) + return _elm.Update.LoggedIn.Account.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Update.LoggedIn.Account", + $Basics = Elm.Basics.make(_elm), + $Dict = Elm.Dict.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Payer = Elm.Model.Payer.make(_elm), + $Model$User = Elm.Model.User.make(_elm), + $Model$View$LoggedIn$Account = Elm.Model.View.LoggedIn.Account.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Time = Elm.Time.make(_elm), + $Utils$Maybe = Elm.Utils.Maybe.make(_elm); + var updateAccount = F2(function (action, + account) { + switch (action.ctor) + {case "ToggleDetail": + return _U.replace([["visibleDetail" + ,$Basics.not(account.visibleDetail)]], + account); + case "ToggleIncomeEdition": + return _U.replace([["incomeEdition" + ,$Utils$Maybe.isJust(account.incomeEdition) ? $Maybe.Nothing : $Maybe.Just($Model$View$LoggedIn$Account.initIncomeEdition(A2($Maybe.withDefault, + 0, + $Model$View$LoggedIn$Account.getCurrentIncome(account))))]], + account); + case "UpdateEditionError": + var _v8 = account.incomeEdition; + switch (_v8.ctor) + {case "Just": + return _U.replace([["incomeEdition" + ,$Maybe.Just(_U.replace([["error" + ,$Maybe.Just(action._0)]], + _v8._0))]], + account); + case "Nothing": return account;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + case "UpdateIncome": + return _U.replace([["payers" + ,A2($Dict.update, + account.me, + function (mbPayer) { + switch (mbPayer.ctor) + {case "Just": + return $Maybe.Just(_U.replace([["incomes" + ,A2($Basics._op["++"], + mbPayer._0.incomes, + _L.fromArray([{_: {} + ,amount: action._1 + ,creation: action._0}]))]], + mbPayer._0)); + case "Nothing": + return $Maybe.Nothing;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + })(account.payers)] + ,["incomeEdition" + ,$Maybe.Nothing]], + account); + case "UpdateIncomeEdition": + var _v12 = account.incomeEdition; + switch (_v12.ctor) + {case "Just": + return _U.replace([["incomeEdition" + ,$Maybe.Just(_U.replace([["income" + ,action._0]], + _v12._0))]], + account); + case "Nothing": return account;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + case "UpdatePayer": + return _U.replace([["payers" + ,A4($Model$Payer.updatePayers, + account.payers, + action._0, + action._1, + action._2)]], + account);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var UpdateIncome = F2(function (a, + b) { + return {ctor: "UpdateIncome" + ,_0: a + ,_1: b}; + }); + var UpdateEditionError = function (a) { + return {ctor: "UpdateEditionError" + ,_0: a}; + }; + var UpdateIncomeEdition = function (a) { + return {ctor: "UpdateIncomeEdition" + ,_0: a}; + }; + var ToggleIncomeEdition = {ctor: "ToggleIncomeEdition"}; + var UpdatePayer = F3(function (a, + b, + c) { + return {ctor: "UpdatePayer" + ,_0: a + ,_1: b + ,_2: c}; + }); + var ToggleDetail = {ctor: "ToggleDetail"}; + _elm.Update.LoggedIn.Account.values = {_op: _op + ,updateAccount: updateAccount + ,ToggleDetail: ToggleDetail + ,UpdatePayer: UpdatePayer + ,ToggleIncomeEdition: ToggleIncomeEdition + ,UpdateIncomeEdition: UpdateIncomeEdition + ,UpdateEditionError: UpdateEditionError + ,UpdateIncome: UpdateIncome}; + return _elm.Update.LoggedIn.Account.values; +}; +Elm.Update = Elm.Update || {}; +Elm.Update.LoggedIn = Elm.Update.LoggedIn || {}; +Elm.Update.LoggedIn.Add = Elm.Update.LoggedIn.Add || {}; +Elm.Update.LoggedIn.Add.make = function (_elm) { + "use strict"; + _elm.Update = _elm.Update || {}; + _elm.Update.LoggedIn = _elm.Update.LoggedIn || {}; + _elm.Update.LoggedIn.Add = _elm.Update.LoggedIn.Add || {}; + if (_elm.Update.LoggedIn.Add.values) + return _elm.Update.LoggedIn.Add.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Update.LoggedIn.Add", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$View$LoggedIn$Add = Elm.Model.View.LoggedIn.Add.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var updateAddPayment = F2(function (action, + addPayment) { + switch (action.ctor) + {case "AddError": + return _U.replace([["nameError" + ,action._0] + ,["costError",action._1]], + addPayment); + case "ToggleFrequency": + return _U.replace([["frequency" + ,_U.eq(addPayment.frequency, + $Model$View$LoggedIn$Add.Punctual) ? $Model$View$LoggedIn$Add.Monthly : $Model$View$LoggedIn$Add.Punctual]], + addPayment); + case "UpdateCost": + return _U.replace([["cost" + ,action._0]], + addPayment); + case "UpdateName": + return _U.replace([["name" + ,action._0]], + addPayment);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var ToggleFrequency = {ctor: "ToggleFrequency"}; + var AddError = F2(function (a, + b) { + return {ctor: "AddError" + ,_0: a + ,_1: b}; + }); + var UpdateCost = function (a) { + return {ctor: "UpdateCost" + ,_0: a}; + }; + var UpdateName = function (a) { + return {ctor: "UpdateName" + ,_0: a}; + }; + _elm.Update.LoggedIn.Add.values = {_op: _op + ,updateAddPayment: updateAddPayment + ,UpdateName: UpdateName + ,UpdateCost: UpdateCost + ,AddError: AddError + ,ToggleFrequency: ToggleFrequency}; + return _elm.Update.LoggedIn.Add.values; +}; +Elm.Update = Elm.Update || {}; +Elm.Update.LoggedIn = Elm.Update.LoggedIn || {}; +Elm.Update.LoggedIn.Monthly = Elm.Update.LoggedIn.Monthly || {}; +Elm.Update.LoggedIn.Monthly.make = function (_elm) { + "use strict"; + _elm.Update = _elm.Update || {}; + _elm.Update.LoggedIn = _elm.Update.LoggedIn || {}; + _elm.Update.LoggedIn.Monthly = _elm.Update.LoggedIn.Monthly || {}; + if (_elm.Update.LoggedIn.Monthly.values) + return _elm.Update.LoggedIn.Monthly.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Update.LoggedIn.Monthly", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Payment = Elm.Model.Payment.make(_elm), + $Model$View$LoggedIn$Monthly = Elm.Model.View.LoggedIn.Monthly.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var updateMonthly = F2(function (action, + monthly) { + switch (action.ctor) + {case "AddPayment": + return _U.replace([["payments" + ,A2($List._op["::"], + action._0, + monthly.payments)] + ,["visibleDetail",true]], + monthly); + case "DeletePayment": + return _U.replace([["payments" + ,A2($List.filter, + function (payment) { + return !_U.eq(payment.id, + action._0); + }, + monthly.payments)]], + monthly); + case "ToggleDetail": + return _U.replace([["visibleDetail" + ,$Basics.not(monthly.visibleDetail)]], + monthly);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var DeletePayment = function (a) { + return {ctor: "DeletePayment" + ,_0: a}; + }; + var AddPayment = function (a) { + return {ctor: "AddPayment" + ,_0: a}; + }; + var ToggleDetail = {ctor: "ToggleDetail"}; + _elm.Update.LoggedIn.Monthly.values = {_op: _op + ,updateMonthly: updateMonthly + ,ToggleDetail: ToggleDetail + ,AddPayment: AddPayment + ,DeletePayment: DeletePayment}; + return _elm.Update.LoggedIn.Monthly.values; +}; +Elm.Update = Elm.Update || {}; +Elm.Update.SignIn = Elm.Update.SignIn || {}; +Elm.Update.SignIn.make = function (_elm) { + "use strict"; + _elm.Update = _elm.Update || {}; + _elm.Update.SignIn = _elm.Update.SignIn || {}; + if (_elm.Update.SignIn.values) + return _elm.Update.SignIn.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Update.SignIn", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$View$SignInView = Elm.Model.View.SignInView.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var updateSignIn = F2(function (action, + signInView) { + switch (action.ctor) + {case "ErrorLogin": + return _U.replace([["result" + ,$Maybe.Just($Result.Err(action._0))]], + signInView);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var ErrorLogin = function (a) { + return {ctor: "ErrorLogin" + ,_0: a}; + }; + _elm.Update.SignIn.values = {_op: _op + ,updateSignIn: updateSignIn + ,ErrorLogin: ErrorLogin}; + return _elm.Update.SignIn.values; +}; +Elm.Utils = Elm.Utils || {}; +Elm.Utils.Dict = Elm.Utils.Dict || {}; +Elm.Utils.Dict.make = function (_elm) { + "use strict"; + _elm.Utils = _elm.Utils || {}; + _elm.Utils.Dict = _elm.Utils.Dict || {}; + if (_elm.Utils.Dict.values) + return _elm.Utils.Dict.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Utils.Dict", + $Basics = Elm.Basics.make(_elm), + $Dict = Elm.Dict.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var onSecond = F2(function (f, + tuple) { + switch (tuple.ctor) + {case "_Tuple2": + return {ctor: "_Tuple2" + ,_0: tuple._0 + ,_1: f(tuple._1)};} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var mapValues = function (f) { + return function ($) { + return $Dict.fromList($List.map(onSecond(f))($Dict.toList($))); + }; + }; + _elm.Utils.Dict.values = {_op: _op + ,mapValues: mapValues}; + return _elm.Utils.Dict.values; +}; +Elm.Utils = Elm.Utils || {}; +Elm.Utils.Either = Elm.Utils.Either || {}; +Elm.Utils.Either.make = function (_elm) { + "use strict"; + _elm.Utils = _elm.Utils || {}; + _elm.Utils.Either = _elm.Utils.Either || {}; + if (_elm.Utils.Either.values) + return _elm.Utils.Either.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Utils.Either", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var toMaybeError = function (result) { + switch (result.ctor) + {case "Err": + return $Maybe.Just(result._0); + case "Ok": + return $Maybe.Nothing;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + _elm.Utils.Either.values = {_op: _op + ,toMaybeError: toMaybeError}; + return _elm.Utils.Either.values; +}; +Elm.Utils = Elm.Utils || {}; +Elm.Utils.Maybe = Elm.Utils.Maybe || {}; +Elm.Utils.Maybe.make = function (_elm) { + "use strict"; + _elm.Utils = _elm.Utils || {}; + _elm.Utils.Maybe = _elm.Utils.Maybe || {}; + if (_elm.Utils.Maybe.values) + return _elm.Utils.Maybe.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Utils.Maybe", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var maybeToList = function (mb) { + switch (mb.ctor) + {case "Just": + return _L.fromArray([mb._0]); + case "Nothing": + return _L.fromArray([]);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var catMaybes = A2($List.foldr, + F2(function (mb,xs) { + switch (mb.ctor) + {case "Just": + return A2($List._op["::"], + mb._0, + xs); + case "Nothing": return xs;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }), + _L.fromArray([])); + var isJust = function (maybe) { + switch (maybe.ctor) + {case "Just": return true; + case "Nothing": return false;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + _elm.Utils.Maybe.values = {_op: _op + ,isJust: isJust + ,catMaybes: catMaybes + ,maybeToList: maybeToList}; + return _elm.Utils.Maybe.values; +}; +Elm.Utils = Elm.Utils || {}; +Elm.Utils.Validation = Elm.Utils.Validation || {}; +Elm.Utils.Validation.make = function (_elm) { + "use strict"; + _elm.Utils = _elm.Utils || {}; + _elm.Utils.Validation = _elm.Utils.Validation || {}; + if (_elm.Utils.Validation.values) + return _elm.Utils.Validation.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "Utils.Validation", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Reads = Elm.Reads.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $String = Elm.String.make(_elm); + var validateNumber = F3(function (message, + numberForm, + str) { + var _v0 = $Reads.readInt(str); + switch (_v0.ctor) + {case "Just": + return numberForm(_v0._0) ? $Result.Ok(_v0._0) : $Result.Err(message); + case "Nothing": + return $Result.Err(message);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var validateNonEmpty = F2(function (message, + str) { + return $String.isEmpty(str) ? $Result.Err(message) : $Result.Ok(str); + }); + _elm.Utils.Validation.values = {_op: _op + ,validateNonEmpty: validateNonEmpty + ,validateNumber: validateNumber}; + return _elm.Utils.Validation.values; +}; +Elm.View = Elm.View || {}; +Elm.View.Date = Elm.View.Date || {}; +Elm.View.Date.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.Date = _elm.View.Date || {}; + if (_elm.View.Date.values) + return _elm.View.Date.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.Date", + $Basics = Elm.Basics.make(_elm), + $Date = Elm.Date.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Translations = Elm.Model.Translations.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $String = Elm.String.make(_elm); + var getMonthKey = function (month) { + switch (month.ctor) + {case "Apr": return "April"; + case "Aug": return "August"; + case "Dec": return "December"; + case "Feb": return "February"; + case "Jan": return "January"; + case "Jul": return "July"; + case "Jun": return "June"; + case "Mar": return "March"; + case "May": return "May"; + case "Nov": return "November"; + case "Oct": return "October"; + case "Sep": return "September";} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var getMonthNumber = function (month) { + switch (month.ctor) + {case "Apr": return 4; + case "Aug": return 8; + case "Dec": return 12; + case "Feb": return 2; + case "Jan": return 1; + case "Jul": return 7; + case "Jun": return 6; + case "Mar": return 3; + case "May": return 5; + case "Nov": return 11; + case "Oct": return 10; + case "Sep": return 9;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var renderLongDate = F2(function (date, + translations) { + var params = _L.fromArray([$Basics.toString($Date.day(date)) + ,A2($Model$Translations.getMessage, + getMonthKey($Date.month(date)), + translations) + ,$Basics.toString($Date.year(date))]); + return A3($Model$Translations.getParamMessage, + params, + "LongDate", + translations); + }); + var renderShortDate = F2(function (date, + translations) { + var params = _L.fromArray([A3($String.pad, + 2, + _U.chr("0"), + $Basics.toString($Date.day(date))) + ,A3($String.pad, + 2, + _U.chr("0"), + $Basics.toString(getMonthNumber($Date.month(date)))) + ,$Basics.toString($Date.year(date))]); + return A3($Model$Translations.getParamMessage, + params, + "ShortDate", + translations); + }); + _elm.View.Date.values = {_op: _op + ,renderShortDate: renderShortDate + ,renderLongDate: renderLongDate}; + return _elm.View.Date.values; +}; +Elm.View = Elm.View || {}; +Elm.View.Events = Elm.View.Events || {}; +Elm.View.Events.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.Events = _elm.View.Events || {}; + if (_elm.View.Events.values) + return _elm.View.Events.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.Events", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $Html$Events = Elm.Html.Events.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var onSubmitPrevDefault = F2(function (address, + value) { + return A4($Html$Events.onWithOptions, + "submit", + _U.replace([["preventDefault" + ,true]], + $Html$Events.defaultOptions), + $Json$Decode.value, + function (_v0) { + var _ = _v0; + return A2($Signal.message, + address, + value); + }); + }); + _elm.View.Events.values = {_op: _op + ,onSubmitPrevDefault: onSubmitPrevDefault}; + return _elm.View.Events.values; +}; +Elm.View = Elm.View || {}; +Elm.View.Expand = Elm.View.Expand || {}; +Elm.View.Expand.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.Expand = _elm.View.Expand || {}; + if (_elm.View.Expand.values) + return _elm.View.Expand.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.Expand", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $Html$Attributes = Elm.Html.Attributes.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $View$Icon = Elm.View.Icon.make(_elm); + var chevronIcon = F2(function (expandType, + isExpanded) { + var _v0 = {ctor: "_Tuple2" + ,_0: expandType + ,_1: isExpanded}; + switch (_v0.ctor) + {case "_Tuple2": + switch (_v0._0.ctor) + {case "ExpandDown": + switch (_v0._1) + {case false: + return "chevron-down"; + case true: return "chevron-up";} + break; + case "ExpandUp": switch (_v0._1) + {case false: + return "chevron-up"; + case true: + return "chevron-down";} + break;} + break;} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var expand = F2(function (expandType, + isExpanded) { + return A2($Html.div, + _L.fromArray([$Html$Attributes.$class("expand")]), + _L.fromArray([$View$Icon.renderIcon(A2(chevronIcon, + expandType, + isExpanded))])); + }); + var ExpandDown = {ctor: "ExpandDown"}; + var ExpandUp = {ctor: "ExpandUp"}; + _elm.View.Expand.values = {_op: _op + ,expand: expand + ,ExpandUp: ExpandUp + ,ExpandDown: ExpandDown}; + return _elm.View.Expand.values; +}; +Elm.View = Elm.View || {}; +Elm.View.Header = Elm.View.Header || {}; +Elm.View.Header.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.Header = _elm.View.Header || {}; + if (_elm.View.Header.values) + return _elm.View.Header.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.Header", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $Html$Attributes = Elm.Html.Attributes.make(_elm), + $Html$Events = Elm.Html.Events.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model = Elm.Model.make(_elm), + $Model$Translations = Elm.Model.Translations.make(_elm), + $Model$View = Elm.Model.View.make(_elm), + $Persona = Elm.Persona.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $View$Icon = Elm.View.Icon.make(_elm); + var renderHeader = function (model) { + return A2($Html.header, + _L.fromArray([]), + _L.fromArray([A2($Html.h1, + _L.fromArray([]), + _L.fromArray([$Html.text(A2($Model$Translations.getMessage, + "SharedCost", + model.translations))])) + ,function () { + var _v0 = model.view; + switch (_v0.ctor) + {case "LoadingView": + return $Html.text(""); + case "LoggedInView": + return A2($Html.button, + _L.fromArray([$Html$Attributes.$class("icon") + ,A2($Html$Events.onClick, + $Persona.operations.address, + $Persona.SignOut)]), + _L.fromArray([$View$Icon.renderIcon("sign-out")])); + case "SignInView": + return A2($Html.button, + _L.fromArray([$Html$Attributes.$class("icon") + ,A2($Html$Events.onClick, + $Persona.operations.address, + $Persona.SignIn)]), + _L.fromArray([$View$Icon.renderIcon("sign-in")]));} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }()])); + }; + _elm.View.Header.values = {_op: _op + ,renderHeader: renderHeader}; + return _elm.View.Header.values; +}; +Elm.View = Elm.View || {}; +Elm.View.Icon = Elm.View.Icon || {}; +Elm.View.Icon.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.Icon = _elm.View.Icon || {}; + if (_elm.View.Icon.values) + return _elm.View.Icon.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.Icon", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $Html$Attributes = Elm.Html.Attributes.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var renderIcon = function (iconClass) { + return A2($Html.i, + _L.fromArray([$Html$Attributes.$class(A2($Basics._op["++"], + "fa fa-fw fa-", + iconClass))]), + _L.fromArray([])); + }; + _elm.View.Icon.values = {_op: _op + ,renderIcon: renderIcon}; + return _elm.View.Icon.values; +}; +Elm.View = Elm.View || {}; +Elm.View.Loading = Elm.View.Loading || {}; +Elm.View.Loading.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.Loading = _elm.View.Loading || {}; + if (_elm.View.Loading.values) + return _elm.View.Loading.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.Loading", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var renderLoading = $Html.text(""); + _elm.View.Loading.values = {_op: _op + ,renderLoading: renderLoading}; + return _elm.View.Loading.values; +}; +Elm.View = Elm.View || {}; +Elm.View.LoggedIn = Elm.View.LoggedIn || {}; +Elm.View.LoggedIn.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.LoggedIn = _elm.View.LoggedIn || {}; + if (_elm.View.LoggedIn.values) + return _elm.View.LoggedIn.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.LoggedIn", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $Html$Attributes = Elm.Html.Attributes.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model = Elm.Model.make(_elm), + $Model$View$LoggedInView = Elm.Model.View.LoggedInView.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $View$LoggedIn$Account = Elm.View.LoggedIn.Account.make(_elm), + $View$LoggedIn$Add = Elm.View.LoggedIn.Add.make(_elm), + $View$LoggedIn$Monthly = Elm.View.LoggedIn.Monthly.make(_elm), + $View$LoggedIn$Paging = Elm.View.LoggedIn.Paging.make(_elm), + $View$LoggedIn$Table = Elm.View.LoggedIn.Table.make(_elm); + var renderLoggedIn = F2(function (model, + loggedInView) { + return A2($Html.div, + _L.fromArray([$Html$Attributes.$class("loggedIn")]), + _L.fromArray([A2($View$LoggedIn$Add.addPayment, + model, + loggedInView) + ,A2($Html.div, + _L.fromArray([$Html$Attributes.$class("expandables")]), + _L.fromArray([A2($View$LoggedIn$Account.account, + model, + loggedInView) + ,A2($View$LoggedIn$Monthly.monthlyPayments, + model, + loggedInView)])) + ,A2($View$LoggedIn$Table.paymentsTable, + model, + loggedInView) + ,$View$LoggedIn$Paging.paymentsPaging(loggedInView)])); + }); + _elm.View.LoggedIn.values = {_op: _op + ,renderLoggedIn: renderLoggedIn}; + return _elm.View.LoggedIn.values; +}; +Elm.View = Elm.View || {}; +Elm.View.LoggedIn = Elm.View.LoggedIn || {}; +Elm.View.LoggedIn.Account = Elm.View.LoggedIn.Account || {}; +Elm.View.LoggedIn.Account.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.LoggedIn = _elm.View.LoggedIn || {}; + _elm.View.LoggedIn.Account = _elm.View.LoggedIn.Account || {}; + if (_elm.View.LoggedIn.Account.values) + return _elm.View.LoggedIn.Account.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.LoggedIn.Account", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $Html$Attributes = Elm.Html.Attributes.make(_elm), + $Html$Events = Elm.Html.Events.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model = Elm.Model.make(_elm), + $Model$Payer = Elm.Model.Payer.make(_elm), + $Model$Translations = Elm.Model.Translations.make(_elm), + $Model$User = Elm.Model.User.make(_elm), + $Model$View$LoggedIn$Account = Elm.Model.View.LoggedIn.Account.make(_elm), + $Model$View$LoggedInView = Elm.Model.View.LoggedInView.make(_elm), + $Result = Elm.Result.make(_elm), + $ServerCommunication = Elm.ServerCommunication.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Update = Elm.Update.make(_elm), + $Update$LoggedIn = Elm.Update.LoggedIn.make(_elm), + $Update$LoggedIn$Account = Elm.Update.LoggedIn.Account.make(_elm), + $View$Events = Elm.View.Events.make(_elm), + $View$Expand = Elm.View.Expand.make(_elm), + $View$Price = Elm.View.Price.make(_elm); + var toggleIncomeEdition = F2(function (className, + name) { + return A2($Html.button, + _L.fromArray([$Html$Attributes.type$("button") + ,$Html$Attributes.$class(className) + ,A2($Html$Events.onClick, + $Update.actions.address, + function ($) { + return $Update.UpdateLoggedIn($Update$LoggedIn.UpdateAccount($)); + }($Update$LoggedIn$Account.ToggleIncomeEdition))]), + _L.fromArray([$Html.text(name)])); + }); + var incomeEdition = F3(function (model, + account, + edition) { + return A2($Html.form, + _L.fromArray([function () { + var _v0 = A2($Model$View$LoggedIn$Account.validateIncome, + edition.income, + model.translations); + switch (_v0.ctor) + {case "Err": + return A2($View$Events.onSubmitPrevDefault, + $Update.actions.address, + function ($) { + return $Update.UpdateLoggedIn($Update$LoggedIn.UpdateAccount($Update$LoggedIn$Account.UpdateEditionError($))); + }(_v0._0)); + case "Ok": + return A2($View$Events.onSubmitPrevDefault, + $ServerCommunication.serverCommunications.address, + A2($ServerCommunication.SetIncome, + model.currentTime, + _v0._0));} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }() + ,$Html$Attributes.$class("income")]), + _L.fromArray([A2($Html.label, + _L.fromArray([$Html$Attributes.$for("incomeInput")]), + _L.fromArray([$Html.text(A2($Model$Translations.getMessage, + "NewIncome", + model.translations))])) + ,A2($Html.input, + _L.fromArray([$Html$Attributes.id("incomeInput") + ,$Html$Attributes.value(edition.income) + ,A3($Html$Events.on, + "input", + $Html$Events.targetValue, + function ($) { + return $Signal.message($Update.actions.address)($Update.UpdateLoggedIn($Update$LoggedIn.UpdateAccount($Update$LoggedIn$Account.UpdateIncomeEdition($)))); + }) + ,$Html$Attributes.maxlength(10)]), + _L.fromArray([])) + ,A2($Html.button, + _L.fromArray([$Html$Attributes.type$("submit") + ,$Html$Attributes.$class("validateIncomeEdition")]), + _L.fromArray([$Html.text(A2($Model$Translations.getMessage, + "Validate", + model.translations))])) + ,A2(toggleIncomeEdition, + "undoIncomeEdition", + A2($Model$Translations.getMessage, + "Undo", + model.translations)) + ,function () { + var _v3 = edition.error; + switch (_v3.ctor) + {case "Just": + return A2($Html.div, + _L.fromArray([$Html$Attributes.$class("error")]), + _L.fromArray([$Html.text(_v3._0)])); + case "Nothing": + return $Html.text("");} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }()])); + }); + var incomeRead = F2(function (model, + account) { + return A2($Html.div, + _L.fromArray([$Html$Attributes.$class("income")]), + _L.fromArray([function () { + var _v5 = $Model$View$LoggedIn$Account.getCurrentIncome(account); + switch (_v5.ctor) + {case "Just": + return $Html.text(A3($Model$Translations.getParamMessage, + _L.fromArray([A2($View$Price.price, + model, + _v5._0)]), + "Income", + model.translations)); + case "Nothing": + return $Html.text(A2($Model$Translations.getMessage, + "NoIncome", + model.translations));} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }() + ,A2(toggleIncomeEdition, + "editIncomeEdition", + A2($Model$Translations.getMessage, + "Edit", + model.translations))])); + }); + var income = F2(function (model, + account) { + var _v7 = account.incomeEdition; + switch (_v7.ctor) + {case "Just": + return A3(incomeEdition, + model, + account, + _v7._0); + case "Nothing": + return A2(incomeRead, + model, + account);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var exceedingPayer = F3(function (model, + loggedInView, + payer) { + return A2($Html.div, + _L.fromArray([$Html$Attributes.$class("exceedingPayer")]), + _L.fromArray([A2($Html.span, + _L.fromArray([$Html$Attributes.$class("userName")]), + _L.fromArray([$Html.text($Maybe.withDefault("−")($Model$User.getUserName(loggedInView.users)(payer.userId)))])) + ,A2($Html.span, + _L.fromArray([$Html$Attributes.$class("amount")]), + _L.fromArray([$Html.text(A2($Basics._op["++"], + "+ ", + A2($View$Price.price, + model, + payer.amount)))]))])); + }); + var exceedingPayers = F2(function (model, + loggedInView) { + return A2($Html.button, + _L.fromArray([$Html$Attributes.$class("header") + ,A2($Html$Events.onClick, + $Update.actions.address, + function ($) { + return $Update.UpdateLoggedIn($Update$LoggedIn.UpdateAccount($)); + }($Update$LoggedIn$Account.ToggleDetail))]), + A2($Basics._op["++"], + A2($List.map, + A2(exceedingPayer, + model, + loggedInView), + A2($Model$Payer.getOrderedExceedingPayers, + model.currentTime, + loggedInView.account.payers)), + _L.fromArray([A2($View$Expand.expand, + $View$Expand.ExpandDown, + loggedInView.account.visibleDetail)]))); + }); + var account = F2(function (model, + loggedInView) { + var account = loggedInView.account; + return A2($Html.div, + _L.fromArray([$Html$Attributes.classList(_L.fromArray([{ctor: "_Tuple2" + ,_0: "account" + ,_1: true} + ,{ctor: "_Tuple2" + ,_0: "detail" + ,_1: account.visibleDetail}]))]), + _L.fromArray([A2(exceedingPayers, + model, + loggedInView) + ,account.visibleDetail ? A2(income, + model, + account) : $Html.text("")])); + }); + _elm.View.LoggedIn.Account.values = {_op: _op + ,account: account}; + return _elm.View.LoggedIn.Account.values; +}; +Elm.View = Elm.View || {}; +Elm.View.LoggedIn = Elm.View.LoggedIn || {}; +Elm.View.LoggedIn.Add = Elm.View.LoggedIn.Add || {}; +Elm.View.LoggedIn.Add.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.LoggedIn = _elm.View.LoggedIn || {}; + _elm.View.LoggedIn.Add = _elm.View.LoggedIn.Add || {}; + if (_elm.View.LoggedIn.Add.values) + return _elm.View.LoggedIn.Add.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.LoggedIn.Add", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $Html$Attributes = Elm.Html.Attributes.make(_elm), + $Html$Events = Elm.Html.Events.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model = Elm.Model.make(_elm), + $Model$Translations = Elm.Model.Translations.make(_elm), + $Model$View$LoggedIn$Add = Elm.Model.View.LoggedIn.Add.make(_elm), + $Model$View$LoggedInView = Elm.Model.View.LoggedInView.make(_elm), + $Result = Elm.Result.make(_elm), + $ServerCommunication = Elm.ServerCommunication.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Update = Elm.Update.make(_elm), + $Update$LoggedIn = Elm.Update.LoggedIn.make(_elm), + $Update$LoggedIn$Add = Elm.Update.LoggedIn.Add.make(_elm), + $Utils$Either = Elm.Utils.Either.make(_elm), + $Utils$Maybe = Elm.Utils.Maybe.make(_elm), + $View$Events = Elm.View.Events.make(_elm), + $View$Icon = Elm.View.Icon.make(_elm); + var paymentFrequency = F2(function (model, + addPayment) { + return A2($Html.button, + _L.fromArray([$Html$Attributes.type$("button") + ,$Html$Attributes.$class("frequency") + ,A2($Html$Events.onClick, + $Update.actions.address, + function ($) { + return $Update.UpdateLoggedIn($Update$LoggedIn.UpdateAdd($)); + }($Update$LoggedIn$Add.ToggleFrequency))]), + _L.fromArray([A2($Html.div, + _L.fromArray([$Html$Attributes.classList(_L.fromArray([{ctor: "_Tuple2" + ,_0: "punctual" + ,_1: true} + ,{ctor: "_Tuple2" + ,_0: "selected" + ,_1: _U.eq(addPayment.frequency, + $Model$View$LoggedIn$Add.Punctual)}]))]), + _L.fromArray([$Html.text(A2($Model$Translations.getMessage, + "Punctual", + model.translations))])) + ,A2($Html.div, + _L.fromArray([$Html$Attributes.classList(_L.fromArray([{ctor: "_Tuple2" + ,_0: "monthly" + ,_1: true} + ,{ctor: "_Tuple2" + ,_0: "selected" + ,_1: _U.eq(addPayment.frequency, + $Model$View$LoggedIn$Add.Monthly)}]))]), + _L.fromArray([$Html.text(A2($Model$Translations.getMessage, + "Monthly", + model.translations))]))])); + }); + var addPaymentCost = F2(function (model, + addPayment) { + return A2($Html.div, + _L.fromArray([$Html$Attributes.classList(_L.fromArray([{ctor: "_Tuple2" + ,_0: "cost" + ,_1: true} + ,{ctor: "_Tuple2" + ,_0: "error" + ,_1: $Utils$Maybe.isJust(addPayment.costError)}]))]), + _L.fromArray([A2($Html.input, + _L.fromArray([$Html$Attributes.id("costInput") + ,$Html$Attributes.value(addPayment.cost) + ,A3($Html$Events.on, + "input", + $Html$Events.targetValue, + function ($) { + return $Signal.message($Update.actions.address)($Update.UpdateLoggedIn($Update$LoggedIn.UpdateAdd($Update$LoggedIn$Add.UpdateCost($)))); + }) + ,$Html$Attributes.maxlength(7)]), + _L.fromArray([])) + ,A2($Html.label, + _L.fromArray([$Html$Attributes.$for("costInput")]), + _L.fromArray([$Html.text(model.config.currency)])) + ,function () { + var _v0 = addPayment.costError; + switch (_v0.ctor) + {case "Just": + return A2($Html.div, + _L.fromArray([$Html$Attributes.$class("errorMessage")]), + _L.fromArray([$Html.text(_v0._0)])); + case "Nothing": + return $Html.text("");} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }()])); + }); + var addPaymentName = function (addPayment) { + return A2($Html.div, + _L.fromArray([$Html$Attributes.classList(_L.fromArray([{ctor: "_Tuple2" + ,_0: "name" + ,_1: true} + ,{ctor: "_Tuple2" + ,_0: "error" + ,_1: $Utils$Maybe.isJust(addPayment.nameError)}]))]), + _L.fromArray([A2($Html.input, + _L.fromArray([$Html$Attributes.id("nameInput") + ,$Html$Attributes.value(addPayment.name) + ,A3($Html$Events.on, + "input", + $Html$Events.targetValue, + function ($) { + return $Signal.message($Update.actions.address)($Update.UpdateLoggedIn($Update$LoggedIn.UpdateAdd($Update$LoggedIn$Add.UpdateName($)))); + }) + ,$Html$Attributes.maxlength(20)]), + _L.fromArray([])) + ,A2($Html.label, + _L.fromArray([$Html$Attributes.$for("nameInput")]), + _L.fromArray([$View$Icon.renderIcon("shopping-cart")])) + ,function () { + var _v2 = addPayment.nameError; + switch (_v2.ctor) + {case "Just": + return A2($Html.div, + _L.fromArray([$Html$Attributes.$class("errorMessage")]), + _L.fromArray([$Html.text(_v2._0)])); + case "Nothing": + return $Html.text("");} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }()])); + }; + var addPayment = F2(function (model, + loggedInView) { + return A2($Html.form, + _L.fromArray([function () { + var _v4 = {ctor: "_Tuple2" + ,_0: A2($Model$View$LoggedIn$Add.validateName, + loggedInView.add.name, + model.translations) + ,_1: A2($Model$View$LoggedIn$Add.validateCost, + loggedInView.add.cost, + model.translations)}; + switch (_v4.ctor) + {case "_Tuple2": + switch (_v4._0.ctor) + {case "Ok": switch (_v4._1.ctor) + {case "Ok": + var action = function () { + var _v9 = loggedInView.add.frequency; + switch (_v9.ctor) + {case "Monthly": + return A2($ServerCommunication.AddMonthlyPayment, + _v4._0._0, + _v4._1._0); + case "Punctual": + return A3($ServerCommunication.AddPayment, + loggedInView.account.me, + _v4._0._0, + _v4._1._0);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }(); + return A2($View$Events.onSubmitPrevDefault, + $ServerCommunication.serverCommunications.address, + action);} + break;} + return A2($View$Events.onSubmitPrevDefault, + $Update.actions.address, + $Update.UpdateLoggedIn($Update$LoggedIn.UpdateAdd(A2($Update$LoggedIn$Add.AddError, + $Utils$Either.toMaybeError(_v4._0), + $Utils$Either.toMaybeError(_v4._1)))));} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }() + ,$Html$Attributes.$class("addPayment")]), + _L.fromArray([addPaymentName(loggedInView.add) + ,A2(addPaymentCost, + model, + loggedInView.add) + ,A2(paymentFrequency, + model, + loggedInView.add) + ,A2($Html.button, + _L.fromArray([$Html$Attributes.type$("submit") + ,$Html$Attributes.$class("add")]), + _L.fromArray([$Html.text(A2($Model$Translations.getMessage, + "Add", + model.translations))]))])); + }); + _elm.View.LoggedIn.Add.values = {_op: _op + ,addPayment: addPayment}; + return _elm.View.LoggedIn.Add.values; +}; +Elm.View = Elm.View || {}; +Elm.View.LoggedIn = Elm.View.LoggedIn || {}; +Elm.View.LoggedIn.Monthly = Elm.View.LoggedIn.Monthly || {}; +Elm.View.LoggedIn.Monthly.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.LoggedIn = _elm.View.LoggedIn || {}; + _elm.View.LoggedIn.Monthly = _elm.View.LoggedIn.Monthly || {}; + if (_elm.View.LoggedIn.Monthly.values) + return _elm.View.LoggedIn.Monthly.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.LoggedIn.Monthly", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $Html$Attributes = Elm.Html.Attributes.make(_elm), + $Html$Events = Elm.Html.Events.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model = Elm.Model.make(_elm), + $Model$Payment = Elm.Model.Payment.make(_elm), + $Model$Translations = Elm.Model.Translations.make(_elm), + $Model$View$LoggedIn$Monthly = Elm.Model.View.LoggedIn.Monthly.make(_elm), + $Model$View$LoggedInView = Elm.Model.View.LoggedInView.make(_elm), + $Result = Elm.Result.make(_elm), + $ServerCommunication = Elm.ServerCommunication.make(_elm), + $Signal = Elm.Signal.make(_elm), + $String = Elm.String.make(_elm), + $Update = Elm.Update.make(_elm), + $Update$LoggedIn = Elm.Update.LoggedIn.make(_elm), + $Update$LoggedIn$Monthly = Elm.Update.LoggedIn.Monthly.make(_elm), + $View$Expand = Elm.View.Expand.make(_elm), + $View$Icon = Elm.View.Icon.make(_elm), + $View$Price = Elm.View.Price.make(_elm); + var paymentLine = F3(function (model, + loggedInView, + payment) { + return A2($Html.a, + _L.fromArray([$Html$Attributes.classList(_L.fromArray([{ctor: "_Tuple2" + ,_0: "row" + ,_1: true} + ,{ctor: "_Tuple2" + ,_0: "edition" + ,_1: _U.eq(loggedInView.paymentEdition, + $Maybe.Just(payment.id))}])) + ,A2($Html$Events.onClick, + $Update.actions.address, + $Update.UpdateLoggedIn($Update$LoggedIn.ToggleEdit(payment.id)))]), + _L.fromArray([A2($Html.div, + _L.fromArray([$Html$Attributes.$class("cell category")]), + _L.fromArray([$Html.text(payment.name)])) + ,A2($Html.div, + _L.fromArray([$Html$Attributes.classList(_L.fromArray([{ctor: "_Tuple2" + ,_0: "cell cost" + ,_1: true} + ,{ctor: "_Tuple2" + ,_0: "refund" + ,_1: _U.cmp(payment.cost, + 0) < 0}]))]), + _L.fromArray([$Html.text(A2($View$Price.price, + model, + payment.cost))])) + ,A2($Html.div, + _L.fromArray([$Html$Attributes.$class("cell delete") + ,A2($Html$Events.onClick, + $ServerCommunication.serverCommunications.address, + $ServerCommunication.DeleteMonthlyPayment(payment.id))]), + _L.fromArray([A2($Html.button, + _L.fromArray([]), + _L.fromArray([$View$Icon.renderIcon("times")]))]))])); + }); + var paymentsTable = F3(function (model, + loggedInView, + monthly) { + return A2($Html.div, + _L.fromArray([$Html$Attributes.$class("table")]), + $List.map(A2(paymentLine, + model, + loggedInView))($List.sortBy(function ($) { + return $String.toLower(function (_) { + return _.name; + }($)); + })(monthly.payments))); + }); + var monthlyCount = F2(function (model, + monthly) { + var total = function ($) { + return $List.sum($List.map(function (_) { + return _.cost; + })($)); + }(monthly.payments); + var count = $List.length(monthly.payments); + var key = _U.cmp(count, + 1) > 0 ? "PluralMonthlyCount" : "SingularMonthlyCount"; + return A2($Html.button, + _L.fromArray([$Html$Attributes.$class("header") + ,A2($Html$Events.onClick, + $Update.actions.address, + function ($) { + return $Update.UpdateLoggedIn($Update$LoggedIn.UpdateMonthly($)); + }($Update$LoggedIn$Monthly.ToggleDetail))]), + _L.fromArray([$Html.text(A3($Model$Translations.getParamMessage, + _L.fromArray([$Basics.toString(count) + ,A2($View$Price.price, + model, + total)]), + key, + model.translations)) + ,A2($View$Expand.expand, + $View$Expand.ExpandDown, + monthly.visibleDetail)])); + }); + var monthlyPayments = F2(function (model, + loggedInView) { + var monthly = loggedInView.monthly; + return _U.eq($List.length(monthly.payments), + 0) ? $Html.text("") : A2($Html.div, + _L.fromArray([$Html$Attributes.classList(_L.fromArray([{ctor: "_Tuple2" + ,_0: "monthlyPayments" + ,_1: true} + ,{ctor: "_Tuple2" + ,_0: "detail" + ,_1: monthly.visibleDetail}]))]), + _L.fromArray([A2(monthlyCount, + model, + monthly) + ,monthly.visibleDetail ? A3(paymentsTable, + model, + loggedInView, + monthly) : $Html.text("")])); + }); + _elm.View.LoggedIn.Monthly.values = {_op: _op + ,monthlyPayments: monthlyPayments}; + return _elm.View.LoggedIn.Monthly.values; +}; +Elm.View = Elm.View || {}; +Elm.View.LoggedIn = Elm.View.LoggedIn || {}; +Elm.View.LoggedIn.Paging = Elm.View.LoggedIn.Paging || {}; +Elm.View.LoggedIn.Paging.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.LoggedIn = _elm.View.LoggedIn || {}; + _elm.View.LoggedIn.Paging = _elm.View.LoggedIn.Paging || {}; + if (_elm.View.LoggedIn.Paging.values) + return _elm.View.LoggedIn.Paging.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.LoggedIn.Paging", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $Html$Attributes = Elm.Html.Attributes.make(_elm), + $Html$Events = Elm.Html.Events.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model$Payment = Elm.Model.Payment.make(_elm), + $Model$View$LoggedInView = Elm.Model.View.LoggedInView.make(_elm), + $Result = Elm.Result.make(_elm), + $ServerCommunication = Elm.ServerCommunication.make(_elm), + $Signal = Elm.Signal.make(_elm), + $View$Icon = Elm.View.Icon.make(_elm); + var paymentsPage = F2(function (loggedInView, + page) { + var onCurrentPage = _U.eq(page, + loggedInView.currentPage); + return A2($Html.button, + _L.fromArray([$Html$Attributes.classList(_L.fromArray([{ctor: "_Tuple2" + ,_0: "page" + ,_1: true} + ,{ctor: "_Tuple2" + ,_0: "current" + ,_1: onCurrentPage}])) + ,$Html$Events.onClick($ServerCommunication.serverCommunications.address)(onCurrentPage ? $ServerCommunication.NoCommunication : $ServerCommunication.UpdatePage(page))]), + _L.fromArray([$Html.text($Basics.toString(page))])); + }); + var lastPage = function (maxPage) { + return A2($Html.button, + _L.fromArray([$Html$Attributes.$class("page") + ,A2($Html$Events.onClick, + $ServerCommunication.serverCommunications.address, + $ServerCommunication.UpdatePage(maxPage))]), + _L.fromArray([$View$Icon.renderIcon("fast-forward")])); + }; + var nextPage = function (loggedInView) { + return A2($Html.button, + _L.fromArray([$Html$Attributes.$class("page") + ,A2($Html$Events.onClick, + $ServerCommunication.serverCommunications.address, + $ServerCommunication.UpdatePage(loggedInView.currentPage + 1))]), + _L.fromArray([$View$Icon.renderIcon("forward")])); + }; + var previousPage = function (loggedInView) { + return A2($Html.button, + _L.fromArray([$Html$Attributes.$class("page") + ,A2($Html$Events.onClick, + $ServerCommunication.serverCommunications.address, + $ServerCommunication.UpdatePage(loggedInView.currentPage - 1))]), + _L.fromArray([$View$Icon.renderIcon("backward")])); + }; + var firstPage = A2($Html.button, + _L.fromArray([$Html$Attributes.$class("page") + ,A2($Html$Events.onClick, + $ServerCommunication.serverCommunications.address, + $ServerCommunication.UpdatePage(1))]), + _L.fromArray([$View$Icon.renderIcon("fast-backward")])); + var showedPages = 5; + var truncatePages = F2(function (currentPage, + pages) { + var showedRightPages = $Basics.floor(($Basics.toFloat(showedPages) - 1) / 2); + var showedLeftPages = $Basics.ceiling(($Basics.toFloat(showedPages) - 1) / 2); + var totalPages = $List.length(pages); + var truncatedPages = _U.cmp(currentPage, + showedLeftPages) < 0 ? _L.range(1, + showedPages) : _U.cmp(currentPage, + totalPages - showedRightPages) > 0 ? _L.range(totalPages - showedPages, + totalPages) : _L.range(currentPage - showedLeftPages, + currentPage + showedRightPages); + return A2($List.filter, + A2($Basics.flip, + $List.member, + pages), + truncatedPages); + }); + var paymentsPaging = function (loggedInView) { + var maxPage = $Basics.ceiling($Basics.toFloat(loggedInView.paymentsCount) / $Basics.toFloat($Model$Payment.perPage)); + var pages = A2(truncatePages, + loggedInView.currentPage, + _L.range(1,maxPage)); + return _U.eq(maxPage, + 1) ? $Html.text("") : A2($Html.div, + _L.fromArray([$Html$Attributes.$class("pages")]), + A2($Basics._op["++"], + _U.cmp(loggedInView.currentPage, + 1) > 0 ? _L.fromArray([firstPage + ,previousPage(loggedInView)]) : _L.fromArray([]), + A2($Basics._op["++"], + A2($List.map, + paymentsPage(loggedInView), + pages), + _U.cmp(loggedInView.currentPage, + maxPage) < 0 ? _L.fromArray([nextPage(loggedInView) + ,lastPage(maxPage)]) : _L.fromArray([])))); + }; + _elm.View.LoggedIn.Paging.values = {_op: _op + ,paymentsPaging: paymentsPaging}; + return _elm.View.LoggedIn.Paging.values; +}; +Elm.View = Elm.View || {}; +Elm.View.LoggedIn = Elm.View.LoggedIn || {}; +Elm.View.LoggedIn.Table = Elm.View.LoggedIn.Table || {}; +Elm.View.LoggedIn.Table.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.LoggedIn = _elm.View.LoggedIn || {}; + _elm.View.LoggedIn.Table = _elm.View.LoggedIn.Table || {}; + if (_elm.View.LoggedIn.Table.values) + return _elm.View.LoggedIn.Table.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.LoggedIn.Table", + $Basics = Elm.Basics.make(_elm), + $Date = Elm.Date.make(_elm), + $Html = Elm.Html.make(_elm), + $Html$Attributes = Elm.Html.Attributes.make(_elm), + $Html$Events = Elm.Html.Events.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model = Elm.Model.make(_elm), + $Model$Payment = Elm.Model.Payment.make(_elm), + $Model$User = Elm.Model.User.make(_elm), + $Model$View$LoggedInView = Elm.Model.View.LoggedInView.make(_elm), + $Result = Elm.Result.make(_elm), + $ServerCommunication = Elm.ServerCommunication.make(_elm), + $Signal = Elm.Signal.make(_elm), + $Update = Elm.Update.make(_elm), + $Update$LoggedIn = Elm.Update.LoggedIn.make(_elm), + $View$Date = Elm.View.Date.make(_elm), + $View$Icon = Elm.View.Icon.make(_elm), + $View$Price = Elm.View.Price.make(_elm); + var paymentLine = F3(function (model, + loggedInView, + payment) { + return A2($Html.a, + _L.fromArray([$Html$Attributes.classList(_L.fromArray([{ctor: "_Tuple2" + ,_0: "row" + ,_1: true} + ,{ctor: "_Tuple2" + ,_0: "edition" + ,_1: _U.eq(loggedInView.paymentEdition, + $Maybe.Just(payment.id))}])) + ,A2($Html$Events.onClick, + $Update.actions.address, + $Update.UpdateLoggedIn($Update$LoggedIn.ToggleEdit(payment.id)))]), + _L.fromArray([A2($Html.div, + _L.fromArray([$Html$Attributes.$class("cell category")]), + _L.fromArray([$Html.text(payment.name)])) + ,A2($Html.div, + _L.fromArray([$Html$Attributes.classList(_L.fromArray([{ctor: "_Tuple2" + ,_0: "cell cost" + ,_1: true} + ,{ctor: "_Tuple2" + ,_0: "refund" + ,_1: _U.cmp(payment.cost, + 0) < 0}]))]), + _L.fromArray([$Html.text(A2($View$Price.price, + model, + payment.cost))])) + ,A2($Html.div, + _L.fromArray([$Html$Attributes.$class("cell user")]), + _L.fromArray([$Html.text($Maybe.withDefault("−")($Model$User.getUserName(loggedInView.users)(payment.userId)))])) + ,A2($Html.div, + _L.fromArray([$Html$Attributes.$class("cell date")]), + _L.fromArray([A2($Html.span, + _L.fromArray([$Html$Attributes.$class("shortDate")]), + _L.fromArray([$Html.text(A2($View$Date.renderShortDate, + payment.creation, + model.translations))])) + ,A2($Html.span, + _L.fromArray([$Html$Attributes.$class("longDate")]), + _L.fromArray([$Html.text(A2($View$Date.renderLongDate, + payment.creation, + model.translations))]))])) + ,_U.eq(loggedInView.account.me, + payment.userId) ? A2($Html.div, + _L.fromArray([$Html$Attributes.$class("cell delete")]), + _L.fromArray([A2($Html.button, + _L.fromArray([A2($Html$Events.onClick, + $ServerCommunication.serverCommunications.address, + A2($ServerCommunication.DeletePayment, + payment, + loggedInView.currentPage))]), + _L.fromArray([$View$Icon.renderIcon("times")]))])) : A2($Html.div, + _L.fromArray([$Html$Attributes.$class("cell")]), + _L.fromArray([]))])); + }); + var paymentLines = F2(function (model, + loggedInView) { + return $List.map(A2(paymentLine, + model, + loggedInView))($List.reverse($List.sortBy(function ($) { + return $Date.toTime(function (_) { + return _.creation; + }($)); + })(loggedInView.payments))); + }); + var headerLine = function (model) { + return A2($Html.div, + _L.fromArray([$Html$Attributes.$class("header")]), + _L.fromArray([A2($Html.div, + _L.fromArray([$Html$Attributes.$class("cell category")]), + _L.fromArray([$View$Icon.renderIcon("shopping-cart")])) + ,A2($Html.div, + _L.fromArray([$Html$Attributes.$class("cell cost")]), + _L.fromArray([$Html.text(model.config.currency)])) + ,A2($Html.div, + _L.fromArray([$Html$Attributes.$class("cell user")]), + _L.fromArray([$View$Icon.renderIcon("user")])) + ,A2($Html.div, + _L.fromArray([$Html$Attributes.$class("cell date")]), + _L.fromArray([$View$Icon.renderIcon("calendar")])) + ,A2($Html.div, + _L.fromArray([$Html$Attributes.$class("cell")]), + _L.fromArray([]))])); + }; + var paymentsTable = F2(function (model, + loggedInView) { + return A2($Html.div, + _L.fromArray([$Html$Attributes.$class("table")]), + A2($List._op["::"], + headerLine(model), + A2(paymentLines, + model, + loggedInView))); + }); + _elm.View.LoggedIn.Table.values = {_op: _op + ,paymentsTable: paymentsTable}; + return _elm.View.LoggedIn.Table.values; +}; +Elm.View = Elm.View || {}; +Elm.View.Page = Elm.View.Page || {}; +Elm.View.Page.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.Page = _elm.View.Page || {}; + if (_elm.View.Page.values) + return _elm.View.Page.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.Page", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model = Elm.Model.make(_elm), + $Model$View = Elm.Model.View.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $View$Header = Elm.View.Header.make(_elm), + $View$Loading = Elm.View.Loading.make(_elm), + $View$LoggedIn = Elm.View.LoggedIn.make(_elm), + $View$SignIn = Elm.View.SignIn.make(_elm); + var renderMain = function (model) { + var _v0 = model.view; + switch (_v0.ctor) + {case "LoadingView": + return $View$Loading.renderLoading; + case "LoggedInView": + return A2($View$LoggedIn.renderLoggedIn, + model, + _v0._0); + case "SignInView": + return A2($View$SignIn.renderSignIn, + model, + _v0._0);} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }; + var renderPage = function (model) { + return A2($Html.div, + _L.fromArray([]), + _L.fromArray([$View$Header.renderHeader(model) + ,renderMain(model)])); + }; + _elm.View.Page.values = {_op: _op + ,renderPage: renderPage}; + return _elm.View.Page.values; +}; +Elm.View = Elm.View || {}; +Elm.View.Price = Elm.View.Price || {}; +Elm.View.Price.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.Price = _elm.View.Price || {}; + if (_elm.View.Price.values) + return _elm.View.Price.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.Price", + $Basics = Elm.Basics.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model = Elm.Model.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm), + $String = Elm.String.make(_elm); + var group = F2(function (n,xs) { + if (_U.cmp($List.length(xs), + n) < 1) + return _L.fromArray([xs]); + else { + var drop = A2($List.drop, + n, + xs); + var take = A2($List.take,n,xs); + return A2($List._op["::"], + take, + A2(group,n,drop)); + } + }); + var formatInt = function (n) { + return $String.append(_U.cmp(n, + 0) < 0 ? "-" : "")($String.fromList($List.reverse($List.concat($List.intersperse(_L.fromArray([_U.chr(" ")]))(group(3)($List.reverse($String.toList($Basics.toString($Basics.abs(n)))))))))); + }; + var price = F2(function (model, + amount) { + return A2($Basics._op["++"], + formatInt(amount), + A2($Basics._op["++"], + " ", + model.config.currency)); + }); + _elm.View.Price.values = {_op: _op + ,price: price}; + return _elm.View.Price.values; +}; +Elm.View = Elm.View || {}; +Elm.View.SignIn = Elm.View.SignIn || {}; +Elm.View.SignIn.make = function (_elm) { + "use strict"; + _elm.View = _elm.View || {}; + _elm.View.SignIn = _elm.View.SignIn || {}; + if (_elm.View.SignIn.values) + return _elm.View.SignIn.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "View.SignIn", + $Basics = Elm.Basics.make(_elm), + $Html = Elm.Html.make(_elm), + $Html$Attributes = Elm.Html.Attributes.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Model = Elm.Model.make(_elm), + $Model$Translations = Elm.Model.Translations.make(_elm), + $Model$View$SignInView = Elm.Model.View.SignInView.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var signInResult = F2(function (model, + signInView) { + var _v0 = signInView.result; + switch (_v0.ctor) + {case "Just": + switch (_v0._0.ctor) + {case "Err": + return A2($Html.div, + _L.fromArray([$Html$Attributes.$class("error")]), + _L.fromArray([$Html.text(_v0._0._0)])); + case "Ok": return A2($Html.div, + _L.fromArray([$Html$Attributes.$class("success")]), + _L.fromArray([$Html.text(A2($Model$Translations.getMessage, + "SignInEmailSent", + model.translations))]));} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + case "Nothing": + return $Html.text("");} + _U.badCase($moduleName, + "bugs in reporting the exact location right now"); + }); + var renderSignIn = F2(function (model, + signInView) { + return A2($Html.div, + _L.fromArray([$Html$Attributes.$class("signIn")]), + _L.fromArray([A2($Html.div, + _L.fromArray([$Html$Attributes.$class("result")]), + _L.fromArray([A2(signInResult, + model, + signInView)]))])); + }); + _elm.View.SignIn.values = {_op: _op + ,renderSignIn: renderSignIn}; + return _elm.View.SignIn.values; +}; +Elm.VirtualDom = Elm.VirtualDom || {}; +Elm.VirtualDom.make = function (_elm) { + "use strict"; + _elm.VirtualDom = _elm.VirtualDom || {}; + if (_elm.VirtualDom.values) + return _elm.VirtualDom.values; + var _op = {}, + _N = Elm.Native, + _U = _N.Utils.make(_elm), + _L = _N.List.make(_elm), + $moduleName = "VirtualDom", + $Basics = Elm.Basics.make(_elm), + $Graphics$Element = Elm.Graphics.Element.make(_elm), + $Json$Decode = Elm.Json.Decode.make(_elm), + $List = Elm.List.make(_elm), + $Maybe = Elm.Maybe.make(_elm), + $Native$VirtualDom = Elm.Native.VirtualDom.make(_elm), + $Result = Elm.Result.make(_elm), + $Signal = Elm.Signal.make(_elm); + var lazy3 = $Native$VirtualDom.lazy3; + var lazy2 = $Native$VirtualDom.lazy2; + var lazy = $Native$VirtualDom.lazy; + var defaultOptions = {_: {} + ,preventDefault: false + ,stopPropagation: false}; + var Options = F2(function (a, + b) { + return {_: {} + ,preventDefault: b + ,stopPropagation: a}; + }); + var onWithOptions = $Native$VirtualDom.on; + var on = F3(function (eventName, + decoder, + toMessage) { + return A4($Native$VirtualDom.on, + eventName, + defaultOptions, + decoder, + toMessage); + }); + var attribute = $Native$VirtualDom.attribute; + var property = $Native$VirtualDom.property; + var Property = {ctor: "Property"}; + var fromElement = $Native$VirtualDom.fromElement; + var toElement = $Native$VirtualDom.toElement; + var text = $Native$VirtualDom.text; + var node = $Native$VirtualDom.node; + var Node = {ctor: "Node"}; + _elm.VirtualDom.values = {_op: _op + ,text: text + ,node: node + ,toElement: toElement + ,fromElement: fromElement + ,property: property + ,attribute: attribute + ,on: on + ,onWithOptions: onWithOptions + ,defaultOptions: defaultOptions + ,lazy: lazy + ,lazy2: lazy2 + ,lazy3: lazy3 + ,Options: Options}; + return _elm.VirtualDom.values; +}; diff --git a/public/javascripts/elmLauncher.js b/public/javascripts/elmLauncher.js deleted file mode 100644 index bdcb479..0000000 --- a/public/javascripts/elmLauncher.js +++ /dev/null @@ -1,13 +0,0 @@ -Elm.fullscreen(Elm.Main, { - signInError: getParameterByName('signInError'), - initialTime: new Date().getTime(), - translations: document.getElementById('messages').innerHTML, - config: document.getElementById('config').innerHTML -}); - -function getParameterByName(name) { - name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); - var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), - results = regex.exec(location.search); - return results && decodeURIComponent(results[1].replace(/\+/g, " ")); -} diff --git a/public/javascripts/main.js b/public/javascripts/main.js new file mode 100644 index 0000000..12593e6 --- /dev/null +++ b/public/javascripts/main.js @@ -0,0 +1,28 @@ +var app = Elm.fullscreen(Elm.Main, { + initialTime: new Date().getTime(), + translations: document.getElementById('messages').innerHTML, + config: document.getElementById('config').innerHTML, + sign: null +}); + +navigator.id.watch({ + loggedInUser: null, + onlogin: function(assertion) { + app.ports.sign.send({ + operation: 'SignIn', + assertion: assertion + }); + }, + onlogout: function() {} +}); + +app.ports.persona.subscribe(function(communication) { + if(communication === 'SignIn') { + navigator.id.request(); + } else if(communication === 'SignOut') { + navigator.id.logout(); + app.ports.sign.send({ + operation: 'SignOut' + }); + } +}); diff --git a/sharedCost.cabal b/sharedCost.cabal index 8b35975..3303568 100644 --- a/sharedCost.cabal +++ b/sharedCost.cabal @@ -11,30 +11,31 @@ executable sharedCost hs-source-dirs: src/server ghc-options: -Wall -fwarn-incomplete-uni-patterns build-depends: base - , scotty == 0.10.1 - , wai == 3.0.3.0 - , wai-middleware-static == 0.7.0.1 - , http-types == 0.8.6 - , time == 1.5.0.1 - , text == 1.2.1.1 - , persistent == 2.2 - , persistent-sqlite == 2.2 - , persistent-template == 2.1.3.4 - , esqueleto == 2.2.7 - , monad-logger == 0.3.13.1 - , resourcet == 1.1.5 - , transformers == 0.4.2.0 - , blaze-html == 0.8.0.2 - , clay == 0.10.1 - , aeson == 0.9.0.1 - , scotty-cookie == 0.1.0.3 - , clientsession == 0.9.1.1 - , uuid == 1.3.10 - , email-validate == 2.1.3 - , mime-mail == 0.4.9 - , ConfigFile == 1.1.4 - , mtl == 2.2.1 - , lens == 4.12.3 - , parsec == 3.1.9 - , unordered-containers == 0.2.5.1 - , containers == 0.5.6.2 + , scotty + , wai + , wai-middleware-static + , http-types + , http-conduit + , time + , text + , bytestring + , persistent + , persistent-sqlite + , persistent-template + , esqueleto + , monad-logger + , resourcet + , transformers + , blaze-html + , clay + , aeson + , scotty-cookie + , clientsession + , uuid + , mime-mail + , ConfigFile + , mtl + , lens + , parsec + , unordered-containers + , containers diff --git a/src/client/Main.elm b/src/client/Main.elm deleted file mode 100644 index 4f96675..0000000 --- a/src/client/Main.elm +++ /dev/null @@ -1,101 +0,0 @@ -module Main - ( main - ) where - -import Graphics.Element exposing (..) - -import Html exposing (Html) - -import Http -import Task exposing (..) -import Time exposing (..) -import Json.Decode as Json exposing ((:=)) -import Dict - -import Model exposing (Model, initialModel) -import Model.User exposing (Users, usersDecoder, UserId, userIdDecoder) -import Model.Payment exposing (Payments, paymentsDecoder, perPage) -import Model.Payer exposing (Payers, payersDecoder) -import Model.Translations exposing (..) -import Model.Config exposing (..) - -import Update exposing (Action(..), actions, updateModel) -import Update.SignIn exposing (..) - -import View.Page exposing (renderPage) - -import ServerCommunication exposing (serverCommunications, sendRequest) - -main : Signal Html -main = Signal.map renderPage model - -model : Signal Model -model = Signal.foldp updateModel (initialModel initialTime translations config) update - -update : Signal Action -update = Signal.mergeMany - [ Signal.map UpdateTime (Time.every 1000) - , actions.signal - ] - ---------------------------------------- - -port signInError : Maybe String - ---------------------------------------- - -port initialTime : Time - ---------------------------------------- - -port translations : String - ---------------------------------------- - -port config : String - ---------------------------------------- - -port initView : Task Http.Error () -port initView = - case signInError of - Just msg -> - Signal.send actions.address (SignInError msg) - Nothing -> - Task.onError goLoggedInView (\_ -> Signal.send actions.address GoSignInView) - -goLoggedInView : Task Http.Error () -goLoggedInView = - Task.andThen getUsers <| \users -> - Task.andThen whoAmI <| \me -> - Task.andThen getMonthlyPayments <| \monthlyPayments -> - Task.andThen getPayments <| \payments -> - Task.andThen getPaymentsCount <| \paymentsCount -> - Task.andThen getPayers <| \payers -> - Signal.send actions.address (GoLoggedInView users me monthlyPayments payments paymentsCount payers) - -getUsers : Task Http.Error Users -getUsers = Http.get usersDecoder "/users" - -whoAmI : Task Http.Error UserId -whoAmI = Http.get ("id" := userIdDecoder) "/whoAmI" - -getMonthlyPayments : Task Http.Error Payments -getMonthlyPayments = Http.get paymentsDecoder "/monthlyPayments" - -getPayments : Task Http.Error Payments -getPayments = Http.get paymentsDecoder ("/payments?page=1&perPage=" ++ toString perPage) - -getPaymentsCount : Task Http.Error Int -getPaymentsCount = Http.get ("number" := Json.int) "/payments/count" - -getPayers : Task Http.Error Payers -getPayers = Http.get payersDecoder "/payers" - ---------------------------------------- - -port serverCommunicationsPort : Signal (Task Http.RawError ()) -port serverCommunicationsPort = - Signal.map - (\comm -> sendRequest comm `Task.andThen` (Signal.send actions.address)) - serverCommunications.signal diff --git a/src/client/Model.elm b/src/client/Model.elm deleted file mode 100644 index 43a19c5..0000000 --- a/src/client/Model.elm +++ /dev/null @@ -1,32 +0,0 @@ -module Model - ( Model - , initialModel - ) where - -import Time exposing (Time) -import Json.Decode as Json - -import Model.View exposing (..) -import Model.Translations exposing (..) -import Model.Config exposing (..) - -type alias Model = - { view : View - , currentTime : Time - , translations : Translations - , config : Config - } - -initialModel : Time -> String -> String -> Model -initialModel initialTime translationsValue configValue = - { view = LoadingView - , currentTime = initialTime - , translations = - case Json.decodeString translationsDecoder translationsValue of - Ok translations -> translations - Err err -> [] - , config = - case Json.decodeString configDecoder configValue of - Ok config -> config - Err err -> { currency = "" } - } diff --git a/src/client/Model/Config.elm b/src/client/Model/Config.elm deleted file mode 100644 index e47b032..0000000 --- a/src/client/Model/Config.elm +++ /dev/null @@ -1,18 +0,0 @@ -module Model.Config - ( Config - , configDecoder - ) where - -import Json.Decode exposing (..) - -type alias Config = - { currency : String - } - -configDecoder : Decoder Config -configDecoder = object1 Config ("currency" := string) - -defaultConfig : Config -defaultConfig = - { currency = "€" - } diff --git a/src/client/Model/Date.elm b/src/client/Model/Date.elm deleted file mode 100644 index 1c56de4..0000000 --- a/src/client/Model/Date.elm +++ /dev/null @@ -1,15 +0,0 @@ -module Model.Date - ( timeDecoder - , dateDecoder - ) where - -import Date as Date exposing (Date) -import Time exposing (Time) - -import Json.Decode as Json exposing (..) - -timeDecoder : Decoder Time -timeDecoder = Json.map Date.toTime dateDecoder - -dateDecoder : Decoder Date -dateDecoder = customDecoder string Date.fromString diff --git a/src/client/Model/Income.elm b/src/client/Model/Income.elm deleted file mode 100644 index 97a5652..0000000 --- a/src/client/Model/Income.elm +++ /dev/null @@ -1,76 +0,0 @@ -module Model.Income - ( Income - , incomeDecoder - , incomeDefinedForAll - , cumulativeIncomesSince - ) where - -import Json.Decode as Json exposing ((:=)) -import Time exposing (Time, hour) -import List exposing (..) - -import Model.Date exposing (timeDecoder) -import Model.User exposing (UserId) - -import Utils.Maybe exposing (isJust, catMaybes, maybeToList) - -type alias Income = - { creation : Time - , amount : Int - } - -incomeDecoder : Json.Decoder Income -incomeDecoder = - Json.object2 Income - ("creation" := timeDecoder) - ("amount" := Json.int) - -incomeDefinedForAll : List (List Income) -> Maybe Time -incomeDefinedForAll usersIncomes = - let firstIncomes = map (head << sortBy .creation) usersIncomes - in if all isJust firstIncomes - then head << reverse << List.sort << map .creation << catMaybes <| firstIncomes - else Nothing - -cumulativeIncomesSince : Time -> Time -> (List Income) -> Int -cumulativeIncomesSince currentTime since incomes = - cumulativeIncome currentTime (getOrderedIncomesSince since incomes) - -getOrderedIncomesSince : Time -> List Income -> List Income -getOrderedIncomesSince time incomes = - let mbStarterIncome = getIncomesAt time incomes - orderedIncomesSince = filter (\income -> income.creation >= time) incomes - in (maybeToList mbStarterIncome) ++ orderedIncomesSince - -getIncomesAt : Time -> List Income -> Maybe Income -getIncomesAt time incomes = - case incomes of - [x] -> - if x.creation < time - then Just { creation = time, amount = x.amount } - else Nothing - x1 :: x2 :: xs -> - if x1.creation < time && x2.creation > time - then Just { creation = time, amount = x2.amount } - else getIncomesAt time (x2 :: xs) - [] -> - Nothing - -cumulativeIncome : Time -> List Income -> Int -cumulativeIncome currentTime incomes = - getIncomesWithDuration (incomes ++ [{ creation = currentTime, amount = 0 }]) - |> map durationIncome - |> sum - -getIncomesWithDuration : List Income -> List (Float, Int) -getIncomesWithDuration incomes = - case incomes of - (income1 :: income2 :: xs) -> - (income2.creation - income1.creation, income1.amount) :: (getIncomesWithDuration (income2 :: xs)) - _ -> - [] - -durationIncome : (Float, Int) -> Int -durationIncome (duration, income) = - duration * toFloat income / (hour * 24 * 365 / 12) - |> truncate diff --git a/src/client/Model/Payer.elm b/src/client/Model/Payer.elm deleted file mode 100644 index 9fd1bb5..0000000 --- a/src/client/Model/Payer.elm +++ /dev/null @@ -1,132 +0,0 @@ -module Model.Payer - ( Payers - , Payer - , ExceedingPayer - , payersDecoder - , updatePayers - , getOrderedExceedingPayers - ) where - -import Json.Decode as Json exposing (..) -import Dict exposing (..) -import List -import Maybe -import Time exposing (Time) - -import Model.User exposing (UserId, userIdDecoder) -import Model.Income exposing (..) - -import Utils.Dict exposing (mapValues) -import Utils.Maybe exposing (isJust) - -type alias Payers = Dict UserId Payer - -type alias Payer = - { preIncomePaymentSum : Int - , postIncomePaymentSum : Int - , incomes : List Income - } - -payersDecoder : Decoder Payers -payersDecoder = Json.map Dict.fromList (list payerDecoder) - -payerDecoder : Decoder (UserId, Payer) -payerDecoder = - object2 (,) - ("userId" := userIdDecoder) - (object3 Payer - ("preIncomePaymentSum" := int) - ("postIncomePaymentSum" := int) - ("incomes" := list incomeDecoder)) - -updatePayers : Payers -> UserId -> Time -> Int -> Payers -updatePayers payers userId creation amountDiff = - payers - |> Dict.update userId (\mbPayer -> - case mbPayer of - Just payer -> - let postIncome = - payersIncomeDefinedForAll payers - |> Maybe.map (\date -> creation > date) - |> Maybe.withDefault False - in if postIncome - then - Just { payer | postIncomePaymentSum <- payer.postIncomePaymentSum + amountDiff } - else - Just { payer | preIncomePaymentSum <- payer.preIncomePaymentSum + amountDiff } - Nothing -> - Nothing - ) - -type alias ExceedingPayer = - { userId : UserId - , amount : Int - } - -getOrderedExceedingPayers : Time -> Payers -> List ExceedingPayer -getOrderedExceedingPayers currentTime payers = - let exceedingPayersOnPreIncome = - payers - |> mapValues .preIncomePaymentSum - |> Dict.toList - |> exceedingPayersFromAmounts - in case payersIncomeDefinedForAll payers of - Just since -> - let postPaymentPayers = - payers - |> mapValues (getPostPaymentPayer currentTime since) - mbMaxRatio = - postPaymentPayers - |> Dict.toList - |> List.map (.ratio << snd) - |> List.maximum - in case mbMaxRatio of - Just maxRatio -> - postPaymentPayers - |> mapValues (getFinalDiff maxRatio) - |> Dict.toList - |> exceedingPayersFromAmounts - Nothing -> - exceedingPayersOnPreIncome - Nothing -> - exceedingPayersOnPreIncome - -payersIncomeDefinedForAll : Payers -> Maybe Time -payersIncomeDefinedForAll payers = - incomeDefinedForAll (List.map (.incomes << snd) << Dict.toList <| payers) - -exceedingPayersFromAmounts : List (UserId, Int) -> List ExceedingPayer -exceedingPayersFromAmounts userAmounts = - let mbMinAmount = List.minimum << List.map snd <| userAmounts - in case mbMinAmount of - Nothing -> - [] - Just minAmount -> - userAmounts - |> List.map (\userAmount -> - { userId = fst userAmount - , amount = snd userAmount - minAmount - } - ) - |> List.filter (\payer -> payer.amount > 0) - -type alias PostPaymentPayer = - { preIncomePaymentSum : Int - , cumulativeIncome : Int - , ratio : Float - } - -getPostPaymentPayer : Time -> Time -> Payer -> PostPaymentPayer -getPostPaymentPayer currentTime since payer = - let cumulativeIncome = cumulativeIncomesSince currentTime since payer.incomes - in { preIncomePaymentSum = payer.preIncomePaymentSum - , cumulativeIncome = cumulativeIncome - , ratio = toFloat payer.postIncomePaymentSum / toFloat cumulativeIncome - } - -getFinalDiff : Float -> PostPaymentPayer -> Int -getFinalDiff maxRatio payer = - let postIncomeDiff = - -1 * (maxRatio - payer.ratio) * toFloat payer.cumulativeIncome - |> truncate - in postIncomeDiff + payer.preIncomePaymentSum diff --git a/src/client/Model/Payment.elm b/src/client/Model/Payment.elm deleted file mode 100644 index c4a8963..0000000 --- a/src/client/Model/Payment.elm +++ /dev/null @@ -1,44 +0,0 @@ -module Model.Payment - ( perPage - , Payments - , Payment - , PaymentId - , paymentsDecoder - , paymentIdDecoder - ) where - -import Date exposing (..) -import Json.Decode as Json exposing ((:=)) - -import Model.User exposing (UserId, userIdDecoder) -import Model.Date exposing (dateDecoder) - -perPage : Int -perPage = 8 - -type alias Payments = List Payment - -type alias Payment = - { id : PaymentId - , creation : Date - , name : String - , cost : Int - , userId : UserId - } - -type alias PaymentId = Int - -paymentsDecoder : Json.Decoder Payments -paymentsDecoder = Json.list paymentDecoder - -paymentDecoder : Json.Decoder Payment -paymentDecoder = - Json.object5 Payment - ("id" := paymentIdDecoder) - ("creation" := dateDecoder) - ("name" := Json.string) - ("cost" := Json.int) - ("userId" := userIdDecoder) - -paymentIdDecoder : Json.Decoder PaymentId -paymentIdDecoder = Json.int diff --git a/src/client/Model/Translations.elm b/src/client/Model/Translations.elm deleted file mode 100644 index bec8c9b..0000000 --- a/src/client/Model/Translations.elm +++ /dev/null @@ -1,69 +0,0 @@ -module Model.Translations - ( translationsDecoder - , Translations - , Translation - , getMessage - , getParamMessage - ) where - -import Maybe exposing (withDefault) -import Json.Decode as Json exposing ((:=)) -import String - -type alias Translations = List Translation - -translationsDecoder : Json.Decoder Translations -translationsDecoder = Json.list translationDecoder - -type alias Translation = - { key : String - , message : List MessagePart - } - -getTranslation : String -> Translations -> Maybe (List MessagePart) -getTranslation key translations = - translations - |> List.filter (\translation -> translation.key == key) - |> List.head - |> Maybe.map .message - -translationDecoder : Json.Decoder Translation -translationDecoder = - Json.object2 Translation - ("key" := Json.string) - ("message" := Json.list partDecoder) - -type MessagePart = - Order Int - | Str String - -partDecoder : Json.Decoder MessagePart -partDecoder = - ("tag" := Json.string) `Json.andThen` partDecoderWithTag - -partDecoderWithTag : String -> Json.Decoder MessagePart -partDecoderWithTag tag = - case tag of - "Order" -> Json.object1 Order ("contents" := Json.int) - "Str" -> Json.object1 Str ("contents" := Json.string) - ------ - -getMessage : String -> Translations -> String -getMessage = getParamMessage [] - -getParamMessage : List String -> String -> Translations -> String -getParamMessage values key translations = - getTranslation key translations - |> Maybe.map (\parts -> String.concat (List.map (replacePart values) parts)) - |> withDefault key - -replacePart : List String -> MessagePart -> String -replacePart values part = - case part of - Str str -> str - Order n -> - values - |> List.drop (n - 1) - |> List.head - |> withDefault ("{" ++ (toString n) ++ "}") diff --git a/src/client/Model/User.elm b/src/client/Model/User.elm deleted file mode 100644 index 1412913..0000000 --- a/src/client/Model/User.elm +++ /dev/null @@ -1,44 +0,0 @@ -module Model.User - ( Users - , usersDecoder - , User - , userDecoder - , UserId - , userIdDecoder - , getUserName - ) where - -import Json.Decode as Json exposing ((:=)) -import Dict exposing (Dict) - -type alias Users = Dict UserId User - -type alias UserId = Int - -type alias User = - { name : String - , email : String - } - -usersDecoder : Json.Decoder Users -usersDecoder = Json.map Dict.fromList (Json.list userWithIdDecoder) - -userWithIdDecoder : Json.Decoder (UserId, User) -userWithIdDecoder = - Json.object2 (,) - ("id" := userIdDecoder) - userDecoder - -userDecoder : Json.Decoder User -userDecoder = - Json.object2 User - ("name" := Json.string) - ("email" := Json.string) - -userIdDecoder : Json.Decoder UserId -userIdDecoder = Json.int - -getUserName : Users -> UserId -> Maybe String -getUserName users userId = - Dict.get userId users - |> Maybe.map .name diff --git a/src/client/Model/View.elm b/src/client/Model/View.elm deleted file mode 100644 index 90c0e53..0000000 --- a/src/client/Model/View.elm +++ /dev/null @@ -1,12 +0,0 @@ -module Model.View - ( View(..) - ) where - -import Model.Payment exposing (Payments) -import Model.View.SignInView exposing (..) -import Model.View.LoggedInView exposing (..) - -type View = - LoadingView - | SignInView SignInView - | LoggedInView LoggedInView diff --git a/src/client/Model/View/LoggedIn/Account.elm b/src/client/Model/View/LoggedIn/Account.elm deleted file mode 100644 index 2bb3ae7..0000000 --- a/src/client/Model/View/LoggedIn/Account.elm +++ /dev/null @@ -1,67 +0,0 @@ -module Model.View.LoggedIn.Account - ( Account - , IncomeEdition - , initAccount - , initIncomeEdition - , getCurrentIncome - , validateIncome - ) where - -import Result as Result exposing (Result(..)) -import Dict - -import Utils.Validation exposing (..) -import Utils.Dict exposing (mapValues) - -import Model.Translations exposing (..) -import Model.Payer exposing (..) -import Model.User exposing (UserId) - -type alias Account = - { me : UserId - , payers : Payers - , visibleDetail : Bool - , incomeEdition : Maybe IncomeEdition - } - -initAccount : UserId -> Payers -> Account -initAccount me payers = - { me = me - , payers = - payers - |> mapValues - (\payer -> - { payer | incomes <- List.sortBy .creation payer.incomes } - ) - , visibleDetail = False - , incomeEdition = Nothing - } - -getCurrentIncome : Account -> Maybe Int -getCurrentIncome account = - case Dict.get account.me account.payers of - Just payer -> - payer.incomes - |> List.sortBy .creation - |> List.reverse - |> List.head - |> Maybe.map .amount - Nothing -> - Nothing - -type alias IncomeEdition = - { income : String - , error : Maybe String - } - -initIncomeEdition : Int -> IncomeEdition -initIncomeEdition income = - { income = toString income - , error = Nothing - } - -validateIncome : String -> Translations -> Result String Int -validateIncome amount translations = - amount - |> validateNonEmpty (getMessage "IncomeRequired" translations) - |> flip Result.andThen (validateNumber (getMessage "IncomeMustBePositiveNumber" translations) (\number -> number > 0)) diff --git a/src/client/Model/View/LoggedIn/Add.elm b/src/client/Model/View/LoggedIn/Add.elm deleted file mode 100644 index 5598084..0000000 --- a/src/client/Model/View/LoggedIn/Add.elm +++ /dev/null @@ -1,43 +0,0 @@ -module Model.View.LoggedIn.Add - ( AddPayment - , Frequency(..) - , initAddPayment - , validateName - , validateCost - ) where - -import Result as Result exposing (Result(..)) - -import Utils.Validation exposing (..) - -import Model.Translations exposing (..) - -type alias AddPayment = - { name : String - , nameError : Maybe String - , cost : String - , costError : Maybe String - , frequency : Frequency - } - -initAddPayment : Frequency -> AddPayment -initAddPayment frequency = - { name = "" - , nameError = Nothing - , cost = "" - , costError = Nothing - , frequency = frequency - } - -validateName : String -> Translations -> Result String String -validateName name translations = - name - |> validateNonEmpty (getMessage "CategoryRequired" translations) - -validateCost : String -> Translations -> Result String Int -validateCost cost translations = - cost - |> validateNonEmpty (getMessage "CostRequired" translations) - |> flip Result.andThen (validateNumber (getMessage "CostMustBeNonNullNumber" translations) ((/=) 0)) - -type Frequency = Punctual | Monthly diff --git a/src/client/Model/View/LoggedIn/Edition.elm b/src/client/Model/View/LoggedIn/Edition.elm deleted file mode 100644 index da6d7b0..0000000 --- a/src/client/Model/View/LoggedIn/Edition.elm +++ /dev/null @@ -1,7 +0,0 @@ -module Model.View.LoggedIn.Edition - ( Edition - ) where - -import Model.Payment exposing (PaymentId) - -type alias Edition = PaymentId diff --git a/src/client/Model/View/LoggedIn/Monthly.elm b/src/client/Model/View/LoggedIn/Monthly.elm deleted file mode 100644 index 3c6f66a..0000000 --- a/src/client/Model/View/LoggedIn/Monthly.elm +++ /dev/null @@ -1,17 +0,0 @@ -module Model.View.LoggedIn.Monthly - ( Monthly - , initMonthly - ) where - -import Model.Payment exposing (Payments) - -type alias Monthly = - { payments : Payments - , visibleDetail : Bool - } - -initMonthly : Payments -> Monthly -initMonthly payments = - { payments = payments - , visibleDetail = False - } diff --git a/src/client/Model/View/LoggedInView.elm b/src/client/Model/View/LoggedInView.elm deleted file mode 100644 index 122c4be..0000000 --- a/src/client/Model/View/LoggedInView.elm +++ /dev/null @@ -1,35 +0,0 @@ -module Model.View.LoggedInView - ( LoggedInView - , initLoggedInView - ) where - -import Model.User exposing (Users, UserId) -import Model.Payment exposing (Payments) -import Model.Payer exposing (Payers) -import Model.View.LoggedIn.Add exposing (..) -import Model.View.LoggedIn.Edition exposing (..) -import Model.View.LoggedIn.Monthly exposing (..) -import Model.View.LoggedIn.Account exposing (..) - -type alias LoggedInView = - { users : Users - , add : AddPayment - , monthly : Monthly - , account : Account - , payments : Payments - , paymentsCount : Int - , paymentEdition : Maybe Edition - , currentPage : Int - } - -initLoggedInView : Users -> UserId -> Payments -> Payments -> Int -> Payers -> LoggedInView -initLoggedInView users me monthlyPayments payments paymentsCount payers = - { users = users - , add = initAddPayment Punctual - , monthly = initMonthly monthlyPayments - , account = initAccount me payers - , payments = payments - , paymentsCount = paymentsCount - , paymentEdition = Nothing - , currentPage = 1 - } diff --git a/src/client/Model/View/SignInView.elm b/src/client/Model/View/SignInView.elm deleted file mode 100644 index 0fbce39..0000000 --- a/src/client/Model/View/SignInView.elm +++ /dev/null @@ -1,15 +0,0 @@ -module Model.View.SignInView - ( SignInView - , initSignInView - ) where - -type alias SignInView = - { login : String - , result : Maybe (Result String String) - } - -initSignInView : SignInView -initSignInView = - { login = "" - , result = Nothing - } diff --git a/src/client/Native/Reads.js b/src/client/Native/Reads.js deleted file mode 100644 index 5785aed..0000000 --- a/src/client/Native/Reads.js +++ /dev/null @@ -1,22 +0,0 @@ -Elm.Native.Reads = {}; -Elm.Native.Reads.make = function(localRuntime) { - - localRuntime.Native = localRuntime.Native || {}; - localRuntime.Native.Reads = localRuntime.Native.Reads || {}; - if(localRuntime.Native.Reads.values) { - return localRuntime.Native.Reads.values; - } - - var Maybe = Elm.Maybe.make(localRuntime); - - function readInt(str) { - var number = Number(str); - return isNaN(number) || str === '' - ? Maybe.Nothing - : Maybe.Just(number); - } - - return localRuntime.Native.Reads.values = { - readInt: readInt - }; -}; diff --git a/src/client/Reads.elm b/src/client/Reads.elm deleted file mode 100644 index f855802..0000000 --- a/src/client/Reads.elm +++ /dev/null @@ -1,10 +0,0 @@ -module Reads - ( readInt - ) where - - -import Native.Reads -import Result exposing (Result) - -readInt : String -> Maybe Int -readInt = Native.Reads.readInt diff --git a/src/client/ServerCommunication.elm b/src/client/ServerCommunication.elm deleted file mode 100644 index 55bf947..0000000 --- a/src/client/ServerCommunication.elm +++ /dev/null @@ -1,143 +0,0 @@ -module ServerCommunication - ( Communication(..) - , sendRequest - , serverCommunications - ) where - -import Signal -import Task as Task exposing (Task) -import Http -import Json.Decode exposing (..) -import Date -import Time exposing (Time) - -import Model.User exposing (UserId) -import Model.Payment exposing (..) -import Model.View.LoggedIn.Add exposing (Frequency(..)) - -import Update as U -import Update.SignIn exposing (..) -import Update.LoggedIn as UL -import Update.LoggedIn.Monthly as UM -import Update.LoggedIn.Account as UA - -type Communication = - NoCommunication - | SignIn String - | AddPayment UserId String Int - | AddMonthlyPayment String Int - | SetIncome Time Int - | DeletePayment Payment Int - | DeleteMonthlyPayment PaymentId - | UpdatePage Int - | SignOut - -serverCommunications : Signal.Mailbox Communication -serverCommunications = Signal.mailbox NoCommunication - -sendRequest : Communication -> Task Http.RawError U.Action -sendRequest communication = - case getRequest communication of - Nothing -> - Task.succeed U.NoOp - Just request -> - Http.send Http.defaultSettings request - |> flip Task.andThen (serverResult communication) - -getRequest : Communication -> Maybe Http.Request -getRequest communication = - case communication of - NoCommunication -> Nothing - SignIn login -> Just (simple "post" ("/signIn?login=" ++ login)) - AddPayment userId name cost -> Just (addPaymentRequest name cost Punctual) - AddMonthlyPayment name cost -> Just (addPaymentRequest name cost Monthly) - SetIncome _ amount -> Just (simple "post" ("/income?amount=" ++ (toString amount))) - DeletePayment payment _ -> Just (deletePaymentRequest payment.id) - DeleteMonthlyPayment paymentId -> Just (deletePaymentRequest paymentId) - UpdatePage page -> Just (updatePageRequest page) - SignOut -> Just (simple "post" "/signOut") - -addPaymentRequest : String -> Int -> Frequency -> Http.Request -addPaymentRequest name cost frequency = - simple "post" ("/payment/add?name=" ++ name ++ "&cost=" ++ (toString cost) ++ "&frequency=" ++ (toString frequency)) - -deletePaymentRequest : PaymentId -> Http.Request -deletePaymentRequest id = - simple "post" ("payment/delete?id=" ++ (toString id)) - -updatePageRequest : Int -> Http.Request -updatePageRequest page = - simple "get" ("payments?page=" ++ toString page ++ "&perPage=" ++ toString perPage) - -simple : String -> String -> Http.Request -simple method url = - { verb = method - , headers = [] - , url = url - , body = Http.empty - } - -serverResult : Communication -> Http.Response -> Task Http.RawError U.Action -serverResult communication response = - case response.status of - 200 -> - case communication of - NoCommunication -> - Task.succeed U.NoOp - SignIn login -> - Task.succeed << U.UpdateSignIn <| ValidLogin login - AddPayment userId name cost -> - Http.send Http.defaultSettings (updatePageRequest 1) - |> flip Task.andThen (decodeOkResponse paymentsDecoder (\payments -> - Task.succeed <| U.UpdateLoggedIn (UL.AddPayment userId name cost payments) - )) - AddMonthlyPayment name cost -> - decodeResponse - ("id" := paymentIdDecoder) - (\id -> Task.succeed <| U.UpdateLoggedIn (UL.AddMonthlyPayment id name cost)) - response - SetIncome currentTime amount -> - Task.succeed <| U.UpdateLoggedIn (UL.UpdateAccount (UA.UpdateIncome currentTime amount)) - DeletePayment payment currentPage -> - Http.send Http.defaultSettings (updatePageRequest currentPage) - |> flip Task.andThen (decodeOkResponse paymentsDecoder (\payments -> - Task.succeed <| U.UpdateLoggedIn (UL.DeletePayment payment payments) - )) - DeleteMonthlyPayment id -> - Task.succeed <| U.UpdateLoggedIn (UL.UpdateMonthly (UM.DeletePayment id)) - UpdatePage page -> - decodeResponse - paymentsDecoder - (\payments -> Task.succeed <| U.UpdateLoggedIn (UL.UpdatePage page payments)) - response - SignOut -> - Task.succeed (U.GoSignInView) - errorStatus -> - case communication of - SignIn _ -> - decodeResponse - ("error" := string) - (\error -> - Task.succeed <| U.UpdateSignIn (ErrorLogin error) - ) - response - _ -> - Task.succeed <| U.NoOp - -decodeOkResponse : Decoder a -> (a -> Task b U.Action) -> Http.Response -> Task b U.Action -decodeOkResponse decoder responseToAction response = - if response.status == 200 - then decodeResponse decoder responseToAction response - else Task.succeed U.NoOp - -decodeResponse : Decoder a -> (a -> Task b U.Action) -> Http.Response -> Task b U.Action -decodeResponse decoder responseToAction response = - case response.value of - Http.Text text -> - case decodeString decoder text of - Ok x -> - responseToAction x - Err _ -> - Task.succeed U.NoOp - Http.Blob _ -> - Task.succeed U.NoOp diff --git a/src/client/Update.elm b/src/client/Update.elm deleted file mode 100644 index 3c4614a..0000000 --- a/src/client/Update.elm +++ /dev/null @@ -1,57 +0,0 @@ -module Update - ( Action(..) - , actions - , updateModel - ) where - -import Time exposing (Time) - -import Model exposing (Model) -import Model.User exposing (Users, UserId) -import Model.Payment exposing (Payments) -import Model.Payer exposing (Payers) -import Model.View as V -import Model.View.SignInView exposing (..) -import Model.View.LoggedInView exposing (..) - -import Update.SignIn exposing (..) -import Update.LoggedIn exposing (..) - -type Action = - NoOp - | UpdateTime Time - | GoSignInView - | SignInError String - | UpdateSignIn SignInAction - | GoLoggedInView Users UserId Payments Payments Int Payers - | UpdateLoggedIn LoggedAction - -actions : Signal.Mailbox Action -actions = Signal.mailbox NoOp - -updateModel : Action -> Model -> Model -updateModel action model = - case action of - NoOp -> - model - UpdateTime time -> - { model | currentTime <- time } - GoSignInView -> - { model | view <- V.SignInView initSignInView } - GoLoggedInView users me monthlyPayments payments paymentsCount payers -> - { model | view <- V.LoggedInView (initLoggedInView users me monthlyPayments payments paymentsCount payers) } - SignInError msg -> - let signInView = { initSignInView | result <- Just (Err msg) } - in { model | view <- V.SignInView signInView } - UpdateSignIn signInAction -> - case model.view of - V.SignInView signInView -> - { model | view <- V.SignInView (updateSignIn signInAction signInView) } - _ -> - model - UpdateLoggedIn loggedAction -> - case model.view of - V.LoggedInView loggedInView -> - { model | view <- V.LoggedInView (updateLoggedIn model loggedAction loggedInView) } - _ -> - model diff --git a/src/client/Update/LoggedIn.elm b/src/client/Update/LoggedIn.elm deleted file mode 100644 index e477094..0000000 --- a/src/client/Update/LoggedIn.elm +++ /dev/null @@ -1,68 +0,0 @@ -module Update.LoggedIn - ( LoggedAction(..) - , updateLoggedIn - ) where - -import Date -import Dict - -import Model exposing (Model) -import Model.User exposing (UserId) -import Model.Payment exposing (..) -import Model.View.LoggedInView exposing (..) -import Model.View.LoggedIn.Add exposing (..) - -import Update.LoggedIn.Add exposing (..) -import Update.LoggedIn.Monthly as UM -import Update.LoggedIn.Account as UA - -type LoggedAction = - UpdateAdd AddPaymentAction - | UpdatePayments Payments - | AddPayment UserId String Int Payments - | AddMonthlyPayment PaymentId String Int - | ToggleEdit PaymentId - | DeletePayment Payment Payments - | UpdatePage Int Payments - | UpdateMonthly UM.MonthlyAction - | UpdateAccount UA.AccountAction - -updateLoggedIn : Model -> LoggedAction -> LoggedInView -> LoggedInView -updateLoggedIn model action loggedInView = - case action of - UpdateAdd addPaymentAction -> - { loggedInView | add <- updateAddPayment addPaymentAction loggedInView.add } - UpdatePayments payments -> - { loggedInView | payments <- payments } - AddPayment userId name cost payments -> - { loggedInView - | payments <- payments - , currentPage <- 1 - , add <- initAddPayment Punctual - , account <- UA.updateAccount (UA.UpdatePayer userId model.currentTime cost) loggedInView.account - , paymentsCount <- loggedInView.paymentsCount + 1 - } - AddMonthlyPayment id name cost -> - { loggedInView - | add <- initAddPayment Monthly - , monthly <- - let payment = Payment id (Date.fromTime model.currentTime) name cost loggedInView.account.me - in UM.updateMonthly (UM.AddPayment payment) loggedInView.monthly - } - ToggleEdit id -> - { loggedInView | paymentEdition <- if loggedInView.paymentEdition == Just id then Nothing else Just id } - DeletePayment payment payments -> - { loggedInView - | payments <- payments - , account <- UA.updateAccount (UA.UpdatePayer payment.userId (Date.toTime payment.creation) -payment.cost) loggedInView.account - , paymentsCount <- loggedInView.paymentsCount - 1 - } - UpdatePage page payments -> - { loggedInView - | currentPage <- page - , payments <- payments - } - UpdateMonthly monthlyAction -> - { loggedInView | monthly <- UM.updateMonthly monthlyAction loggedInView.monthly } - UpdateAccount accountAction -> - { loggedInView | account <- UA.updateAccount accountAction loggedInView.account } diff --git a/src/client/Update/LoggedIn/Account.elm b/src/client/Update/LoggedIn/Account.elm deleted file mode 100644 index cf4c834..0000000 --- a/src/client/Update/LoggedIn/Account.elm +++ /dev/null @@ -1,64 +0,0 @@ -module Update.LoggedIn.Account - ( AccountAction(..) - , updateAccount - ) where - -import Maybe -import Time exposing (Time) -import Dict - -import Model.User exposing (UserId) -import Model.Payer exposing (..) -import Model.View.LoggedIn.Account exposing (..) - -import Utils.Maybe exposing (isJust) - -type AccountAction = - ToggleDetail - | UpdatePayer UserId Time Int - | ToggleIncomeEdition - | UpdateIncomeEdition String - | UpdateEditionError String - | UpdateIncome Time Int - -updateAccount : AccountAction -> Account -> Account -updateAccount action account = - case action of - ToggleDetail -> - { account | visibleDetail <- not account.visibleDetail } - UpdatePayer userId creation amountDiff -> - { account | payers <- updatePayers account.payers userId creation amountDiff } - ToggleIncomeEdition -> - { account | incomeEdition <- - if isJust account.incomeEdition - then Nothing - else Just (initIncomeEdition (Maybe.withDefault 0 (getCurrentIncome account))) - } - UpdateIncomeEdition income -> - case account.incomeEdition of - Just incomeEdition -> - { account | incomeEdition <- Just { incomeEdition | income <- income } } - Nothing -> - account - UpdateEditionError error -> - case account.incomeEdition of - Just incomeEdition -> - { account | incomeEdition <- Just { incomeEdition | error <- Just error } } - Nothing -> - account - UpdateIncome currentTime amount -> - { account - | payers <- - account.payers - |> Dict.update account.me (\mbPayer -> - case mbPayer of - Just payer -> - Just - { payer - | incomes <- payer.incomes ++ [{ creation = currentTime, amount = amount }] - } - Nothing -> - Nothing - ) - , incomeEdition <- Nothing - } diff --git a/src/client/Update/LoggedIn/Add.elm b/src/client/Update/LoggedIn/Add.elm deleted file mode 100644 index 1f28997..0000000 --- a/src/client/Update/LoggedIn/Add.elm +++ /dev/null @@ -1,29 +0,0 @@ -module Update.LoggedIn.Add - ( AddPaymentAction(..) - , updateAddPayment - ) where - -import Model.View.LoggedIn.Add exposing (..) - -type AddPaymentAction = - UpdateName String - | UpdateCost String - | AddError (Maybe String) (Maybe String) - | ToggleFrequency - -updateAddPayment : AddPaymentAction -> AddPayment -> AddPayment -updateAddPayment action addPayment = - case action of - UpdateName name -> - { addPayment | name <- name } - UpdateCost cost -> - { addPayment | cost <- cost } - AddError nameError costError -> - { addPayment - | nameError <- nameError - , costError <- costError - } - ToggleFrequency -> - { addPayment - | frequency <- if addPayment.frequency == Punctual then Monthly else Punctual - } diff --git a/src/client/Update/LoggedIn/Monthly.elm b/src/client/Update/LoggedIn/Monthly.elm deleted file mode 100644 index 1379323..0000000 --- a/src/client/Update/LoggedIn/Monthly.elm +++ /dev/null @@ -1,27 +0,0 @@ -module Update.LoggedIn.Monthly - ( MonthlyAction(..) - , updateMonthly - ) where - -import Model.Payment exposing (Payment, PaymentId) -import Model.View.LoggedIn.Monthly exposing (..) - -type MonthlyAction = - ToggleDetail - | AddPayment Payment - | DeletePayment PaymentId - -updateMonthly : MonthlyAction -> Monthly -> Monthly -updateMonthly action monthly = - case action of - ToggleDetail -> - { monthly | visibleDetail <- not monthly.visibleDetail } - AddPayment payment -> - { monthly - | payments <- payment :: monthly.payments - , visibleDetail <- True - } - DeletePayment id -> - { monthly - | payments <- List.filter (\payment -> payment.id /= id) monthly.payments - } diff --git a/src/client/Update/SignIn.elm b/src/client/Update/SignIn.elm deleted file mode 100644 index 0aa7c84..0000000 --- a/src/client/Update/SignIn.elm +++ /dev/null @@ -1,24 +0,0 @@ -module Update.SignIn - ( SignInAction(..) - , updateSignIn - ) where - -import Model.View.SignInView exposing (..) - -type SignInAction = - UpdateLogin String - | ValidLogin String - | ErrorLogin String - -updateSignIn : SignInAction -> SignInView -> SignInView -updateSignIn action signInView = - case action of - UpdateLogin login -> - { signInView | login <- login } - ValidLogin message -> - { signInView - | login <- "" - , result <- Just (Ok message) - } - ErrorLogin message -> - { signInView | result <- Just (Err message) } diff --git a/src/client/Utils/Dict.elm b/src/client/Utils/Dict.elm deleted file mode 100644 index dc01b17..0000000 --- a/src/client/Utils/Dict.elm +++ /dev/null @@ -1,11 +0,0 @@ -module Utils.Dict - ( mapValues - ) where - -import Dict as Dict exposing (..) - -mapValues : (a -> b) -> Dict comparable a -> Dict comparable b -mapValues f = Dict.fromList << List.map (onSecond f) << Dict.toList - -onSecond : (a -> b) -> (comparable, a) -> (comparable, b) -onSecond f tuple = case tuple of (x, y) -> (x, f y) diff --git a/src/client/Utils/Either.elm b/src/client/Utils/Either.elm deleted file mode 100644 index 10c40e3..0000000 --- a/src/client/Utils/Either.elm +++ /dev/null @@ -1,9 +0,0 @@ -module Utils.Either - ( toMaybeError - ) where - -toMaybeError : Result a b -> Maybe a -toMaybeError result = - case result of - Ok _ -> Nothing - Err x -> Just x diff --git a/src/client/Utils/Maybe.elm b/src/client/Utils/Maybe.elm deleted file mode 100644 index d954ae0..0000000 --- a/src/client/Utils/Maybe.elm +++ /dev/null @@ -1,27 +0,0 @@ -module Utils.Maybe - ( isJust - , catMaybes - , maybeToList - ) where - -isJust : Maybe a -> Bool -isJust maybe = - case maybe of - Just _ -> True - Nothing -> False - -catMaybes : List (Maybe a) -> List a -catMaybes = - List.foldr - (\mb xs -> - case mb of - Just x -> x :: xs - Nothing -> xs - ) - [] - -maybeToList : Maybe a -> List a -maybeToList mb = - case mb of - Just a -> [a] - Nothing -> [] diff --git a/src/client/Utils/Validation.elm b/src/client/Utils/Validation.elm deleted file mode 100644 index b9bccb3..0000000 --- a/src/client/Utils/Validation.elm +++ /dev/null @@ -1,23 +0,0 @@ -module Utils.Validation - ( validateNonEmpty - , validateNumber - ) where - -import String -import Reads exposing (readInt) - -validateNonEmpty : String -> String -> Result String String -validateNonEmpty message str = - if String.isEmpty str - then Err message - else Ok str - -validateNumber : String -> (Int -> Bool) -> String -> Result String Int -validateNumber message numberForm str = - case readInt str of - Just number -> - if numberForm number - then Ok number - else Err message - Nothing -> - Err message diff --git a/src/client/View/Date.elm b/src/client/View/Date.elm deleted file mode 100644 index 81c5112..0000000 --- a/src/client/View/Date.elm +++ /dev/null @@ -1,59 +0,0 @@ -module View.Date - ( renderShortDate - , renderLongDate - ) where - -import Date exposing (..) -import String - -import Model.Translations exposing (..) - -renderShortDate : Date -> Translations -> String -renderShortDate date translations = - let params = - [ String.pad 2 '0' (toString (Date.day date)) - , String.pad 2 '0' (toString (getMonthNumber (Date.month date))) - , toString (Date.year date) - ] - in getParamMessage params "ShortDate" translations - -renderLongDate : Date -> Translations -> String -renderLongDate date translations = - let params = - [ toString (Date.day date) - , (getMessage (getMonthKey (Date.month date)) translations) - , toString (Date.year date) - ] - in getParamMessage params "LongDate" translations - -getMonthNumber : Month -> Int -getMonthNumber month = - case month of - Jan -> 1 - Feb -> 2 - Mar -> 3 - Apr -> 4 - May -> 5 - Jun -> 6 - Jul -> 7 - Aug -> 8 - Sep -> 9 - Oct -> 10 - Nov -> 11 - Dec -> 12 - -getMonthKey : Month -> String -getMonthKey month = - case month of - Jan -> "January" - Feb -> "February" - Mar -> "March" - Apr -> "April" - May -> "May" - Jun -> "June" - Jul -> "July" - Aug -> "August" - Sep -> "September" - Oct -> "October" - Nov -> "November" - Dec -> "December" diff --git a/src/client/View/Events.elm b/src/client/View/Events.elm deleted file mode 100644 index 1eb9027..0000000 --- a/src/client/View/Events.elm +++ /dev/null @@ -1,19 +0,0 @@ -module View.Events - ( onSubmitPrevDefault - ) where - -import Signal -import Json.Decode as Json -import Html exposing (..) -import Html.Events exposing (..) -import Html.Attributes exposing (..) - -onSubmitPrevDefault : Signal.Address a -> a -> Attribute -onSubmitPrevDefault address value = - onWithOptions - "submit" - { defaultOptions | preventDefault <- True } - Json.value - (\_ -> - Signal.message address value - ) diff --git a/src/client/View/Expand.elm b/src/client/View/Expand.elm deleted file mode 100644 index 53b4fe5..0000000 --- a/src/client/View/Expand.elm +++ /dev/null @@ -1,25 +0,0 @@ -module View.Expand - ( expand - , ExpandType(..) - ) where - -import Html exposing (..) -import Html.Attributes exposing (..) - -import View.Icon exposing (renderIcon) - -type ExpandType = ExpandUp | ExpandDown - -expand : ExpandType -> Bool -> Html -expand expandType isExpanded = - div - [ class "expand" ] - [ renderIcon (chevronIcon expandType isExpanded) ] - -chevronIcon : ExpandType -> Bool -> String -chevronIcon expandType isExpanded = - case (expandType, isExpanded) of - (ExpandUp, True) -> "chevron-down" - (ExpandUp, False) -> "chevron-up" - (ExpandDown, True) -> "chevron-up" - (ExpandDown, False) -> "chevron-down" diff --git a/src/client/View/Header.elm b/src/client/View/Header.elm deleted file mode 100644 index 9d31183..0000000 --- a/src/client/View/Header.elm +++ /dev/null @@ -1,36 +0,0 @@ -module View.Header - ( renderHeader - ) where - -import Html exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (..) - -import ServerCommunication as SC -import ServerCommunication exposing (serverCommunications) - -import Model exposing (Model) -import Model.View exposing (..) -import Model.Translations exposing (getMessage) - -import View.Icon exposing (renderIcon) - -renderHeader : Model -> Html -renderHeader model = - header - [] - [ h1 - [] - [ text (getMessage "SharedCost" model.translations) ] - , case model.view of - LoadingView -> - text "" - SignInView _ -> - text "" - LoggedInView _ -> - button - [ class "signOut" - , onClick serverCommunications.address SC.SignOut - ] - [ renderIcon "power-off" ] - ] diff --git a/src/client/View/Icon.elm b/src/client/View/Icon.elm deleted file mode 100644 index f22c1a2..0000000 --- a/src/client/View/Icon.elm +++ /dev/null @@ -1,12 +0,0 @@ -module View.Icon - ( renderIcon - ) where - -import Html exposing (..) -import Html.Attributes exposing (..) - -renderIcon : String -> Html -renderIcon iconClass = - i - [ class <| "fa fa-fw fa-" ++ iconClass ] - [] diff --git a/src/client/View/Loading.elm b/src/client/View/Loading.elm deleted file mode 100644 index f8c6cd6..0000000 --- a/src/client/View/Loading.elm +++ /dev/null @@ -1,8 +0,0 @@ -module View.Loading - ( renderLoading - ) where - -import Html exposing (..) - -renderLoading : Html -renderLoading = text "" diff --git a/src/client/View/LoggedIn.elm b/src/client/View/LoggedIn.elm deleted file mode 100644 index 96916e0..0000000 --- a/src/client/View/LoggedIn.elm +++ /dev/null @@ -1,30 +0,0 @@ -module View.LoggedIn - ( renderLoggedIn - ) where - -import Html exposing (..) -import Html.Attributes exposing (..) - -import Model exposing (Model) -import Model.Payment exposing (Payments) -import Model.View.LoggedInView exposing (LoggedInView) - -import View.LoggedIn.Add exposing (addPayment) -import View.LoggedIn.Monthly exposing (monthlyPayments) -import View.LoggedIn.Account exposing (account) -import View.LoggedIn.Table exposing (paymentsTable) -import View.LoggedIn.Paging exposing (paymentsPaging) - -renderLoggedIn : Model -> LoggedInView -> Html -renderLoggedIn model loggedInView = - div - [ class "loggedIn" ] - [ addPayment model loggedInView - , div - [ class "expandables" ] - [ account model loggedInView - , monthlyPayments model loggedInView - ] - , paymentsTable model loggedInView - , paymentsPaging loggedInView - ] diff --git a/src/client/View/LoggedIn/Account.elm b/src/client/View/LoggedIn/Account.elm deleted file mode 100644 index 706f7cc..0000000 --- a/src/client/View/LoggedIn/Account.elm +++ /dev/null @@ -1,130 +0,0 @@ -module View.LoggedIn.Account - ( account - ) where - -import Html exposing (..) -import Html as H exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (..) -import List - -import ServerCommunication as SC exposing (serverCommunications) - -import Update exposing (..) -import Update.LoggedIn exposing (..) -import Update.LoggedIn.Account exposing (..) - -import Model exposing (Model) -import Model.User exposing (getUserName) -import Model.Payer exposing (..) -import Model.View.LoggedInView exposing (LoggedInView) -import Model.Translations exposing (getParamMessage, getMessage) -import Model.View.LoggedIn.Account exposing (..) - -import View.Expand exposing (..) -import View.Price exposing (price) -import View.Events exposing (onSubmitPrevDefault) - -import Utils.Either exposing (toMaybeError) - -account : Model -> LoggedInView -> Html -account model loggedInView = - let account = loggedInView.account - in div - [ classList - [ ("account", True) - , ("detail", account.visibleDetail) - ] - ] - [ exceedingPayers model loggedInView - , if account.visibleDetail - then income model account - else text "" - ] - -exceedingPayers : Model -> LoggedInView -> Html -exceedingPayers model loggedInView = - button - [ class "header" - , onClick actions.address (UpdateLoggedIn << UpdateAccount <| ToggleDetail) - ] - ( (List.map (exceedingPayer model loggedInView) (getOrderedExceedingPayers model.currentTime loggedInView.account.payers)) - ++ [ expand ExpandDown loggedInView.account.visibleDetail ] - ) - -exceedingPayer : Model -> LoggedInView -> ExceedingPayer -> Html -exceedingPayer model loggedInView payer = - div - [ class "exceedingPayer" ] - [ span - [ class "userName" ] - [ payer.userId - |> getUserName loggedInView.users - |> Maybe.withDefault "−" - |> text - ] - , span - [ class "amount" ] - [ text ("+ " ++ (price model payer.amount)) ] - ] - -income : Model -> Account -> Html -income model account = - case account.incomeEdition of - Just edition -> - incomeEdition model account edition - Nothing -> - incomeRead model account - -incomeRead : Model -> Account -> Html -incomeRead model account = - div - [ class "income" ] - [ ( case getCurrentIncome account of - Nothing -> - text (getMessage "NoIncome" model.translations) - Just income -> - text (getParamMessage [price model income] "Income" model.translations) - ) - , toggleIncomeEdition "editIncomeEdition" (getMessage "Edit" model.translations) - ] - -incomeEdition : Model -> Account -> IncomeEdition -> Html -incomeEdition model account edition = - H.form - [ case validateIncome edition.income model.translations of - Ok validatedAmount -> - onSubmitPrevDefault serverCommunications.address (SC.SetIncome model.currentTime validatedAmount) - Err error -> - onSubmitPrevDefault actions.address (UpdateLoggedIn << UpdateAccount << UpdateEditionError <| error) - , class "income" - ] - [ label - [ for "incomeInput" ] - [ text (getMessage "NewIncome" model.translations) ] - , input - [ id "incomeInput" - , value edition.income - , on "input" targetValue (Signal.message actions.address << UpdateLoggedIn << UpdateAccount << UpdateIncomeEdition) - , maxlength 10 - ] - [] - , button - [ type' "submit" - , class "validateIncomeEdition" - ] - [ text (getMessage "Validate" model.translations) ] - , toggleIncomeEdition "undoIncomeEdition" (getMessage "Undo" model.translations) - , case edition.error of - Just error -> div [ class "error" ] [ text error ] - Nothing -> text "" - ] - -toggleIncomeEdition : String -> String -> Html -toggleIncomeEdition className name = - button - [ type' "button" - , class className - , onClick actions.address (UpdateLoggedIn << UpdateAccount <| ToggleIncomeEdition) - ] - [ text name ] diff --git a/src/client/View/LoggedIn/Add.elm b/src/client/View/LoggedIn/Add.elm deleted file mode 100644 index 572bdf6..0000000 --- a/src/client/View/LoggedIn/Add.elm +++ /dev/null @@ -1,122 +0,0 @@ -module View.LoggedIn.Add - ( addPayment - ) where - -import Html as H exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (..) -import Reads exposing (readInt) -import Result exposing (..) - -import ServerCommunication as SC exposing (serverCommunications) - -import Update exposing (..) -import Update.LoggedIn exposing (..) -import Update.LoggedIn.Add exposing (..) - -import Model exposing (Model) -import Model.View.LoggedIn.Add exposing (..) -import Model.Translations exposing (getMessage) -import Model.View.LoggedInView exposing (LoggedInView) - -import View.Events exposing (onSubmitPrevDefault) -import View.Icon exposing (renderIcon) - -import Utils.Maybe exposing (isJust) -import Utils.Either exposing (toMaybeError) - -addPayment : Model -> LoggedInView -> Html -addPayment model loggedInView = - H.form - [ case (validateName loggedInView.add.name model.translations, validateCost loggedInView.add.cost model.translations) of - (Ok name, Ok cost) -> - let action = - case loggedInView.add.frequency of - Punctual -> SC.AddPayment loggedInView.account.me name cost - Monthly -> SC.AddMonthlyPayment name cost - in onSubmitPrevDefault serverCommunications.address action - (resName, resCost) -> - onSubmitPrevDefault actions.address (UpdateLoggedIn <| UpdateAdd <| AddError (toMaybeError resName) (toMaybeError resCost)) - , class "addPayment" - ] - [ addPaymentName loggedInView.add - , addPaymentCost model loggedInView.add - , paymentFrequency model loggedInView.add - , button - [ type' "submit" - , class "add" ] - [ text (getMessage "Add" model.translations)] - ] - -addPaymentName : AddPayment -> Html -addPaymentName addPayment = - div - [ classList - [ ("name", True) - , ("error", isJust addPayment.nameError) - ] - ] - [ input - [ id "nameInput" - , value addPayment.name - , on "input" targetValue (Signal.message actions.address << UpdateLoggedIn << UpdateAdd << UpdateName) - , maxlength 20 - ] - [] - , label - [ for "nameInput" ] - [ renderIcon "shopping-cart" ] - , case addPayment.nameError of - Just error -> - div [ class "errorMessage" ] [ text error ] - Nothing -> - text "" - ] - -addPaymentCost : Model -> AddPayment -> Html -addPaymentCost model addPayment = - div - [ classList - [ ("cost", True) - , ("error", isJust addPayment.costError) - ] - ] - [ input - [ id "costInput" - , value addPayment.cost - , on "input" targetValue (Signal.message actions.address << UpdateLoggedIn << UpdateAdd << UpdateCost) - , maxlength 7 - ] - [] - , label - [ for "costInput" ] - [ text model.config.currency ] - , case addPayment.costError of - Just error -> - div [ class "errorMessage" ] [ text error ] - Nothing -> - text "" - ] - -paymentFrequency : Model -> AddPayment -> Html -paymentFrequency model addPayment = - button - [ type' "button" - , class "frequency" - , onClick actions.address (UpdateLoggedIn << UpdateAdd <| ToggleFrequency) - ] - [ div - [ classList - [ ("punctual", True) - , ("selected", addPayment.frequency == Punctual) - ] - ] - [ text (getMessage "Punctual" model.translations) ] - , div - [ classList - [ ("monthly", True) - , ("selected", addPayment.frequency == Monthly) - ] - ] - [ text (getMessage "Monthly" model.translations) ] - ] diff --git a/src/client/View/LoggedIn/Monthly.elm b/src/client/View/LoggedIn/Monthly.elm deleted file mode 100644 index a274015..0000000 --- a/src/client/View/LoggedIn/Monthly.elm +++ /dev/null @@ -1,89 +0,0 @@ -module View.LoggedIn.Monthly - ( monthlyPayments - ) where - -import String - -import Html exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (..) - -import Update exposing (..) -import Update.LoggedIn exposing (..) -import Update.LoggedIn.Monthly exposing (..) - -import Model exposing (Model) -import Model.View.LoggedIn.Monthly exposing (Monthly) -import Model.Payment exposing (Payments, Payment) -import Model.View.LoggedInView exposing (LoggedInView) -import Model.Translations exposing (getMessage, getParamMessage) - -import ServerCommunication as SC exposing (serverCommunications) - -import View.Icon exposing (renderIcon) -import View.Expand exposing (..) -import View.Price exposing (price) - -monthlyPayments : Model -> LoggedInView -> Html -monthlyPayments model loggedInView = - let monthly = loggedInView.monthly - in if List.length monthly.payments == 0 - then - text "" - else - div - [ classList - [ ("monthlyPayments", True) - , ("detail", monthly.visibleDetail) - ] - ] - [ monthlyCount model monthly - , if monthly.visibleDetail then paymentsTable model loggedInView monthly else text "" - ] - -monthlyCount : Model -> Monthly -> Html -monthlyCount model monthly = - let count = List.length monthly.payments - total = List.sum << List.map .cost <| monthly.payments - key = if count > 1 then "PluralMonthlyCount" else "SingularMonthlyCount" - in button - [ class "header" - , onClick actions.address (UpdateLoggedIn << UpdateMonthly <| ToggleDetail) - ] - [ text (getParamMessage [toString count, price model total] key model.translations) - , expand ExpandDown monthly.visibleDetail - ] - -paymentsTable : Model -> LoggedInView -> Monthly -> Html -paymentsTable model loggedInView monthly = - div - [ class "table" ] - ( monthly.payments - |> List.sortBy (String.toLower << .name) - |> List.map (paymentLine model loggedInView) - ) - -paymentLine : Model -> LoggedInView -> Payment -> Html -paymentLine model loggedInView payment = - a - [ classList - [ ("row", True) - , ("edition", loggedInView.paymentEdition == Just payment.id) - ] - , onClick actions.address (UpdateLoggedIn (ToggleEdit payment.id)) - ] - [ div [ class "cell category" ] [ text (payment.name) ] - , div - [ classList - [ ("cell cost", True) - , ("refund", payment.cost < 0) - ] - ] - [ text (price model payment.cost) ] - , div - [ class "cell delete" - , onClick serverCommunications.address (SC.DeleteMonthlyPayment payment.id) - ] - [ button [] [ renderIcon "times" ] - ] - ] diff --git a/src/client/View/LoggedIn/Paging.elm b/src/client/View/LoggedIn/Paging.elm deleted file mode 100644 index 93d7f1d..0000000 --- a/src/client/View/LoggedIn/Paging.elm +++ /dev/null @@ -1,100 +0,0 @@ -module View.LoggedIn.Paging - ( paymentsPaging - ) where - -import Html exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (..) - -import Model.View.LoggedInView exposing (..) -import Model.Payment exposing (perPage) - -import ServerCommunication as SC exposing (serverCommunications) - -import Update exposing (..) -import Update.LoggedIn exposing (..) - -import View.Icon exposing (renderIcon) - -showedPages : Int -showedPages = 5 - -paymentsPaging : LoggedInView -> Html -paymentsPaging loggedInView = - let maxPage = ceiling (toFloat loggedInView.paymentsCount / toFloat perPage) - pages = truncatePages loggedInView.currentPage [1..maxPage] - in if maxPage == 1 - then - text "" - else - div - [ class "pages" ] - ( ( if loggedInView.currentPage > 1 - then [ firstPage, previousPage loggedInView ] - else [] - ) - ++ ( List.map (paymentsPage loggedInView) pages) - ++ ( if loggedInView.currentPage < maxPage - then [ nextPage loggedInView, lastPage maxPage ] - else [] - ) - ) - -truncatePages : Int -> List Int -> List Int -truncatePages currentPage pages = - let totalPages = List.length pages - showedLeftPages = ceiling ((toFloat showedPages - 1) / 2) - showedRightPages = floor ((toFloat showedPages - 1) / 2) - truncatedPages = - if | currentPage < showedLeftPages -> - [1..showedPages] - | currentPage > totalPages - showedRightPages -> - [(totalPages - showedPages)..totalPages] - | otherwise -> - [(currentPage - showedLeftPages)..(currentPage + showedRightPages)] - in List.filter (flip List.member pages) truncatedPages - -firstPage : Html -firstPage = - button - [ class "page" - , onClick serverCommunications.address (SC.UpdatePage 1) - ] - [ renderIcon "fast-backward" ] - -previousPage : LoggedInView -> Html -previousPage loggedInView = - button - [ class "page" - , onClick serverCommunications.address (SC.UpdatePage (loggedInView.currentPage - 1)) - ] - [ renderIcon "backward" ] - -nextPage : LoggedInView -> Html -nextPage loggedInView = - button - [ class "page" - , onClick serverCommunications.address (SC.UpdatePage (loggedInView.currentPage + 1)) - ] - [ renderIcon "forward" ] - -lastPage : Int -> Html -lastPage maxPage = - button - [ class "page" - , onClick serverCommunications.address (SC.UpdatePage maxPage) - ] - [ renderIcon "fast-forward" ] - -paymentsPage : LoggedInView -> Int -> Html -paymentsPage loggedInView page = - let onCurrentPage = page == loggedInView.currentPage - in button - [ classList - [ ("page", True) - , ("current", onCurrentPage) - ] - , onClick serverCommunications.address <| - if onCurrentPage then SC.NoCommunication else SC.UpdatePage page - ] - [ text (toString page) ] diff --git a/src/client/View/LoggedIn/Table.elm b/src/client/View/LoggedIn/Table.elm deleted file mode 100644 index f5a08b5..0000000 --- a/src/client/View/LoggedIn/Table.elm +++ /dev/null @@ -1,97 +0,0 @@ -module View.LoggedIn.Table - ( paymentsTable - ) where - -import Html exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (..) -import Dict exposing (..) - -import Date -import Date exposing (Date) - -import String exposing (append) - -import Model exposing (Model) -import Model.User exposing (getUserName) -import Model.Payment exposing (..) -import Model.View.LoggedInView exposing (LoggedInView) -import Model.Translations exposing (getMessage) - -import ServerCommunication as SC exposing (serverCommunications) - -import Update exposing (..) -import Update.LoggedIn exposing (..) - -import View.Icon exposing (renderIcon) -import View.Date exposing (..) -import View.Price exposing (price) - -paymentsTable : Model -> LoggedInView -> Html -paymentsTable model loggedInView = - div - [ class "table" ] - ( headerLine model :: paymentLines model loggedInView) - -headerLine : Model -> Html -headerLine model = - div - [ class "header" ] - [ div [ class "cell category" ] [ renderIcon "shopping-cart" ] - , div [ class "cell cost" ] [ text model.config.currency ] - , div [ class "cell user" ] [ renderIcon "user" ] - , div [ class "cell date" ] [ renderIcon "calendar" ] - , div [ class "cell" ] [] - ] - -paymentLines : Model -> LoggedInView -> List Html -paymentLines model loggedInView = - loggedInView.payments - |> List.sortBy (Date.toTime << .creation) - |> List.reverse - |> List.map (paymentLine model loggedInView) - -paymentLine : Model -> LoggedInView -> Payment -> Html -paymentLine model loggedInView payment = - a - [ classList - [ ("row", True) - , ("edition", loggedInView.paymentEdition == Just payment.id) - ] - , onClick actions.address (UpdateLoggedIn (ToggleEdit payment.id)) - ] - [ div [ class "cell category" ] [ text payment.name ] - , div - [ classList - [ ("cell cost", True) - , ("refund", payment.cost < 0) - ] - ] - [ text (price model payment.cost) ] - , div - [ class "cell user" ] - [ payment.userId - |> getUserName loggedInView.users - |> Maybe.withDefault "−" - |> text - ] - , div - [ class "cell date" ] - [ span - [ class "shortDate" ] - [ text (renderShortDate payment.creation model.translations) ] - , span - [ class "longDate" ] - [ text (renderLongDate payment.creation model.translations) ] - ] - , if loggedInView.account.me == payment.userId - then - div - [ class "cell delete" ] - [ button - [ onClick serverCommunications.address (SC.DeletePayment payment loggedInView.currentPage) ] - [ renderIcon "times" ] - ] - else - div [ class "cell" ] [] - ] diff --git a/src/client/View/Page.elm b/src/client/View/Page.elm deleted file mode 100644 index 763734d..0000000 --- a/src/client/View/Page.elm +++ /dev/null @@ -1,31 +0,0 @@ -module View.Page - ( renderPage - ) where - -import Html exposing (..) - -import Model exposing (Model) -import Model.View exposing (..) - -import View.Header exposing (renderHeader) -import View.Loading exposing (renderLoading) -import View.SignIn exposing (renderSignIn) -import View.LoggedIn exposing (renderLoggedIn) - -renderPage : Model -> Html -renderPage model = - div - [] - [ renderHeader model - , renderMain model - ] - -renderMain : Model -> Html -renderMain model = - case model.view of - LoadingView -> - renderLoading - SignInView signInView -> - renderSignIn model signInView - LoggedInView loggedInView -> - renderLoggedIn model loggedInView diff --git a/src/client/View/Price.elm b/src/client/View/Price.elm deleted file mode 100644 index 286bcaa..0000000 --- a/src/client/View/Price.elm +++ /dev/null @@ -1,38 +0,0 @@ -module View.Price - ( price - ) where - -import String exposing (..) - -import Model exposing (Model) -import Model.Translations exposing (getMessage) - -price : Model -> Int -> String -price model amount = - ( formatInt amount - ++ " " - ++ model.config.currency - ) - -formatInt : Int -> String -formatInt n = - abs n - |> toString - |> toList - |> List.reverse - |> group 3 - |> List.intersperse [' '] - |> List.concat - |> List.reverse - |> fromList - |> append (if n < 0 then "-" else "") - -group : Int -> List a -> List (List a) -group n xs = - if List.length xs <= n - then - [xs] - else - let take = List.take n xs - drop = List.drop n xs - in take :: (group n drop) diff --git a/src/client/View/SignIn.elm b/src/client/View/SignIn.elm deleted file mode 100644 index 2a6cbca..0000000 --- a/src/client/View/SignIn.elm +++ /dev/null @@ -1,57 +0,0 @@ -module View.SignIn - ( renderSignIn - ) where - -import Html as H exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (..) - -import Json.Decode as Json - -import Update exposing (..) -import Update.SignIn exposing (..) - -import ServerCommunication as SC -import ServerCommunication exposing (serverCommunications) - -import Model exposing (Model) -import Model.View.SignInView exposing (..) -import Model.Translations exposing (getMessage) - -import View.Events exposing (onSubmitPrevDefault) - -renderSignIn : Model -> SignInView -> Html -renderSignIn model signInView = - div - [ class "signIn" ] - [ H.form - [ onSubmitPrevDefault serverCommunications.address (SC.SignIn signInView.login) ] - [ input - [ value signInView.login - , on "input" targetValue (Signal.message actions.address << UpdateSignIn << UpdateLogin) - ] - [] - , button - [] - [ text (getMessage "SignIn" model.translations)] - ] - , div - [ class "result" ] - [ signInResult model signInView ] - ] - -signInResult : Model -> SignInView -> Html -signInResult model signInView = - case signInView.result of - Just result -> - case result of - Ok login -> - div - [ class "success" ] - [ text (getMessage "SignInEmailSent" model.translations) ] - Err error -> - div - [ class "error" ] - [ text error ] - Nothing -> - text "" diff --git a/src/client/elm/InitViewAction.elm b/src/client/elm/InitViewAction.elm new file mode 100644 index 0000000..7c353a7 --- /dev/null +++ b/src/client/elm/InitViewAction.elm @@ -0,0 +1,25 @@ +module InitViewAction + ( initViewAction + ) where + +import Task exposing (..) +import Http +import Json.Decode as Json exposing ((:=)) + +import Update exposing (Action(GoLoggedInView, GoSignInView)) + +import Model.Payment exposing (Payments, paymentsDecoder, perPage) +import Model.Payer exposing (Payers, payersDecoder) +import Model.User exposing (Users, usersDecoder, UserId, userIdDecoder) + +initViewAction : Task Http.Error Action +initViewAction = Task.onError loggedInView (always <| Task.succeed GoSignInView) + +loggedInView : Task Http.Error Action +loggedInView = + Task.map GoLoggedInView (Http.get usersDecoder "/users") + `Task.andMap` (Http.get ("id" := userIdDecoder) "/whoAmI") + `Task.andMap` (Http.get paymentsDecoder "/monthlyPayments") + `Task.andMap` (Http.get paymentsDecoder ("/payments?page=1&perPage=" ++ toString perPage)) + `Task.andMap` (Http.get ("number" := Json.int) "/payments/count") + `Task.andMap` (Http.get payersDecoder "/payers") diff --git a/src/client/elm/Main.elm b/src/client/elm/Main.elm new file mode 100644 index 0000000..f79d6a0 --- /dev/null +++ b/src/client/elm/Main.elm @@ -0,0 +1,89 @@ +module Main + ( main + ) where + +import Graphics.Element exposing (..) + +import Html exposing (Html) + +import Http +import Task exposing (..) +import Time exposing (..) +import Json.Decode as Json +import Dict +import String + +import Model exposing (Model, initialModel) +import Model.Translations exposing (..) +import Model.Config exposing (..) + +import Update exposing (Action(..), actions, updateModel) +import Update.SignIn exposing (..) + +import View.Page exposing (renderPage) + +import ServerCommunication as SC exposing (serverCommunications, sendRequest) + +import Persona as Persona exposing (operations) + +import InitViewAction exposing (initViewAction) + +import Sign + +main : Signal Html +main = Signal.map renderPage model + +model : Signal Model +model = Signal.foldp updateModel (initialModel initialTime translations config) update + +update : Signal Action +update = Signal.mergeMany + [ Signal.map UpdateTime (Time.every 1000) + , actions.signal + ] + +--------------------------------------- + +port initialTime : Time + +--------------------------------------- + +port translations : String + +--------------------------------------- + +port config : String + +--------------------------------------- + +port ready : Signal String +port ready = Signal.constant "ready" + +--------------------------------------- + +port initView : Task Http.Error () +port initView = initViewAction `Task.andThen` (Signal.send actions.address) + +--------------------------------------- + +port serverCommunicationsPort : Signal (Task Http.Error ()) +port serverCommunicationsPort = + Signal.map + (\comm -> + sendRequest comm + |> flip Task.andThen (\action -> Signal.send actions.address action) + ) + (Signal.merge signCommunication serverCommunications.signal) + +--------------------------------------- + +port persona : Signal String +port persona = Signal.map Persona.toString operations.signal + +--------------------------------------- + +port sign : Signal Json.Value + +signCommunication : Signal SC.Communication +signCommunication = + Signal.map (Sign.toServerCommunication << Sign.decodeOperation) sign diff --git a/src/client/elm/Model.elm b/src/client/elm/Model.elm new file mode 100644 index 0000000..43a19c5 --- /dev/null +++ b/src/client/elm/Model.elm @@ -0,0 +1,32 @@ +module Model + ( Model + , initialModel + ) where + +import Time exposing (Time) +import Json.Decode as Json + +import Model.View exposing (..) +import Model.Translations exposing (..) +import Model.Config exposing (..) + +type alias Model = + { view : View + , currentTime : Time + , translations : Translations + , config : Config + } + +initialModel : Time -> String -> String -> Model +initialModel initialTime translationsValue configValue = + { view = LoadingView + , currentTime = initialTime + , translations = + case Json.decodeString translationsDecoder translationsValue of + Ok translations -> translations + Err err -> [] + , config = + case Json.decodeString configDecoder configValue of + Ok config -> config + Err err -> { currency = "" } + } diff --git a/src/client/elm/Model/Config.elm b/src/client/elm/Model/Config.elm new file mode 100644 index 0000000..e47b032 --- /dev/null +++ b/src/client/elm/Model/Config.elm @@ -0,0 +1,18 @@ +module Model.Config + ( Config + , configDecoder + ) where + +import Json.Decode exposing (..) + +type alias Config = + { currency : String + } + +configDecoder : Decoder Config +configDecoder = object1 Config ("currency" := string) + +defaultConfig : Config +defaultConfig = + { currency = "€" + } diff --git a/src/client/elm/Model/Date.elm b/src/client/elm/Model/Date.elm new file mode 100644 index 0000000..1c56de4 --- /dev/null +++ b/src/client/elm/Model/Date.elm @@ -0,0 +1,15 @@ +module Model.Date + ( timeDecoder + , dateDecoder + ) where + +import Date as Date exposing (Date) +import Time exposing (Time) + +import Json.Decode as Json exposing (..) + +timeDecoder : Decoder Time +timeDecoder = Json.map Date.toTime dateDecoder + +dateDecoder : Decoder Date +dateDecoder = customDecoder string Date.fromString diff --git a/src/client/elm/Model/Income.elm b/src/client/elm/Model/Income.elm new file mode 100644 index 0000000..97a5652 --- /dev/null +++ b/src/client/elm/Model/Income.elm @@ -0,0 +1,76 @@ +module Model.Income + ( Income + , incomeDecoder + , incomeDefinedForAll + , cumulativeIncomesSince + ) where + +import Json.Decode as Json exposing ((:=)) +import Time exposing (Time, hour) +import List exposing (..) + +import Model.Date exposing (timeDecoder) +import Model.User exposing (UserId) + +import Utils.Maybe exposing (isJust, catMaybes, maybeToList) + +type alias Income = + { creation : Time + , amount : Int + } + +incomeDecoder : Json.Decoder Income +incomeDecoder = + Json.object2 Income + ("creation" := timeDecoder) + ("amount" := Json.int) + +incomeDefinedForAll : List (List Income) -> Maybe Time +incomeDefinedForAll usersIncomes = + let firstIncomes = map (head << sortBy .creation) usersIncomes + in if all isJust firstIncomes + then head << reverse << List.sort << map .creation << catMaybes <| firstIncomes + else Nothing + +cumulativeIncomesSince : Time -> Time -> (List Income) -> Int +cumulativeIncomesSince currentTime since incomes = + cumulativeIncome currentTime (getOrderedIncomesSince since incomes) + +getOrderedIncomesSince : Time -> List Income -> List Income +getOrderedIncomesSince time incomes = + let mbStarterIncome = getIncomesAt time incomes + orderedIncomesSince = filter (\income -> income.creation >= time) incomes + in (maybeToList mbStarterIncome) ++ orderedIncomesSince + +getIncomesAt : Time -> List Income -> Maybe Income +getIncomesAt time incomes = + case incomes of + [x] -> + if x.creation < time + then Just { creation = time, amount = x.amount } + else Nothing + x1 :: x2 :: xs -> + if x1.creation < time && x2.creation > time + then Just { creation = time, amount = x2.amount } + else getIncomesAt time (x2 :: xs) + [] -> + Nothing + +cumulativeIncome : Time -> List Income -> Int +cumulativeIncome currentTime incomes = + getIncomesWithDuration (incomes ++ [{ creation = currentTime, amount = 0 }]) + |> map durationIncome + |> sum + +getIncomesWithDuration : List Income -> List (Float, Int) +getIncomesWithDuration incomes = + case incomes of + (income1 :: income2 :: xs) -> + (income2.creation - income1.creation, income1.amount) :: (getIncomesWithDuration (income2 :: xs)) + _ -> + [] + +durationIncome : (Float, Int) -> Int +durationIncome (duration, income) = + duration * toFloat income / (hour * 24 * 365 / 12) + |> truncate diff --git a/src/client/elm/Model/Payer.elm b/src/client/elm/Model/Payer.elm new file mode 100644 index 0000000..9fd1bb5 --- /dev/null +++ b/src/client/elm/Model/Payer.elm @@ -0,0 +1,132 @@ +module Model.Payer + ( Payers + , Payer + , ExceedingPayer + , payersDecoder + , updatePayers + , getOrderedExceedingPayers + ) where + +import Json.Decode as Json exposing (..) +import Dict exposing (..) +import List +import Maybe +import Time exposing (Time) + +import Model.User exposing (UserId, userIdDecoder) +import Model.Income exposing (..) + +import Utils.Dict exposing (mapValues) +import Utils.Maybe exposing (isJust) + +type alias Payers = Dict UserId Payer + +type alias Payer = + { preIncomePaymentSum : Int + , postIncomePaymentSum : Int + , incomes : List Income + } + +payersDecoder : Decoder Payers +payersDecoder = Json.map Dict.fromList (list payerDecoder) + +payerDecoder : Decoder (UserId, Payer) +payerDecoder = + object2 (,) + ("userId" := userIdDecoder) + (object3 Payer + ("preIncomePaymentSum" := int) + ("postIncomePaymentSum" := int) + ("incomes" := list incomeDecoder)) + +updatePayers : Payers -> UserId -> Time -> Int -> Payers +updatePayers payers userId creation amountDiff = + payers + |> Dict.update userId (\mbPayer -> + case mbPayer of + Just payer -> + let postIncome = + payersIncomeDefinedForAll payers + |> Maybe.map (\date -> creation > date) + |> Maybe.withDefault False + in if postIncome + then + Just { payer | postIncomePaymentSum <- payer.postIncomePaymentSum + amountDiff } + else + Just { payer | preIncomePaymentSum <- payer.preIncomePaymentSum + amountDiff } + Nothing -> + Nothing + ) + +type alias ExceedingPayer = + { userId : UserId + , amount : Int + } + +getOrderedExceedingPayers : Time -> Payers -> List ExceedingPayer +getOrderedExceedingPayers currentTime payers = + let exceedingPayersOnPreIncome = + payers + |> mapValues .preIncomePaymentSum + |> Dict.toList + |> exceedingPayersFromAmounts + in case payersIncomeDefinedForAll payers of + Just since -> + let postPaymentPayers = + payers + |> mapValues (getPostPaymentPayer currentTime since) + mbMaxRatio = + postPaymentPayers + |> Dict.toList + |> List.map (.ratio << snd) + |> List.maximum + in case mbMaxRatio of + Just maxRatio -> + postPaymentPayers + |> mapValues (getFinalDiff maxRatio) + |> Dict.toList + |> exceedingPayersFromAmounts + Nothing -> + exceedingPayersOnPreIncome + Nothing -> + exceedingPayersOnPreIncome + +payersIncomeDefinedForAll : Payers -> Maybe Time +payersIncomeDefinedForAll payers = + incomeDefinedForAll (List.map (.incomes << snd) << Dict.toList <| payers) + +exceedingPayersFromAmounts : List (UserId, Int) -> List ExceedingPayer +exceedingPayersFromAmounts userAmounts = + let mbMinAmount = List.minimum << List.map snd <| userAmounts + in case mbMinAmount of + Nothing -> + [] + Just minAmount -> + userAmounts + |> List.map (\userAmount -> + { userId = fst userAmount + , amount = snd userAmount - minAmount + } + ) + |> List.filter (\payer -> payer.amount > 0) + +type alias PostPaymentPayer = + { preIncomePaymentSum : Int + , cumulativeIncome : Int + , ratio : Float + } + +getPostPaymentPayer : Time -> Time -> Payer -> PostPaymentPayer +getPostPaymentPayer currentTime since payer = + let cumulativeIncome = cumulativeIncomesSince currentTime since payer.incomes + in { preIncomePaymentSum = payer.preIncomePaymentSum + , cumulativeIncome = cumulativeIncome + , ratio = toFloat payer.postIncomePaymentSum / toFloat cumulativeIncome + } + +getFinalDiff : Float -> PostPaymentPayer -> Int +getFinalDiff maxRatio payer = + let postIncomeDiff = + -1 * (maxRatio - payer.ratio) * toFloat payer.cumulativeIncome + |> truncate + in postIncomeDiff + payer.preIncomePaymentSum diff --git a/src/client/elm/Model/Payment.elm b/src/client/elm/Model/Payment.elm new file mode 100644 index 0000000..c4a8963 --- /dev/null +++ b/src/client/elm/Model/Payment.elm @@ -0,0 +1,44 @@ +module Model.Payment + ( perPage + , Payments + , Payment + , PaymentId + , paymentsDecoder + , paymentIdDecoder + ) where + +import Date exposing (..) +import Json.Decode as Json exposing ((:=)) + +import Model.User exposing (UserId, userIdDecoder) +import Model.Date exposing (dateDecoder) + +perPage : Int +perPage = 8 + +type alias Payments = List Payment + +type alias Payment = + { id : PaymentId + , creation : Date + , name : String + , cost : Int + , userId : UserId + } + +type alias PaymentId = Int + +paymentsDecoder : Json.Decoder Payments +paymentsDecoder = Json.list paymentDecoder + +paymentDecoder : Json.Decoder Payment +paymentDecoder = + Json.object5 Payment + ("id" := paymentIdDecoder) + ("creation" := dateDecoder) + ("name" := Json.string) + ("cost" := Json.int) + ("userId" := userIdDecoder) + +paymentIdDecoder : Json.Decoder PaymentId +paymentIdDecoder = Json.int diff --git a/src/client/elm/Model/Translations.elm b/src/client/elm/Model/Translations.elm new file mode 100644 index 0000000..bec8c9b --- /dev/null +++ b/src/client/elm/Model/Translations.elm @@ -0,0 +1,69 @@ +module Model.Translations + ( translationsDecoder + , Translations + , Translation + , getMessage + , getParamMessage + ) where + +import Maybe exposing (withDefault) +import Json.Decode as Json exposing ((:=)) +import String + +type alias Translations = List Translation + +translationsDecoder : Json.Decoder Translations +translationsDecoder = Json.list translationDecoder + +type alias Translation = + { key : String + , message : List MessagePart + } + +getTranslation : String -> Translations -> Maybe (List MessagePart) +getTranslation key translations = + translations + |> List.filter (\translation -> translation.key == key) + |> List.head + |> Maybe.map .message + +translationDecoder : Json.Decoder Translation +translationDecoder = + Json.object2 Translation + ("key" := Json.string) + ("message" := Json.list partDecoder) + +type MessagePart = + Order Int + | Str String + +partDecoder : Json.Decoder MessagePart +partDecoder = + ("tag" := Json.string) `Json.andThen` partDecoderWithTag + +partDecoderWithTag : String -> Json.Decoder MessagePart +partDecoderWithTag tag = + case tag of + "Order" -> Json.object1 Order ("contents" := Json.int) + "Str" -> Json.object1 Str ("contents" := Json.string) + +----- + +getMessage : String -> Translations -> String +getMessage = getParamMessage [] + +getParamMessage : List String -> String -> Translations -> String +getParamMessage values key translations = + getTranslation key translations + |> Maybe.map (\parts -> String.concat (List.map (replacePart values) parts)) + |> withDefault key + +replacePart : List String -> MessagePart -> String +replacePart values part = + case part of + Str str -> str + Order n -> + values + |> List.drop (n - 1) + |> List.head + |> withDefault ("{" ++ (toString n) ++ "}") diff --git a/src/client/elm/Model/User.elm b/src/client/elm/Model/User.elm new file mode 100644 index 0000000..1412913 --- /dev/null +++ b/src/client/elm/Model/User.elm @@ -0,0 +1,44 @@ +module Model.User + ( Users + , usersDecoder + , User + , userDecoder + , UserId + , userIdDecoder + , getUserName + ) where + +import Json.Decode as Json exposing ((:=)) +import Dict exposing (Dict) + +type alias Users = Dict UserId User + +type alias UserId = Int + +type alias User = + { name : String + , email : String + } + +usersDecoder : Json.Decoder Users +usersDecoder = Json.map Dict.fromList (Json.list userWithIdDecoder) + +userWithIdDecoder : Json.Decoder (UserId, User) +userWithIdDecoder = + Json.object2 (,) + ("id" := userIdDecoder) + userDecoder + +userDecoder : Json.Decoder User +userDecoder = + Json.object2 User + ("name" := Json.string) + ("email" := Json.string) + +userIdDecoder : Json.Decoder UserId +userIdDecoder = Json.int + +getUserName : Users -> UserId -> Maybe String +getUserName users userId = + Dict.get userId users + |> Maybe.map .name diff --git a/src/client/elm/Model/View.elm b/src/client/elm/Model/View.elm new file mode 100644 index 0000000..90c0e53 --- /dev/null +++ b/src/client/elm/Model/View.elm @@ -0,0 +1,12 @@ +module Model.View + ( View(..) + ) where + +import Model.Payment exposing (Payments) +import Model.View.SignInView exposing (..) +import Model.View.LoggedInView exposing (..) + +type View = + LoadingView + | SignInView SignInView + | LoggedInView LoggedInView diff --git a/src/client/elm/Model/View/LoggedIn/Account.elm b/src/client/elm/Model/View/LoggedIn/Account.elm new file mode 100644 index 0000000..2bb3ae7 --- /dev/null +++ b/src/client/elm/Model/View/LoggedIn/Account.elm @@ -0,0 +1,67 @@ +module Model.View.LoggedIn.Account + ( Account + , IncomeEdition + , initAccount + , initIncomeEdition + , getCurrentIncome + , validateIncome + ) where + +import Result as Result exposing (Result(..)) +import Dict + +import Utils.Validation exposing (..) +import Utils.Dict exposing (mapValues) + +import Model.Translations exposing (..) +import Model.Payer exposing (..) +import Model.User exposing (UserId) + +type alias Account = + { me : UserId + , payers : Payers + , visibleDetail : Bool + , incomeEdition : Maybe IncomeEdition + } + +initAccount : UserId -> Payers -> Account +initAccount me payers = + { me = me + , payers = + payers + |> mapValues + (\payer -> + { payer | incomes <- List.sortBy .creation payer.incomes } + ) + , visibleDetail = False + , incomeEdition = Nothing + } + +getCurrentIncome : Account -> Maybe Int +getCurrentIncome account = + case Dict.get account.me account.payers of + Just payer -> + payer.incomes + |> List.sortBy .creation + |> List.reverse + |> List.head + |> Maybe.map .amount + Nothing -> + Nothing + +type alias IncomeEdition = + { income : String + , error : Maybe String + } + +initIncomeEdition : Int -> IncomeEdition +initIncomeEdition income = + { income = toString income + , error = Nothing + } + +validateIncome : String -> Translations -> Result String Int +validateIncome amount translations = + amount + |> validateNonEmpty (getMessage "IncomeRequired" translations) + |> flip Result.andThen (validateNumber (getMessage "IncomeMustBePositiveNumber" translations) (\number -> number > 0)) diff --git a/src/client/elm/Model/View/LoggedIn/Add.elm b/src/client/elm/Model/View/LoggedIn/Add.elm new file mode 100644 index 0000000..5598084 --- /dev/null +++ b/src/client/elm/Model/View/LoggedIn/Add.elm @@ -0,0 +1,43 @@ +module Model.View.LoggedIn.Add + ( AddPayment + , Frequency(..) + , initAddPayment + , validateName + , validateCost + ) where + +import Result as Result exposing (Result(..)) + +import Utils.Validation exposing (..) + +import Model.Translations exposing (..) + +type alias AddPayment = + { name : String + , nameError : Maybe String + , cost : String + , costError : Maybe String + , frequency : Frequency + } + +initAddPayment : Frequency -> AddPayment +initAddPayment frequency = + { name = "" + , nameError = Nothing + , cost = "" + , costError = Nothing + , frequency = frequency + } + +validateName : String -> Translations -> Result String String +validateName name translations = + name + |> validateNonEmpty (getMessage "CategoryRequired" translations) + +validateCost : String -> Translations -> Result String Int +validateCost cost translations = + cost + |> validateNonEmpty (getMessage "CostRequired" translations) + |> flip Result.andThen (validateNumber (getMessage "CostMustBeNonNullNumber" translations) ((/=) 0)) + +type Frequency = Punctual | Monthly diff --git a/src/client/elm/Model/View/LoggedIn/Edition.elm b/src/client/elm/Model/View/LoggedIn/Edition.elm new file mode 100644 index 0000000..da6d7b0 --- /dev/null +++ b/src/client/elm/Model/View/LoggedIn/Edition.elm @@ -0,0 +1,7 @@ +module Model.View.LoggedIn.Edition + ( Edition + ) where + +import Model.Payment exposing (PaymentId) + +type alias Edition = PaymentId diff --git a/src/client/elm/Model/View/LoggedIn/Monthly.elm b/src/client/elm/Model/View/LoggedIn/Monthly.elm new file mode 100644 index 0000000..3c6f66a --- /dev/null +++ b/src/client/elm/Model/View/LoggedIn/Monthly.elm @@ -0,0 +1,17 @@ +module Model.View.LoggedIn.Monthly + ( Monthly + , initMonthly + ) where + +import Model.Payment exposing (Payments) + +type alias Monthly = + { payments : Payments + , visibleDetail : Bool + } + +initMonthly : Payments -> Monthly +initMonthly payments = + { payments = payments + , visibleDetail = False + } diff --git a/src/client/elm/Model/View/LoggedInView.elm b/src/client/elm/Model/View/LoggedInView.elm new file mode 100644 index 0000000..122c4be --- /dev/null +++ b/src/client/elm/Model/View/LoggedInView.elm @@ -0,0 +1,35 @@ +module Model.View.LoggedInView + ( LoggedInView + , initLoggedInView + ) where + +import Model.User exposing (Users, UserId) +import Model.Payment exposing (Payments) +import Model.Payer exposing (Payers) +import Model.View.LoggedIn.Add exposing (..) +import Model.View.LoggedIn.Edition exposing (..) +import Model.View.LoggedIn.Monthly exposing (..) +import Model.View.LoggedIn.Account exposing (..) + +type alias LoggedInView = + { users : Users + , add : AddPayment + , monthly : Monthly + , account : Account + , payments : Payments + , paymentsCount : Int + , paymentEdition : Maybe Edition + , currentPage : Int + } + +initLoggedInView : Users -> UserId -> Payments -> Payments -> Int -> Payers -> LoggedInView +initLoggedInView users me monthlyPayments payments paymentsCount payers = + { users = users + , add = initAddPayment Punctual + , monthly = initMonthly monthlyPayments + , account = initAccount me payers + , payments = payments + , paymentsCount = paymentsCount + , paymentEdition = Nothing + , currentPage = 1 + } diff --git a/src/client/elm/Model/View/SignInView.elm b/src/client/elm/Model/View/SignInView.elm new file mode 100644 index 0000000..0fbce39 --- /dev/null +++ b/src/client/elm/Model/View/SignInView.elm @@ -0,0 +1,15 @@ +module Model.View.SignInView + ( SignInView + , initSignInView + ) where + +type alias SignInView = + { login : String + , result : Maybe (Result String String) + } + +initSignInView : SignInView +initSignInView = + { login = "" + , result = Nothing + } diff --git a/src/client/elm/Native/Reads.js b/src/client/elm/Native/Reads.js new file mode 100644 index 0000000..5785aed --- /dev/null +++ b/src/client/elm/Native/Reads.js @@ -0,0 +1,22 @@ +Elm.Native.Reads = {}; +Elm.Native.Reads.make = function(localRuntime) { + + localRuntime.Native = localRuntime.Native || {}; + localRuntime.Native.Reads = localRuntime.Native.Reads || {}; + if(localRuntime.Native.Reads.values) { + return localRuntime.Native.Reads.values; + } + + var Maybe = Elm.Maybe.make(localRuntime); + + function readInt(str) { + var number = Number(str); + return isNaN(number) || str === '' + ? Maybe.Nothing + : Maybe.Just(number); + } + + return localRuntime.Native.Reads.values = { + readInt: readInt + }; +}; diff --git a/src/client/elm/Persona.elm b/src/client/elm/Persona.elm new file mode 100644 index 0000000..51b5fc6 --- /dev/null +++ b/src/client/elm/Persona.elm @@ -0,0 +1,28 @@ +module Persona + ( Operation(..) + , operations + , fromString + , toString + ) where + +type Operation = + NoOp + | SignIn + | SignOut + +operations : Signal.Mailbox Operation +operations = Signal.mailbox NoOp + +fromString : String -> Operation +fromString str = + case str of + "SignIn" -> SignIn + "SignOut" -> SignOut + _ -> NoOp + +toString : Operation -> String +toString operation = + case operation of + SignIn -> "SignIn" + SignOut -> "SignOut" + _ -> "NoOp" diff --git a/src/client/elm/Reads.elm b/src/client/elm/Reads.elm new file mode 100644 index 0000000..f855802 --- /dev/null +++ b/src/client/elm/Reads.elm @@ -0,0 +1,10 @@ +module Reads + ( readInt + ) where + + +import Native.Reads +import Result exposing (Result) + +readInt : String -> Maybe Int +readInt = Native.Reads.readInt diff --git a/src/client/elm/ServerCommunication.elm b/src/client/elm/ServerCommunication.elm new file mode 100644 index 0000000..70612cb --- /dev/null +++ b/src/client/elm/ServerCommunication.elm @@ -0,0 +1,95 @@ +module ServerCommunication + ( Communication(..) + , sendRequest + , serverCommunications + ) where + +import Signal +import Task as Task exposing (Task) +import Http +import Json.Decode exposing (..) +import Date +import Time exposing (Time) +import Debug + +import SimpleHTTP exposing (..) + +import Model.User exposing (UserId) +import Model.Payment exposing (..) +import Model.View.LoggedIn.Add exposing (Frequency(..)) + +import Update as U +import Update.SignIn exposing (..) +import Update.LoggedIn as UL +import Update.LoggedIn.Monthly as UM +import Update.LoggedIn.Account as UA + +import InitViewAction exposing (initViewAction) + +type Communication = + NoCommunication + | SignIn String + | AddPayment UserId String Int + | AddMonthlyPayment String Int + | SetIncome Time Int + | DeletePayment Payment Int + | DeleteMonthlyPayment PaymentId + | UpdatePage Int + | SignOut + +serverCommunications : Signal.Mailbox Communication +serverCommunications = Signal.mailbox NoCommunication + +sendRequest : Communication -> Task Http.Error U.Action +sendRequest communication = + case communication of + + NoCommunication -> + Task.succeed U.NoOp + + SignIn assertion -> + post ("/signIn?assertion=" ++ assertion) + |> flip Task.andThen (always initViewAction) + + AddPayment userId name cost -> + post (addPaymentURL name cost Punctual) + |> flip Task.andThen (always (getPaymentsAtPage 1)) + |> Task.map (\payments -> U.UpdateLoggedIn (UL.AddPayment userId name cost payments)) + + AddMonthlyPayment name cost -> + post (addPaymentURL name cost Monthly) + |> flip Task.andThen (decodeHttpValue <| "id" := paymentIdDecoder) + |> Task.map (\id -> U.UpdateLoggedIn (UL.AddMonthlyPayment id name cost)) + + DeletePayment payment currentPage -> + post (deletePaymentURL payment.id) + |> flip Task.andThen (always (getPaymentsAtPage currentPage)) + |> Task.map (\payments -> U.UpdateLoggedIn (UL.DeletePayment payment payments)) + + DeleteMonthlyPayment id -> + post (deletePaymentURL id) + |> Task.map (always (U.UpdateLoggedIn (UL.UpdateMonthly (UM.DeletePayment id)))) + + UpdatePage page -> + getPaymentsAtPage page + |> flip Task.andThen (Task.succeed << U.UpdateLoggedIn << UL.UpdatePage page) + + SetIncome currentTime amount -> + post ("/income?amount=" ++ (toString amount)) + |> Task.map (always (U.UpdateLoggedIn (UL.UpdateAccount (UA.UpdateIncome currentTime amount)))) + + SignOut -> + post "/signOut" + |> Task.map (always U.GoSignInView) + +getPaymentsAtPage : Int -> Task Http.Error Payments +getPaymentsAtPage page = + Http.get paymentsDecoder ("payments?page=" ++ toString page ++ "&perPage=" ++ toString perPage) + +addPaymentURL : String -> Int -> Frequency -> String +addPaymentURL name cost frequency = + "/payment/add?name=" ++ name ++ "&cost=" ++ (toString cost) ++ "&frequency=" ++ (toString frequency) + +deletePaymentURL : PaymentId -> String +deletePaymentURL id = + "payment/delete?id=" ++ (toString id) diff --git a/src/client/elm/Sign.elm b/src/client/elm/Sign.elm new file mode 100644 index 0000000..44f23b8 --- /dev/null +++ b/src/client/elm/Sign.elm @@ -0,0 +1,43 @@ +module Sign + ( Operation(..) + , decodeOperation + , toServerCommunication + ) where + +import Json.Decode as Json +import Json.Decode exposing (Value, Decoder, (:=)) +import Maybe + +import ServerCommunication as SC + +type Operation = + NoOp + | SignIn String + | SignOut + +decodeOperation : Value -> Operation +decodeOperation value = + Json.decodeValue operationDecoder value + |> Result.toMaybe + |> Maybe.withDefault NoOp + +toServerCommunication : Operation -> SC.Communication +toServerCommunication operation = + case operation of + NoOp -> SC.NoCommunication + SignIn assertion -> SC.SignIn assertion + SignOut -> SC.SignOut + +operationDecoder : Decoder Operation +operationDecoder = + ("operation" := Json.string) `Json.andThen` operationDecoderWithTag + +operationDecoderWithTag : String -> Decoder Operation +operationDecoderWithTag operation = + case operation of + "SignIn" -> + Json.map SignIn ("assertion" := Json.string) + "SignOut" -> + Json.succeed SignOut + _ -> + Json.succeed NoOp diff --git a/src/client/elm/SimpleHTTP.elm b/src/client/elm/SimpleHTTP.elm new file mode 100644 index 0000000..99a7056 --- /dev/null +++ b/src/client/elm/SimpleHTTP.elm @@ -0,0 +1,41 @@ +module SimpleHTTP + ( post + , decodeHttpValue + ) where + +import Http exposing (..) +import Task exposing (..) +import Json.Decode as Json exposing (Decoder) + +post : String -> Task Error Value +post url = + { verb = "POST" + , headers = [] + , url = url + , body = empty + } + |> Http.send defaultSettings + |> mapError promoteError + |> flip Task.andThen handleResponse + +handleResponse : Response -> Task Error Value +handleResponse response = + if 200 <= response.status && response.status < 300 + then Task.succeed response.value + else fail (BadResponse response.status response.statusText) + +promoteError : RawError -> Error +promoteError rawError = + case rawError of + RawTimeout -> Timeout + RawNetworkError -> NetworkError + +decodeHttpValue : Decoder a -> Value -> Task Error a +decodeHttpValue decoder value = + case value of + Text str -> + case Json.decodeString decoder str of + Ok v -> succeed v + Err msg -> fail (UnexpectedPayload msg) + _ -> + fail (UnexpectedPayload "Response body is a blob, expecting a string.") diff --git a/src/client/elm/Update.elm b/src/client/elm/Update.elm new file mode 100644 index 0000000..3c4614a --- /dev/null +++ b/src/client/elm/Update.elm @@ -0,0 +1,57 @@ +module Update + ( Action(..) + , actions + , updateModel + ) where + +import Time exposing (Time) + +import Model exposing (Model) +import Model.User exposing (Users, UserId) +import Model.Payment exposing (Payments) +import Model.Payer exposing (Payers) +import Model.View as V +import Model.View.SignInView exposing (..) +import Model.View.LoggedInView exposing (..) + +import Update.SignIn exposing (..) +import Update.LoggedIn exposing (..) + +type Action = + NoOp + | UpdateTime Time + | GoSignInView + | SignInError String + | UpdateSignIn SignInAction + | GoLoggedInView Users UserId Payments Payments Int Payers + | UpdateLoggedIn LoggedAction + +actions : Signal.Mailbox Action +actions = Signal.mailbox NoOp + +updateModel : Action -> Model -> Model +updateModel action model = + case action of + NoOp -> + model + UpdateTime time -> + { model | currentTime <- time } + GoSignInView -> + { model | view <- V.SignInView initSignInView } + GoLoggedInView users me monthlyPayments payments paymentsCount payers -> + { model | view <- V.LoggedInView (initLoggedInView users me monthlyPayments payments paymentsCount payers) } + SignInError msg -> + let signInView = { initSignInView | result <- Just (Err msg) } + in { model | view <- V.SignInView signInView } + UpdateSignIn signInAction -> + case model.view of + V.SignInView signInView -> + { model | view <- V.SignInView (updateSignIn signInAction signInView) } + _ -> + model + UpdateLoggedIn loggedAction -> + case model.view of + V.LoggedInView loggedInView -> + { model | view <- V.LoggedInView (updateLoggedIn model loggedAction loggedInView) } + _ -> + model diff --git a/src/client/elm/Update/LoggedIn.elm b/src/client/elm/Update/LoggedIn.elm new file mode 100644 index 0000000..e477094 --- /dev/null +++ b/src/client/elm/Update/LoggedIn.elm @@ -0,0 +1,68 @@ +module Update.LoggedIn + ( LoggedAction(..) + , updateLoggedIn + ) where + +import Date +import Dict + +import Model exposing (Model) +import Model.User exposing (UserId) +import Model.Payment exposing (..) +import Model.View.LoggedInView exposing (..) +import Model.View.LoggedIn.Add exposing (..) + +import Update.LoggedIn.Add exposing (..) +import Update.LoggedIn.Monthly as UM +import Update.LoggedIn.Account as UA + +type LoggedAction = + UpdateAdd AddPaymentAction + | UpdatePayments Payments + | AddPayment UserId String Int Payments + | AddMonthlyPayment PaymentId String Int + | ToggleEdit PaymentId + | DeletePayment Payment Payments + | UpdatePage Int Payments + | UpdateMonthly UM.MonthlyAction + | UpdateAccount UA.AccountAction + +updateLoggedIn : Model -> LoggedAction -> LoggedInView -> LoggedInView +updateLoggedIn model action loggedInView = + case action of + UpdateAdd addPaymentAction -> + { loggedInView | add <- updateAddPayment addPaymentAction loggedInView.add } + UpdatePayments payments -> + { loggedInView | payments <- payments } + AddPayment userId name cost payments -> + { loggedInView + | payments <- payments + , currentPage <- 1 + , add <- initAddPayment Punctual + , account <- UA.updateAccount (UA.UpdatePayer userId model.currentTime cost) loggedInView.account + , paymentsCount <- loggedInView.paymentsCount + 1 + } + AddMonthlyPayment id name cost -> + { loggedInView + | add <- initAddPayment Monthly + , monthly <- + let payment = Payment id (Date.fromTime model.currentTime) name cost loggedInView.account.me + in UM.updateMonthly (UM.AddPayment payment) loggedInView.monthly + } + ToggleEdit id -> + { loggedInView | paymentEdition <- if loggedInView.paymentEdition == Just id then Nothing else Just id } + DeletePayment payment payments -> + { loggedInView + | payments <- payments + , account <- UA.updateAccount (UA.UpdatePayer payment.userId (Date.toTime payment.creation) -payment.cost) loggedInView.account + , paymentsCount <- loggedInView.paymentsCount - 1 + } + UpdatePage page payments -> + { loggedInView + | currentPage <- page + , payments <- payments + } + UpdateMonthly monthlyAction -> + { loggedInView | monthly <- UM.updateMonthly monthlyAction loggedInView.monthly } + UpdateAccount accountAction -> + { loggedInView | account <- UA.updateAccount accountAction loggedInView.account } diff --git a/src/client/elm/Update/LoggedIn/Account.elm b/src/client/elm/Update/LoggedIn/Account.elm new file mode 100644 index 0000000..cf4c834 --- /dev/null +++ b/src/client/elm/Update/LoggedIn/Account.elm @@ -0,0 +1,64 @@ +module Update.LoggedIn.Account + ( AccountAction(..) + , updateAccount + ) where + +import Maybe +import Time exposing (Time) +import Dict + +import Model.User exposing (UserId) +import Model.Payer exposing (..) +import Model.View.LoggedIn.Account exposing (..) + +import Utils.Maybe exposing (isJust) + +type AccountAction = + ToggleDetail + | UpdatePayer UserId Time Int + | ToggleIncomeEdition + | UpdateIncomeEdition String + | UpdateEditionError String + | UpdateIncome Time Int + +updateAccount : AccountAction -> Account -> Account +updateAccount action account = + case action of + ToggleDetail -> + { account | visibleDetail <- not account.visibleDetail } + UpdatePayer userId creation amountDiff -> + { account | payers <- updatePayers account.payers userId creation amountDiff } + ToggleIncomeEdition -> + { account | incomeEdition <- + if isJust account.incomeEdition + then Nothing + else Just (initIncomeEdition (Maybe.withDefault 0 (getCurrentIncome account))) + } + UpdateIncomeEdition income -> + case account.incomeEdition of + Just incomeEdition -> + { account | incomeEdition <- Just { incomeEdition | income <- income } } + Nothing -> + account + UpdateEditionError error -> + case account.incomeEdition of + Just incomeEdition -> + { account | incomeEdition <- Just { incomeEdition | error <- Just error } } + Nothing -> + account + UpdateIncome currentTime amount -> + { account + | payers <- + account.payers + |> Dict.update account.me (\mbPayer -> + case mbPayer of + Just payer -> + Just + { payer + | incomes <- payer.incomes ++ [{ creation = currentTime, amount = amount }] + } + Nothing -> + Nothing + ) + , incomeEdition <- Nothing + } diff --git a/src/client/elm/Update/LoggedIn/Add.elm b/src/client/elm/Update/LoggedIn/Add.elm new file mode 100644 index 0000000..1f28997 --- /dev/null +++ b/src/client/elm/Update/LoggedIn/Add.elm @@ -0,0 +1,29 @@ +module Update.LoggedIn.Add + ( AddPaymentAction(..) + , updateAddPayment + ) where + +import Model.View.LoggedIn.Add exposing (..) + +type AddPaymentAction = + UpdateName String + | UpdateCost String + | AddError (Maybe String) (Maybe String) + | ToggleFrequency + +updateAddPayment : AddPaymentAction -> AddPayment -> AddPayment +updateAddPayment action addPayment = + case action of + UpdateName name -> + { addPayment | name <- name } + UpdateCost cost -> + { addPayment | cost <- cost } + AddError nameError costError -> + { addPayment + | nameError <- nameError + , costError <- costError + } + ToggleFrequency -> + { addPayment + | frequency <- if addPayment.frequency == Punctual then Monthly else Punctual + } diff --git a/src/client/elm/Update/LoggedIn/Monthly.elm b/src/client/elm/Update/LoggedIn/Monthly.elm new file mode 100644 index 0000000..1379323 --- /dev/null +++ b/src/client/elm/Update/LoggedIn/Monthly.elm @@ -0,0 +1,27 @@ +module Update.LoggedIn.Monthly + ( MonthlyAction(..) + , updateMonthly + ) where + +import Model.Payment exposing (Payment, PaymentId) +import Model.View.LoggedIn.Monthly exposing (..) + +type MonthlyAction = + ToggleDetail + | AddPayment Payment + | DeletePayment PaymentId + +updateMonthly : MonthlyAction -> Monthly -> Monthly +updateMonthly action monthly = + case action of + ToggleDetail -> + { monthly | visibleDetail <- not monthly.visibleDetail } + AddPayment payment -> + { monthly + | payments <- payment :: monthly.payments + , visibleDetail <- True + } + DeletePayment id -> + { monthly + | payments <- List.filter (\payment -> payment.id /= id) monthly.payments + } diff --git a/src/client/elm/Update/SignIn.elm b/src/client/elm/Update/SignIn.elm new file mode 100644 index 0000000..cabe4cb --- /dev/null +++ b/src/client/elm/Update/SignIn.elm @@ -0,0 +1,15 @@ +module Update.SignIn + ( SignInAction(..) + , updateSignIn + ) where + +import Model.View.SignInView exposing (..) + +type SignInAction = + ErrorLogin String + +updateSignIn : SignInAction -> SignInView -> SignInView +updateSignIn action signInView = + case action of + ErrorLogin message -> + { signInView | result <- Just (Err message) } diff --git a/src/client/elm/Utils/Dict.elm b/src/client/elm/Utils/Dict.elm new file mode 100644 index 0000000..dc01b17 --- /dev/null +++ b/src/client/elm/Utils/Dict.elm @@ -0,0 +1,11 @@ +module Utils.Dict + ( mapValues + ) where + +import Dict as Dict exposing (..) + +mapValues : (a -> b) -> Dict comparable a -> Dict comparable b +mapValues f = Dict.fromList << List.map (onSecond f) << Dict.toList + +onSecond : (a -> b) -> (comparable, a) -> (comparable, b) +onSecond f tuple = case tuple of (x, y) -> (x, f y) diff --git a/src/client/elm/Utils/Either.elm b/src/client/elm/Utils/Either.elm new file mode 100644 index 0000000..10c40e3 --- /dev/null +++ b/src/client/elm/Utils/Either.elm @@ -0,0 +1,9 @@ +module Utils.Either + ( toMaybeError + ) where + +toMaybeError : Result a b -> Maybe a +toMaybeError result = + case result of + Ok _ -> Nothing + Err x -> Just x diff --git a/src/client/elm/Utils/Maybe.elm b/src/client/elm/Utils/Maybe.elm new file mode 100644 index 0000000..d954ae0 --- /dev/null +++ b/src/client/elm/Utils/Maybe.elm @@ -0,0 +1,27 @@ +module Utils.Maybe + ( isJust + , catMaybes + , maybeToList + ) where + +isJust : Maybe a -> Bool +isJust maybe = + case maybe of + Just _ -> True + Nothing -> False + +catMaybes : List (Maybe a) -> List a +catMaybes = + List.foldr + (\mb xs -> + case mb of + Just x -> x :: xs + Nothing -> xs + ) + [] + +maybeToList : Maybe a -> List a +maybeToList mb = + case mb of + Just a -> [a] + Nothing -> [] diff --git a/src/client/elm/Utils/Validation.elm b/src/client/elm/Utils/Validation.elm new file mode 100644 index 0000000..b9bccb3 --- /dev/null +++ b/src/client/elm/Utils/Validation.elm @@ -0,0 +1,23 @@ +module Utils.Validation + ( validateNonEmpty + , validateNumber + ) where + +import String +import Reads exposing (readInt) + +validateNonEmpty : String -> String -> Result String String +validateNonEmpty message str = + if String.isEmpty str + then Err message + else Ok str + +validateNumber : String -> (Int -> Bool) -> String -> Result String Int +validateNumber message numberForm str = + case readInt str of + Just number -> + if numberForm number + then Ok number + else Err message + Nothing -> + Err message diff --git a/src/client/elm/View/Date.elm b/src/client/elm/View/Date.elm new file mode 100644 index 0000000..81c5112 --- /dev/null +++ b/src/client/elm/View/Date.elm @@ -0,0 +1,59 @@ +module View.Date + ( renderShortDate + , renderLongDate + ) where + +import Date exposing (..) +import String + +import Model.Translations exposing (..) + +renderShortDate : Date -> Translations -> String +renderShortDate date translations = + let params = + [ String.pad 2 '0' (toString (Date.day date)) + , String.pad 2 '0' (toString (getMonthNumber (Date.month date))) + , toString (Date.year date) + ] + in getParamMessage params "ShortDate" translations + +renderLongDate : Date -> Translations -> String +renderLongDate date translations = + let params = + [ toString (Date.day date) + , (getMessage (getMonthKey (Date.month date)) translations) + , toString (Date.year date) + ] + in getParamMessage params "LongDate" translations + +getMonthNumber : Month -> Int +getMonthNumber month = + case month of + Jan -> 1 + Feb -> 2 + Mar -> 3 + Apr -> 4 + May -> 5 + Jun -> 6 + Jul -> 7 + Aug -> 8 + Sep -> 9 + Oct -> 10 + Nov -> 11 + Dec -> 12 + +getMonthKey : Month -> String +getMonthKey month = + case month of + Jan -> "January" + Feb -> "February" + Mar -> "March" + Apr -> "April" + May -> "May" + Jun -> "June" + Jul -> "July" + Aug -> "August" + Sep -> "September" + Oct -> "October" + Nov -> "November" + Dec -> "December" diff --git a/src/client/elm/View/Events.elm b/src/client/elm/View/Events.elm new file mode 100644 index 0000000..1eb9027 --- /dev/null +++ b/src/client/elm/View/Events.elm @@ -0,0 +1,19 @@ +module View.Events + ( onSubmitPrevDefault + ) where + +import Signal +import Json.Decode as Json +import Html exposing (..) +import Html.Events exposing (..) +import Html.Attributes exposing (..) + +onSubmitPrevDefault : Signal.Address a -> a -> Attribute +onSubmitPrevDefault address value = + onWithOptions + "submit" + { defaultOptions | preventDefault <- True } + Json.value + (\_ -> + Signal.message address value + ) diff --git a/src/client/elm/View/Expand.elm b/src/client/elm/View/Expand.elm new file mode 100644 index 0000000..53b4fe5 --- /dev/null +++ b/src/client/elm/View/Expand.elm @@ -0,0 +1,25 @@ +module View.Expand + ( expand + , ExpandType(..) + ) where + +import Html exposing (..) +import Html.Attributes exposing (..) + +import View.Icon exposing (renderIcon) + +type ExpandType = ExpandUp | ExpandDown + +expand : ExpandType -> Bool -> Html +expand expandType isExpanded = + div + [ class "expand" ] + [ renderIcon (chevronIcon expandType isExpanded) ] + +chevronIcon : ExpandType -> Bool -> String +chevronIcon expandType isExpanded = + case (expandType, isExpanded) of + (ExpandUp, True) -> "chevron-down" + (ExpandUp, False) -> "chevron-up" + (ExpandDown, True) -> "chevron-up" + (ExpandDown, False) -> "chevron-down" diff --git a/src/client/elm/View/Header.elm b/src/client/elm/View/Header.elm new file mode 100644 index 0000000..3a6241b --- /dev/null +++ b/src/client/elm/View/Header.elm @@ -0,0 +1,39 @@ +module View.Header + ( renderHeader + ) where + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) + +import Persona exposing (operations) + +import Model exposing (Model) +import Model.View exposing (..) +import Model.Translations exposing (getMessage) + +import View.Icon exposing (renderIcon) + +renderHeader : Model -> Html +renderHeader model = + header + [] + [ h1 + [] + [ text (getMessage "SharedCost" model.translations) ] + , case model.view of + LoadingView -> + text "" + SignInView _ -> + button + [ class "icon" + , onClick operations.address Persona.SignIn + ] + [ renderIcon "sign-in" ] + LoggedInView _ -> + button + [ class "icon" + , onClick operations.address Persona.SignOut + ] + [ renderIcon "sign-out" ] + ] diff --git a/src/client/elm/View/Icon.elm b/src/client/elm/View/Icon.elm new file mode 100644 index 0000000..f22c1a2 --- /dev/null +++ b/src/client/elm/View/Icon.elm @@ -0,0 +1,12 @@ +module View.Icon + ( renderIcon + ) where + +import Html exposing (..) +import Html.Attributes exposing (..) + +renderIcon : String -> Html +renderIcon iconClass = + i + [ class <| "fa fa-fw fa-" ++ iconClass ] + [] diff --git a/src/client/elm/View/Loading.elm b/src/client/elm/View/Loading.elm new file mode 100644 index 0000000..f8c6cd6 --- /dev/null +++ b/src/client/elm/View/Loading.elm @@ -0,0 +1,8 @@ +module View.Loading + ( renderLoading + ) where + +import Html exposing (..) + +renderLoading : Html +renderLoading = text "" diff --git a/src/client/elm/View/LoggedIn.elm b/src/client/elm/View/LoggedIn.elm new file mode 100644 index 0000000..96916e0 --- /dev/null +++ b/src/client/elm/View/LoggedIn.elm @@ -0,0 +1,30 @@ +module View.LoggedIn + ( renderLoggedIn + ) where + +import Html exposing (..) +import Html.Attributes exposing (..) + +import Model exposing (Model) +import Model.Payment exposing (Payments) +import Model.View.LoggedInView exposing (LoggedInView) + +import View.LoggedIn.Add exposing (addPayment) +import View.LoggedIn.Monthly exposing (monthlyPayments) +import View.LoggedIn.Account exposing (account) +import View.LoggedIn.Table exposing (paymentsTable) +import View.LoggedIn.Paging exposing (paymentsPaging) + +renderLoggedIn : Model -> LoggedInView -> Html +renderLoggedIn model loggedInView = + div + [ class "loggedIn" ] + [ addPayment model loggedInView + , div + [ class "expandables" ] + [ account model loggedInView + , monthlyPayments model loggedInView + ] + , paymentsTable model loggedInView + , paymentsPaging loggedInView + ] diff --git a/src/client/elm/View/LoggedIn/Account.elm b/src/client/elm/View/LoggedIn/Account.elm new file mode 100644 index 0000000..706f7cc --- /dev/null +++ b/src/client/elm/View/LoggedIn/Account.elm @@ -0,0 +1,130 @@ +module View.LoggedIn.Account + ( account + ) where + +import Html exposing (..) +import Html as H exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import List + +import ServerCommunication as SC exposing (serverCommunications) + +import Update exposing (..) +import Update.LoggedIn exposing (..) +import Update.LoggedIn.Account exposing (..) + +import Model exposing (Model) +import Model.User exposing (getUserName) +import Model.Payer exposing (..) +import Model.View.LoggedInView exposing (LoggedInView) +import Model.Translations exposing (getParamMessage, getMessage) +import Model.View.LoggedIn.Account exposing (..) + +import View.Expand exposing (..) +import View.Price exposing (price) +import View.Events exposing (onSubmitPrevDefault) + +import Utils.Either exposing (toMaybeError) + +account : Model -> LoggedInView -> Html +account model loggedInView = + let account = loggedInView.account + in div + [ classList + [ ("account", True) + , ("detail", account.visibleDetail) + ] + ] + [ exceedingPayers model loggedInView + , if account.visibleDetail + then income model account + else text "" + ] + +exceedingPayers : Model -> LoggedInView -> Html +exceedingPayers model loggedInView = + button + [ class "header" + , onClick actions.address (UpdateLoggedIn << UpdateAccount <| ToggleDetail) + ] + ( (List.map (exceedingPayer model loggedInView) (getOrderedExceedingPayers model.currentTime loggedInView.account.payers)) + ++ [ expand ExpandDown loggedInView.account.visibleDetail ] + ) + +exceedingPayer : Model -> LoggedInView -> ExceedingPayer -> Html +exceedingPayer model loggedInView payer = + div + [ class "exceedingPayer" ] + [ span + [ class "userName" ] + [ payer.userId + |> getUserName loggedInView.users + |> Maybe.withDefault "−" + |> text + ] + , span + [ class "amount" ] + [ text ("+ " ++ (price model payer.amount)) ] + ] + +income : Model -> Account -> Html +income model account = + case account.incomeEdition of + Just edition -> + incomeEdition model account edition + Nothing -> + incomeRead model account + +incomeRead : Model -> Account -> Html +incomeRead model account = + div + [ class "income" ] + [ ( case getCurrentIncome account of + Nothing -> + text (getMessage "NoIncome" model.translations) + Just income -> + text (getParamMessage [price model income] "Income" model.translations) + ) + , toggleIncomeEdition "editIncomeEdition" (getMessage "Edit" model.translations) + ] + +incomeEdition : Model -> Account -> IncomeEdition -> Html +incomeEdition model account edition = + H.form + [ case validateIncome edition.income model.translations of + Ok validatedAmount -> + onSubmitPrevDefault serverCommunications.address (SC.SetIncome model.currentTime validatedAmount) + Err error -> + onSubmitPrevDefault actions.address (UpdateLoggedIn << UpdateAccount << UpdateEditionError <| error) + , class "income" + ] + [ label + [ for "incomeInput" ] + [ text (getMessage "NewIncome" model.translations) ] + , input + [ id "incomeInput" + , value edition.income + , on "input" targetValue (Signal.message actions.address << UpdateLoggedIn << UpdateAccount << UpdateIncomeEdition) + , maxlength 10 + ] + [] + , button + [ type' "submit" + , class "validateIncomeEdition" + ] + [ text (getMessage "Validate" model.translations) ] + , toggleIncomeEdition "undoIncomeEdition" (getMessage "Undo" model.translations) + , case edition.error of + Just error -> div [ class "error" ] [ text error ] + Nothing -> text "" + ] + +toggleIncomeEdition : String -> String -> Html +toggleIncomeEdition className name = + button + [ type' "button" + , class className + , onClick actions.address (UpdateLoggedIn << UpdateAccount <| ToggleIncomeEdition) + ] + [ text name ] diff --git a/src/client/elm/View/LoggedIn/Add.elm b/src/client/elm/View/LoggedIn/Add.elm new file mode 100644 index 0000000..572bdf6 --- /dev/null +++ b/src/client/elm/View/LoggedIn/Add.elm @@ -0,0 +1,122 @@ +module View.LoggedIn.Add + ( addPayment + ) where + +import Html as H exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Reads exposing (readInt) +import Result exposing (..) + +import ServerCommunication as SC exposing (serverCommunications) + +import Update exposing (..) +import Update.LoggedIn exposing (..) +import Update.LoggedIn.Add exposing (..) + +import Model exposing (Model) +import Model.View.LoggedIn.Add exposing (..) +import Model.Translations exposing (getMessage) +import Model.View.LoggedInView exposing (LoggedInView) + +import View.Events exposing (onSubmitPrevDefault) +import View.Icon exposing (renderIcon) + +import Utils.Maybe exposing (isJust) +import Utils.Either exposing (toMaybeError) + +addPayment : Model -> LoggedInView -> Html +addPayment model loggedInView = + H.form + [ case (validateName loggedInView.add.name model.translations, validateCost loggedInView.add.cost model.translations) of + (Ok name, Ok cost) -> + let action = + case loggedInView.add.frequency of + Punctual -> SC.AddPayment loggedInView.account.me name cost + Monthly -> SC.AddMonthlyPayment name cost + in onSubmitPrevDefault serverCommunications.address action + (resName, resCost) -> + onSubmitPrevDefault actions.address (UpdateLoggedIn <| UpdateAdd <| AddError (toMaybeError resName) (toMaybeError resCost)) + , class "addPayment" + ] + [ addPaymentName loggedInView.add + , addPaymentCost model loggedInView.add + , paymentFrequency model loggedInView.add + , button + [ type' "submit" + , class "add" ] + [ text (getMessage "Add" model.translations)] + ] + +addPaymentName : AddPayment -> Html +addPaymentName addPayment = + div + [ classList + [ ("name", True) + , ("error", isJust addPayment.nameError) + ] + ] + [ input + [ id "nameInput" + , value addPayment.name + , on "input" targetValue (Signal.message actions.address << UpdateLoggedIn << UpdateAdd << UpdateName) + , maxlength 20 + ] + [] + , label + [ for "nameInput" ] + [ renderIcon "shopping-cart" ] + , case addPayment.nameError of + Just error -> + div [ class "errorMessage" ] [ text error ] + Nothing -> + text "" + ] + +addPaymentCost : Model -> AddPayment -> Html +addPaymentCost model addPayment = + div + [ classList + [ ("cost", True) + , ("error", isJust addPayment.costError) + ] + ] + [ input + [ id "costInput" + , value addPayment.cost + , on "input" targetValue (Signal.message actions.address << UpdateLoggedIn << UpdateAdd << UpdateCost) + , maxlength 7 + ] + [] + , label + [ for "costInput" ] + [ text model.config.currency ] + , case addPayment.costError of + Just error -> + div [ class "errorMessage" ] [ text error ] + Nothing -> + text "" + ] + +paymentFrequency : Model -> AddPayment -> Html +paymentFrequency model addPayment = + button + [ type' "button" + , class "frequency" + , onClick actions.address (UpdateLoggedIn << UpdateAdd <| ToggleFrequency) + ] + [ div + [ classList + [ ("punctual", True) + , ("selected", addPayment.frequency == Punctual) + ] + ] + [ text (getMessage "Punctual" model.translations) ] + , div + [ classList + [ ("monthly", True) + , ("selected", addPayment.frequency == Monthly) + ] + ] + [ text (getMessage "Monthly" model.translations) ] + ] diff --git a/src/client/elm/View/LoggedIn/Monthly.elm b/src/client/elm/View/LoggedIn/Monthly.elm new file mode 100644 index 0000000..a274015 --- /dev/null +++ b/src/client/elm/View/LoggedIn/Monthly.elm @@ -0,0 +1,89 @@ +module View.LoggedIn.Monthly + ( monthlyPayments + ) where + +import String + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) + +import Update exposing (..) +import Update.LoggedIn exposing (..) +import Update.LoggedIn.Monthly exposing (..) + +import Model exposing (Model) +import Model.View.LoggedIn.Monthly exposing (Monthly) +import Model.Payment exposing (Payments, Payment) +import Model.View.LoggedInView exposing (LoggedInView) +import Model.Translations exposing (getMessage, getParamMessage) + +import ServerCommunication as SC exposing (serverCommunications) + +import View.Icon exposing (renderIcon) +import View.Expand exposing (..) +import View.Price exposing (price) + +monthlyPayments : Model -> LoggedInView -> Html +monthlyPayments model loggedInView = + let monthly = loggedInView.monthly + in if List.length monthly.payments == 0 + then + text "" + else + div + [ classList + [ ("monthlyPayments", True) + , ("detail", monthly.visibleDetail) + ] + ] + [ monthlyCount model monthly + , if monthly.visibleDetail then paymentsTable model loggedInView monthly else text "" + ] + +monthlyCount : Model -> Monthly -> Html +monthlyCount model monthly = + let count = List.length monthly.payments + total = List.sum << List.map .cost <| monthly.payments + key = if count > 1 then "PluralMonthlyCount" else "SingularMonthlyCount" + in button + [ class "header" + , onClick actions.address (UpdateLoggedIn << UpdateMonthly <| ToggleDetail) + ] + [ text (getParamMessage [toString count, price model total] key model.translations) + , expand ExpandDown monthly.visibleDetail + ] + +paymentsTable : Model -> LoggedInView -> Monthly -> Html +paymentsTable model loggedInView monthly = + div + [ class "table" ] + ( monthly.payments + |> List.sortBy (String.toLower << .name) + |> List.map (paymentLine model loggedInView) + ) + +paymentLine : Model -> LoggedInView -> Payment -> Html +paymentLine model loggedInView payment = + a + [ classList + [ ("row", True) + , ("edition", loggedInView.paymentEdition == Just payment.id) + ] + , onClick actions.address (UpdateLoggedIn (ToggleEdit payment.id)) + ] + [ div [ class "cell category" ] [ text (payment.name) ] + , div + [ classList + [ ("cell cost", True) + , ("refund", payment.cost < 0) + ] + ] + [ text (price model payment.cost) ] + , div + [ class "cell delete" + , onClick serverCommunications.address (SC.DeleteMonthlyPayment payment.id) + ] + [ button [] [ renderIcon "times" ] + ] + ] diff --git a/src/client/elm/View/LoggedIn/Paging.elm b/src/client/elm/View/LoggedIn/Paging.elm new file mode 100644 index 0000000..93d7f1d --- /dev/null +++ b/src/client/elm/View/LoggedIn/Paging.elm @@ -0,0 +1,100 @@ +module View.LoggedIn.Paging + ( paymentsPaging + ) where + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) + +import Model.View.LoggedInView exposing (..) +import Model.Payment exposing (perPage) + +import ServerCommunication as SC exposing (serverCommunications) + +import Update exposing (..) +import Update.LoggedIn exposing (..) + +import View.Icon exposing (renderIcon) + +showedPages : Int +showedPages = 5 + +paymentsPaging : LoggedInView -> Html +paymentsPaging loggedInView = + let maxPage = ceiling (toFloat loggedInView.paymentsCount / toFloat perPage) + pages = truncatePages loggedInView.currentPage [1..maxPage] + in if maxPage == 1 + then + text "" + else + div + [ class "pages" ] + ( ( if loggedInView.currentPage > 1 + then [ firstPage, previousPage loggedInView ] + else [] + ) + ++ ( List.map (paymentsPage loggedInView) pages) + ++ ( if loggedInView.currentPage < maxPage + then [ nextPage loggedInView, lastPage maxPage ] + else [] + ) + ) + +truncatePages : Int -> List Int -> List Int +truncatePages currentPage pages = + let totalPages = List.length pages + showedLeftPages = ceiling ((toFloat showedPages - 1) / 2) + showedRightPages = floor ((toFloat showedPages - 1) / 2) + truncatedPages = + if | currentPage < showedLeftPages -> + [1..showedPages] + | currentPage > totalPages - showedRightPages -> + [(totalPages - showedPages)..totalPages] + | otherwise -> + [(currentPage - showedLeftPages)..(currentPage + showedRightPages)] + in List.filter (flip List.member pages) truncatedPages + +firstPage : Html +firstPage = + button + [ class "page" + , onClick serverCommunications.address (SC.UpdatePage 1) + ] + [ renderIcon "fast-backward" ] + +previousPage : LoggedInView -> Html +previousPage loggedInView = + button + [ class "page" + , onClick serverCommunications.address (SC.UpdatePage (loggedInView.currentPage - 1)) + ] + [ renderIcon "backward" ] + +nextPage : LoggedInView -> Html +nextPage loggedInView = + button + [ class "page" + , onClick serverCommunications.address (SC.UpdatePage (loggedInView.currentPage + 1)) + ] + [ renderIcon "forward" ] + +lastPage : Int -> Html +lastPage maxPage = + button + [ class "page" + , onClick serverCommunications.address (SC.UpdatePage maxPage) + ] + [ renderIcon "fast-forward" ] + +paymentsPage : LoggedInView -> Int -> Html +paymentsPage loggedInView page = + let onCurrentPage = page == loggedInView.currentPage + in button + [ classList + [ ("page", True) + , ("current", onCurrentPage) + ] + , onClick serverCommunications.address <| + if onCurrentPage then SC.NoCommunication else SC.UpdatePage page + ] + [ text (toString page) ] diff --git a/src/client/elm/View/LoggedIn/Table.elm b/src/client/elm/View/LoggedIn/Table.elm new file mode 100644 index 0000000..f5a08b5 --- /dev/null +++ b/src/client/elm/View/LoggedIn/Table.elm @@ -0,0 +1,97 @@ +module View.LoggedIn.Table + ( paymentsTable + ) where + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Dict exposing (..) + +import Date +import Date exposing (Date) + +import String exposing (append) + +import Model exposing (Model) +import Model.User exposing (getUserName) +import Model.Payment exposing (..) +import Model.View.LoggedInView exposing (LoggedInView) +import Model.Translations exposing (getMessage) + +import ServerCommunication as SC exposing (serverCommunications) + +import Update exposing (..) +import Update.LoggedIn exposing (..) + +import View.Icon exposing (renderIcon) +import View.Date exposing (..) +import View.Price exposing (price) + +paymentsTable : Model -> LoggedInView -> Html +paymentsTable model loggedInView = + div + [ class "table" ] + ( headerLine model :: paymentLines model loggedInView) + +headerLine : Model -> Html +headerLine model = + div + [ class "header" ] + [ div [ class "cell category" ] [ renderIcon "shopping-cart" ] + , div [ class "cell cost" ] [ text model.config.currency ] + , div [ class "cell user" ] [ renderIcon "user" ] + , div [ class "cell date" ] [ renderIcon "calendar" ] + , div [ class "cell" ] [] + ] + +paymentLines : Model -> LoggedInView -> List Html +paymentLines model loggedInView = + loggedInView.payments + |> List.sortBy (Date.toTime << .creation) + |> List.reverse + |> List.map (paymentLine model loggedInView) + +paymentLine : Model -> LoggedInView -> Payment -> Html +paymentLine model loggedInView payment = + a + [ classList + [ ("row", True) + , ("edition", loggedInView.paymentEdition == Just payment.id) + ] + , onClick actions.address (UpdateLoggedIn (ToggleEdit payment.id)) + ] + [ div [ class "cell category" ] [ text payment.name ] + , div + [ classList + [ ("cell cost", True) + , ("refund", payment.cost < 0) + ] + ] + [ text (price model payment.cost) ] + , div + [ class "cell user" ] + [ payment.userId + |> getUserName loggedInView.users + |> Maybe.withDefault "−" + |> text + ] + , div + [ class "cell date" ] + [ span + [ class "shortDate" ] + [ text (renderShortDate payment.creation model.translations) ] + , span + [ class "longDate" ] + [ text (renderLongDate payment.creation model.translations) ] + ] + , if loggedInView.account.me == payment.userId + then + div + [ class "cell delete" ] + [ button + [ onClick serverCommunications.address (SC.DeletePayment payment loggedInView.currentPage) ] + [ renderIcon "times" ] + ] + else + div [ class "cell" ] [] + ] diff --git a/src/client/elm/View/Page.elm b/src/client/elm/View/Page.elm new file mode 100644 index 0000000..763734d --- /dev/null +++ b/src/client/elm/View/Page.elm @@ -0,0 +1,31 @@ +module View.Page + ( renderPage + ) where + +import Html exposing (..) + +import Model exposing (Model) +import Model.View exposing (..) + +import View.Header exposing (renderHeader) +import View.Loading exposing (renderLoading) +import View.SignIn exposing (renderSignIn) +import View.LoggedIn exposing (renderLoggedIn) + +renderPage : Model -> Html +renderPage model = + div + [] + [ renderHeader model + , renderMain model + ] + +renderMain : Model -> Html +renderMain model = + case model.view of + LoadingView -> + renderLoading + SignInView signInView -> + renderSignIn model signInView + LoggedInView loggedInView -> + renderLoggedIn model loggedInView diff --git a/src/client/elm/View/Price.elm b/src/client/elm/View/Price.elm new file mode 100644 index 0000000..286bcaa --- /dev/null +++ b/src/client/elm/View/Price.elm @@ -0,0 +1,38 @@ +module View.Price + ( price + ) where + +import String exposing (..) + +import Model exposing (Model) +import Model.Translations exposing (getMessage) + +price : Model -> Int -> String +price model amount = + ( formatInt amount + ++ " " + ++ model.config.currency + ) + +formatInt : Int -> String +formatInt n = + abs n + |> toString + |> toList + |> List.reverse + |> group 3 + |> List.intersperse [' '] + |> List.concat + |> List.reverse + |> fromList + |> append (if n < 0 then "-" else "") + +group : Int -> List a -> List (List a) +group n xs = + if List.length xs <= n + then + [xs] + else + let take = List.take n xs + drop = List.drop n xs + in take :: (group n drop) diff --git a/src/client/elm/View/SignIn.elm b/src/client/elm/View/SignIn.elm new file mode 100644 index 0000000..8fcac16 --- /dev/null +++ b/src/client/elm/View/SignIn.elm @@ -0,0 +1,46 @@ +module View.SignIn + ( renderSignIn + ) where + +import Html as H exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) + +import Json.Decode as Json + +import Update exposing (..) +import Update.SignIn exposing (..) + +import ServerCommunication as SC +import ServerCommunication exposing (serverCommunications) + +import Model exposing (Model) +import Model.View.SignInView exposing (..) +import Model.Translations exposing (getMessage) + +import View.Events exposing (onSubmitPrevDefault) + +renderSignIn : Model -> SignInView -> Html +renderSignIn model signInView = + div + [ class "signIn" ] + [ div + [ class "result" ] + [ signInResult model signInView ] + ] + +signInResult : Model -> SignInView -> Html +signInResult model signInView = + case signInView.result of + Just result -> + case result of + Ok login -> + div + [ class "success" ] + [ text (getMessage "SignInEmailSent" model.translations) ] + Err error -> + div + [ class "error" ] + [ text error ] + Nothing -> + text "" diff --git a/src/client/js/main.js b/src/client/js/main.js new file mode 100644 index 0000000..12593e6 --- /dev/null +++ b/src/client/js/main.js @@ -0,0 +1,28 @@ +var app = Elm.fullscreen(Elm.Main, { + initialTime: new Date().getTime(), + translations: document.getElementById('messages').innerHTML, + config: document.getElementById('config').innerHTML, + sign: null +}); + +navigator.id.watch({ + loggedInUser: null, + onlogin: function(assertion) { + app.ports.sign.send({ + operation: 'SignIn', + assertion: assertion + }); + }, + onlogout: function() {} +}); + +app.ports.persona.subscribe(function(communication) { + if(communication === 'SignIn') { + navigator.id.request(); + } else if(communication === 'SignOut') { + navigator.id.logout(); + app.ports.sign.send({ + operation: 'SignOut' + }); + } +}); diff --git a/src/server/Config.hs b/src/server/Config.hs index bd7f325..37f57ec 100644 --- a/src/server/Config.hs +++ b/src/server/Config.hs @@ -18,7 +18,6 @@ import Control.Arrow (left) data Config = Config { hostname :: Text , port :: Int - , signInExpirationMn :: Int , currency :: Text } deriving (Read, Eq, Show) @@ -29,6 +28,5 @@ getConfig filePath = Config <$> (T.pack <$> get cp "DEFAULT" "hostname") <*> (get cp "DEFAULT" "port") <*> - (get cp "DEFAULT" "sign-in-expiration-mn") <*> (T.pack <$> get cp "DEFAULT" "currency") ) diff --git a/src/server/Controller/SignIn.hs b/src/server/Controller/SignIn.hs index 31cd510..8eceb56 100644 --- a/src/server/Controller/SignIn.hs +++ b/src/server/Controller/SignIn.hs @@ -2,32 +2,21 @@ module Controller.SignIn ( signIn - , validateSignIn ) where import Web.Scotty import Network.HTTP.Types.Status (ok200) -import Database.Persist - import Control.Monad.IO.Class (liftIO) import Data.Text (Text) -import qualified Data.Text as T -import qualified Data.Text.Lazy as TL -import qualified Data.Text.Encoding as TE -import Data.Time.Clock (getCurrentTime, diffUTCTime) import Data.Maybe (isJust) import qualified LoginSession import Config -import SendMail - -import Text.Email.Validate as Email - import Model.Database import Model.User import Model.SignIn @@ -36,65 +25,20 @@ import Model.Message (getMessage) import Json (jsonError) -import Secure (getUserFromToken) - -import qualified View.Mail.SignIn as SignIn +import Persona (verifyEmail) signIn :: Config -> Text -> ActionM () -signIn config login = - if Email.isValid (TE.encodeUtf8 login) - then do - maybeUser <- liftIO . runDb $ getUser login - case maybeUser of - Just user -> do - token <- liftIO . runDb $ createSignInToken login - let url = T.concat ["http://", hostname config, "/validateSignIn?token=", token] - maybeSentMail <- liftIO . sendMail $ SignIn.getMail (entityVal user) url [login] - case maybeSentMail of - Right _ -> - status ok200 - Left _ -> - jsonError (getMessage SendEmailFail) - Nothing -> - jsonError (getMessage Unauthorized) - else - jsonError (getMessage EnterValidEmail) - -validateSignIn :: Config -> Text -> ActionM () -validateSignIn config textToken = do - alreadySigned <- isAlreadySigned - if alreadySigned - then - redirect "/" - else do - mbSignIn <- liftIO . runDb $ getSignInToken textToken - now <- liftIO getCurrentTime - case mbSignIn of - Just signIn -> - if signInIsUsed . entityVal $ signIn - then - redirectError (getMessage SignInUsed) - else - let diffTime = now `diffUTCTime` (signInCreation . entityVal $ signIn) - in if diffTime > (fromIntegral $ (signInExpirationMn config) * 60) - then - redirectError (getMessage SignInExpired) - else do - LoginSession.put (signInToken . entityVal $ signIn) - liftIO . runDb . signInTokenToUsed . entityKey $ signIn - redirect "/" - Nothing -> - redirectError (getMessage SignInInvalid) - -isAlreadySigned :: ActionM Bool -isAlreadySigned = do - mbToken <- LoginSession.get - case mbToken of +signIn config assertion = do + mbEmail <- liftIO $ verifyEmail config assertion + case mbEmail of Nothing -> - return False - Just token -> do - liftIO . runDb . fmap isJust $ getUserFromToken token - -redirectError :: Text -> ActionM () -redirectError msg = - redirect . TL.fromStrict . T.concat $ ["/?signInError=", msg] + jsonError (getMessage InvalidEmail) + Just email -> do + isAuthorized <- liftIO . fmap isJust . runDb $ getUser email + if isAuthorized + then do + token <- liftIO . runDb $ createSignInToken email + LoginSession.put token + status ok200 + else + jsonError (getMessage Unauthorized) diff --git a/src/server/Design/Header.hs b/src/server/Design/Header.hs index 7b82577..9f83778 100644 --- a/src/server/Design/Header.hs +++ b/src/server/Design/Header.hs @@ -25,7 +25,7 @@ headerDesign = marginBottom blockMarginBottom paddingLeft sidePercent - button # ".signOut" ? do + button # ".icon" ? do let iconHeight = 50 let sideMargin = ((headerHeight - iconHeight) `Prelude.div` 2) position absolute diff --git a/src/server/Design/SignIn.hs b/src/server/Design/SignIn.hs index 6bacc3a..7aff720 100644 --- a/src/server/Design/SignIn.hs +++ b/src/server/Design/SignIn.hs @@ -15,26 +15,6 @@ signInDesign = ".signIn" ? do - opacityAnimation - - form ? do - let inputHeight = 50 - width (px 500) - marginTop (px 100) - marginLeft auto - marginRight auto - - input ? do - defaultInput inputHeight - display block - width (pct 100) - marginBottom (px 10) - - button ? do - defaultButton C.red C.white (px inputHeight) - display block - width (pct 100) - ".result" ? do marginTop (px 40) textAlign (alignSide sideCenter) diff --git a/src/server/Main.hs b/src/server/Main.hs index 3d61481..3539120 100644 --- a/src/server/Main.hs +++ b/src/server/Main.hs @@ -8,7 +8,7 @@ import Control.Concurrent (forkIO) import MonthlyPaymentJob (monthlyPaymentJobListener) import Data.Text (Text) -import qualified Data.Text.IO as TIO +import qualified Data.Text.IO as T import Controller.Index import Controller.SignIn @@ -28,7 +28,7 @@ main = do eitherConfig <- Config.getConfig "config.txt" case eitherConfig of Left errorMessage -> - TIO.putStrLn errorMessage + T.putStrLn errorMessage Right config -> do scotty (Config.port config) $ do middleware $ @@ -40,12 +40,8 @@ main = do -- SignIn post "/signIn" $ do - login <- param "login" :: ActionM Text - signIn config login - - get "/validateSignIn" $ do - token <- param "token" :: ActionM Text - validateSignIn config token + assertion <- param "assertion" :: ActionM Text + signIn config assertion -- Users diff --git a/src/server/Model/Database.hs b/src/server/Model/Database.hs index 8d1da25..67cc8b3 100644 --- a/src/server/Model/Database.hs +++ b/src/server/Model/Database.hs @@ -44,7 +44,6 @@ SignIn token Text creation UTCTime email Text - isUsed Bool UniqSignInToken token deriving Show Job diff --git a/src/server/Model/Message/Key.hs b/src/server/Model/Message/Key.hs index e9f8ef6..7f49ae7 100644 --- a/src/server/Model/Message/Key.hs +++ b/src/server/Model/Message/Key.hs @@ -12,12 +12,8 @@ data Key = | SharedCost | SignIn - | SendEmailFail + | InvalidEmail | Unauthorized - | EnterValidEmail - | SignInUsed - | SignInExpired - | SignInInvalid | SignInMailTitle | SignInMail | SignInEmailSent diff --git a/src/server/Model/Message/Translations.hs b/src/server/Model/Message/Translations.hs index a2e9a30..29b21ea 100644 --- a/src/server/Model/Message/Translations.hs +++ b/src/server/Model/Message/Translations.hs @@ -34,36 +34,16 @@ m l SignIn = English -> "Sign in" French -> "Connexion" -m l SendEmailFail = +m l InvalidEmail = case l of - English -> "Sorry, we failed to send you the sign up email." - French -> "Désolé, nous n'avons pas pu t'envoyer le courriel de connexion." + English -> "Your email is not valid." + French -> "Votre courriel n'est pas valide." m l Unauthorized = case l of English -> "You are not authorized to sign in." French -> "Tu n'es pas autorisé à te connecter." -m l EnterValidEmail = - case l of - English -> "Please enter a valid email address." - French -> "Ton courriel n'est pas valide." - -m l SignInUsed = - case l of - English -> "You already used this link, please sign in again." - French -> "Tu as déjà utilisé ce lien, connecte-toi à nouveau." - -m l SignInExpired = - case l of - English -> "The link expired, please sign in again." - French -> "Le lien sur lequel tu as cliqué a expiré, connecte-toi à nouveau." - -m l SignInInvalid = - case l of - English -> "The link is invalid, please sign in again." - French -> "Le lien sur lequel tu as cliqué est invalide, connecte-toi à nouveau." - m l SignInMailTitle = case l of English -> T.concat ["Sign in to ", m l SharedCost] diff --git a/src/server/Model/SignIn.hs b/src/server/Model/SignIn.hs index 117b8b5..b475fdb 100644 --- a/src/server/Model/SignIn.hs +++ b/src/server/Model/SignIn.hs @@ -1,8 +1,6 @@ module Model.SignIn ( createSignInToken , getSignInToken - , signInTokenToUsed - , isLastValidToken ) where import Data.Text (Text) @@ -19,22 +17,9 @@ createSignInToken :: Text -> Persist Text createSignInToken email = do now <- liftIO getCurrentTime token <- liftIO generateUUID - _ <- insert $ SignIn token now email False + _ <- insert $ SignIn token now email return token getSignInToken :: Text -> Persist (Maybe (Entity SignIn)) getSignInToken token = selectFirst [SignInToken ==. token] [] - -signInTokenToUsed :: SignInId -> Persist () -signInTokenToUsed tokenId = - update tokenId [SignInIsUsed =. True] - -isLastValidToken :: SignIn -> Persist Bool -isLastValidToken signIn = do - maybe False ((== (signInToken signIn)) . signInToken . entityVal) <$> - selectFirst - [ SignInEmail ==. (signInEmail signIn) - , SignInIsUsed ==. True - ] - [ Desc SignInCreation ] diff --git a/src/server/Persona.hs b/src/server/Persona.hs new file mode 100644 index 0000000..8055e8b --- /dev/null +++ b/src/server/Persona.hs @@ -0,0 +1,42 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Persona + ( verifyEmail + ) where + +import Control.Monad (guard) + +import Network.HTTP.Conduit + +import Data.Text (Text) +import qualified Data.Text as T +import Data.ByteString.Lazy (fromStrict, toStrict) +import Data.Text.Encoding (encodeUtf8, decodeUtf8) +import Data.Aeson +import Data.Aeson.Types (parseMaybe) + +import Config + +verifyEmail :: Config -> Text -> IO (Maybe Text) +verifyEmail config assertion = do + + initReq <- parseUrl "https://verifier.login.persona.org/verify" + + let request = + (flip urlEncodedBody) initReq $ + [ ("assertion", encodeUtf8 $ assertion) + , ("audience", encodeUtf8 $ hostname config) + ] + + manager <- newManager tlsManagerSettings + response <- httpLbs request manager + + return . parseEmail . decodeUtf8 . toStrict . responseBody $ response + +parseEmail :: Text -> Maybe Text +parseEmail payload = do + result <- decode . fromStrict . encodeUtf8 $ payload + flip parseMaybe result $ \obj -> do + status <- T.pack <$> obj .: "status" + guard (status == "okay") + obj .: "email" diff --git a/src/server/Secure.hs b/src/server/Secure.hs index 192fa10..7b6e6de 100644 --- a/src/server/Secure.hs +++ b/src/server/Secure.hs @@ -12,7 +12,7 @@ import Network.HTTP.Types.Status (forbidden403) import Database.Persist (Entity, entityVal) import Model.User (getUser) -import Model.SignIn (getSignInToken, isLastValidToken) +import Model.SignIn (getSignInToken) import Model.Database import Control.Monad.IO.Class (liftIO) @@ -44,9 +44,6 @@ getUserFromToken token = do mbSignIn <- fmap entityVal <$> getSignInToken token case mbSignIn of Just signIn -> do - isValid <- isLastValidToken signIn - if isValid - then getUser (signInEmail signIn) - else return Nothing + getUser (signInEmail signIn) Nothing -> return Nothing diff --git a/src/server/View/Page.hs b/src/server/View/Page.hs index 2865337..7310fbb 100644 --- a/src/server/View/Page.hs +++ b/src/server/View/Page.hs @@ -26,7 +26,9 @@ page config = renderHtml . docTypeHtml $ do H.head $ do meta ! charset "UTF-8" + meta ! httpEquiv "X-UA-Compatible" ! content "IE=Edge" -- IE8+ only is valid to use with persona H.title (toHtml $ getMessage SharedCost) + script ! src "https://login.persona.org/include.js" $ "" script ! src "javascripts/client.js" $ "" script ! A.id "messages" ! type_ "application/json" $ toHtml . decodeUtf8 . encode $ getTranslations script ! A.id "config" ! type_ "application/json" $ toHtml . decodeUtf8 . encode $ config @@ -35,4 +37,4 @@ page config = link ! rel "icon" ! type_ "image/png" ! href "images/icon.png" H.style $ toHtml globalDesign body $ do - script ! src "javascripts/elmLauncher.js" $ "" + script ! src "javascripts/main.js" $ "" -- cgit v1.2.3