├── .gitignore ├── .travis.yml ├── README.md ├── elm-package.json ├── gulpfile.js ├── package.json ├── runTests.js ├── src ├── CNAME ├── assets │ └── scss │ │ ├── _progress.scss │ │ └── style.scss ├── elm │ ├── App │ │ ├── Model.elm │ │ ├── Router.elm │ │ ├── Test.elm │ │ ├── Update.elm │ │ └── View.elm │ ├── Email │ │ └── Model.elm │ ├── Main.elm │ ├── Pages │ │ ├── Inbox │ │ │ ├── Model.elm │ │ │ ├── Test.elm │ │ │ ├── Update.elm │ │ │ ├── Utils.elm │ │ │ └── View.elm │ │ ├── PageNotFound │ │ │ └── View.elm │ │ └── ScoreDashboard │ │ │ └── View.elm │ └── TestRunner.elm ├── index.html └── js │ └── README.md └── static ├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md ├── _config.build.yml ├── _config.yml ├── gulpfile.js ├── package.json └── src ├── _includes ├── footer.html ├── header.html ├── mail_item.html └── user_row.html ├── _layouts └── default.html ├── admin └── index.html ├── assets ├── javascript │ ├── jquery-2.2.3.min.js │ └── script.js └── scss │ ├── .scss-lint.yml │ └── style.scss ├── dashboard └── index.html ├── inbox └── index.html └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff 2 | node_modules 3 | serve 4 | dist 5 | 6 | #OS Files 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4.1" 4 | 5 | 6 | cache: 7 | directories: 8 | - sysconfcpus 9 | 10 | install: 11 | - npm install -g elm@~0.17.0 casperjs http-server 12 | - elm-package install -y 13 | # Getting elm-make to run quicker. 14 | # See https://github.com/elm-lang/elm-compiler/issues/1473#issuecomment-245704142 15 | - | 16 | if [ ! -d sysconfcpus/bin ]; 17 | then 18 | git clone https://github.com/obmarg/libsysconfcpus.git; 19 | cd libsysconfcpus; 20 | ./configure --prefix=$TRAVIS_BUILD_DIR/sysconfcpus; 21 | make && make install; 22 | cd ..; 23 | fi 24 | before_script: 25 | - $TRAVIS_BUILD_DIR/sysconfcpus/bin/sysconfcpus -n 2 elm-make ./src/elm/TestRunner.elm --output test.html 26 | - http-server '.' & 27 | # Wait for compilation is done. 28 | - until $(curl --output /dev/null --silent --head --fail http://127.0.0.1:8080/test.html); do echo "." && sleep 1; done 29 | 30 | script: casperjs test runTests.js 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/Gizra/inbox-simulation.svg?branch=master)](https://travis-ci.org/Gizra/inbox-simulation) 2 | 3 | > Inbox training 4 | 5 | ## Installation 6 | 7 | Make sure the following are installed: 8 | 9 | * NodeJs (and npm) 10 | * Elm (e.g. `npm install -g elm@~0.17.0`) 11 | * Compass (for SASS) (`gem update --system && gem install compass`) 12 | 13 | ``` 14 | npm install 15 | elm-package install -y 16 | ``` 17 | 18 | ## Usage 19 | 20 | 1. Serve locally, and watch file changes: `gulp` 21 | 1. Prepare file for publishing (e.g. minify, and rev file names): `gulp publish` 22 | 1. Deploy to GitHub's pages (`gh-pages` branch of your repository): `gulp deploy` 23 | 24 | ## Unit Tests 25 | 26 | In order to view the tests on the browser Start elm reactor (elm-reactor) and navigate to http://0.0.0.0:8000/src/elm/TestRunner.elm 27 | 28 | ## License 29 | 30 | MIT 31 | -------------------------------------------------------------------------------- /elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "Single page application example in Elm v0.17", 4 | "repository": "https://github.com/Gizra/elm-spa-exmple.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "src/elm" 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "elm-community/elm-test": "1.1.0 <= v < 2.0.0", 12 | "elm-community/json-extra": "1.0.0 <= v < 2.0.0", 13 | "elm-lang/core": "4.0.0 <= v < 5.0.0", 14 | "elm-lang/dom": "1.1.0 <= v < 2.0.0", 15 | "elm-lang/html": "1.0.0 <= v < 2.0.0", 16 | "elm-lang/navigation": "1.0.0 <= v < 2.0.0", 17 | "evancz/elm-http": "3.0.1 <= v < 4.0.0", 18 | "krisajenkins/elm-exts": "25.4.0 <= v < 26.0.0", 19 | "rgrempel/elm-route-url": "2.0.0 <= v < 3.0.0", 20 | "sporto/erl": "9.0.1 <= v < 10.0.0" 21 | }, 22 | "elm-version": "0.17.0 <= v < 0.18.0" 23 | } 24 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | // Generated on 2015-05-04 using generator-jekyllized 0.7.3 2 | "use strict"; 3 | 4 | var gulp = require("gulp"); 5 | 6 | var gulpSequence = require('gulp-sequence'); 7 | 8 | // Loads the plugins without having to list all of them, but you need 9 | // to call them as $.pluginname 10 | var $ = require("gulp-load-plugins")(); 11 | // "del" is used to clean out directories and such 12 | var del = require("del"); 13 | // BrowserSync isn"t a gulp package, and needs to be loaded manually 14 | var browserSync = require("browser-sync"); 15 | 16 | var elm = require('gulp-elm'); 17 | 18 | var fs = require('fs'); 19 | 20 | // merge is used to merge the output from two different streams into the same stream 21 | var merge = require("merge-stream"); 22 | // Need a command for reloading webpages using BrowserSync 23 | 24 | var plumber = require("gulp-plumber"); 25 | 26 | var reload = browserSync.reload; 27 | // And define a variable that BrowserSync uses in its function 28 | var bs; 29 | 30 | var wiredep = require('wiredep').stream; 31 | 32 | // Deletes the directory that is used to serve the site during development 33 | gulp.task("clean:dev", function(cb) { 34 | return del(["serve"], cb); 35 | }); 36 | 37 | 38 | // Deletes the directory that the optimized site is output to 39 | gulp.task("clean:prod", function(cb) { 40 | return del(["dist"], cb); 41 | }); 42 | 43 | 44 | // Compiles the SASS files and moves them into the "assets/stylesheets" directory 45 | gulp.task("styles", function () { 46 | // Looks at the style.scss file for what to include and creates a style.css file 47 | return gulp.src("src/assets/scss/style.scss") 48 | .pipe(plumber()) 49 | .pipe($.sass()) 50 | .on('error', function(err){ 51 | browserSync.notify("SASS error"); 52 | 53 | console.error(err.message); 54 | 55 | // Save the error to index.html, with a simple HTML wrapper 56 | // so browserSync can inject itself in. 57 | fs.writeFileSync('serve/index.html', "
" + err.message + "
"); 58 | 59 | // No need to continue processing. 60 | this.emit('end'); 61 | }) 62 | // AutoPrefix your CSS so it works between browsers 63 | .pipe($.autoprefixer("last 1 version", { cascade: true })) 64 | // Directory your CSS file goes to 65 | .pipe(gulp.dest("serve/assets/stylesheets/")) 66 | // Outputs the size of the CSS file 67 | .pipe($.size({title: "styles"})) 68 | // Injects the CSS changes to your browser since Jekyll doesn"t rebuild the CSS 69 | .pipe(reload({stream: true})); 70 | }); 71 | 72 | // Optimizes the images that exists 73 | gulp.task("images", function () { 74 | return gulp.src("src/assets/images/**") 75 | .pipe($.changed("dist/assets/images")) 76 | .pipe($.imagemin({ 77 | // Lossless conversion to progressive JPGs 78 | progressive: true, 79 | // Interlace GIFs for progressive rendering 80 | interlaced: true 81 | })) 82 | .pipe(gulp.dest("dist/assets/images")) 83 | .pipe($.size({title: "images"})); 84 | }); 85 | 86 | // Copy over fonts to the "dist" directory 87 | gulp.task("fonts", function () { 88 | return gulp.src("src/assets/fonts/**") 89 | .pipe(gulp.dest("dist/assets/fonts")) 90 | .pipe($.size({ title: "fonts" })); 91 | }); 92 | 93 | // Copy index.html and CNAME files to the "serve" directory 94 | gulp.task("copy:dev", ["copy:bower"], function () { 95 | return gulp.src(["src/index.html", "src/CNAME", "src/js/**/*", "src/assets/images/**/*"]) 96 | .pipe(gulp.dest("serve")) 97 | .pipe($.size({ title: "index.html & CNAME" })) 98 | }); 99 | 100 | // Copy bower. 101 | gulp.task("copy:bower", function () { 102 | return gulp.src(["bower_components/**/*"]) 103 | .pipe(gulp.dest("serve/bower_components")) 104 | .pipe($.size({ title: "Bower" })) 105 | }); 106 | 107 | // Copy images. 108 | gulp.task("copy:images", function () { 109 | return gulp.src([]) 110 | .pipe(gulp.dest("serve/assets/images")) 111 | .pipe($.size({ title: "Assets images" })) 112 | }); 113 | 114 | 115 | gulp.task("cname", function () { 116 | return gulp.src(["serve/CNAME"]) 117 | .pipe(gulp.dest("dist")) 118 | .pipe($.size({ title: "CNAME" })) 119 | }); 120 | 121 | gulp.task('bower', function () { 122 | gulp.src("src/index.html") 123 | .pipe(wiredep()) 124 | .pipe(gulp.dest("serve")); 125 | }); 126 | 127 | 128 | // Optimizes all the CSS, HTML and concats the JS etc 129 | gulp.task("minify", ["styles"], function () { 130 | var assets = $.useref.assets({searchPath: "serve"}); 131 | 132 | return gulp.src("serve/**/*.*") 133 | // Concatenate JavaScript files and preserve important comments 134 | .pipe($.if("*.js", $.uglify({preserveComments: "some"}))) 135 | // Minify CSS 136 | .pipe($.if("*.css", $.minifyCss())) 137 | // Start cache busting the files 138 | .pipe($.revAll({ ignore: ["index.html", ".eot", ".svg", ".ttf", ".woff"] })) 139 | .pipe(assets.restore()) 140 | // Replace the asset names with their cache busted names 141 | .pipe($.revReplace()) 142 | // Minify HTML 143 | .pipe($.if("*.html", $.htmlmin({ 144 | removeComments: true, 145 | removeCommentsFromCDATA: true, 146 | removeCDATASectionsFromCDATA: true, 147 | collapseWhitespace: true, 148 | collapseBooleanAttributes: true, 149 | removeAttributeQuotes: true, 150 | removeRedundantAttributes: true 151 | }))) 152 | // Send the output to the correct folder 153 | .pipe(gulp.dest("dist")) 154 | .pipe($.size({title: "optimizations"})); 155 | }); 156 | 157 | 158 | // Task to upload your site to your GH Pages repo 159 | gulp.task("deploy", [], function () { 160 | // Deploys your optimized site, you can change the settings in the html task if you want to 161 | return gulp.src("dist/**/*") 162 | .pipe($.ghPages({branch: "gh-pages"})); 163 | }); 164 | 165 | gulp.task('elm-init', elm.init); 166 | gulp.task('elm', ['elm-init'], function(){ 167 | return gulp.src('src/elm/Main.elm') 168 | .pipe(plumber()) 169 | .pipe(elm()) 170 | .on('error', function(err) { 171 | console.error(err.message); 172 | 173 | browserSync.notify("Elm compile error", 5000); 174 | 175 | // Save the error to index.html, with a simple HTML wrapper 176 | // so browserSync can inject itself in. 177 | fs.writeFileSync('serve/index.html', "
" + err.message + "
"); 178 | }) 179 | .pipe(gulp.dest('serve')); 180 | }); 181 | 182 | // BrowserSync will serve our site on a local server for us and other devices to use 183 | // It will also autoreload across all devices as well as keep the viewport synchronized 184 | // between them. 185 | gulp.task("serve:dev", ["build"], function () { 186 | bs = browserSync({ 187 | notify: true, 188 | // tunnel: "", 189 | server: { 190 | baseDir: "serve" 191 | } 192 | }); 193 | }); 194 | 195 | 196 | // These tasks will look for files that change while serving and will auto-regenerate or 197 | // reload the website accordingly. Update or add other files you need to be watched. 198 | gulp.task("watch", function () { 199 | // We need to copy dev, so index.html may be replaced by error messages. 200 | gulp.watch(["src/index.html", "src/js/**/*.js"], ["copy:dev", reload]); 201 | gulp.watch(["src/elm/**/*.elm"], ["elm", "copy:dev", reload]); 202 | gulp.watch(["src/assets/scss/**/*.scss"], ["styles", "copy:dev", reload]); 203 | }); 204 | 205 | // Serve the site after optimizations to see that everything looks fine 206 | gulp.task("serve:prod", function () { 207 | bs = browserSync({ 208 | notify: false, 209 | // tunnel: true, 210 | server: { 211 | baseDir: "dist" 212 | } 213 | }); 214 | }); 215 | 216 | // Default task, run when just writing "gulp" in the terminal 217 | gulp.task("default", ["serve:dev", "watch"]); 218 | 219 | // Builds the site but doesnt serve it to you 220 | // @todo: Add "bower" here 221 | gulp.task("build", gulpSequence("clean:dev", ["styles", "copy:dev", "elm"])); 222 | 223 | // Builds your site with the "build" command and then runs all the optimizations on 224 | // it and outputs it to "./dist" 225 | gulp.task("publish", ["build", "clean:prod"], function () { 226 | gulp.start("minify", "cname", "images", "fonts"); 227 | }); 228 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generated-elm", 3 | "private": false, 4 | "version": "0.1.0", 5 | "description": "Generated elm", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "browser-sync": "1.9.2", 9 | "del": "1.2.1", 10 | "gulp": "3.9.1", 11 | "gulp-autoprefixer": "2.3.1", 12 | "gulp-cache": "0.2.10", 13 | "gulp-cached": "1.0.1", 14 | "gulp-changed": "1.0.0", 15 | "gulp-elm": "0.4.1", 16 | "gulp-filter": "2.0.2", 17 | "gulp-gh-pages": "0.4.0", 18 | "gulp-gzip": "0.0.8", 19 | "gulp-htmlmin": "0.2.0", 20 | "gulp-if": "1.2.5", 21 | "gulp-imagemin": "2.3.0", 22 | "gulp-jshint": "1.11.2", 23 | "gulp-load-plugins": "0.8.1", 24 | "gulp-minify-css": "0.3.13", 25 | "gulp-plumber": "1.0.1", 26 | "gulp-rev-all": "0.7.6", 27 | "gulp-rev-replace": "0.3.4", 28 | "gulp-sass": "2.1.0", 29 | "gulp-sequence": "0.4.1", 30 | "gulp-shell": "0.2.11", 31 | "gulp-size": "1.3.0", 32 | "gulp-uglify": "1.4.1", 33 | "gulp-uncss": "0.5.2", 34 | "gulp-useref": "1.3.0", 35 | "jshint-stylish": "1.0.2", 36 | "merge-stream": "0.1.8", 37 | "wiredep": "2.2.2" 38 | }, 39 | "engines": { 40 | "node": ">0.10.0" 41 | }, 42 | "scripts": {} 43 | } 44 | -------------------------------------------------------------------------------- /runTests.js: -------------------------------------------------------------------------------- 1 | var url = 'http://127.0.0.1:8080/test.html'; 2 | 3 | casper.options.waitTimeout = 60000; 4 | 5 | casper.test.begin('Run the Elm tests', function suite(test) { 6 | casper.start(url).then(function() { 7 | // The test runner doesn't provide a class, so we have to do this query 8 | // selector. 9 | casper.waitForSelector('pre', function() { 10 | test.assertSelectorHasText('pre', 'All tests passed'); 11 | }); 12 | 13 | }); 14 | 15 | casper.run(function() { 16 | test.done(); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/CNAME: -------------------------------------------------------------------------------- 1 | inbox-simulation.gizra.com 2 | -------------------------------------------------------------------------------- /src/assets/scss/_progress.scss: -------------------------------------------------------------------------------- 1 | progress { 2 | background-color: #f3f3f3; 3 | border: 0; 4 | height: 2.5em; 5 | width: 100%; 6 | } 7 | 8 | progress::-webkit-progress-bar { 9 | background-color: #e0eaf0; 10 | } 11 | 12 | progress::-webkit-progress-value { 13 | background-color: #329ad1; 14 | } 15 | 16 | progress::-moz-progress-bar { 17 | background-color: #329ad1; 18 | } 19 | -------------------------------------------------------------------------------- /src/assets/scss/style.scss: -------------------------------------------------------------------------------- 1 | @import "progress"; 2 | 3 | body { 4 | font-family: 'Open Sans'; 5 | } 6 | 7 | .header-menu { 8 | margin-bottom: 25px; 9 | } 10 | 11 | .ui.footer.form-page { 12 | margin-top: 50px; 13 | } 14 | 15 | .main { 16 | display: flex; 17 | height: 75vh; 18 | } 19 | 20 | .content { 21 | flex-basis: 100%; 22 | } 23 | 24 | .content__messages { 25 | display: flex; 26 | height: 100%; 27 | 28 | } 29 | 30 | .top__menu { 31 | font-size: 20px !important; 32 | } 33 | .microsoft_logo { 34 | background-color: #0078D7 !important; 35 | } 36 | 37 | .content__messages__list { 38 | flex: 1 0 40%; 39 | border-right: 1px solid #DEECF9; 40 | height: auto; 41 | overflow-y: scroll; 42 | } 43 | .content__messages__selected { 44 | flex: 2 0 57%; 45 | overflow-y: scroll; 46 | padding-right: 25px; 47 | } 48 | 49 | .content__messages__action { 50 | margin-top: 25px !important; 51 | margin-bottom: 100px !important; 52 | } 53 | 54 | .sidebar_left { 55 | flex-basis: 25%; 56 | background-color: #f4f4f4; 57 | } 58 | 59 | .sidebar_left__search { 60 | padding-left: 10px; 61 | padding-top: 10px; 62 | background-color: #DEECF9; 63 | height: 40px; 64 | } 65 | 66 | .sidebar_left__folders { 67 | background-color: #f4f4f4; 68 | } 69 | 70 | .content__toolbar { 71 | background-color: #EFF6FC; 72 | height: 40px; 73 | } 74 | 75 | .content__messages__list__checkbox { 76 | margin-top: 5px; 77 | margin-left: 5px; 78 | float: left; 79 | } 80 | .content__messages__list__item { 81 | margin-left: 20px; 82 | margin-right: 5px; 83 | float: left; 84 | } 85 | 86 | .content__messages__list__item.not-opened { 87 | font-weight: bold; 88 | } 89 | 90 | .content__messages__list__item__from, 91 | .content__messages__list__item__subject, 92 | .content__messages__list__item__content { 93 | margin-top: 5px; 94 | max-width: 300px; 95 | max-height: 1.5em; 96 | overflow: hidden 97 | } 98 | 99 | .content__messages__list__item__from { 100 | font-size: 16px; 101 | } 102 | 103 | .content__messages__list__item__subject { 104 | font-size: 12px; 105 | } 106 | 107 | .content__messages__list__item__content { 108 | font-size: 12px; 109 | font-weight: 300; 110 | } 111 | 112 | .content__messages__selected { 113 | margin: 20px; 114 | } 115 | 116 | .content__messages__selected__header_title { 117 | font-size: 18px; 118 | margin-bottom: 10px; 119 | } 120 | 121 | .content__messages__selected__header_sender { 122 | margin-bottom: 10px; 123 | } 124 | 125 | .content__messages__selected__header_date { 126 | font-size: 12px; 127 | } 128 | 129 | .content__messages__selected__header__reply_all { 130 | float: right; 131 | display: block; 132 | background-color: #EFF6FC; 133 | padding: 5px 10px; 134 | } 135 | 136 | .content__messages__selected__content { 137 | margin-top: 30px; 138 | } 139 | -------------------------------------------------------------------------------- /src/elm/App/Model.elm: -------------------------------------------------------------------------------- 1 | module App.Model exposing (emptyModel, Model, Page(..)) 2 | 3 | import Pages.Inbox.Model exposing (emptyModel, Model) 4 | 5 | 6 | type Page 7 | = Inbox 8 | | PageNotFound 9 | | ScoreDashboard 10 | 11 | 12 | type alias Model = 13 | { activePage : Page 14 | , pageInbox : Pages.Inbox.Model.Model 15 | } 16 | 17 | 18 | emptyModel : Model 19 | emptyModel = 20 | { activePage = Inbox 21 | , pageInbox = Pages.Inbox.Model.emptyModel 22 | } 23 | -------------------------------------------------------------------------------- /src/elm/App/Router.elm: -------------------------------------------------------------------------------- 1 | module App.Router exposing (delta2url, location2messages) 2 | 3 | import App.Model exposing (..) 4 | import App.Update exposing (..) 5 | import Navigation exposing (Location) 6 | import RouteUrl exposing (HistoryEntry(..), UrlChange) 7 | 8 | 9 | delta2url : Model -> Model -> Maybe UrlChange 10 | delta2url previous current = 11 | case current.activePage of 12 | Inbox -> 13 | Just <| UrlChange NewEntry "/#inbox" 14 | 15 | PageNotFound -> 16 | Just <| UrlChange NewEntry "/#404" 17 | 18 | ScoreDashboard -> 19 | Just <| UrlChange NewEntry "/#scores" 20 | 21 | 22 | location2messages : Location -> List Msg 23 | location2messages location = 24 | case location.hash of 25 | "" -> 26 | [] 27 | 28 | "#inbox" -> 29 | [ SetActivePage Inbox ] 30 | 31 | "#scores" -> 32 | [ SetActivePage ScoreDashboard ] 33 | 34 | "#404" -> 35 | [ SetActivePage PageNotFound ] 36 | 37 | _ -> 38 | [ SetActivePage PageNotFound ] 39 | -------------------------------------------------------------------------------- /src/elm/App/Test.elm: -------------------------------------------------------------------------------- 1 | module App.Test exposing (all) 2 | 3 | import ElmTest exposing (..) 4 | import Exts.RemoteData exposing (RemoteData(..)) 5 | import App.Model exposing (..) 6 | import App.Update exposing (..) 7 | 8 | 9 | setActivePage : Test 10 | setActivePage = 11 | suite "SetActivePage msg" 12 | [ test "set new active page" 13 | (assertEqual PageNotFound (getPageAsAnonymous PageNotFound)) 14 | , test "set Login page for anonymous user" 15 | (assertEqual Login (getPageAsAnonymous Login)) 16 | , test "set My account page for anonymous user" 17 | (assertEqual AccessDenied (getPageAsAnonymous MyAccount)) 18 | , test "set Login page for authenticated user" 19 | (assertEqual AccessDenied (getPageAsAuthenticated Login)) 20 | , test "set My account page for authenticated user" 21 | (assertEqual MyAccount (getPageAsAuthenticated MyAccount)) 22 | ] 23 | 24 | 25 | getPageAsAnonymous : Page -> Page 26 | getPageAsAnonymous page = 27 | update (SetActivePage page) emptyModel 28 | |> fst 29 | |> .activePage 30 | 31 | 32 | getPageAsAuthenticated : Page -> Page 33 | getPageAsAuthenticated page = 34 | let 35 | dummyUser = 36 | { name = Just "Foo", login = "foo", avatarUrl = "https://example.com" } 37 | 38 | model = 39 | { emptyModel | user = Success dummyUser } 40 | in 41 | update (SetActivePage page) model 42 | |> fst 43 | |> .activePage 44 | 45 | 46 | all : Test 47 | all = 48 | suite "App tests" 49 | [ setActivePage 50 | ] 51 | -------------------------------------------------------------------------------- /src/elm/App/Update.elm: -------------------------------------------------------------------------------- 1 | module App.Update exposing (init, update, Msg(..)) 2 | 3 | import App.Model exposing (..) 4 | import Pages.Inbox.Update exposing (Msg) 5 | 6 | 7 | type Msg 8 | = PageInbox Pages.Inbox.Update.Msg 9 | | SetActivePage Page 10 | 11 | 12 | init : ( Model, Cmd Msg ) 13 | init = 14 | emptyModel ! [] 15 | 16 | 17 | update : Msg -> Model -> ( Model, Cmd Msg ) 18 | update msg model = 19 | case msg of 20 | PageInbox msg -> 21 | let 22 | ( val, cmds ) = 23 | Pages.Inbox.Update.update msg model.pageInbox 24 | 25 | model' = 26 | { model | pageInbox = val } 27 | in 28 | ( model', Cmd.map PageInbox cmds ) 29 | 30 | SetActivePage page -> 31 | { model | activePage = page } ! [] 32 | -------------------------------------------------------------------------------- /src/elm/App/View.elm: -------------------------------------------------------------------------------- 1 | module App.View exposing (..) 2 | 3 | import Html exposing (..) 4 | import Html.Attributes exposing (class, classList, href, src, style, target) 5 | import Html.App as Html 6 | import Html.Events exposing (onClick) 7 | import App.Model exposing (..) 8 | import App.Update exposing (..) 9 | import Pages.Inbox.View exposing (..) 10 | import Pages.PageNotFound.View exposing (..) 11 | import Pages.ScoreDashboard.View exposing (..) 12 | 13 | 14 | view : Model -> Html Msg 15 | view model = 16 | div [] 17 | [ viewHeader model 18 | , viewMainContent model 19 | , viewFooter 20 | ] 21 | 22 | 23 | viewHeader : Model -> Html Msg 24 | viewHeader model = 25 | div [ class "ui container header-menu" ] 26 | [ div [ class "ui secondary pointing menu" ] (navbarAnonymous model) 27 | ] 28 | 29 | 30 | navbarAnonymous : Model -> List (Html Msg) 31 | navbarAnonymous model = 32 | [ a 33 | [ classByPage Inbox model.activePage 34 | , onClick <| SetActivePage Inbox 35 | ] 36 | [ text "Inbox" ] 37 | , a 38 | [ classByPage ScoreDashboard model.activePage 39 | , onClick <| SetActivePage ScoreDashboard 40 | ] 41 | [ text "Score Dashboard" ] 42 | ] 43 | 44 | 45 | viewPageNotFoundItem : Page -> Html Msg 46 | viewPageNotFoundItem activePage = 47 | a 48 | [ classByPage PageNotFound activePage 49 | , onClick <| SetActivePage PageNotFound 50 | ] 51 | [ text "404 page" ] 52 | 53 | 54 | viewMainContent : Model -> Html Msg 55 | viewMainContent model = 56 | case model.activePage of 57 | Inbox -> 58 | Html.map PageInbox (Pages.Inbox.View.view model.pageInbox) 59 | 60 | PageNotFound -> 61 | -- We don't need to pass any cmds, so we can call the view directly 62 | Pages.PageNotFound.View.view 63 | 64 | ScoreDashboard -> 65 | Pages.ScoreDashboard.View.view model.pageInbox 66 | 67 | 68 | viewFooter : Html Msg 69 | viewFooter = 70 | div 71 | [ class "ui inverted vertical footer segment form-page" 72 | ] 73 | [ div [ class "ui container" ] 74 | [ a 75 | [ href "http://gizra.com" 76 | , target "_blank" 77 | ] 78 | [ text "Gizra" ] 79 | , span [] [ text " // " ] 80 | , a 81 | [ href "https://github.com/Gizra/inbox-training" 82 | , target "_blank" 83 | ] 84 | [ text "Github" ] 85 | ] 86 | ] 87 | 88 | 89 | {-| Get menu items classes. This function gets the active page and checks if 90 | it is indeed the page used. 91 | -} 92 | classByPage : Page -> Page -> Attribute a 93 | classByPage page activePage = 94 | classList 95 | [ ( "item", True ) 96 | , ( "active", page == activePage ) 97 | ] 98 | -------------------------------------------------------------------------------- /src/elm/Email/Model.elm: -------------------------------------------------------------------------------- 1 | module Email.Model exposing (..) 2 | 3 | import Dict exposing (..) 4 | 5 | 6 | type alias EmailType = 7 | String 8 | 9 | 10 | type alias EmailsStatus = 11 | Dict EmailType Int 12 | 13 | 14 | type EmailDelivery 15 | = Delayed 16 | | Immediate 17 | 18 | 19 | type alias Email = 20 | { from : String 21 | , email : String 22 | , subject : String 23 | , teaser : String 24 | , body : String 25 | , emailDelivery : EmailDelivery 26 | , options : Dict Int EmailOption 27 | } 28 | 29 | 30 | type alias EmailOption = 31 | { label : String 32 | , score : Int 33 | , triggerEmail : Maybe EmailType 34 | } 35 | -------------------------------------------------------------------------------- /src/elm/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (..) 2 | 3 | import App.Model exposing (Model) 4 | import App.Router exposing (..) 5 | import App.Update exposing (init, update, Msg) 6 | import App.View exposing (view) 7 | import RouteUrl 8 | 9 | 10 | main : Program Never 11 | main = 12 | RouteUrl.program 13 | { delta2url = delta2url 14 | , location2messages = location2messages 15 | , init = App.Update.init 16 | , update = App.Update.update 17 | , view = App.View.view 18 | , subscriptions = subscriptions 19 | } 20 | 21 | 22 | 23 | -- SUBSCRIPTIONS 24 | 25 | 26 | subscriptions : Model -> Sub Msg 27 | subscriptions model = 28 | Sub.none 29 | -------------------------------------------------------------------------------- /src/elm/Pages/Inbox/Model.elm: -------------------------------------------------------------------------------- 1 | module Pages.Inbox.Model exposing (emptyModel, Model) 2 | 3 | import Dict exposing (..) 4 | import Email.Model exposing (..) 5 | 6 | 7 | type alias Model = 8 | { emails : Dict EmailType Email 9 | , selectedEmail : Maybe EmailType 10 | , shownEmails : List EmailType 11 | , emailsStatus : EmailsStatus 12 | } 13 | 14 | 15 | emptyModel : Model 16 | emptyModel = 17 | { emails = Dict.fromList emails 18 | , selectedEmail = Just "Urgent" 19 | , shownEmails = getImmediateEmails emails 20 | , emailsStatus = Dict.insert "Urgent" 0 Dict.empty 21 | } 22 | 23 | 24 | 25 | -- Get only the emails that should be delivered "immediately". 26 | 27 | 28 | getImmediateEmails : List ( EmailType, Email ) -> List EmailType 29 | getImmediateEmails emails = 30 | List.filter (\( emailType, email ) -> email.emailDelivery == Immediate) emails 31 | |> List.map fst 32 | 33 | 34 | 35 | -- @todo: Get mail from JSON file 36 | 37 | 38 | emails : List ( EmailType, Email ) 39 | emails = 40 | [ ( "Urgent" 41 | , { from = "Josie Packard" 42 | , email = "" 43 | , subject = "Urgent - need draft of the report" 44 | , teaser = "Can I get an update on the latest version of the" 45 | , body = 46 | """ 47 |

48 | John, 49 |

50 | 51 |

52 | Can I get an update on the latest version of the proposal to working group? 53 |

54 | 55 |

56 | The meeting is on Thursday and I think it's important that we all be on the same page coming into the meeting. If everyone has had a chance to vet the document in advance, I think that we have a better chance of moving the proposal to the next stage. 57 |

58 | 59 |

60 | This proposal has wide implications for the way we do business and it would be a shame for it to get held up at this stage because nobody had eyes on it before the meeting. 61 |

62 | 63 |

64 | I appreciate your attention to this! 65 |

66 | 67 |

68 | Thanks,
69 | Josie 70 |

71 |

72 | ---
73 | Josie Packard
74 | SVP for Financial Products
75 | Great Northern Company
76 |

77 | """ 78 | , emailDelivery = Immediate 79 | , options = 80 | Dict.fromList 81 | [ ( 1, EmailOption "Respond to Josie explaining that the proposal is on track to be delivered tomorrow, at least 24 hours in advance of the meeting" 10 Nothing ) 82 | , ( 2, EmailOption "Respond to Josie explaining that there is not enough time to complete the draft before the meeting and that she should dedicate the first 10 minutes of the meeting to reviewing the document." 50 Nothing ) 83 | , ( 3, EmailOption "Respond to Josie's email after you have completed the draft." 100 Nothing ) 84 | ] 85 | } 86 | ) 87 | , ( "Vacation" 88 | , { from = "Dale Cooper" 89 | , email = "" 90 | , subject = "Corporate Vacation Policy" 91 | , teaser = " Hi Everyone, It's come to our attention that" 92 | , body = 93 | """ 94 |

95 | Hi Everyone, 96 |

97 | 98 |

99 | It has come to our attention that the updated guidance on the corporate vacation policy contained an error. Please note that employees do not have unlimited vacation days as part of the revised policy. 100 |

101 | 102 |

103 | This email notification serves as an effective update of the corporate vacation policy until a new policy statement can be drafted. Please reply immediately to this email to acknowledge receipt of this communication and acceptance of the update. 104 |

105 | 106 |

107 | Sincerely,
108 | Dale Cooper 109 |

110 | 111 |

112 | ---
113 | Dale Cooper
114 | Human Resources Director
115 | Great Northern Company
116 |

117 | """ 118 | , emailDelivery = Immediate 119 | , options = 120 | Dict.fromList 121 | [ ( 1, EmailOption "Respond immediately acknowledging that you have received the correspondence and accept the policy." 15 Nothing ) 122 | , ( 2, EmailOption "Reply to all requesting clarification if vacation days taken since the policy update will count toward allowed vacation time." 75 Nothing ) 123 | , ( 3, EmailOption "Move on to another email, respond later." 55 <| Just "VacationIgnoreResponse" ) 124 | ] 125 | } 126 | ) 127 | , ( "London" 128 | , { from = "Leo Johnson" 129 | , email = "" 130 | , subject = "Instructions to our office in London" 131 | , teaser = " Dear Mr. Smith, I just wanted to point out that in the instructions" 132 | , body = 133 | """ 134 |

135 | Dear Mr. Smith, 136 |

137 | 138 |

139 | I just wanted to point out that in the instructions you sent to out global network, you failed to mention that for our London office, disclosure documentation needs to be delivered in hard and electronic format as is the custom here. This is a part of our workflow that is crucial to maintaining client confidence. 140 |

141 | 142 |

143 | As I'm sure you understand, it is important to take into account local customs and business practice when circulating procedural documentation to our network of 53 offices worldwide. It's a daunting task, but one that is of the utmost importance. 144 |

145 | 146 |

147 | Thank you for your attention to this matter. I kindly request that you reissue the instructions with correction to this matter. 148 |

149 | 150 |

151 | Kindly Yours,
152 | Leo Johnson 153 |

154 | 155 |

156 | ---
157 | Leo Johnson
158 | General Director, London Office
159 | Great Northern Company 160 |

161 | """ 162 | , emailDelivery = Immediate 163 | , options = 164 | Dict.fromList 165 | [ ( 1, EmailOption "Respond immediately indicating that you will review and revise the instructions." 15 Nothing ) 166 | , ( 2, EmailOption "Respond immediately indicating that you understand the concern, but accommodations like this cannot be made for each of the global offices." 75 Nothing ) 167 | , ( 3, EmailOption "Move on to another email, respond later." 55 <| Just "LondonIgnoreResponse" ) 168 | ] 169 | } 170 | ) 171 | , ( "VacationIgnoreResponse" 172 | , { from = "Dale Cooper" 173 | , email = "" 174 | , subject = "Important: Response Required" 175 | , teaser = "It is important that you respond immediately to the previous" 176 | , body = 177 | """ 178 |

179 | It is important that you respond immediately to the previous correspondence regarding the corporate vacation policy. Failure to do so may result in immediate suspension of employee benefits. 180 |

181 | 182 |

183 | Thank you for your attention. 184 |

185 | 186 |

187 | Sincerely,
188 | Dale Cooper 189 |

190 | 191 |

192 | ---
193 | Dale Cooper
194 | Human Resources Director
195 | Great Northern Company
196 |

197 | """ 198 | , emailDelivery = Delayed 199 | , options = 200 | Dict.fromList 201 | [ ( 1, EmailOption "Respond immediately to this email acknowledging that you have received the correspondence and accept the policy." 15 Nothing ) 202 | , ( 2, EmailOption "Respond immediately to the prior email acknowledging that you have received the correspondence and accept the policy." 25 Nothing ) 203 | , ( 3, EmailOption "Move on to another email, respond later." 25 Nothing ) 204 | ] 205 | } 206 | ) 207 | , ( "LondonIgnoreResponse" 208 | , { from = "Leo Johnson" 209 | , email = "" 210 | , subject = "Re: Instructions to our office in London" 211 | , teaser = " Dear Mr. Smith, I just wanted to point out that in the instructions" 212 | , body = 213 | """ 214 |

215 | Dear Mr. Smith, 216 |

217 | 218 |

219 | Just following up on the previous issue. Please acknowledge. 220 |

221 | 222 |

223 | Yours,
224 | Leo Johnson 225 |

226 | 227 |

228 | ---
229 | Leo Johnson
230 | General Director, London Office
231 | Great Northern Company 232 |

233 | """ 234 | , emailDelivery = Delayed 235 | , options = 236 | Dict.fromList 237 | [ ( 1, EmailOption "Respond immediately indicating that you will review and revise the instructions." 15 Nothing ) 238 | , ( 2, EmailOption "Respond immediately indicating that you understand the concern, but accommodations like this cannot be made for each of the global offices." 75 Nothing ) 239 | , ( 3, EmailOption "Move on to another email, respond later." 25 Nothing ) 240 | ] 241 | } 242 | ) 243 | ] 244 | -------------------------------------------------------------------------------- /src/elm/Pages/Inbox/Test.elm: -------------------------------------------------------------------------------- 1 | module Pages.Inbox.Test exposing (..) 2 | 3 | import ElmTest exposing (..) 4 | import Pages.Inbox.Model as Inbox exposing (emptyModel, Model) 5 | import Pages.Inbox.Update as Inbox exposing (..) 6 | 7 | 8 | setActiveEmail : Test 9 | setActiveEmail = 10 | suite "setActiveEmail action" 11 | [ test "no active email" (assertEqual Nothing (.selectedEmail <| updateInbox <| Inbox.SetSelectedEmail Nothing)) 12 | , test "active email" (assertEqual (Just "Vacation") (.selectedEmail <| updateInbox <| Inbox.SetSelectedEmail <| Just "Vacation")) 13 | ] 14 | 15 | 16 | updateInbox : Inbox.Msg -> Inbox.Model 17 | updateInbox action = 18 | fst <| Inbox.update action emptyModel 19 | 20 | 21 | all : Test 22 | all = 23 | suite "Inbox tests" 24 | [ setActiveEmail 25 | ] 26 | -------------------------------------------------------------------------------- /src/elm/Pages/Inbox/Update.elm: -------------------------------------------------------------------------------- 1 | module Pages.Inbox.Update exposing (update, Msg(..)) 2 | 3 | import Dict exposing (..) 4 | import Dom.Scroll as Dom exposing (..) 5 | import Email.Model exposing (..) 6 | import Pages.Inbox.Model as Inbox exposing (..) 7 | import Process exposing (sleep) 8 | import Task 9 | 10 | 11 | init : ( Model, Cmd Msg ) 12 | init = 13 | emptyModel ! [] 14 | 15 | 16 | type Msg 17 | = DeliverEmail EmailType 18 | | NoOp 19 | | SetEmailStatus EmailType Int 20 | | SetSelectedEmail (Maybe EmailType) 21 | 22 | 23 | update : Msg -> Model -> ( Model, Cmd Msg ) 24 | update action model = 25 | case action of 26 | DeliverEmail emailType -> 27 | -- Rebuild the dict, by appending the new email to the list. 28 | let 29 | shownEmails = 30 | if (List.any (\type' -> emailType == type') model.shownEmails) then 31 | -- Email type is already in the shown emails list. 32 | model.shownEmails 33 | else 34 | -- Append the new email type to the shown list. 35 | emailType :: model.shownEmails 36 | in 37 | { model | shownEmails = shownEmails } ! [] 38 | 39 | NoOp -> 40 | model ! [] 41 | 42 | SetEmailStatus emailType keyOption -> 43 | let 44 | emailsStatus = 45 | Dict.insert emailType keyOption model.emailsStatus 46 | 47 | cmd = 48 | case Dict.get emailType model.emails of 49 | Nothing -> 50 | [] 51 | 52 | Just email -> 53 | case Dict.get keyOption email.options of 54 | Nothing -> 55 | [] 56 | 57 | Just option -> 58 | case option.triggerEmail of 59 | Nothing -> 60 | [] 61 | 62 | Just emailType' -> 63 | -- Sleep for 2 seconds, and send the triggered email. 64 | [ Process.sleep (2 * 1000) |> Task.perform (always <| DeliverEmail emailType') (always <| DeliverEmail emailType') ] 65 | in 66 | { model | emailsStatus = emailsStatus } ! cmd 67 | 68 | SetSelectedEmail emailType -> 69 | let 70 | -- Set email status 71 | emailsStatus = 72 | case emailType of 73 | Nothing -> 74 | model.emailsStatus 75 | 76 | Just val -> 77 | case (Dict.get val model.emailsStatus) of 78 | Nothing -> 79 | let 80 | model' = 81 | fst <| update (SetEmailStatus val 0) model 82 | in 83 | model'.emailsStatus 84 | 85 | Just _ -> 86 | model.emailsStatus 87 | 88 | scrollTo = 89 | Dom.toTop "selected-email" 90 | in 91 | { model 92 | | selectedEmail = emailType 93 | , emailsStatus = emailsStatus 94 | } 95 | ! [ Task.perform (\_ -> NoOp) (\_ -> NoOp) scrollTo ] 96 | -------------------------------------------------------------------------------- /src/elm/Pages/Inbox/Utils.elm: -------------------------------------------------------------------------------- 1 | module Pages.Inbox.Utils exposing (getScore) 2 | 3 | import Pages.Inbox.Model as Inbox exposing (..) 4 | import Dict exposing (..) 5 | 6 | 7 | getScore : Model -> Int 8 | getScore model = 9 | let 10 | calculate emailType optionKey total = 11 | case (Dict.get emailType model.emails) of 12 | Nothing -> 13 | 0 14 | 15 | Just email -> 16 | case (Dict.get optionKey email.options) of 17 | Nothing -> 18 | 0 19 | 20 | Just option -> 21 | option.score + total 22 | in 23 | Dict.foldl calculate 0 model.emailsStatus 24 | -------------------------------------------------------------------------------- /src/elm/Pages/Inbox/View.elm: -------------------------------------------------------------------------------- 1 | module Pages.Inbox.View exposing (view) 2 | 3 | import Dict exposing (..) 4 | import Html exposing (..) 5 | import Html.Attributes exposing (..) 6 | import Html.Events exposing (onClick) 7 | import Json.Encode as JSON exposing (string) 8 | import Pages.Inbox.Model exposing (..) 9 | import Pages.Inbox.Update exposing (..) 10 | import Email.Model exposing (..) 11 | 12 | 13 | view : Model -> Html Msg 14 | view model = 15 | div [ class "ui container" ] 16 | [ viewNavbar model 17 | , viewMain model 18 | ] 19 | 20 | 21 | viewNavbar : Model -> Html Msg 22 | viewNavbar model = 23 | div [ class "container" ] 24 | [ div [ class "ui inverted menu top__menu" ] 25 | [ a [ class "ui item microsoft_logo" ] 26 | [ i [ class "grid layout icon" ] 27 | [] 28 | ] 29 | , div [ class "header item" ] 30 | [ text "Outlook Mail" ] 31 | , div [ class "right menu" ] 32 | [ a [ class "ui item" ] 33 | [ i [ class "skype icon" ] 34 | [] 35 | ] 36 | , a [ class "ui item" ] 37 | [ i [ class "alarm icon" ] 38 | [] 39 | ] 40 | , a [ class "ui item" ] 41 | [ i [ class "setting icon" ] 42 | [] 43 | ] 44 | , a [ class "ui item" ] 45 | [ i [ class "help icon" ] 46 | [] 47 | ] 48 | ] 49 | ] 50 | ] 51 | 52 | 53 | viewMain : Model -> Html Msg 54 | viewMain model = 55 | div [ class "ui container" ] 56 | [ div [ class "main" ] 57 | [ div [ class "sidebar_left" ] 58 | [ div [ class "sidebar_left__search" ] 59 | [ span [ class "sidebar_left__search__text" ] 60 | [ text "Search Mail and People" ] 61 | , span [ class "sidebar_left__search__icon" ] 62 | [ i [ class "search icon" ] 63 | [] 64 | ] 65 | ] 66 | , div [ class "sidebar_left__folders" ] 67 | [ div [ class "ui vertical secondary menu" ] 68 | [ div [ class "item" ] 69 | [ div [ class "header" ] 70 | [ text "Folders" ] 71 | , div [ class "menu" ] 72 | [ a [ class "item" ] 73 | [ text "Inbox" ] 74 | , a [ class "item" ] 75 | [ text "Junk Mail" ] 76 | , a [ class "item" ] 77 | [ text "Drafts" ] 78 | , a [ class "item" ] 79 | [ text "Sent Items" ] 80 | , a [ class "item" ] 81 | [ text "Deleted Items" ] 82 | ] 83 | ] 84 | ] 85 | ] 86 | ] 87 | , div [ class "content" ] 88 | [ div [ class "content__toolbar" ] 89 | [ div [ class "ui secondary menu" ] 90 | [ a [ class "ui item" ] 91 | [ i [ class "plus square outline icon" ] 92 | [] 93 | , text "New" 94 | ] 95 | , a [ class "ui item" ] 96 | [ i [ class "trash outline icon" ] 97 | [] 98 | , text "Delete" 99 | ] 100 | , a [ class "ui item" ] 101 | [ i [ class "archive icon" ] 102 | [] 103 | , text "Archive" 104 | ] 105 | , a [ class "ui item" ] 106 | [ text "Junk" 107 | , i [ class "angle down icon" ] 108 | [] 109 | ] 110 | , a [ class "ui item" ] 111 | [ text "Sweep" ] 112 | , a [ class "ui item" ] 113 | [ text "Move to" 114 | , i [ class "angle down icon" ] 115 | [] 116 | ] 117 | , a [ class "ui item" ] 118 | [ text "Categories" 119 | , i [ class "angle down icon" ] 120 | [] 121 | ] 122 | , div [ class "ui right secondary menu" ] 123 | [ a [ class "ui item" ] 124 | [ i [ class "undo icon" ] 125 | [] 126 | , text "Undo" 127 | ] 128 | ] 129 | ] 130 | ] 131 | , div [ class "content__messages" ] 132 | [ div [ class "content__messages__list" ] 133 | [ div [ class "ui relaxed divided list" ] 134 | (List.map (viewMailItem model.emails model.emailsStatus) model.shownEmails) 135 | ] 136 | , (viewSelectedEmail model) 137 | ] 138 | ] 139 | ] 140 | ] 141 | 142 | 143 | viewMailItem : Dict EmailType Email -> EmailsStatus -> EmailType -> Html Msg 144 | viewMailItem emails emailsStatus emailType = 145 | case Dict.get emailType emails of 146 | Nothing -> 147 | -- This shouldn't ever happen. 148 | div [] [] 149 | 150 | Just email -> 151 | let 152 | notOpened = 153 | case Dict.get emailType emailsStatus of 154 | Nothing -> 155 | True 156 | 157 | Just _ -> 158 | False 159 | in 160 | div [ class "item" ] 161 | [ div [ class "content__messages__list__checkbox" ] 162 | [ input [ type' "checkbox" ] 163 | [] 164 | ] 165 | , div 166 | [ classList 167 | [ ( "content__messages__list__item", True ) 168 | , ( "not-opened", notOpened ) 169 | ] 170 | ] 171 | [ a 172 | [ class "content__messages__list__item__from" 173 | , (onClick <| SetSelectedEmail <| Just emailType) 174 | ] 175 | [ text email.from ] 176 | , div [ class "content__messages__list__item__subject" ] 177 | [ text email.subject ] 178 | , div [ class "content__messages__list__item__content" ] 179 | [ text email.teaser ] 180 | ] 181 | ] 182 | 183 | 184 | viewSelectedEmail : Model -> Html Msg 185 | viewSelectedEmail model = 186 | case model.selectedEmail of 187 | Nothing -> 188 | div [] [] 189 | 190 | Just name -> 191 | let 192 | memail = 193 | Dict.get name model.emails 194 | in 195 | case memail of 196 | Nothing -> 197 | div [] [] 198 | 199 | Just email -> 200 | div 201 | [ class "content__messages__selected" 202 | , id "selected-email" 203 | ] 204 | [ div [ class "content__messages__selected__header" ] 205 | [ div [ class "content__messages__selected__header_title" ] 206 | [ text email.subject ] 207 | , div [ class "content__messages__selected__header_sender" ] 208 | [ text email.from ] 209 | , div [ class "content__messages__selected__header__reply" ] 210 | [ div [ class "content__messages__selected__header__reply_all" ] 211 | [ i [ class "reply all icon" ] 212 | [] 213 | , text "Reply All" 214 | , i [ class "angle down icon" ] 215 | [] 216 | ] 217 | ] 218 | , div [ class "content__messages__selected__header_date" ] 219 | [ text "Tue 9/13/2016 10:26 AM" ] 220 | ] 221 | , div 222 | [ class "content__messages__selected__content" 223 | -- Treat the email body as HTML. 224 | , property "innerHTML" <| JSON.string email.body 225 | ] 226 | [] 227 | , div [ class "ui form icon message content__messages__action" ] 228 | [ i [ class "inbox icon" ] 229 | [] 230 | , div [ class "grouped fields" ] 231 | [ label [] [ text "Action:" ] 232 | , div [] (List.map (viewShowOption name model.emailsStatus) <| Dict.toList email.options) 233 | ] 234 | ] 235 | ] 236 | 237 | 238 | viewShowOption : EmailType -> EmailsStatus -> ( Int, EmailOption ) -> Html Msg 239 | viewShowOption emailType emailsStatus ( optionKey, option ) = 240 | let 241 | ( isChecked, optionSelected ) = 242 | case (Dict.get emailType emailsStatus) of 243 | Nothing -> 244 | ( False, False ) 245 | 246 | Just index -> 247 | ( optionKey == index, index > 0 ) 248 | in 249 | div [ class "field" ] 250 | [ div [ class "ui radio checkbox" ] 251 | [ input 252 | [ type' "radio" 253 | , name "radio" 254 | , checked isChecked 255 | , onClick <| SetEmailStatus emailType optionKey 256 | , disabled optionSelected 257 | ] 258 | [] 259 | , label [] 260 | [ text option.label ] 261 | ] 262 | ] 263 | -------------------------------------------------------------------------------- /src/elm/Pages/PageNotFound/View.elm: -------------------------------------------------------------------------------- 1 | module Pages.PageNotFound.View exposing (view) 2 | 3 | import Html exposing (a, div, h2, text, Html) 4 | import Html.Attributes exposing (class, href) 5 | 6 | 7 | -- VIEW 8 | 9 | 10 | view : Html a 11 | view = 12 | div [ class "ui segment center aligned" ] 13 | [ h2 [] [ text "This is a 404 page!" ] 14 | ] 15 | -------------------------------------------------------------------------------- /src/elm/Pages/ScoreDashboard/View.elm: -------------------------------------------------------------------------------- 1 | module Pages.ScoreDashboard.View exposing (view) 2 | 3 | import Json.Encode as Json 4 | import Html exposing (..) 5 | import Html.Attributes exposing (..) 6 | import Pages.Inbox.Model exposing (..) 7 | import Pages.Inbox.Utils exposing (..) 8 | 9 | 10 | -- VIEW 11 | 12 | 13 | view : Model -> Html a 14 | view model = 15 | div [ class "ui container" ] 16 | [ div [ class "ui segment stacked" ] 17 | [ table [ class "ui very basic collapsing celled table" ] 18 | [ thead [] 19 | [ tr [] 20 | [ th [ rowspan 2 ] 21 | [ text "Employee" ] 22 | , th [ rowspan 2 ] 23 | [ text "Overall Progress" ] 24 | , th [ colspan 2 ] 25 | [ text "Inbox Simulation" ] 26 | , th [ colspan 2 ] 27 | [ text "Leadership Assessment" ] 28 | , th [ rowspan 2 ] 29 | [ text "Overall Score" ] 30 | ] 31 | , tr [] 32 | [ th [] 33 | [ text "Time to Complete" ] 34 | , th [] 35 | [ text "Score" ] 36 | , th [] 37 | [ text "Time to Complete" ] 38 | , th [] 39 | [ text "Score" ] 40 | ] 41 | ] 42 | , tbody [] (List.map (hardcodedRows model) dummyScores) 43 | ] 44 | ] 45 | ] 46 | 47 | 48 | type alias ScoreAndTime = 49 | { score : 50 | Int 51 | -- @todo: Change to Time 52 | , time : String 53 | } 54 | 55 | 56 | type alias Score = 57 | { name : String 58 | , position : String 59 | , progress : Int 60 | , inboxSimulation : ScoreAndTime 61 | , leadershipAssessment : ScoreAndTime 62 | , overallScore : Int 63 | } 64 | 65 | 66 | dummyScores : List Score 67 | dummyScores = 68 | [ { name = "John Q. Smith" 69 | , position = "Human Resources" 70 | , progress = 96 71 | , inboxSimulation = ScoreAndTime 22 "10:22" 72 | , leadershipAssessment = ScoreAndTime 45 "12:10" 73 | , overallScore = 0 74 | } 75 | , { name = "Laura Palmer" 76 | , position = "Financial Services" 77 | , progress = 50 78 | , inboxSimulation = ScoreAndTime 66 "15:22" 79 | , leadershipAssessment = ScoreAndTime 25 "20:09" 80 | , overallScore = 91 81 | } 82 | , { name = "Josie Packard" 83 | , position = "Financial Services" 84 | , progress = 22 85 | , inboxSimulation = ScoreAndTime 38 "14:22" 86 | , leadershipAssessment = ScoreAndTime 22 "24:10" 87 | , overallScore = 60 88 | } 89 | , { name = "Dale Cooper" 90 | , position = "Human Resources" 91 | , progress = 78 92 | , inboxSimulation = ScoreAndTime 25 "15:18" 93 | , leadershipAssessment = ScoreAndTime 36 "20:10" 94 | , overallScore = 61 95 | } 96 | , { name = "Leo Johnson" 97 | , position = "Human Resources" 98 | , progress = 34 99 | , inboxSimulation = ScoreAndTime 62 "14:21" 100 | , leadershipAssessment = ScoreAndTime 23 "18:04" 101 | , overallScore = 85 102 | } 103 | , { name = "Audrey Horne" 104 | , position = "Financial Services" 105 | , progress = 58 106 | , inboxSimulation = ScoreAndTime 62 "13:28" 107 | , leadershipAssessment = ScoreAndTime 25 "19:18" 108 | , overallScore = 87 109 | } 110 | ] 111 | 112 | 113 | hardcodedRows : Model -> Score -> Html msg 114 | hardcodedRows model scoreData = 115 | let 116 | -- For the first user in the row, we use the calculated overall score. 117 | overallScore = 118 | if scoreData.name == "John Q. Smith" then 119 | getScore model 120 | else 121 | scoreData.overallScore 122 | in 123 | tr [] 124 | [ td [] 125 | [ h4 [ class "ui image header" ] 126 | [ i [ class "user icon" ] 127 | [] 128 | , div [ class "content" ] 129 | [ text scoreData.name 130 | , div [ class "sub header" ] 131 | [ text scoreData.position ] 132 | ] 133 | ] 134 | ] 135 | , td [] 136 | -- Progress bar 137 | [ (node "progress") 138 | [ value <| toString scoreData.progress 139 | , Html.Attributes.max "100" 140 | ] 141 | [] 142 | ] 143 | , td [] 144 | [ text scoreData.inboxSimulation.time ] 145 | , td [] 146 | [ text <| toString scoreData.inboxSimulation.score ] 147 | , td [] 148 | [ text scoreData.leadershipAssessment.time ] 149 | , td [] 150 | [ text <| toString scoreData.leadershipAssessment.score ] 151 | , td [] 152 | [ text <| toString overallScore ] 153 | ] 154 | -------------------------------------------------------------------------------- /src/elm/TestRunner.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (..) 2 | 3 | import ElmTest exposing (..) 4 | import Pages.Inbox.Test as Inbox exposing (all) 5 | 6 | 7 | allTests : Test 8 | allTests = 9 | suite "All tests" 10 | [ Inbox.all 11 | ] 12 | 13 | 14 | main : Program Never 15 | main = 16 | runSuiteHtml allTests 17 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Elm 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/js/README.md: -------------------------------------------------------------------------------- 1 | Place JS, and JS-inerop files in this folder. 2 | -------------------------------------------------------------------------------- /static/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | *.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | Icon? 37 | ehthumbs.db 38 | Thumbs.db 39 | 40 | # IDE # 41 | ########## 42 | .project 43 | .settings/ 44 | .idea 45 | 46 | node_modules 47 | .tmp 48 | 49 | # Jekyll compiled stylesheet 50 | src/assets/stylesheets/style.css 51 | site/assets/stylesheets/style.css 52 | 53 | # Jekyll related folders 54 | serve 55 | site 56 | -------------------------------------------------------------------------------- /static/Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gem 'jekyll' 4 | gem 'redcarpet' 5 | gem 'classifier-reborn' 6 | gem 'fast-stemmer' 7 | gem 'scss_lint' 8 | -------------------------------------------------------------------------------- /static/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | classifier-reborn (2.0.4) 5 | fast-stemmer (~> 1.0) 6 | colorator (0.1) 7 | fast-stemmer (1.0.2) 8 | ffi (1.9.10) 9 | jekyll (3.1.6) 10 | colorator (~> 0.1) 11 | jekyll-sass-converter (~> 1.0) 12 | jekyll-watch (~> 1.1) 13 | kramdown (~> 1.3) 14 | liquid (~> 3.0) 15 | mercenary (~> 0.3.3) 16 | rouge (~> 1.7) 17 | safe_yaml (~> 1.0) 18 | jekyll-sass-converter (1.4.0) 19 | sass (~> 3.4) 20 | jekyll-watch (1.4.0) 21 | listen (~> 3.0, < 3.1) 22 | kramdown (1.11.1) 23 | liquid (3.0.6) 24 | listen (3.0.8) 25 | rb-fsevent (~> 0.9, >= 0.9.4) 26 | rb-inotify (~> 0.9, >= 0.9.7) 27 | mercenary (0.3.6) 28 | rake (11.2.2) 29 | rb-fsevent (0.9.7) 30 | rb-inotify (0.9.7) 31 | ffi (>= 0.5.0) 32 | redcarpet (3.3.4) 33 | rouge (1.11.1) 34 | safe_yaml (1.0.4) 35 | sass (3.4.22) 36 | scss_lint (0.48.0) 37 | rake (>= 0.9, < 12) 38 | sass (~> 3.4.15) 39 | 40 | PLATFORMS 41 | ruby 42 | 43 | DEPENDENCIES 44 | classifier-reborn 45 | fast-stemmer 46 | jekyll 47 | redcarpet 48 | scss_lint 49 | -------------------------------------------------------------------------------- /static/README.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | 3 | ```bash 4 | npm install 5 | bundle install 6 | ``` 7 | 8 | Then run `gulp` to start the local server with BrowserSync. 9 | 10 | ## Deploying to `gh-pages` 11 | In order to publish your work run `gulp publish && gulp deploy` while on the master branch. 12 | -------------------------------------------------------------------------------- /static/_config.build.yml: -------------------------------------------------------------------------------- 1 | # Run during the 'gulp build' command 2 | # Overrides these options in _config.yml 3 | 4 | # Hides your drafts and future posts 5 | future: false 6 | show_drafts: false 7 | # Gives you more accurate related posts 8 | lsi: true 9 | # Removes the upper limit for posts generated 10 | limit_posts: 0 11 | 12 | source: 'src' 13 | destination: 'serve' 14 | -------------------------------------------------------------------------------- /static/_config.yml: -------------------------------------------------------------------------------- 1 | # Title, decription, tagline and URL for your site 2 | # Can be used in your theme by calling 'site.title' and so on 3 | title: Jekyll-base 4 | description: 5 | tagline: 6 | url: localhost:4000 7 | 8 | source: src 9 | destination: serve 10 | -------------------------------------------------------------------------------- /static/gulpfile.js: -------------------------------------------------------------------------------- 1 | // Generated on 2015-05-04 using generator-jekyllized 0.7.3 2 | "use strict"; 3 | 4 | var gulp = require("gulp"); 5 | // Loads the plugins without having to list all of them, but you need 6 | // to call them as $.pluginname 7 | var $ = require("gulp-load-plugins")(); 8 | // "del" is used to clean out directories and such 9 | var del = require("del"); 10 | // BrowserSync isn"t a gulp package, and needs to be loaded manually 11 | var browserSync = require("browser-sync"); 12 | // merge is used to merge the output from two different streams into the same stream 13 | var merge = require("merge-stream"); 14 | // Need a command for reloading webpages using BrowserSync 15 | var reload = browserSync.reload; 16 | // And define a variable that BrowserSync uses in it"s function 17 | var bs; 18 | 19 | var util = require("gulp-util"); 20 | var source = require("vinyl-source-stream"); 21 | var svgstore = require('gulp-svgstore'); 22 | var svgmin = require('gulp-svgmin'); 23 | 24 | // Deletes the directory that is used to serve the site during development 25 | gulp.task("clean:dev", del.bind(null, ["serve"])); 26 | 27 | // Deletes the directory that the optimized site is output to 28 | gulp.task("clean:prod", del.bind(null, ["site"])); 29 | 30 | // Runs the build command for Jekyll to compile the site locally 31 | // This will build the site with the production settings 32 | gulp.task("jekyll:dev", $.shell.task("jekyll build")); 33 | gulp.task("jekyll-rebuild", ["jekyll:dev", "js"], function () { 34 | reload; 35 | }); 36 | 37 | // Almost identical to the above task, but instead we load in the build configuration 38 | // that overwrites some of the settings in the regular configuration so that you 39 | // don"t end up publishing your drafts or future posts 40 | gulp.task("jekyll:prod", $.shell.task("jekyll build --config _config.yml,_config.build.yml")); 41 | 42 | // Compiles the SASS files and moves them into the "assets/stylesheets" directory 43 | gulp.task("styles", function () { 44 | // Looks at the style.scss file for what to include and creates a style.css file 45 | return gulp.src("src/assets/scss/style.scss") 46 | .pipe($.sass()) 47 | // AutoPrefix your CSS so it works between browsers 48 | .pipe($.autoprefixer("last 1 version", { cascade: true })) 49 | // Directory your CSS file goes to 50 | .pipe(gulp.dest("src/assets/stylesheets/")) 51 | .pipe(gulp.dest("serve/assets/stylesheets/")) 52 | .pipe(gulp.dest("site/assets/stylesheets/")) 53 | // Outputs the size of the CSS file 54 | .pipe($.size({title: "styles"})) 55 | // Injects the CSS changes to your browser since Jekyll doesn"t rebuild the CSS 56 | .pipe(reload({stream: true})); 57 | }); 58 | 59 | gulp.task("js", function() { 60 | gulp.src('./src/assets/javascript/*.js') 61 | .pipe(gulp.dest('./src/assets/javascript')) 62 | .pipe(gulp.dest("site/assets/javascript/")); 63 | }); 64 | 65 | // Optimizes the images that exists 66 | gulp.task("images", function () { 67 | return gulp.src("src/images/**") 68 | .pipe($.changed("site/images")) 69 | .pipe($.imagemin({ 70 | // Lossless conversion to progressive JPGs 71 | progressive: true, 72 | // Interlace GIFs for progressive rendering 73 | interlaced: true 74 | })) 75 | .pipe(gulp.dest("site/images")) 76 | .pipe($.size({title: "images"})); 77 | }); 78 | 79 | // Copy over fonts to the "site" directory 80 | gulp.task("fonts", function () { 81 | return gulp.src("src/assets/fonts/**") 82 | .pipe(gulp.dest("site/assets/fonts")) 83 | .pipe($.size({ title: "fonts" })); 84 | }); 85 | 86 | // Copy over vendors to the "site" directory 87 | gulp.task("vendors", function () { 88 | return gulp.src("src/assets/javascript/vendors/**") 89 | .pipe(gulp.dest("site/assets/javascript/vendors")) 90 | .pipe($.size({ title: "vendors" })); 91 | }); 92 | 93 | // Copy xml and txt files to the "site" directory 94 | gulp.task("copy", function () { 95 | return gulp.src(["serve/*.txt", "serve/*.xml", "serve/shoovify.sh"]) 96 | .pipe(gulp.dest("site")) 97 | .pipe($.size({ title: "xml & txt & shoovify.sh" })) 98 | }); 99 | 100 | gulp.task("cname", function () { 101 | return gulp.src(["serve/CNAME"]) 102 | .pipe(gulp.dest("site")) 103 | .pipe($.size({ title: "CNAMe" })) 104 | }); 105 | 106 | 107 | // Optimizes all the CSS, HTML and concats the JS etc 108 | gulp.task("html", ["styles"], function () { 109 | var assets = $.useref.assets({searchPath: "serve"}); 110 | 111 | return gulp.src("serve/**/*.html") 112 | .pipe(assets) 113 | // Concatenate JavaScript files and preserve important comments 114 | .pipe($.if("*.js", $.uglify({preserveComments: "some"}))) 115 | // Minify CSS 116 | .pipe($.if("*.css", $.minifyCss())) 117 | // Start cache busting the files 118 | .pipe($.revAll({ ignore: [".eot", ".svg", ".ttf", ".woff"] })) 119 | .pipe(assets.restore()) 120 | // Conctenate your files based on what you specified in _layout/header.html 121 | .pipe($.useref()) 122 | // Replace the asset names with their cache busted names 123 | .pipe($.revReplace()) 124 | // Minify HTML 125 | .pipe($.if("*.html", $.htmlmin({ 126 | removeComments: true, 127 | removeCommentsFromCDATA: true, 128 | removeCDATASectionsFromCDATA: true, 129 | collapseWhitespace: true, 130 | collapseBooleanAttributes: true, 131 | removeAttributeQuotes: true, 132 | removeRedundantAttributes: true, 133 | keepClosingSlash: true 134 | }))) 135 | // Send the output to the correct folder 136 | .pipe(gulp.dest("site")) 137 | .pipe($.size({title: "optimizations"})); 138 | }); 139 | 140 | 141 | // Task to upload your site to your personal GH Pages repo 142 | gulp.task("deploy", function () { 143 | // Deploys your optimized site, you can change the settings in the html task if you want to 144 | return gulp.src("./site/**/*") 145 | .pipe($.ghPages({ 146 | // Currently only personal GitHub Pages are supported so it will upload to the master 147 | // branch and automatically overwrite anything that is in the directory 148 | cacheDir: "./.tmp" 149 | })); 150 | }); 151 | 152 | // Run JS Lint against your JS 153 | gulp.task("jslint", function () { 154 | gulp.src("./serve/assets/javascript/*.js") 155 | // Checks your JS code quality against your .jshintrc file 156 | .pipe($.jshint(".jshintrc")) 157 | .pipe($.jshint.reporter()); 158 | }); 159 | 160 | // Runs "jekyll doctor" on your site to check for errors with your configuration 161 | // and will check for URL errors a well 162 | gulp.task("doctor", $.shell.task("jekyll doctor")); 163 | 164 | // BrowserSync will serve our site on a local server for us and other devices to use 165 | // It will also autoreload across all devices as well as keep the viewport synchronized 166 | // between them. 167 | gulp.task("serve:dev", ["styles", "jekyll:dev", "js"], function () { 168 | bs = browserSync({ 169 | notify: true, 170 | open: true, 171 | // tunnel: "", 172 | server: { 173 | baseDir: "serve" 174 | } 175 | }); 176 | }); 177 | 178 | // These tasks will look for files that change while serving and will auto-regenerate or 179 | // reload the website accordingly. Update or add other files you need to be watched. 180 | gulp.task("watch", function () { 181 | gulp.watch(["src/**/*.md", "src/**/*.html", "src/**/*.xml", "src/**/*.txt", "src/**/*.js"], ["jekyll-rebuild", reload]); 182 | gulp.watch(["serve/assets/stylesheets/*.css"], reload({stream: true})); 183 | gulp.watch(["src/assets/scss/**/*.scss"], ["styles", reload]); 184 | }); 185 | 186 | // Serve the site after optimizations to see that everything looks fine 187 | gulp.task("serve:prod", function () { 188 | bs = browserSync({ 189 | notify: false, 190 | // tunnel: true, 191 | server: { 192 | baseDir: "site" 193 | } 194 | }); 195 | }); 196 | 197 | // Default task, run when just writing "gulp" in the terminal 198 | gulp.task("default", ["serve:dev", "watch"]); 199 | 200 | // Checks your CSS, JS and Jekyll for errors 201 | gulp.task("check", ["jslint", "doctor"], function () { 202 | // Better hope nothing is wrong. 203 | }); 204 | 205 | // Builds the site but doesn"t serve it to you 206 | gulp.task("build", ["jekyll:prod", "styles", "js"], function () {}); 207 | 208 | // Copies favicon to the site folder 209 | gulp.task("copy-favicon", function() { 210 | return gulp.src("src/assets/favicon.png") 211 | .pipe(gulp.dest("site/assets")); 212 | }); 213 | 214 | // Builds your site with the "build" command and then runs all the optimizations on 215 | // it and outputs it to "./site" 216 | gulp.task("publish", ["build"], function () { 217 | gulp.start("html", "copy", "cname", "images", "fonts", "vendors", "js", "copy-favicon"); 218 | }); 219 | 220 | 221 | 222 | gulp.task('svgstore', function () { 223 | return gulp 224 | .src('src/images/elements/*.svg') 225 | .pipe(svgmin(function (file) { 226 | return { 227 | plugins: [{ 228 | removeTitle: true 229 | }, { 230 | cleanupNumericValues: { 231 | floatPrecision: 1 232 | } 233 | } 234 | ] 235 | } 236 | })) 237 | .pipe(svgstore()) 238 | .pipe(gulp.dest('src/_includes/')); 239 | }); 240 | -------------------------------------------------------------------------------- /static/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Jekyll-base", 3 | "private": true, 4 | "version": "0.0.0", 5 | "description": "Yeoman workflow for Ifat News", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "browser-sync": "^1.5.7", 9 | "del": "^1.1.1", 10 | "gulp": "^3.8.8", 11 | "gulp-autoprefixer": "^2.0.0", 12 | "gulp-cache": "~0.2.2", 13 | "gulp-cached": "^1.0.1", 14 | "gulp-changed": "^1.0.0", 15 | "gulp-filter": "^2.0.0", 16 | "gulp-gh-pages": "0.5.4", 17 | "gulp-gzip": "0.0.8", 18 | "gulp-htmlmin": "^0.2.0", 19 | "gulp-if": "^1.2.4", 20 | "gulp-imagemin": "^2.1.0", 21 | "gulp-jshint": "^1.8.5", 22 | "gulp-load-plugins": "^0.8.0", 23 | "gulp-minify-css": "^0.3.10", 24 | "gulp-rev-all": "^0.7.5", 25 | "gulp-rev-replace": "^0.3.1", 26 | "gulp-sass": "^2.0.4", 27 | "gulp-shell": "^0.2.9", 28 | "gulp-size": "^1.1.0", 29 | "gulp-svgmin": "^1.2.2", 30 | "gulp-svgstore": "^6.0.0", 31 | "gulp-uglify": "^1.0.1", 32 | "gulp-uncss": "^0.5.0", 33 | "gulp-useref": "^1.0.2", 34 | "gulp-util": "^3.0.7", 35 | "install": "^0.7.3", 36 | "jshint-stylish": "^1.0.0", 37 | "merge-stream": "^0.1.6", 38 | "npm": "^3.9.0", 39 | "vinyl-source-stream": "^1.1.0" 40 | }, 41 | "engines": { 42 | "node": ">0.10.0" 43 | }, 44 | "scripts": {} 45 | } 46 | -------------------------------------------------------------------------------- /static/src/_includes/footer.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Copyright 2016, Gizra. 4 |
5 |
6 | -------------------------------------------------------------------------------- /static/src/_includes/header.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 | Inbox Simulation 5 |

6 |
7 | -------------------------------------------------------------------------------- /static/src/_includes/mail_item.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 |
Adam Stewart
7 |
Urgent needs attention
8 |
Can we get an update on the latest version of the document.
9 |
10 |
11 | -------------------------------------------------------------------------------- /static/src/_includes/user_row.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | 5 |
6 | John Q. Smith 7 |
Human Resources
8 |
9 |

10 | 11 | 12 |
13 |
14 |
15 |
16 |
17 | 18 | 10:22 19 | 22 20 | 12:10 21 | 45 22 | 87 23 | 24 | -------------------------------------------------------------------------------- /static/src/_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% include header.html %} 10 |
11 | {{ content }} 12 |
13 | 14 | {% include footer.html %} 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /static/src/admin/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | label: Admin 4 | --- 5 | 6 |

Administer Inbox Simulation

7 | 8 |
9 |
10 |
11 | 12 | 13 |
14 |

Emails

15 | 21 | 27 | 33 |
34 |
35 |
36 | 37 | 38 |
39 |
40 | 41 | 42 |
43 |
44 |
45 | 46 | 47 |
48 |
49 |
50 | 51 | 52 |
53 |
54 | 55 | 56 |
57 |
58 | 59 | 60 |
61 |
62 | 63 |
64 | 65 |
66 | 67 |
68 |
69 | -------------------------------------------------------------------------------- /static/src/assets/javascript/jquery-2.2.3.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v2.2.3 | (c) jQuery Foundation | jquery.org/license */ 2 | !function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="2.2.3",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isPlainObject:function(a){var b;if("object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype||{},"isPrototypeOf"))return!1;for(b in a);return void 0===b||k.call(a,b)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=d.createElement("script"),b.text=a,d.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:h.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(d=e.call(arguments,2),f=function(){return a.apply(b||this,d.concat(e.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return h.call(b,a)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&f.parentNode&&(this.length=1,this[0]=f),this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?void 0!==c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?h.call(n(a),this[0]):h.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||n.uniqueSort(e),D.test(a)&&e.reverse()),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.removeEventListener("DOMContentLoaded",J),a.removeEventListener("load",J),n.ready()}n.ready.promise=function(b){return I||(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(n.ready):(d.addEventListener("DOMContentLoaded",J),a.addEventListener("load",J))),I.promise(b)},n.ready.promise();var K=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)K(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},L=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function M(){this.expando=n.expando+M.uid++}M.uid=1,M.prototype={register:function(a,b){var c=b||{};return a.nodeType?a[this.expando]=c:Object.defineProperty(a,this.expando,{value:c,writable:!0,configurable:!0}),a[this.expando]},cache:function(a){if(!L(a))return{};var b=a[this.expando];return b||(b={},L(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[b]=c;else for(d in b)e[d]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=a[this.expando];if(void 0!==f){if(void 0===b)this.register(a);else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in f?d=[b,e]:(d=e,d=d in f?[d]:d.match(G)||[])),c=d.length;while(c--)delete f[d[c]]}(void 0===b||n.isEmptyObject(f))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!n.isEmptyObject(b)}};var N=new M,O=new M,P=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Q=/[A-Z]/g;function R(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Q,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:P.test(c)?n.parseJSON(c):c; 3 | }catch(e){}O.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return O.hasData(a)||N.hasData(a)},data:function(a,b,c){return O.access(a,b,c)},removeData:function(a,b){O.remove(a,b)},_data:function(a,b,c){return N.access(a,b,c)},_removeData:function(a,b){N.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=O.get(f),1===f.nodeType&&!N.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),R(f,d,e[d])));N.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){O.set(this,a)}):K(this,function(b){var c,d;if(f&&void 0===b){if(c=O.get(f,a)||O.get(f,a.replace(Q,"-$&").toLowerCase()),void 0!==c)return c;if(d=n.camelCase(a),c=O.get(f,d),void 0!==c)return c;if(c=R(f,d,void 0),void 0!==c)return c}else d=n.camelCase(a),this.each(function(){var c=O.get(this,d);O.set(this,d,b),a.indexOf("-")>-1&&void 0!==c&&O.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){O.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=N.get(a,b),c&&(!d||n.isArray(c)?d=N.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return N.get(a,c)||N.access(a,c,{empty:n.Callbacks("once memory").add(function(){N.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length",""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};$.optgroup=$.option,$.tbody=$.tfoot=$.colgroup=$.caption=$.thead,$.th=$.td;function _(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function aa(a,b){for(var c=0,d=a.length;d>c;c++)N.set(a[c],"globalEval",!b||N.get(b[c],"globalEval"))}var ba=/<|&#?\w+;/;function ca(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],o=0,p=a.length;p>o;o++)if(f=a[o],f||0===f)if("object"===n.type(f))n.merge(m,f.nodeType?[f]:f);else if(ba.test(f)){g=g||l.appendChild(b.createElement("div")),h=(Y.exec(f)||["",""])[1].toLowerCase(),i=$[h]||$._default,g.innerHTML=i[1]+n.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;n.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",o=0;while(f=m[o++])if(d&&n.inArray(f,d)>-1)e&&e.push(f);else if(j=n.contains(f.ownerDocument,f),g=_(l.appendChild(f),"script"),j&&aa(g),c){k=0;while(f=g[k++])Z.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var da=/^key/,ea=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,fa=/^([^.]*)(?:\.(.+)|)/;function ga(){return!0}function ha(){return!1}function ia(){try{return d.activeElement}catch(a){}}function ja(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ja(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ha;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return"undefined"!=typeof n&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(G)||[""],j=b.length;while(j--)h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.hasData(a)&&N.get(a);if(r&&(i=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&N.remove(a,"handle events")}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(N.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!==this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,la=/\s*$/g;function pa(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function qa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function ra(a){var b=na.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function sa(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(N.hasData(a)&&(f=N.access(a),g=N.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}O.hasData(a)&&(h=O.access(a),i=n.extend({},h),O.set(b,i))}}function ta(a,b){var c=b.nodeName.toLowerCase();"input"===c&&X.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function ua(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&ma.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),ua(f,b,c,d)});if(o&&(e=ca(b,a[0].ownerDocument,!1,a,d),g=e.firstChild,1===e.childNodes.length&&(e=g),g||d)){for(h=n.map(_(e,"script"),qa),i=h.length;o>m;m++)j=e,m!==p&&(j=n.clone(j,!0,!0),i&&n.merge(h,_(j,"script"))),c.call(a[m],j,m);if(i)for(k=h[h.length-1].ownerDocument,n.map(h,ra),m=0;i>m;m++)j=h[m],Z.test(j.type||"")&&!N.access(j,"globalEval")&&n.contains(k,j)&&(j.src?n._evalUrl&&n._evalUrl(j.src):n.globalEval(j.textContent.replace(oa,"")))}return a}function va(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(_(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&aa(_(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(ka,"<$1>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=_(h),f=_(a),d=0,e=f.length;e>d;d++)ta(f[d],g[d]);if(b)if(c)for(f=f||_(a),g=g||_(h),d=0,e=f.length;e>d;d++)sa(f[d],g[d]);else sa(a,h);return g=_(h,"script"),g.length>0&&aa(g,!i&&_(a,"script")),h},cleanData:function(a){for(var b,c,d,e=n.event.special,f=0;void 0!==(c=a[f]);f++)if(L(c)){if(b=c[N.expando]){if(b.events)for(d in b.events)e[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);c[N.expando]=void 0}c[O.expando]&&(c[O.expando]=void 0)}}}),n.fn.extend({domManip:ua,detach:function(a){return va(this,a,!0)},remove:function(a){return va(this,a)},text:function(a){return K(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.appendChild(a)}})},prepend:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(_(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return K(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!la.test(a)&&!$[(Y.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(_(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return ua(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(_(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),f=e.length-1,h=0;f>=h;h++)c=h===f?this:this.clone(!0),n(e[h])[b](c),g.apply(d,c.get());return this.pushStack(d)}});var wa,xa={HTML:"block",BODY:"block"};function ya(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function za(a){var b=d,c=xa[a];return c||(c=ya(a,b),"none"!==c&&c||(wa=(wa||n("