From 973a039b54327df74396605410ea9abe19c8a4e7 Mon Sep 17 00:00:00 2001 From: Joris Date: Sun, 4 Sep 2016 21:21:11 +0200 Subject: Upgrade to elm 0.17.1 --- .gitignore | 3 +- .tmuxinator.yml | 8 + Makefile | 26 ++ alarm.wav | Bin 661032 -> 0 bytes design/design.css | 171 ------- design/font-awesome/css/font-awesome.min.css | 4 - design/font-awesome/fonts/FontAwesome.otf | Bin 85908 -> 0 bytes design/font-awesome/fonts/fontawesome-webfont.eot | Bin 56006 -> 0 bytes design/font-awesome/fonts/fontawesome-webfont.svg | 520 --------------------- design/font-awesome/fonts/fontawesome-webfont.ttf | Bin 112160 -> 0 bytes design/font-awesome/fonts/fontawesome-webfont.woff | Bin 65452 -> 0 bytes design/reset.css | 68 --- elm-package.json | 11 +- gen | 2 - images/icon.png | Bin 886 -> 0 bytes index.html | 47 -- package.json | 6 + public/alarm.wav | Bin 0 -> 661032 bytes public/images/icon.png | Bin 0 -> 886 bytes public/index.html | 40 ++ public/style/font-awesome/css/font-awesome.min.css | 4 + public/style/font-awesome/fonts/FontAwesome.otf | Bin 0 -> 85908 bytes .../font-awesome/fonts/fontawesome-webfont.eot | Bin 0 -> 56006 bytes .../font-awesome/fonts/fontawesome-webfont.svg | 520 +++++++++++++++++++++ .../font-awesome/fonts/fontawesome-webfont.ttf | Bin 0 -> 112160 bytes .../font-awesome/fonts/fontawesome-webfont.woff | Bin 0 -> 65452 bytes public/style/main.css | 171 +++++++ public/style/reset.css | 68 +++ shell.nix | 15 + src/Edition/Model.elm | 45 ++ src/Edition/Model/Name.elm | 19 + src/Edition/Model/Time.elm | 52 +++ src/Edition/Msg.elm | 9 + src/Edition/Update.elm | 30 ++ src/Main.elm | 72 +-- src/Model.elm | 40 ++ src/Model/Edition/Edition.elm | 44 -- src/Model/Edition/NameEdition.elm | 18 - src/Model/Edition/TimeEdition.elm | 52 --- src/Model/Id.elm | 4 +- src/Model/IdGenerator.elm | 6 +- src/Model/Keyboard.elm | 5 + src/Model/Model.elm | 34 -- src/Model/Position.elm | 4 +- src/Model/Timer.elm | 27 -- src/Model/TimerState.elm | 8 - src/Msg.elm | 23 + src/Ring.elm | 5 + src/Timer/Model.elm | 27 ++ src/Timer/Model/State.elm | 8 + src/Timer/Msg.elm | 13 + src/Timer/Update.elm | 56 +++ src/Timer/View.elm | 175 +++++++ src/Update.elm | 141 ++++++ src/Update/Update.elm | 131 ------ src/Update/UpdateEdition.elm | 33 -- src/Update/UpdateTimer.elm | 57 --- src/Utils/List.elm | 18 +- src/Utils/Maybe.elm | 4 +- src/View.elm | 56 +++ src/View/ActivatedClasses.elm | 15 - src/View/Timer.elm | 171 ------- src/View/View.elm | 67 --- 63 files changed, 1605 insertions(+), 1548 deletions(-) create mode 100644 .tmuxinator.yml create mode 100644 Makefile delete mode 100644 alarm.wav delete mode 100644 design/design.css delete mode 100644 design/font-awesome/css/font-awesome.min.css delete mode 100644 design/font-awesome/fonts/FontAwesome.otf delete mode 100644 design/font-awesome/fonts/fontawesome-webfont.eot delete mode 100644 design/font-awesome/fonts/fontawesome-webfont.svg delete mode 100644 design/font-awesome/fonts/fontawesome-webfont.ttf delete mode 100644 design/font-awesome/fonts/fontawesome-webfont.woff delete mode 100644 design/reset.css delete mode 100755 gen delete mode 100644 images/icon.png delete mode 100644 index.html create mode 100644 package.json create mode 100644 public/alarm.wav create mode 100644 public/images/icon.png create mode 100644 public/index.html create mode 100644 public/style/font-awesome/css/font-awesome.min.css create mode 100644 public/style/font-awesome/fonts/FontAwesome.otf create mode 100644 public/style/font-awesome/fonts/fontawesome-webfont.eot create mode 100644 public/style/font-awesome/fonts/fontawesome-webfont.svg create mode 100644 public/style/font-awesome/fonts/fontawesome-webfont.ttf create mode 100644 public/style/font-awesome/fonts/fontawesome-webfont.woff create mode 100644 public/style/main.css create mode 100644 public/style/reset.css create mode 100644 shell.nix create mode 100644 src/Edition/Model.elm create mode 100644 src/Edition/Model/Name.elm create mode 100644 src/Edition/Model/Time.elm create mode 100644 src/Edition/Msg.elm create mode 100644 src/Edition/Update.elm create mode 100644 src/Model.elm delete mode 100644 src/Model/Edition/Edition.elm delete mode 100644 src/Model/Edition/NameEdition.elm delete mode 100644 src/Model/Edition/TimeEdition.elm create mode 100644 src/Model/Keyboard.elm delete mode 100644 src/Model/Model.elm delete mode 100644 src/Model/Timer.elm delete mode 100644 src/Model/TimerState.elm create mode 100644 src/Msg.elm create mode 100644 src/Ring.elm create mode 100644 src/Timer/Model.elm create mode 100644 src/Timer/Model/State.elm create mode 100644 src/Timer/Msg.elm create mode 100644 src/Timer/Update.elm create mode 100644 src/Timer/View.elm create mode 100644 src/Update.elm delete mode 100644 src/Update/Update.elm delete mode 100644 src/Update/UpdateEdition.elm delete mode 100644 src/Update/UpdateTimer.elm create mode 100644 src/View.elm delete mode 100644 src/View/ActivatedClasses.elm delete mode 100644 src/View/Timer.elm delete mode 100644 src/View/View.elm diff --git a/.gitignore b/.gitignore index 3bd52a1..4f4679c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +node_modules elm-stuff -elm.js +public/client.js diff --git a/.tmuxinator.yml b/.tmuxinator.yml new file mode 100644 index 0000000..2be1fda --- /dev/null +++ b/.tmuxinator.yml @@ -0,0 +1,8 @@ +name: sharedCost + +windows: + - main: + layout: fff4,119x58,0,0{94x58,0,0,0,24x58,95,0,1} + panes: + - # Empty + - make install && make && make watch diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..daea513 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +all: build + +clean: + @rm -r node_modules >/dev/null 2>&1 || true + @rm -r elm-stuff >/dev/null 2>&1 || true + +install: + @npm install + @elm package install + +watch: kill-server launch-server watch-client + +# Build and launch +# ---------------- + +kill-server: + @fuser -k 8080/tcp || true + +launch-server: + @./node_modules/http-server/bin/http-server ./public -p 8080 & + +build: + @elm make src/Main.elm --output public/client.js || true + +watch-client: + @./node_modules/nodemon/bin/nodemon.js --watch src -e elm --exec 'clear && make build --silent' diff --git a/alarm.wav b/alarm.wav deleted file mode 100644 index 41d6709..0000000 Binary files a/alarm.wav and /dev/null differ diff --git a/design/design.css b/design/design.css deleted file mode 100644 index 7602c7d..0000000 --- a/design/design.css +++ /dev/null @@ -1,171 +0,0 @@ -/**************/ -/* Header bar */ -/**************/ - -.headerBar { - background-color: #111111; - font-size: 70px; - box-shadow: 0px 2px 2px grey; - min-width: 1020px; - margin-bottom: 2px; -} - -.headerBar > button { - background-color: #111111; - color: white; - height: 150px; - line-height: 150px; - font-size: 70px; - border: none; - font-family: "DejaVu Serif"; - transition: color 0.4s ease; -} - -.headerBar > button:focus { - background-color: #222222; -} - -.headerBar > button.title { - letter-spacing: 12px; - padding: 0 50px; -} - -.headerBar > button.addTimer { - float: right; - padding: 0 50px; -} - -.headerBar > button:hover, -.headerBar > button:hover { - color: #CCCC88; - border-color: #CCCC88; -} - -.headerBar > button { - background-color: #111111; - color: white; - height: 150px; - line-height: 150px; - font-size: 70px; - border: none; - font-family: "DejaVu Serif"; - transition: color 0.4s ease; -} - -.headerBar > button:focus { - background-color: #222222; -} - -.headerBar > button.title { - letter-spacing: 12px; - padding: 0 50px; -} - -.headerBar > button.addTimer { - float: right; - padding: 0 50px; -} - -.headerBar > button:hover, -.headerBar > button:hover { - color: #CCCC88; - border-color: #CCCC88; -} - -/**********/ -/* Timers */ -/**********/ - -.timers { - text-align: center; - font-size: 30px; - display: table; - table-layout: auto; - margin-left: auto; - margin-right: auto; - width: 1020px; -} - -.timer { - line-height: 140px; - height: 140px; - position: relative; - display: table-row; -} - -.timer:nth-child(even) { - background-color: #FAFAFA; -} - -.timer.isRinging:hover { - background-color: #FED5AE; -} - -.timer .name, -.timer .time, -.timer button { - display: table-cell; -} - -.timer button { - background-color: rgba(0, 0, 0, 0); - border: none; - width: 140px; - height: 140px; - font-size: 35px; - padding: 0px; - position: relative; -} - -.timer button.remove { - color: #AA2222; -} - -.timer:only-of-type button.remove { - color: grey; -} - -.timer .name { - width: 400px; - letter-spacing: 2px; - cursor: text; -} - -.timer .time { - width: 200px; - letter-spacing: 2px; - cursor: text; - border-radius: 0px; -} - -.timer .progressBar { - background-color: darkgrey; - position: absolute; - left: 0; - top: 133px; - height: 10px; -} - -.timer.isRunning .progressBar { - background-color: #CDE4C2; -} - -.timer.isRinging .progressBar { - background-color: #FED5AE; -} - -.timer:hover.isRinging .progressBar { - top: 0px; - height: 143px; - z-index: 1; - opacity: 0; - cursor: pointer; -} - -.timer .edition.empty { - color: #DDDDDD; -} - -.timer .edition:not(.empty) { - color: darkgrey; -} diff --git a/design/font-awesome/css/font-awesome.min.css b/design/font-awesome/css/font-awesome.min.css deleted file mode 100644 index ec53d4d..0000000 --- a/design/font-awesome/css/font-awesome.min.css +++ /dev/null @@ -1,4 +0,0 @@ -/*! - * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.2.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"} \ No newline at end of file diff --git a/design/font-awesome/fonts/FontAwesome.otf b/design/font-awesome/fonts/FontAwesome.otf deleted file mode 100644 index 81c9ad9..0000000 Binary files a/design/font-awesome/fonts/FontAwesome.otf and /dev/null differ diff --git a/design/font-awesome/fonts/fontawesome-webfont.eot b/design/font-awesome/fonts/fontawesome-webfont.eot deleted file mode 100644 index 84677bc..0000000 Binary files a/design/font-awesome/fonts/fontawesome-webfont.eot and /dev/null differ diff --git a/design/font-awesome/fonts/fontawesome-webfont.svg b/design/font-awesome/fonts/fontawesome-webfont.svg deleted file mode 100644 index d907b25..0000000 --- a/design/font-awesome/fonts/fontawesome-webfont.svg +++ /dev/null @@ -1,520 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/design/font-awesome/fonts/fontawesome-webfont.ttf b/design/font-awesome/fonts/fontawesome-webfont.ttf deleted file mode 100644 index 96a3639..0000000 Binary files a/design/font-awesome/fonts/fontawesome-webfont.ttf and /dev/null differ diff --git a/design/font-awesome/fonts/fontawesome-webfont.woff b/design/font-awesome/fonts/fontawesome-webfont.woff deleted file mode 100644 index 628b6a5..0000000 Binary files a/design/font-awesome/fonts/fontawesome-webfont.woff and /dev/null differ diff --git a/design/reset.css b/design/reset.css deleted file mode 100644 index 58fedc2..0000000 --- a/design/reset.css +++ /dev/null @@ -1,68 +0,0 @@ -/* http://meyerweb.com/eric/tools/css/reset/ - v2.0 | 20110126 - License: none (public domain) -*/ - -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; -} -/* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; -} -body { - line-height: 1; -} -ol, ul { - list-style: none; -} -blockquote, q { - quotes: none; -} -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} -table { - border-collapse: collapse; - border-spacing: 0; -} - -html { - box-sizing: border-box; -} -*, *:before, *:after { - box-sizing: inherit; -} - -button { - cursor: pointer; - box-sizing: border-box; -} - -button::-moz-focus-inner { - border: 0; -} - -button:focus { - outline: 0; -} diff --git a/elm-package.json b/elm-package.json index 995d8e9..4d64f3b 100644 --- a/elm-package.json +++ b/elm-package.json @@ -1,14 +1,13 @@ { - "version": "0.0.1", - "summary": "", - "description": "", + "version": "1.0.0", + "summary": "timer", "repository": "https://github.com/guyonvarch/timer.git", "license": "BSD3", "source-directories": ["src"], "exposed-modules": [], - "elm-version": "0.15.0 <= v < 0.16.0", + "elm-version": "0.17.1 <= v < 0.18.0", "dependencies": { - "elm-lang/core": "2.0.1 <= v < 3.0.0", - "evancz/elm-html": "3.0.0 <= v < 4.0.0" + "elm-lang/core": "4.0.5 <= v < 5.0.0", + "elm-lang/html": "1.1.0 <= v < 2.0.0" } } diff --git a/gen b/gen deleted file mode 100755 index e1d94dc..0000000 --- a/gen +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -elm-make src/Main.elm --output elm.js diff --git a/images/icon.png b/images/icon.png deleted file mode 100644 index a489112..0000000 Binary files a/images/icon.png and /dev/null differ diff --git a/index.html b/index.html deleted file mode 100644 index f5f825e..0000000 --- a/index.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - Timer - - - - - - - - - - - - - - diff --git a/package.json b/package.json new file mode 100644 index 0000000..ba7eb37 --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "devDependencies": { + "nodemon": "1.9.1", + "http-server": "0.8.5" + } +} diff --git a/public/alarm.wav b/public/alarm.wav new file mode 100644 index 0000000..41d6709 Binary files /dev/null and b/public/alarm.wav differ diff --git a/public/images/icon.png b/public/images/icon.png new file mode 100644 index 0000000..a489112 Binary files /dev/null and b/public/images/icon.png differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..44749fc --- /dev/null +++ b/public/index.html @@ -0,0 +1,40 @@ + + + + Timer + + + + + + + + + + + + + diff --git a/public/style/font-awesome/css/font-awesome.min.css b/public/style/font-awesome/css/font-awesome.min.css new file mode 100644 index 0000000..ec53d4d --- /dev/null +++ b/public/style/font-awesome/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.2.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"} \ No newline at end of file diff --git a/public/style/font-awesome/fonts/FontAwesome.otf b/public/style/font-awesome/fonts/FontAwesome.otf new file mode 100644 index 0000000..81c9ad9 Binary files /dev/null and b/public/style/font-awesome/fonts/FontAwesome.otf differ diff --git a/public/style/font-awesome/fonts/fontawesome-webfont.eot b/public/style/font-awesome/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..84677bc Binary files /dev/null and b/public/style/font-awesome/fonts/fontawesome-webfont.eot differ diff --git a/public/style/font-awesome/fonts/fontawesome-webfont.svg b/public/style/font-awesome/fonts/fontawesome-webfont.svg new file mode 100644 index 0000000..d907b25 --- /dev/null +++ b/public/style/font-awesome/fonts/fontawesome-webfont.svg @@ -0,0 +1,520 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/style/font-awesome/fonts/fontawesome-webfont.ttf b/public/style/font-awesome/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..96a3639 Binary files /dev/null and b/public/style/font-awesome/fonts/fontawesome-webfont.ttf differ diff --git a/public/style/font-awesome/fonts/fontawesome-webfont.woff b/public/style/font-awesome/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..628b6a5 Binary files /dev/null and b/public/style/font-awesome/fonts/fontawesome-webfont.woff differ diff --git a/public/style/main.css b/public/style/main.css new file mode 100644 index 0000000..7602c7d --- /dev/null +++ b/public/style/main.css @@ -0,0 +1,171 @@ +/**************/ +/* Header bar */ +/**************/ + +.headerBar { + background-color: #111111; + font-size: 70px; + box-shadow: 0px 2px 2px grey; + min-width: 1020px; + margin-bottom: 2px; +} + +.headerBar > button { + background-color: #111111; + color: white; + height: 150px; + line-height: 150px; + font-size: 70px; + border: none; + font-family: "DejaVu Serif"; + transition: color 0.4s ease; +} + +.headerBar > button:focus { + background-color: #222222; +} + +.headerBar > button.title { + letter-spacing: 12px; + padding: 0 50px; +} + +.headerBar > button.addTimer { + float: right; + padding: 0 50px; +} + +.headerBar > button:hover, +.headerBar > button:hover { + color: #CCCC88; + border-color: #CCCC88; +} + +.headerBar > button { + background-color: #111111; + color: white; + height: 150px; + line-height: 150px; + font-size: 70px; + border: none; + font-family: "DejaVu Serif"; + transition: color 0.4s ease; +} + +.headerBar > button:focus { + background-color: #222222; +} + +.headerBar > button.title { + letter-spacing: 12px; + padding: 0 50px; +} + +.headerBar > button.addTimer { + float: right; + padding: 0 50px; +} + +.headerBar > button:hover, +.headerBar > button:hover { + color: #CCCC88; + border-color: #CCCC88; +} + +/**********/ +/* Timers */ +/**********/ + +.timers { + text-align: center; + font-size: 30px; + display: table; + table-layout: auto; + margin-left: auto; + margin-right: auto; + width: 1020px; +} + +.timer { + line-height: 140px; + height: 140px; + position: relative; + display: table-row; +} + +.timer:nth-child(even) { + background-color: #FAFAFA; +} + +.timer.isRinging:hover { + background-color: #FED5AE; +} + +.timer .name, +.timer .time, +.timer button { + display: table-cell; +} + +.timer button { + background-color: rgba(0, 0, 0, 0); + border: none; + width: 140px; + height: 140px; + font-size: 35px; + padding: 0px; + position: relative; +} + +.timer button.remove { + color: #AA2222; +} + +.timer:only-of-type button.remove { + color: grey; +} + +.timer .name { + width: 400px; + letter-spacing: 2px; + cursor: text; +} + +.timer .time { + width: 200px; + letter-spacing: 2px; + cursor: text; + border-radius: 0px; +} + +.timer .progressBar { + background-color: darkgrey; + position: absolute; + left: 0; + top: 133px; + height: 10px; +} + +.timer.isRunning .progressBar { + background-color: #CDE4C2; +} + +.timer.isRinging .progressBar { + background-color: #FED5AE; +} + +.timer:hover.isRinging .progressBar { + top: 0px; + height: 143px; + z-index: 1; + opacity: 0; + cursor: pointer; +} + +.timer .edition.empty { + color: #DDDDDD; +} + +.timer .edition:not(.empty) { + color: darkgrey; +} diff --git a/public/style/reset.css b/public/style/reset.css new file mode 100644 index 0000000..58fedc2 --- /dev/null +++ b/public/style/reset.css @@ -0,0 +1,68 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} + +html { + box-sizing: border-box; +} +*, *:before, *:after { + box-sizing: inherit; +} + +button { + cursor: pointer; + box-sizing: border-box; +} + +button::-moz-focus-inner { + border: 0; +} + +button:focus { + outline: 0; +} diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..d3365c2 --- /dev/null +++ b/shell.nix @@ -0,0 +1,15 @@ +with import {}; { + env = stdenv.mkDerivation { + name = "env"; + buildInputs = [ + elmPackages.elm + nodejs + tmux + tmuxinator + ]; + shellHook = '' + tmux kill-session -t timer >/dev/null 2>&1 + tmuxinator local + ''; + }; +} diff --git a/src/Edition/Model.elm b/src/Edition/Model.elm new file mode 100644 index 0000000..5c1b295 --- /dev/null +++ b/src/Edition/Model.elm @@ -0,0 +1,45 @@ +module Edition.Model exposing + ( Edition + , Kind(..) + , newEdition + , keyCodeToChar + , isEmpty + ) + +import String + +import Model.Id exposing (..) +import Model.Keyboard exposing (KeyCode) + +import Edition.Model.Name as NameEdition +import Edition.Model.Time as TimeEdition + +type alias Edition = + { id : Id + , kind : Kind + , chars : List Char + } + +type Kind = + Name + | Time + +newEdition id kind = + { id = id + , kind = kind + , chars = [] + } + +keyCodeToChar : Kind -> KeyCode -> Maybe Char +keyCodeToChar kind = + case kind of + Name -> NameEdition.keyCodeToChar + Time -> TimeEdition.keyCodeToChar + +isEmpty : Edition -> Bool +isEmpty edition = + edition.chars + |> String.fromList + |> String.trim + |> String.length + |> (==) 0 diff --git a/src/Edition/Model/Name.elm b/src/Edition/Model/Name.elm new file mode 100644 index 0000000..19f4c04 --- /dev/null +++ b/src/Edition/Model/Name.elm @@ -0,0 +1,19 @@ +module Edition.Model.Name exposing + ( keyCodeToChar + , renderNameEdition + ) + +import Char +import String +import List + +import Model.Keyboard exposing (KeyCode) + +keyCodeToChar : KeyCode -> Maybe Char +keyCodeToChar = Just << Char.fromCode + +renderNameEdition : List Char -> String +renderNameEdition chars = + chars + |> List.reverse + |> String.fromList diff --git a/src/Edition/Model/Time.elm b/src/Edition/Model/Time.elm new file mode 100644 index 0000000..35971c3 --- /dev/null +++ b/src/Edition/Model/Time.elm @@ -0,0 +1,52 @@ +module Edition.Model.Time exposing + ( keyCodeToChar + , toTime + , toMinutesAndSeconds + ) + +import Time exposing (Time) +import List +import Array +import String +import Char + +import Model.Keyboard exposing (KeyCode) +import Utils.List exposing (..) +import Utils.Maybe exposing (..) + +keyCodeToChar : KeyCode -> Maybe Char +keyCodeToChar code = + let char = Char.fromCode code + in if Char.isDigit char + then Just char + else Nothing + +toTime : List Char -> Time +toTime numbers = + numbers + |> toMinutesAndSeconds + |> \(a, b) -> (stringToInt a, stringToInt b) + |> \(minutes, seconds) -> (toFloat minutes) * 60 * 1000 + (toFloat seconds) * 1000 + +toMinutesAndSeconds : List Char -> (String, String) +toMinutesAndSeconds numbers = + numbers + |> List.take 4 + |> List.reverse + |> completeBegin '0' 4 + |> splitAt 2 + |> \(a, b) -> (String.fromList a, String.fromList b) + +completeBegin : a -> Int -> List a -> List a +completeBegin x count xs = + let length = List.length xs + in List.append (repeat (count - length) x) xs + +stringToInt : String -> Int +stringToInt str = + str + |> String.toInt + |> \res -> + case res of + Ok n -> n + Err _ -> 0 diff --git a/src/Edition/Msg.elm b/src/Edition/Msg.elm new file mode 100644 index 0000000..546e45b --- /dev/null +++ b/src/Edition/Msg.elm @@ -0,0 +1,9 @@ +module Edition.Msg exposing + ( Msg(..) + ) + +import Char exposing (KeyCode) + +type Msg = + DeleteLast + | AddChar KeyCode diff --git a/src/Edition/Update.elm b/src/Edition/Update.elm new file mode 100644 index 0000000..e219629 --- /dev/null +++ b/src/Edition/Update.elm @@ -0,0 +1,30 @@ +module Edition.Update exposing + ( updateEdition + ) + +import Char +import Char exposing (KeyCode) +import List + +import Edition.Model exposing (..) +import Edition.Msg exposing (..) + +updateEdition : Msg -> Edition -> Edition +updateEdition msg edition = + case msg of + + DeleteLast -> + case List.tail edition.chars of + Just tailChars -> + { edition | chars = tailChars } + Nothing -> + edition + + AddChar keyCode -> + case keyCodeToChar edition.kind keyCode of + Just char -> + if keyCode == 32 && List.head edition.chars == Just (Char.fromCode 32) + then edition + else { edition | chars = char :: edition.chars } + Nothing -> + edition diff --git a/src/Main.elm b/src/Main.elm index 746df14..743c776 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -1,51 +1,29 @@ -module Main +port module Main exposing ( main - ) where + ) -import Signal -import Html exposing (Html) -import Time exposing (..) -import Mouse -import Json.Encode exposing (Value) -import Keyboard +import Html.App exposing (programWithFlags) +import Time import Char -import Dict -import List -import Keyboard exposing (KeyCode) -import Model.Model exposing (..) -import Model.Position exposing (..) -import Model.TimerState exposing (..) -import Model.Id exposing (..) -import Update.Update exposing (..) -import View.View exposing (view) - -main : Signal Html -main = Signal.map view model - -model : Signal Model -model = Signal.foldp logUpdate (initialModel initialTime) input - -input : Signal Action -input = - Signal.mergeMany - [ actions.signal - , Signal.map DeltaTime (fps 30) - , Signal.map KeyPressed keyPress - ] - -port ringingTimers : Signal Bool -port ringingTimers = - Signal.map - (\model -> - model.timers - |> Dict.toList - |> List.map snd - |> List.any (\timer -> timer.state == Ringing) - ) - model - |> Signal.dropRepeats - -port keyPress : Signal KeyCode - -port initialTime : Time +import Model exposing (init) +import Model.Keyboard exposing (KeyCode) +import Msg +import Update exposing (update) +import View exposing (view) + +main : Program Float +main = + programWithFlags + { init = init + , update = update + , subscriptions = (\model -> + Sub.batch + [ Time.every 40 Msg.Time + , keyPress Msg.KeyPressed + ] + ) + , view = view + } + +port keyPress : (KeyCode -> msg) -> Sub msg diff --git a/src/Model.elm b/src/Model.elm new file mode 100644 index 0000000..6e75c39 --- /dev/null +++ b/src/Model.elm @@ -0,0 +1,40 @@ +module Model exposing + ( Model + , init + , numberOfTimers + ) + +import Dict exposing (Dict) +import Dict +import Time exposing (Time) +import List + +import Msg exposing (Msg) + +import Model.Id exposing (..) +import Model.IdGenerator exposing (..) + +import Timer.Model as Timer exposing (Timer) + +import Edition.Model exposing (Edition) + +type alias Model = + { time : Time + , timers : Dict Id Timer + , timerIdGenerator : IdGenerator + , edition : Maybe Edition + } + +init : Time -> (Model, Cmd Msg) +init initialTime = + let (id, idGenerator) = getId initialIdGenerator + model = + { time = initialTime + , timers = Dict.insert id (Timer.init initialTime) Dict.empty + , timerIdGenerator = idGenerator + , edition = Nothing + } + in (model, Cmd.none) + +numberOfTimers : Model -> Int +numberOfTimers = List.length << Dict.toList << .timers diff --git a/src/Model/Edition/Edition.elm b/src/Model/Edition/Edition.elm deleted file mode 100644 index 9a28253..0000000 --- a/src/Model/Edition/Edition.elm +++ /dev/null @@ -1,44 +0,0 @@ -module Model.Edition.Edition - ( Edition - , Kind(..) - , newEdition - , keyCodeToChar - , isEmpty - ) where - -import Keyboard exposing (KeyCode) -import String - -import Model.Id exposing (..) -import Model.Edition.NameEdition as NameEdition -import Model.Edition.TimeEdition as TimeEdition - -type alias Edition = - { id : Id - , kind : Kind - , chars : List Char - } - -type Kind = - Name - | Time - -newEdition id kind = - { id = id - , kind = kind - , chars = [] - } - -keyCodeToChar : Kind -> KeyCode -> Maybe Char -keyCodeToChar kind = - case kind of - Name -> NameEdition.keyCodeToChar - Time -> TimeEdition.keyCodeToChar - -isEmpty : Edition -> Bool -isEmpty edition = - edition.chars - |> String.fromList - |> String.trim - |> String.length - |> (==) 0 diff --git a/src/Model/Edition/NameEdition.elm b/src/Model/Edition/NameEdition.elm deleted file mode 100644 index be7c6b3..0000000 --- a/src/Model/Edition/NameEdition.elm +++ /dev/null @@ -1,18 +0,0 @@ -module Model.Edition.NameEdition - ( keyCodeToChar - , renderNameEdition - ) where - -import Char -import Keyboard exposing (KeyCode) -import String -import List - -keyCodeToChar : KeyCode -> Maybe Char -keyCodeToChar = Just << Char.fromCode - -renderNameEdition : List Char -> String -renderNameEdition chars = - chars - |> List.reverse - |> String.fromList diff --git a/src/Model/Edition/TimeEdition.elm b/src/Model/Edition/TimeEdition.elm deleted file mode 100644 index 3b70c3d..0000000 --- a/src/Model/Edition/TimeEdition.elm +++ /dev/null @@ -1,52 +0,0 @@ -module Model.Edition.TimeEdition - ( keyCodeToChar - , toTime - , toMinutesAndSeconds - ) where - -import Time exposing (Time) -import List -import Array -import String -import Keyboard exposing (KeyCode) -import Char - -import Utils.List exposing (..) -import Utils.Maybe exposing (..) - -keyCodeToChar : KeyCode -> Maybe Char -keyCodeToChar code = - let char = Char.fromCode code - in if Char.isDigit char - then Just char - else Nothing - -toTime : List Char -> Time -toTime numbers = - numbers - |> toMinutesAndSeconds - |> \(a, b) -> (stringToInt a, stringToInt b) - |> \(minutes, seconds) -> (toFloat minutes) * 60 * 1000 + (toFloat seconds) * 1000 - -toMinutesAndSeconds : List Char -> (String, String) -toMinutesAndSeconds numbers = - numbers - |> List.take 4 - |> List.reverse - |> completeBegin '0' 4 - |> splitAt 2 - |> \(a, b) -> (String.fromList a, String.fromList b) - -completeBegin : a -> Int -> List a -> List a -completeBegin x count xs = - let length = List.length xs - in List.append (repeat (count - length) x) xs - -stringToInt : String -> Int -stringToInt str = - str - |> String.toInt - |> \res -> - case res of - Ok n -> n - Err _ -> 0 diff --git a/src/Model/Id.elm b/src/Model/Id.elm index c416804..f71b71e 100644 --- a/src/Model/Id.elm +++ b/src/Model/Id.elm @@ -1,5 +1,5 @@ -module Model.Id +module Model.Id exposing ( Id - ) where + ) type alias Id = Int diff --git a/src/Model/IdGenerator.elm b/src/Model/IdGenerator.elm index 9001191..f3eefad 100644 --- a/src/Model/IdGenerator.elm +++ b/src/Model/IdGenerator.elm @@ -1,8 +1,8 @@ -module Model.IdGenerator +module Model.IdGenerator exposing ( IdGenerator , initialIdGenerator , getId - ) where + ) import Model.Id exposing (..) @@ -17,5 +17,5 @@ initialIdGenerator = getId : IdGenerator -> (Id, IdGenerator) getId idGenerator = ( idGenerator.counter - , { idGenerator | counter <- idGenerator.counter + 1 } + , { idGenerator | counter = idGenerator.counter + 1 } ) diff --git a/src/Model/Keyboard.elm b/src/Model/Keyboard.elm new file mode 100644 index 0000000..7649a56 --- /dev/null +++ b/src/Model/Keyboard.elm @@ -0,0 +1,5 @@ +module Model.Keyboard exposing + ( KeyCode + ) + +type alias KeyCode = Int diff --git a/src/Model/Model.elm b/src/Model/Model.elm deleted file mode 100644 index a660b16..0000000 --- a/src/Model/Model.elm +++ /dev/null @@ -1,34 +0,0 @@ -module Model.Model - ( Model - , initialModel - , numberOfTimers - ) where - -import Dict exposing (Dict) -import Dict -import Time exposing (Time) -import List - -import Model.Timer exposing (..) -import Model.Edition.Edition exposing (..) -import Model.Id exposing (..) -import Model.IdGenerator exposing (..) - -type alias Model = - { currentTime : Time - , timers : Dict Id Timer - , timerIdGenerator : IdGenerator - , edition : Maybe Edition - } - -initialModel : Time -> Model -initialModel initialTime = - let (id, idGenerator) = getId initialIdGenerator - in { currentTime = initialTime - , timers = Dict.insert id (initialTimer initialTime) Dict.empty - , timerIdGenerator = idGenerator - , edition = Nothing - } - -numberOfTimers : Model -> Int -numberOfTimers = List.length << Dict.toList << .timers diff --git a/src/Model/Position.elm b/src/Model/Position.elm index d15e853..12d2f9b 100644 --- a/src/Model/Position.elm +++ b/src/Model/Position.elm @@ -1,6 +1,6 @@ -module Model.Position +module Model.Position exposing ( positionEncoder - ) where + ) import Json.Encode exposing (..) diff --git a/src/Model/Timer.elm b/src/Model/Timer.elm deleted file mode 100644 index 51d293d..0000000 --- a/src/Model/Timer.elm +++ /dev/null @@ -1,27 +0,0 @@ -module Model.Timer - ( Timer - , initialTimer - ) where - -import List -import Time exposing (Time) - -import Model.TimerState exposing (..) - -type alias Timer = - { creationTime : Time - , name : Maybe String - , initialTime : Time - , currentTime : Time - , state : TimerState - } - -initialTimer : Time -> Timer -initialTimer creationTime = - let initialTime = 5 * 60 * 1000 - in { creationTime = creationTime - , name = Nothing - , initialTime = initialTime - , currentTime = initialTime - , state = Idle - } diff --git a/src/Model/TimerState.elm b/src/Model/TimerState.elm deleted file mode 100644 index dbbcb80..0000000 --- a/src/Model/TimerState.elm +++ /dev/null @@ -1,8 +0,0 @@ -module Model.TimerState - ( TimerState(..) - ) where - -type TimerState = - Idle - | Running - | Ringing diff --git a/src/Msg.elm b/src/Msg.elm new file mode 100644 index 0000000..b5efe2f --- /dev/null +++ b/src/Msg.elm @@ -0,0 +1,23 @@ +module Msg exposing + ( Msg(..) + ) + +import Time exposing (Time) + +import Model.Id exposing (Id) +import Model.Keyboard exposing (KeyCode) + +import Timer.Msg as Timer + +import Edition.Model exposing (Kind) + +type Msg = + NoOp + | Initialize + | AddNewTimer + | Time Time + | UpdateTimer Id Timer.Msg + | RemoveTimer Id + | Edit Id Kind + | ClickAway + | KeyPressed KeyCode diff --git a/src/Ring.elm b/src/Ring.elm new file mode 100644 index 0000000..fa29928 --- /dev/null +++ b/src/Ring.elm @@ -0,0 +1,5 @@ +port module Ring exposing + ( ring + ) + +port ring : Bool -> Cmd msg diff --git a/src/Timer/Model.elm b/src/Timer/Model.elm new file mode 100644 index 0000000..89af67e --- /dev/null +++ b/src/Timer/Model.elm @@ -0,0 +1,27 @@ +module Timer.Model exposing + ( Timer + , init + ) + +import List +import Time exposing (Time) + +import Timer.Model.State exposing (..) + +type alias Timer = + { creationTime : Time + , name : Maybe String + , initialTime : Time + , time : Time + , state : State + } + +init : Time -> Timer +init creationTime = + let initialTime = 5 * 60 * 1000 + in { creationTime = creationTime + , name = Nothing + , initialTime = initialTime + , time = initialTime + , state = Idle + } diff --git a/src/Timer/Model/State.elm b/src/Timer/Model/State.elm new file mode 100644 index 0000000..1162e59 --- /dev/null +++ b/src/Timer/Model/State.elm @@ -0,0 +1,8 @@ +module Timer.Model.State exposing + ( State(..) + ) + +type State = + Idle + | Running + | Ringing diff --git a/src/Timer/Msg.elm b/src/Timer/Msg.elm new file mode 100644 index 0000000..150d4fc --- /dev/null +++ b/src/Timer/Msg.elm @@ -0,0 +1,13 @@ +module Timer.Msg exposing + ( Msg(..) + ) + +import Time exposing (Time) + +type Msg = + Rename String + | Pause + | ToggleRunning + | Stop + | SetTime Time + | SubstractTime Time diff --git a/src/Timer/Update.elm b/src/Timer/Update.elm new file mode 100644 index 0000000..31e8a43 --- /dev/null +++ b/src/Timer/Update.elm @@ -0,0 +1,56 @@ +module Timer.Update exposing + ( updateTimer + ) + +import Time exposing (Time) + +import Model.Id exposing (..) + +import Timer.Model exposing (..) +import Timer.Model.State exposing (..) +import Timer.Msg exposing (..) + +updateTimer : Msg -> Timer -> Timer +updateTimer msg timer = + case msg of + + Rename name -> + { timer | name = Just name } + + Pause -> + { timer | state = Idle } + + ToggleRunning -> + { timer + | state = + if timer.time > 0 && timer.state == Idle + then Running + else Idle + } + + Stop -> + { timer + | time = timer.initialTime + , state = Idle + } + + SetTime time -> + { timer + | initialTime = time + , time = time + } + + SubstractTime time -> + if timer.state == Running + then + let newTime = timer.time - time + in if newTime <= 0.0 + then + { timer + | time = 0.0 + , state = Ringing + } + else + { timer | time = newTime } + else + timer diff --git a/src/Timer/View.elm b/src/Timer/View.elm new file mode 100644 index 0000000..561ae8f --- /dev/null +++ b/src/Timer/View.elm @@ -0,0 +1,175 @@ +module Timer.View exposing + ( view + ) + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Time exposing (Time) +import Maybe +import List +import String + +import Msg exposing (Msg) + +import Model exposing (..) +import Model.Id exposing (..) + +import Timer.Model exposing (..) +import Timer.Model.State exposing (..) +import Timer.Update exposing (..) +import Timer.Msg as Timer + +import Edition.Model exposing (..) +import Edition.Model.Name exposing (..) +import Edition.Model.Time exposing (..) + +import Update exposing (..) + +import Utils.Maybe exposing (..) + +view : Model -> (Id, Timer) -> Html Msg +view model (id, timer) = + div + [ [ ("timer", True) + , ("isRinging", timer.state == Ringing) + , ("isRunning", timer.state == Running) + ] + |> classList + ] + [ renderMaybeEdition model id Name (nameBlockReadOnly id timer) (nameBlockEdition id timer) + , renderMaybeEdition model id Time (timeBlockReadOnly (id, timer)) (timeBlockEdition (id, timer)) + , playPauseTimer (id, timer) + , stopTimer (id, timer) + , removeTimer (id, timer) + ] + +nameBlockReadOnly : Id -> Timer -> Html Msg +nameBlockReadOnly id timer = + div + [ class "name" + , onClick (Msg.Edit id Name) + ] + [ text (timerName id timer) ] + +nameBlockEdition : Id -> Timer -> Edition -> Html Msg +nameBlockEdition id timer edition = + div + [ [ ("name", True) + , ("edition", True) + , ("empty", isEmpty edition) + ] + |> classList + , onClick Msg.NoOp + ] + [ if isEmpty edition + then + text (timerName id timer) + else + edition.chars + |> renderNameEdition + |> flip String.append "_" + |> text + ] + +timerName : Id -> Timer -> String +timerName id = Maybe.withDefault ("Timer " ++ toString id) << .name + +timeBlockReadOnly : (Id, Timer) -> Html Msg +timeBlockReadOnly (id, timer) = + div + [ class "time" + , onClick (Msg.Edit id Time) + ] + [ timeWithProgressBar (id, timer) ] + +timeBlockEdition : (Id, Timer) -> Edition -> Html Msg +timeBlockEdition (id, timer) edition = + div + [ [ ("time", True) + , ("edition", True) + , ("empty", isEmpty edition) + ] + |> classList + , onClick Msg.NoOp + ] + [ if isEmpty edition + then + timeWithProgressBar (id, timer) + else + text (editionView edition.chars) + ] + +editionView : List Char -> String +editionView numbers = + let (minutes, seconds) = toMinutesAndSeconds numbers + in minutes ++ " : " ++ seconds + +timeView : Time -> String +timeView time = + let totalSeconds = ceiling (time / 1000) + totalMinutes = totalSeconds // 60 + restSeconds = totalSeconds `rem` 60 + in (String.padLeft 2 '0' (toString totalMinutes)) ++ " : " ++ (String.padLeft 2 '0' (toString restSeconds)) + +timeWithProgressBar : (Id, Timer) -> Html Msg +timeWithProgressBar (id, timer) = + div + [] + [ span + [ class "progressBar" + , let width = + 1 - timer.time / timer.initialTime + |> (*) 1020 + |> round + |> toString + |> flip String.append "px" + in style [ ("width", width) ] + , onClick (Msg.UpdateTimer id Timer.Stop) + ] + [] + , span + [ class "text" ] + [ text (timeView timer.time) ] + ] + +playPauseTimer : (Id, Timer) -> Html Msg +playPauseTimer (id, timer) = + button + [ class <| "playPause" + , onClick (Msg.UpdateTimer id Timer.ToggleRunning) + ] + [ let icon = if timer.state == Running then "fa-pause" else "fa-play" + in i + [ class <| "fa fa-fw " ++ icon ] + [] + ] + +stopTimer : (Id, Timer) -> Html Msg +stopTimer (id, timer) = + button + [ class <| "stop" + , onClick (Msg.UpdateTimer id Timer.Stop) + ] + [ i [ class "fa fa-fw fa-stop" ] [] ] + +removeTimer : (Id, Timer) -> Html Msg +removeTimer (id, timer) = + button + [ class <| "remove" + , onClick (Msg.RemoveTimer id) + ] + [ i [ class "fa fa-fw fa-remove" ] [] ] + +renderMaybeEdition : Model -> Id -> Kind -> Html Msg -> (Edition -> Html Msg) -> Html Msg +renderMaybeEdition model id kind readOnlyView editionView = + let maybeEdition = filterMaybe (\edition -> edition.id == id) model.edition + in case maybeEdition of + Just edition -> + if edition.kind == kind + then + editionView edition + else + readOnlyView + Nothing -> + readOnlyView diff --git a/src/Update.elm b/src/Update.elm new file mode 100644 index 0000000..42eb682 --- /dev/null +++ b/src/Update.elm @@ -0,0 +1,141 @@ +module Update exposing + ( update + ) + +import Dict + +import Msg exposing (Msg) + +import Ring + +import Model exposing (Model) +import Model.Id exposing (..) +import Model.IdGenerator exposing (..) +import Model.Keyboard exposing (KeyCode) + +import Timer.Model as Timer exposing (..) +import Timer.Model.State as TimerState +import Timer.Update exposing (..) +import Timer.Msg as Timer + +import Edition.Model exposing (..) +import Edition.Model.Name exposing (..) +import Edition.Model.Time exposing (..) +import Edition.Update exposing (..) +import Edition.Msg as Edition + +import Utils.Maybe exposing (..) + +update : Msg -> Model -> (Model, Cmd Msg) +update msg model = + case msg of + + Msg.NoOp -> + (model, Cmd.none) + + Msg.Initialize -> + Model.init model.time + + Msg.AddNewTimer -> + let (id, newTimerIdGenerator) = getId model.timerIdGenerator + in ( { model + | timers = Dict.insert id (Timer.init model.time) model.timers + , timerIdGenerator = newTimerIdGenerator + } + , Cmd.none + ) + + Msg.Time time -> + let delta = time - model.time + newModel = + { model + | time = time + , timers = Dict.map (\id timer -> updateTimer (Timer.SubstractTime delta) timer) model.timers + } + ringing = + newModel.timers + |> Dict.values + |> List.map .state + |> List.member TimerState.Ringing + in ( newModel + , Ring.ring ringing + ) + + Msg.UpdateTimer id timerMsg -> + let maybeEdition = filterMaybe (\edition -> edition.id == id) model.edition + newModel = + case maybeEdition of + Just edition -> + if edition.kind == Time then validEdition model else model + Nothing -> + model + in ( { newModel | timers = Dict.update id (Maybe.map (updateTimer timerMsg)) newModel.timers } + , Cmd.none + ) + + Msg.RemoveTimer id -> + if Model.numberOfTimers model > 1 + then + ( { model | timers = Dict.remove id model.timers } + , Cmd.none + ) + else + (model, Cmd.none) + + Msg.Edit id kind -> + ( { model + | edition = Just (newEdition id kind) + , timers = + if kind == Time + then Dict.update id (Maybe.map (updateTimer Timer.Pause)) model.timers + else model.timers + } + , Cmd.none + ) + + Msg.ClickAway -> + ( { model | edition = Nothing } + , Cmd.none + ) + + Msg.KeyPressed keyCode -> + if isEnterKeyCode keyCode + then + (validEdition model, Cmd.none) + else + let editionAction = + if isRemoveKeyCode keyCode + then Edition.DeleteLast + else Edition.AddChar keyCode + in ( { model | edition = Maybe.map (updateEdition editionAction) model.edition } + , Cmd.none + ) + +validEdition : Model -> Model +validEdition model = + case model.edition of + Just edition -> + if isEmpty edition + then + { model + | edition = Nothing + } + else + let timerMsg = + case edition.kind of + Name -> + Timer.Rename (renderNameEdition edition.chars) + Time -> + Timer.SetTime (toTime edition.chars) + in { model + | timers = Dict.update edition.id (Maybe.map (updateTimer timerMsg)) model.timers + , edition = Nothing + } + Nothing -> + model + +isEnterKeyCode : KeyCode -> Bool +isEnterKeyCode = (==) 13 + +isRemoveKeyCode : KeyCode -> Bool +isRemoveKeyCode = (==) 8 diff --git a/src/Update/Update.elm b/src/Update/Update.elm deleted file mode 100644 index b4cf741..0000000 --- a/src/Update/Update.elm +++ /dev/null @@ -1,131 +0,0 @@ -module Update.Update - ( Action(..) - , actions - , logUpdate - , update - ) where - -import Signal -import Dict -import Dict exposing (Dict) -import Time exposing (Time) -import Maybe -import Keyboard exposing (KeyCode) -import Char -import List -import Debug - -import Model.Model exposing (..) -import Model.Timer exposing (..) -import Model.Edition.Edition exposing (..) -import Model.Edition.NameEdition exposing (..) -import Model.Edition.TimeEdition exposing (..) -import Model.Id exposing (..) -import Model.IdGenerator exposing (..) - -import Update.UpdateTimer exposing (..) -import Update.UpdateEdition exposing (..) - -import Utils.Maybe exposing (..) - -type Action = - NoOp - | Initialize - | AddNewTimer - | DeltaTime Time - | UpdateTimer Id TimerAction - | RemoveTimer Id - | Edit Id Kind - | ClickAway - | KeyPressed KeyCode - -actions : Signal.Mailbox Action -actions = Signal.mailbox NoOp - -logUpdate : Action -> Model -> Model -logUpdate action model = - case action of - DeltaTime _ -> update action model - _ -> update (Debug.log "action" action) model - -update : Action -> Model -> Model -update action model = - case action of - NoOp -> model - Initialize -> - initialModel model.currentTime - AddNewTimer -> - let (id, newTimerIdGenerator) = getId model.timerIdGenerator - in { model - | timers <- Dict.insert id (initialTimer model.currentTime) model.timers - , timerIdGenerator <- newTimerIdGenerator - } - DeltaTime delta -> - { model - | currentTime <- model.currentTime + delta - , timers <- Dict.map (\id timer -> updateTimer (SubstractTime delta) timer) model.timers - } - UpdateTimer id timerAction -> - let maybeEdition = filterMaybe (\edition -> edition.id == id) model.edition - newModel = - case maybeEdition of - Just edition -> - if edition.kind == Time then validEdition model else model - Nothing -> - model - in { newModel | timers <- Dict.update id (Maybe.map (updateTimer timerAction)) newModel.timers } - RemoveTimer id -> - if numberOfTimers model > 1 - then - { model | timers <- Dict.remove id model.timers } - else - model - Edit id kind -> - { model - | edition <- Just (newEdition id kind) - , timers <- - if kind == Time - then Dict.update id (Maybe.map (updateTimer Pause)) model.timers - else model.timers - } - ClickAway -> - { model | edition <- Nothing } - KeyPressed keyCode -> - if isEnterKeyCode keyCode - then - validEdition model - else - let editionAction = - if isRemoveKeyCode keyCode - then DeleteLast - else AddChar keyCode - in { model | edition <- Maybe.map (updateEdition editionAction) model.edition } - -validEdition : Model -> Model -validEdition model = - case model.edition of - Just edition -> - if isEmpty edition - then - { model - | edition <- Nothing - } - else - let timerAction = - case edition.kind of - Name -> - Rename (renderNameEdition edition.chars) - Time -> - SetTime (toTime edition.chars) - in { model - | timers <- Dict.update edition.id (Maybe.map (updateTimer timerAction)) model.timers - , edition <- Nothing - } - Nothing -> - model - -isEnterKeyCode : KeyCode -> Bool -isEnterKeyCode = (==) 13 - -isRemoveKeyCode : KeyCode -> Bool -isRemoveKeyCode = (==) 8 diff --git a/src/Update/UpdateEdition.elm b/src/Update/UpdateEdition.elm deleted file mode 100644 index 47b0e22..0000000 --- a/src/Update/UpdateEdition.elm +++ /dev/null @@ -1,33 +0,0 @@ -module Update.UpdateEdition - ( updateEdition - , EditionAction(..) - ) where - -import Char -import Char exposing (KeyCode) - -import Model.Edition.Edition exposing (..) - -import Utils.List exposing (..) - -type EditionAction = - DeleteLast - | AddChar KeyCode - -updateEdition : EditionAction -> Edition -> Edition -updateEdition action edition = - case action of - DeleteLast -> - case maybeTail edition.chars of - Just tailChars -> - { edition | chars <- tailChars } - Nothing -> - edition - AddChar keyCode -> - case keyCodeToChar edition.kind keyCode of - Just char -> - if keyCode == 32 && maybeHead edition.chars == Just (Char.fromCode 32) - then edition - else { edition | chars <- char :: edition.chars } - Nothing -> - edition diff --git a/src/Update/UpdateTimer.elm b/src/Update/UpdateTimer.elm deleted file mode 100644 index 08b9969..0000000 --- a/src/Update/UpdateTimer.elm +++ /dev/null @@ -1,57 +0,0 @@ -module Update.UpdateTimer - ( TimerAction(..) - , updateTimer - ) where - -import Time exposing (Time) - -import Model.Timer exposing (..) -import Model.TimerState exposing (..) -import Model.Id exposing (..) - -type TimerAction = - Rename String - | Pause - | ToggleRunning - | Stop - | SetTime Time - | SubstractTime Time - -updateTimer : TimerAction -> Timer -> Timer -updateTimer action timer = - case action of - Rename name -> - { timer | name <- Just name } - Pause -> - { timer | state <- Idle } - ToggleRunning -> - { timer - | state <- - if timer.currentTime > 0 && timer.state == Idle - then Running - else Idle - } - Stop -> - { timer - | currentTime <- timer.initialTime - , state <- Idle - } - SetTime time -> - { timer - | initialTime <- time - , currentTime <- time - } - SubstractTime time -> - if timer.state == Running - then - let newTime = timer.currentTime - time - in if newTime <= 0.0 - then - { timer - | currentTime <- 0.0 - , state <- Ringing - } - else - { timer | currentTime <- newTime } - else - timer diff --git a/src/Utils/List.elm b/src/Utils/List.elm index 83b11eb..7f2e928 100644 --- a/src/Utils/List.elm +++ b/src/Utils/List.elm @@ -1,9 +1,7 @@ -module Utils.List +module Utils.List exposing ( repeat , splitAt - , maybeHead - , maybeTail - ) where + ) import List @@ -17,15 +15,3 @@ repeat count elem = splitAt : Int -> List a -> (List a, List a) splitAt n xs = (List.take n xs, List.drop n xs) - -maybeHead : List a -> Maybe a -maybeHead xs = - case xs of - x :: _ -> Just x - _ -> Nothing - -maybeTail : List a -> Maybe (List a) -maybeTail xs = - case xs of - _ :: tl -> Just tl - _ -> Nothing diff --git a/src/Utils/Maybe.elm b/src/Utils/Maybe.elm index 355ded9..db25bff 100644 --- a/src/Utils/Maybe.elm +++ b/src/Utils/Maybe.elm @@ -1,7 +1,7 @@ -module Utils.Maybe +module Utils.Maybe exposing ( filterMaybe , orElse - ) where + ) filterMaybe : (a -> Bool) -> Maybe a -> Maybe a filterMaybe cond maybe = diff --git a/src/View.elm b/src/View.elm new file mode 100644 index 0000000..5b3ad8d --- /dev/null +++ b/src/View.elm @@ -0,0 +1,56 @@ +module View exposing + ( view + ) + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import List +import Dict +import Json.Decode as Json + +import Msg exposing (Msg) + +import Model exposing (..) +import Model.Id exposing (..) + +import Timer.Model exposing (..) +import Timer.View as Timer + +import Update exposing (..) + +view : Model -> Html Msg +view model = + div + [] + [ title + , model.timers + |> Dict.toList + |> List.sortBy (.creationTime << snd) + |> timers model + ] + +title : Html Msg +title = + div + [ class "headerBar" ] + [ button + [ onClick Msg.Initialize + , class "title" + ] + [ text "Timer" ] + , button + [ onClick Msg.AddNewTimer + , class "addTimer" + ] + [ i + [ class "fa fa-fw fa-plus" ] + [] + ] + ] + +timers : Model -> List (Id, Timer) -> Html Msg +timers model timers = + div + [ class "timers" ] + (List.map (Timer.view model) timers) diff --git a/src/View/ActivatedClasses.elm b/src/View/ActivatedClasses.elm deleted file mode 100644 index 85b0841..0000000 --- a/src/View/ActivatedClasses.elm +++ /dev/null @@ -1,15 +0,0 @@ -module View.ActivatedClasses - ( activatedClasses - ) where - -import Html -import Html.Attributes exposing (..) -import List -import String - -activatedClasses : List (Bool, String) -> Html.Attribute -activatedClasses = - class - << String.concat - << List.intersperse " " - << List.filterMap (\(activated, str) -> if activated then Just str else Nothing) diff --git a/src/View/Timer.elm b/src/View/Timer.elm deleted file mode 100644 index 4672594..0000000 --- a/src/View/Timer.elm +++ /dev/null @@ -1,171 +0,0 @@ -module View.Timer - ( timerView - ) where - -import Html exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (..) -import Time exposing (Time) -import Signal -import Maybe -import List -import String - -import Model.Model exposing (..) -import Model.Timer exposing (..) -import Model.Edition.Edition exposing (..) -import Model.Edition.NameEdition exposing (..) -import Model.Edition.TimeEdition exposing (..) -import Model.TimerState exposing (..) -import Model.Id exposing (..) - -import Update.Update exposing (..) -import Update.UpdateTimer exposing (..) - -import View.ActivatedClasses exposing (..) - -import Utils.Maybe exposing (..) - -timerView : Model -> (Id, Timer) -> Html -timerView model (id, timer) = - div - [ [ (True, "timer") - , (timer.state == Ringing, "isRinging") - , (timer.state == Running, "isRunning") - ] - |> activatedClasses - ] - [ renderMaybeEdition model id Name (nameBlockReadOnly id timer) (nameBlockEdition id timer) - , renderMaybeEdition model id Time (timeBlockReadOnly (id, timer)) (timeBlockEdition (id, timer)) - , playPauseTimer (id, timer) - , stopTimer (id, timer) - , removeTimer (id, timer) - ] - -nameBlockReadOnly : Id -> Timer -> Html -nameBlockReadOnly id timer = - div - [ class "name" - , onClick actions.address (Edit id Name) - ] - [ text (timerName id timer) ] - -nameBlockEdition : Id -> Timer -> Edition -> Html -nameBlockEdition id timer edition = - div - [ [ (True, "name edition") - , (isEmpty edition, "empty") - ] - |> activatedClasses - , onClick actions.address NoOp - ] - [ if isEmpty edition - then - text (timerName id timer) - else - edition.chars - |> renderNameEdition - |> flip String.append "_" - |> text - ] - -timerName : Id -> Timer -> String -timerName id = Maybe.withDefault ("Timer " ++ toString id) << .name - -timeBlockReadOnly : (Id, Timer) -> Html -timeBlockReadOnly (id, timer) = - div - [ class "time" - , onClick actions.address (Edit id Time) - ] - [ timeWithProgressBar (id, timer) ] - -timeBlockEdition : (Id, Timer) -> Edition -> Html -timeBlockEdition (id, timer) edition = - div - [ [ (True, "time edition") - , (isEmpty edition, "empty") - ] - |> activatedClasses - , onClick actions.address NoOp - ] - [ if isEmpty edition - then - timeWithProgressBar (id, timer) - else - text (editionView edition.chars) - ] - -editionView : List Char -> String -editionView numbers = - let (minutes, seconds) = toMinutesAndSeconds numbers - in minutes ++ " : " ++ seconds - -timeView : Time -> String -timeView time = - let totalSeconds = ceiling (time / 1000) - totalMinutes = totalSeconds // 60 - restSeconds = totalSeconds `rem` 60 - in (String.padLeft 2 '0' (toString totalMinutes)) ++ " : " ++ (String.padLeft 2 '0' (toString restSeconds)) - -timeWithProgressBar : (Id, Timer) -> Html -timeWithProgressBar (id, timer) = - div - [] - [ span - [ class "progressBar" - , let width = - 1 - timer.currentTime / timer.initialTime - |> (*) 1020 - |> round - |> toString - |> flip String.append "px" - in style [ ("width", width) ] - , onClick actions.address (UpdateTimer id Stop) - ] - [] - , span - [ class "text" ] - [ text (timeView timer.currentTime) ] - ] - -playPauseTimer : (Id, Timer) -> Html -playPauseTimer (id, timer) = - button - [ class <| "playPause" - , onClick actions.address (UpdateTimer id ToggleRunning) - ] - [ let icon = if timer.state == Running then "fa-pause" else "fa-play" - in i - [ class <| "fa fa-fw " ++ icon ] - [] - ] - -stopTimer : (Id, Timer) -> Html -stopTimer (id, timer) = - button - [ class <| "stop" - , onClick actions.address (UpdateTimer id Stop) - ] - [ i [ class "fa fa-fw fa-stop" ] [] ] - -removeTimer : (Id, Timer) -> Html -removeTimer (id, timer) = - button - [ class <| "remove" - , onClick actions.address (RemoveTimer id) - ] - [ i [ class "fa fa-fw fa-remove" ] [] ] - -renderMaybeEdition : Model -> Id -> Kind -> Html -> (Edition -> Html) -> Html -renderMaybeEdition model id kind readOnlyView editionView = - let maybeEdition = filterMaybe (\edition -> edition.id == id) model.edition - in case maybeEdition of - Just edition -> - if edition.kind == kind - then - editionView edition - else - readOnlyView - Nothing -> - readOnlyView diff --git a/src/View/View.elm b/src/View/View.elm deleted file mode 100644 index a69d662..0000000 --- a/src/View/View.elm +++ /dev/null @@ -1,67 +0,0 @@ -module View.View - ( view - ) where - -import Html exposing (..) -import Html.Attributes exposing (..) -import Html.Events exposing (..) -import Signal -import List -import Dict -import Json.Decode as Json - -import Model.Model exposing (..) -import Model.Timer exposing (..) -import Model.Id exposing (..) - -import Update.Update exposing (..) - -import View.Timer exposing (timerView) - -view : Model -> Html -view model = - div - [] - [ title - , model.timers - |> Dict.toList - |> List.sortBy (.creationTime << snd) - |> timers model - ] - -title : Html -title = - div - [ class "headerBar" ] - [ button - [ onClick actions.address Initialize - , class "title" - ] - [ text "Timer" ] - , button - [ onClick actions.address AddNewTimer - , class "addTimer" - ] - [ i - [ class "fa fa-fw fa-plus" ] - [] - ] - ] - -onEnter : Signal.Message -> Attribute -onEnter message = - on "keydown" - (Json.customDecoder keyCode is13) - (always message) - -is13 : Int -> Result String () -is13 code = - if code == 13 - then Ok() - else Err "Not the right key code" - -timers : Model -> List (Id, Timer) -> Html -timers model timers = - div - [ class "timers" ] - (List.map (timerView model) timers) -- cgit v1.2.3