From 5310ac7d8eb1838a6297117bc7f9fca70291f46a Mon Sep 17 00:00:00 2001 From: Alex Tatiyants Date: Sun, 3 Jan 2016 17:17:48 -0800 Subject: initial commit --- .editorconfig | 15 + .gitignore | 39 ++ .jshintrc | 25 + LICENSE | 21 + README.md | 1 + app/assets/css/styles.css | 4 + app/assets/fonts/FontAwesome.otf | Bin 0 -> 109688 bytes app/assets/fonts/fontawesome-webfont.eot | Bin 0 -> 70807 bytes app/assets/fonts/fontawesome-webfont.ttf | Bin 0 -> 142072 bytes app/assets/fonts/fontawesome-webfont.woff | Bin 0 -> 83588 bytes app/assets/fonts/fontawesome-webfont.woff2 | Bin 0 -> 66624 bytes app/assets/sass/_buttons.scss | 47 ++ app/assets/sass/_common.scss | 53 ++ app/assets/sass/_footer.scss | 14 + app/assets/sass/_forms.scss | 33 + app/assets/sass/_menu.scss | 65 ++ app/assets/sass/_modal.scss | 52 ++ app/assets/sass/_nav.scss | 10 + app/assets/sass/_page.scss | 17 + app/assets/sass/_plan-node.scss | 111 ++++ app/assets/sass/_plan.scss | 121 ++++ app/assets/sass/_table.scss | 12 + app/assets/sass/_variables.scss | 51 ++ app/assets/sass/font-awesome/_animated.scss | 34 + app/assets/sass/font-awesome/_bordered-pulled.scss | 25 + app/assets/sass/font-awesome/_core.scss | 12 + app/assets/sass/font-awesome/_fixed-width.scss | 6 + app/assets/sass/font-awesome/_icons.scss | 697 ++++++++++++++++++++ app/assets/sass/font-awesome/_larger.scss | 13 + app/assets/sass/font-awesome/_list.scss | 19 + app/assets/sass/font-awesome/_mixins.scss | 26 + app/assets/sass/font-awesome/_path.scss | 15 + app/assets/sass/font-awesome/_rotated-flipped.scss | 20 + app/assets/sass/font-awesome/_stacked.scss | 20 + app/assets/sass/font-awesome/_styles.scss | 17 + app/assets/sass/font-awesome/_variables.scss | 708 +++++++++++++++++++++ app/assets/sass/styles.scss | 16 + app/assets/styles.css | 4 + app/bootstrap.ts | 9 + app/components/app/app.html | 6 + app/components/app/app.ts | 22 + app/components/plan-list/plan-list.html | 38 ++ app/components/plan-list/plan-list.ts | 47 ++ app/components/plan-new/plan-new.html | 18 + app/components/plan-new/plan-new.ts | 26 + app/components/plan-node/plan-node.html | 50 ++ app/components/plan-node/plan-node.ts | 176 +++++ app/components/plan-view/plan-view.html | 72 +++ app/components/plan-view/plan-view.ts | 96 +++ app/enums.ts | 11 + app/index.html | 32 + app/interfaces/iplan.ts | 7 + app/pipes.ts | 10 + app/sample-plans/plan1.json | 334 ++++++++++ app/services/plan-service.ts | 166 +++++ gulpfile.ts | 83 +++ karma.conf.js | 103 +++ package.json | 93 +++ test-main.js | 53 ++ tools/config.ts | 101 +++ tools/tasks/build.bundles.ts | 26 + tools/tasks/build.deps.ts | 20 + tools/tasks/build.docs.ts | 27 + tools/tasks/build.fonts.dev.ts | 15 + tools/tasks/build.html_css.prod.ts | 24 + tools/tasks/build.img.dev.ts | 14 + tools/tasks/build.index.ts | 34 + tools/tasks/build.js.dev.ts | 25 + tools/tasks/build.js.prod.ts | 22 + tools/tasks/build.sass.dev.ts | 22 + tools/tasks/build.test.ts | 21 + tools/tasks/check.versions.ts | 35 + tools/tasks/clean.ts | 34 + tools/tasks/karma.start.ts | 11 + tools/tasks/npm.ts | 5 + tools/tasks/serve.docs.ts | 7 + tools/tasks/server.start.ts | 7 + tools/tasks/tsd.ts | 7 + tools/tasks/tslint.ts | 21 + tools/tasks/watch.dev.ts | 8 + tools/tasks/watch.serve.ts | 12 + tools/tasks/watch.test.ts | 8 + tools/typings/connect-livereload.d.ts | 5 + tools/typings/gulp-load-plugins.d.ts | 42 ++ tools/typings/karma.d.ts | 12 + tools/typings/merge-stream.d.ts | 8 + tools/typings/open.d.ts | 8 + tools/typings/run-sequence.d.ts | 5 + tools/typings/slash.d.ts | 5 + tools/typings/systemjs-builder.d.ts | 10 + tools/typings/tiny-lr.d.ts | 10 + tools/typings/yargs.d.ts | 9 + tools/utils.ts | 11 + tools/utils/server.ts | 45 ++ tools/utils/tasks_tools.ts | 64 ++ tools/utils/template-injectables.ts | 25 + tools/utils/template-locals.ts | 13 + tsconfig.json | 17 + tsd.json | 63 ++ tslint.json | 36 ++ 100 files changed, 4669 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .jshintrc create mode 100644 LICENSE create mode 100644 README.md create mode 100644 app/assets/css/styles.css create mode 100644 app/assets/fonts/FontAwesome.otf create mode 100644 app/assets/fonts/fontawesome-webfont.eot create mode 100644 app/assets/fonts/fontawesome-webfont.ttf create mode 100644 app/assets/fonts/fontawesome-webfont.woff create mode 100644 app/assets/fonts/fontawesome-webfont.woff2 create mode 100644 app/assets/sass/_buttons.scss create mode 100644 app/assets/sass/_common.scss create mode 100644 app/assets/sass/_footer.scss create mode 100644 app/assets/sass/_forms.scss create mode 100644 app/assets/sass/_menu.scss create mode 100644 app/assets/sass/_modal.scss create mode 100644 app/assets/sass/_nav.scss create mode 100644 app/assets/sass/_page.scss create mode 100644 app/assets/sass/_plan-node.scss create mode 100644 app/assets/sass/_plan.scss create mode 100644 app/assets/sass/_table.scss create mode 100644 app/assets/sass/_variables.scss create mode 100644 app/assets/sass/font-awesome/_animated.scss create mode 100644 app/assets/sass/font-awesome/_bordered-pulled.scss create mode 100644 app/assets/sass/font-awesome/_core.scss create mode 100644 app/assets/sass/font-awesome/_fixed-width.scss create mode 100644 app/assets/sass/font-awesome/_icons.scss create mode 100644 app/assets/sass/font-awesome/_larger.scss create mode 100644 app/assets/sass/font-awesome/_list.scss create mode 100644 app/assets/sass/font-awesome/_mixins.scss create mode 100644 app/assets/sass/font-awesome/_path.scss create mode 100644 app/assets/sass/font-awesome/_rotated-flipped.scss create mode 100644 app/assets/sass/font-awesome/_stacked.scss create mode 100644 app/assets/sass/font-awesome/_styles.scss create mode 100644 app/assets/sass/font-awesome/_variables.scss create mode 100644 app/assets/sass/styles.scss create mode 100644 app/assets/styles.css create mode 100644 app/bootstrap.ts create mode 100644 app/components/app/app.html create mode 100644 app/components/app/app.ts create mode 100644 app/components/plan-list/plan-list.html create mode 100644 app/components/plan-list/plan-list.ts create mode 100644 app/components/plan-new/plan-new.html create mode 100644 app/components/plan-new/plan-new.ts create mode 100644 app/components/plan-node/plan-node.html create mode 100644 app/components/plan-node/plan-node.ts create mode 100644 app/components/plan-view/plan-view.html create mode 100644 app/components/plan-view/plan-view.ts create mode 100644 app/enums.ts create mode 100644 app/index.html create mode 100644 app/interfaces/iplan.ts create mode 100644 app/pipes.ts create mode 100644 app/sample-plans/plan1.json create mode 100644 app/services/plan-service.ts create mode 100644 gulpfile.ts create mode 100644 karma.conf.js create mode 100644 package.json create mode 100644 test-main.js create mode 100644 tools/config.ts create mode 100644 tools/tasks/build.bundles.ts create mode 100644 tools/tasks/build.deps.ts create mode 100644 tools/tasks/build.docs.ts create mode 100644 tools/tasks/build.fonts.dev.ts create mode 100644 tools/tasks/build.html_css.prod.ts create mode 100644 tools/tasks/build.img.dev.ts create mode 100644 tools/tasks/build.index.ts create mode 100644 tools/tasks/build.js.dev.ts create mode 100644 tools/tasks/build.js.prod.ts create mode 100644 tools/tasks/build.sass.dev.ts create mode 100644 tools/tasks/build.test.ts create mode 100644 tools/tasks/check.versions.ts create mode 100644 tools/tasks/clean.ts create mode 100644 tools/tasks/karma.start.ts create mode 100644 tools/tasks/npm.ts create mode 100644 tools/tasks/serve.docs.ts create mode 100644 tools/tasks/server.start.ts create mode 100644 tools/tasks/tsd.ts create mode 100644 tools/tasks/tslint.ts create mode 100644 tools/tasks/watch.dev.ts create mode 100644 tools/tasks/watch.serve.ts create mode 100644 tools/tasks/watch.test.ts create mode 100644 tools/typings/connect-livereload.d.ts create mode 100644 tools/typings/gulp-load-plugins.d.ts create mode 100644 tools/typings/karma.d.ts create mode 100644 tools/typings/merge-stream.d.ts create mode 100644 tools/typings/open.d.ts create mode 100644 tools/typings/run-sequence.d.ts create mode 100644 tools/typings/slash.d.ts create mode 100644 tools/typings/systemjs-builder.d.ts create mode 100644 tools/typings/tiny-lr.d.ts create mode 100644 tools/typings/yargs.d.ts create mode 100644 tools/utils.ts create mode 100644 tools/utils/server.ts create mode 100644 tools/utils/tasks_tools.ts create mode 100644 tools/utils/template-injectables.ts create mode 100644 tools/utils/template-locals.ts create mode 100644 tsconfig.json create mode 100644 tsd.json create mode 100644 tslint.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f1cc3ad --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7548caf --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +node_modules +.sass-cache + +# Users Environment Variables +.lock-wscript +.tsdrc + +#IDE configuration files +.idea +.vscode + +dist +dev +docs +lib +test +tools/typings/tsd +tmp diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..0f847e2 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,25 @@ +{ + "bitwise": true, + "immed": true, + "newcap": true, + "noarg": true, + "noempty": true, + "nonew": true, + "trailing": true, + "maxlen": 200, + "boss": true, + "eqnull": true, + "expr": true, + "globalstrict": true, + "laxbreak": true, + "loopfunc": true, + "sub": true, + "undef": true, + "indent": 2, + "unused": true, + + "node": true, + "globals": { + "System": true + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..69a333e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Alex Tatiyants + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d81c36e --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Postgres Explain Visualizer (PEV) diff --git a/app/assets/css/styles.css b/app/assets/css/styles.css new file mode 100644 index 0000000..ec0a393 --- /dev/null +++ b/app/assets/css/styles.css @@ -0,0 +1,4 @@ +html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font:inherit;font-size:100%;vertical-align:baseline}html{line-height:1}ol,ul{list-style:none}table{border-collapse:collapse;border-spacing:0}caption,th,td{text-align:left;font-weight:normal;vertical-align:middle}q,blockquote{quotes:none}q:before,q:after,blockquote:before,blockquote:after{content:"";content:none}a img{border:none}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}/*! + * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.5.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff2?v=4.5.0") format("woff2"),url("../fonts/fontawesome-webfont.woff?v=4.5.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.5.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before{content:""}.fa-check-circle:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before{content:""}.fa-arrow-circle-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-hotel:before,.fa-bed:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-yc:before,.fa-y-combinator:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-tv:before,.fa-television:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}html{height:100%}body{font-size:13px;font-weight:300;color:#4d525a;height:100%;width:100%;background-color:#f7f7f7;line-height:1.3}strong{font-weight:600}body,input,a,button,textarea{font-family:"noto sans";font-weight:300}.text-muted{color:#999ea7}.hero-container{margin:30px;font-size:22px;text-align:center}.pull-right{float:right}.align-right{text-align:right}a{color:#00B5E2;text-decoration:none}.fa{margin-right:3px}.clickable{cursor:pointer}.btn{border-radius:3px;padding:6px 10px;font-size:13px;line-height:1.2;text-decoration:none;text-transform:uppercase}.btn-default{border:1px solid #00B5E2;color:#00B5E2;background-color:#fff}.btn-default:hover{background-color:#65DDFB}.btn-lg{padding:6px 20px;font-size:16px}.btn-danger{border:1px solid #AF2F11;color:#AF2F11;text-transform:uppercase;background-color:#fff}.btn-danger:hover{background-color:#FB8165}.btn-primary{border:0;background:#00B5E2;box-shadow:1px 1px 1px #ababab;color:#ffffff}.btn-primary:hover{background:#008CAF}.input-box:-moz-placeholder{color:#ababab;font-size:18px}.input-box::-moz-placeholder{color:#ababab;font-size:18px}.input-box:-ms-input-placeholder{color:#ababab;font-size:18px}.input-box::-webkit-input-placeholder{color:#ababab;font-size:18px}.input-box:focus{box-shadow:0 0 5px #51cbee}.input-box-main{font-size:18px;width:700px;border:0;border-bottom:2px solid #00B5E2;padding:10px;margin-top:10px;margin-bottom:10px}.input-box-lg{width:100%;height:280px;margin-bottom:6px;margin-bottom:10px;border-radius:3px;border:1px solid #dedede;padding:10px}nav{font-size:17px;background-color:#fff;padding:15px}nav .nav-container{width:1000px;margin:auto}.plan{padding-bottom:30px;overflow:auto;height:100%;width:100%}.plan ul{display:flex;padding-top:12px;position:relative;margin:auto;transition:all 0.5s;margin-top:-5px}.plan ul ul::before{content:'';position:absolute;top:0;left:50%;border-left:2px solid #c4c4c4;height:12px;width:0}.plan ul li{float:left;text-align:center;list-style-type:none;position:relative;padding:12px 3px 0 3px;transition:all 0.5s}.plan ul li:before,.plan ul li:after{content:'';position:absolute;top:0;right:50%;border-top:2px solid #c4c4c4;width:50%;height:12px}.plan ul li:after{right:auto;left:50%;border-left:2px solid #c4c4c4}.plan ul li:only-child{padding-top:0}.plan ul li:only-child:after,.plan ul li:only-child:before{display:none}.plan ul li:first-child::before,.plan ul li:last-child::after{border:0 none}.plan ul li:last-child::before{border-right:2px solid #c4c4c4;border-radius:0 6px 0 0}.plan ul li:first-child::after{border-radius:6px 0 0 0}.plan ul li .plan-node:hover+ul::before{border-color:#00B5E2}.plan ul li .plan-node:hover+ul li::after,.plan ul li .plan-node:hover+ul li::before,.plan ul li .plan-node:hover+ul ul::before{border-color:#008CAF}.plan-stats{display:flex;font-size:13px;margin:0 auto 10px auto;padding-bottom:10px;border-bottom:1px solid #dedede;border-radius:12px;width:650px;position:relative}.plan-stats div{padding-right:10px;flex-grow:1}.plan-stats .stat-value{display:block;text-align:center;font-size:17px}.plan-stats .stat-label{display:block;text-align:center;font-size:12px}.plan-stats:after{content:'';position:absolute;top:100%;left:50%;margin-left:-9px;width:0;height:0;border-top:solid 9px #dedede;border-left:solid 9px transparent;border-right:solid 9px transparent}.plan-node{text-decoration:none;color:#4d525a;display:inline-block;transition:all 0.5s;position:relative;padding:6px 10px;background-color:#fff;font-size:12px;border:2px solid #dedede;border-radius:3px;overflow:hidden;overflow-wrap:break-word;word-wrap:break-word;word-break:break-all;min-width:220px}.plan-node header{margin-bottom:6px;overflow:hidden;cursor:pointer}.plan-node header:hover{background-color:#f7f7f7}.plan-node header h4{font-size:13px;float:left;font-weight:600}.plan-node header .node-duration{float:right;margin-left:10px;font-size:13px}.plan-node .prop-list{float:left;text-align:left;width:400px;overflow-wrap:break-word;word-wrap:break-word;word-break:break-all;margin-top:10px;margin-bottom:6px}.plan-node .relation-name{text-align:left;max-width:220px}.plan-node .planner-estimate{float:left;clear:both;text-align:left;margin-top:6px;width:100%}.plan-node .tags{margin-top:6px;text-align:left}.plan-node .tags span{display:inline-block;background-color:#FB4418;color:#fff;font-size:10px;font-weight:600;margin-right:3px;padding:3px;border-radius:3px;line-height:1.1}.plan-node:hover{border-color:#00B5E2}.node-bar-container{float:left;height:5px;margin-top:6px;margin-left:auto;margin-right:auto;border:1px solid #dedede;border-radius:3px;color:#fff;background-color:#454545;position:relative}.node-bar-container .node-bar{height:100%;text-align:left;position:absolute;left:0;top:0}.node-bar-label{text-align:left;display:block}.menu{width:190px;height:200px;position:absolute;font-size:12px;top:115px;left:0;background-color:#777;box-shadow:1px 1px 2px 1px rgba(0,0,0,0.5);color:#fff;border-top-right-radius:3px;border-bottom-right-radius:3px;z-index:1;transition:all 0.3s}.menu header h3{padding-top:10px;margin-bottom:20px;font-size:16px;font-weight:600;line-height:2;text-align:right;padding-right:20px}.menu ul{margin-left:10px}.menu ul li{line-height:2.5}.menu-toggle{font-size:26px;float:left;padding-left:10px;line-height:2;cursor:pointer}.menu-hidden{width:50px;height:50px;border-top-right-radius:50%;border-bottom-right-radius:50%}.menu-hidden ul,.menu-hidden h3{visibility:hidden}.menu button{border:1px solid #fff;background-color:#544D4D;color:#fff;padding:2px;border-radius:3px}.page,.page-stretch{padding-top:10px;margin:auto;width:1000px;min-height:600px}.page h2,.page-stretch h2{font-size:26px;margin-bottom:6px}.page-stretch{margin:auto;width:95%}.table{width:100%}.table td{border-bottom:1px solid #dedede;padding:6px}.table tr:hover{background-color:#f7f7f7}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000;opacity:0.7}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050}.modal .modal-dialog{position:relative;transform:translate(0);margin:30px auto;width:500px;opacity:1}.modal .modal-dialog .modal-content{padding:30px;position:relative;background-color:#fff;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;outline:0;box-shadow:0 3px 9px rgba(0,0,0,0.5);display:block}.modal .modal-dialog .modal-content .modal-body{padding:3px}.modal .modal-dialog .modal-content .modal-footer{text-align:right}.modal .modal-dialog .modal-content .modal-footer button{margin-left:3px}footer{border-top:2px solid #dedede;margin:20px;padding:20px;color:#ababab;text-align:center;width:600px;margin:auto}footer .fa{font-size:17px;margin-left:6px} diff --git a/app/assets/fonts/FontAwesome.otf b/app/assets/fonts/FontAwesome.otf new file mode 100644 index 0000000..3ed7f8b Binary files /dev/null and b/app/assets/fonts/FontAwesome.otf differ diff --git a/app/assets/fonts/fontawesome-webfont.eot b/app/assets/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000..9b6afae Binary files /dev/null and b/app/assets/fonts/fontawesome-webfont.eot differ diff --git a/app/assets/fonts/fontawesome-webfont.ttf b/app/assets/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000..26dea79 Binary files /dev/null and b/app/assets/fonts/fontawesome-webfont.ttf differ diff --git a/app/assets/fonts/fontawesome-webfont.woff b/app/assets/fonts/fontawesome-webfont.woff new file mode 100644 index 0000000..dc35ce3 Binary files /dev/null and b/app/assets/fonts/fontawesome-webfont.woff differ diff --git a/app/assets/fonts/fontawesome-webfont.woff2 b/app/assets/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000..500e517 Binary files /dev/null and b/app/assets/fonts/fontawesome-webfont.woff2 differ diff --git a/app/assets/sass/_buttons.scss b/app/assets/sass/_buttons.scss new file mode 100644 index 0000000..760d5ec --- /dev/null +++ b/app/assets/sass/_buttons.scss @@ -0,0 +1,47 @@ +.btn { + border-radius: $border-radius-base; + padding: $padding-base $padding-lg; + font-size: $font-size-base; + line-height: 1.2; + text-decoration: none; + text-transform: uppercase; + + &-default { + border: 1px solid $blue; + color: $blue; + background-color: #fff; + + &:hover { + background-color: $light-blue; + } + } + + &-lg { + padding: $padding-base $padding-lg*2; + font-size: round($font-size-base * 1.2); + } + + &-danger { + border: 1px solid $red; + color: $red; + text-transform: uppercase; + background-color: #fff; + + &:hover { + background-color: $light-red; + } + } + + &-primary { + border: 0; + background: $blue; + // background-image: linear-gradient(to bottom, $blue, $dark-blue); + box-shadow: 1px 1px 1px $gray; + color: #ffffff; + + &:hover { + background: $dark-blue; + // background-image: linear-gradient(to bottom, $dark-blue, $blue); + } + } +} diff --git a/app/assets/sass/_common.scss b/app/assets/sass/_common.scss new file mode 100644 index 0000000..5d38773 --- /dev/null +++ b/app/assets/sass/_common.scss @@ -0,0 +1,53 @@ +html { + height: 100%; +} + +body { + font-size: $font-size-base; + font-weight: 300; + color: $text-color; + height: 100%; + width: 100%; + background-color: $bg-color; + line-height: $line-height-base; +} + +strong { + font-weight: 600; +} + +body, input, a, button, textarea { + font-family: $font-family-sans-serif; + font-weight: 300; +} + +.text-muted { + color: $text-color-light; +} + +.hero-container { + margin: $padding-lg * 3; + font-size: $font-size-xl; + text-align: center; +} + +.pull-right { + float: right; +} + +.align-right { + text-align: right; +} + +a { + color: $link-color; + text-decoration: none; +} + +.fa { + margin-right: $padding-sm; +} + +.clickable { + cursor: pointer; +} diff --git a/app/assets/sass/_footer.scss b/app/assets/sass/_footer.scss new file mode 100644 index 0000000..603ff5a --- /dev/null +++ b/app/assets/sass/_footer.scss @@ -0,0 +1,14 @@ +footer { + border-top: 2px solid $gray-light; + margin: round($padding-lg * 2); + padding: round($padding-lg * 2); + color: $gray; + text-align: center; + width: 600px; + margin: auto; + + .fa { + font-size: $font-size-lg; + margin-left: $padding-base; + } +} diff --git a/app/assets/sass/_forms.scss b/app/assets/sass/_forms.scss new file mode 100644 index 0000000..4ab4dfe --- /dev/null +++ b/app/assets/sass/_forms.scss @@ -0,0 +1,33 @@ +@import "compass/css3/user-interface"; + +.input-box { + @include input-placeholder { + color: $gray; + font-size: round($font-size-base * 1.4); + } + + &:focus { + box-shadow: 0 0 5px rgba(81, 203, 238, 1); + } + + &-main { + font-size: round($font-size-base * 1.4); + width: 700px; + border: 0; + border-bottom: 2px solid $blue; + padding: $padding-lg; + margin-top: $padding-lg; + margin-bottom: $padding-lg; + } + + &-lg { + width: 100%; + height: 280px; + margin-bottom: $padding-base; + margin-bottom: $padding-lg; + border-radius: $border-radius-base; + border: 1px solid $line-color; + padding: $padding-lg; + } + +} diff --git a/app/assets/sass/_menu.scss b/app/assets/sass/_menu.scss new file mode 100644 index 0000000..d294686 --- /dev/null +++ b/app/assets/sass/_menu.scss @@ -0,0 +1,65 @@ +$menu-offset: 120px; +$menu-toggle-height: 45px; + +.menu { + width: 190px; + height: 200px; + position: absolute; + font-size: $font-size-sm; + top: $menu-offset - 5; + left: 0; + background-color: $gray-dark; + box-shadow: 1px 1px 2px 1px rgba(0,0,0,0.5); + color: #fff; + border-top-right-radius: $border-radius-base; + border-bottom-right-radius: $border-radius-base; + z-index: 1; + transition: all 0.3s; + + header { + h3 { + padding-top: $padding-lg; + margin-bottom: $padding-lg * 2; + font-size: round($font-size-base * 1.2); + font-weight: 600; + line-height: 2; + text-align: right; + padding-right: $padding-lg * 2; + } + } + + ul { + margin-left: $padding-lg; + + li { + line-height: 2.5; + } + } + + &-toggle { + font-size: round($font-size-lg * 1.5); + float: left; + padding-left: $padding-lg; + line-height: 2; + cursor: pointer; + } + + &-hidden { + width: 50px; + height: 50px; + border-top-right-radius: 50%; + border-bottom-right-radius: 50%; + + ul, h3 { + visibility: hidden; + } + } +} + +.menu button { + border: 1px solid #fff; + background-color: #544D4D; + color: #fff; + padding: 2px; + border-radius: 3px; +} diff --git a/app/assets/sass/_modal.scss b/app/assets/sass/_modal.scss new file mode 100644 index 0000000..19d4bfc --- /dev/null +++ b/app/assets/sass/_modal.scss @@ -0,0 +1,52 @@ +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #000; + opacity: 0.7; +} + +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + + .modal-dialog { + position: relative; + transform: translate(0); + margin: 30px auto; + width: 500px; + opacity: 1; + + .modal-content { + padding: 30px; + position: relative; + background-color: #fff; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0,0,0,.2); + border-radius: 6px; + outline: 0; + box-shadow: 0 3px 9px rgba(0,0,0,.5); + display: block; + + .modal-body { + padding: $padding-sm; + } + + .modal-footer { + text-align: right; + + button { + margin-left: $padding-sm; + } + } + } + } +} diff --git a/app/assets/sass/_nav.scss b/app/assets/sass/_nav.scss new file mode 100644 index 0000000..496213b --- /dev/null +++ b/app/assets/sass/_nav.scss @@ -0,0 +1,10 @@ +nav { + font-size: round($font-size-base * 1.3); + background-color: #fff; + padding: round($padding-lg * 1.5); + + .nav-container { + width: $page-width; + margin: auto; + } +} diff --git a/app/assets/sass/_page.scss b/app/assets/sass/_page.scss new file mode 100644 index 0000000..64e1891 --- /dev/null +++ b/app/assets/sass/_page.scss @@ -0,0 +1,17 @@ +.page { + padding-top: $padding-lg; + margin: auto; + width: $page-width; + min-height: 600px; + + h2 { + font-size: round($font-size-base * 2); + margin-bottom: $padding-base; + } +} + +.page-stretch { + @extend .page; + margin: auto; + width: 95%; +} diff --git a/app/assets/sass/_plan-node.scss b/app/assets/sass/_plan-node.scss new file mode 100644 index 0000000..25b3a05 --- /dev/null +++ b/app/assets/sass/_plan-node.scss @@ -0,0 +1,111 @@ +.plan-node { + text-decoration: none; + color: $text-color; + display: inline-block; + transition: all 0.5s; + position: relative; + padding: $padding-base $padding-lg; + background-color: #fff; + font-size: $font-size-sm; + border: 2px solid $line-color; + border-radius: $border-radius-base; + overflow: hidden; + overflow-wrap: break-word; + word-wrap: break-word; + word-break: break-all; + min-width: 220px; + + header { + margin-bottom: $padding-base; + overflow: hidden; + cursor: pointer; + + &:hover { + background-color: $gray-lightest; + } + + h4 { + font-size: $font-size-base; + float: left; + font-weight: 600; + } + + .node-duration { + float: right; + margin-left: $padding-lg; + font-size: $font-size-base; + } + } + + .prop-list { + float: left; + text-align: left; + width: 400px; + overflow-wrap: break-word; + word-wrap: break-word; + word-break: break-all; + margin-top: $padding-lg; + margin-bottom: $padding-base; + } + + .relation-name { + text-align: left; + max-width: 220px; + } + + .planner-estimate { + float: left; + clear: both; + text-align: left; + margin-top: $padding-base; + width: 100%; + } + + .tags { + margin-top: $padding-base; + text-align: left; + + span { + display: inline-block; + background-color: $alert-color; + color: #fff; + font-size: round($font-size-sm * 0.8); + font-weight: 600; + margin-right: $padding-sm; + padding: $padding-sm; + border-radius: $border-radius-base; + line-height: 1.1; + } + } + + //hovers + &:hover { + border-color: $highlight-color; + } +} + +.node-bar-container { + float: left; + height: 5px; + margin-top: $padding-base; + margin-left: auto; + margin-right: auto; + border: 1px solid $line-color; + border-radius: $border-radius-base; + color: #fff; + background-color: $gray-darkest; + position: relative; + + .node-bar { + height: 100%; + text-align: left; + position: absolute; + left: 0; + top: 0; + } +} + +.node-bar-label { + text-align: left; + display: block; +} diff --git a/app/assets/sass/_plan.scss b/app/assets/sass/_plan.scss new file mode 100644 index 0000000..7b8808d --- /dev/null +++ b/app/assets/sass/_plan.scss @@ -0,0 +1,121 @@ +$connector-height: 12px; +$connector-line: 2px solid darken($line-color, 10%); + +.plan { + padding-bottom: $padding-lg * 3; + overflow: auto; + height: 100%; + width: 100%; + + ul { + display: flex; + padding-top: $connector-height; + position: relative; + margin: auto; + transition: all 0.5s; + margin-top: -5px; + + // vertical connector + ul::before { + content: ''; + position: absolute; top: 0; left: 50%; + border-left: $connector-line; + height: $connector-height; + width: 0; + } + + li { + float: left; text-align: center; + list-style-type: none; + position: relative; + padding: $connector-height $padding-sm 0 $padding-sm; + transition: all 0.5s; + + // connectors + &:before, &:after { + content: ''; + position: absolute; top: 0; right: 50%; + border-top: $connector-line; + width: 50%; height: $connector-height; + } + + &:after { + right: auto; left: 50%; + border-left: $connector-line; + } + + &:only-child { + padding-top: 0; + &:after, &:before { + display: none; + } + } + + &:first-child::before, &:last-child::after { + border: 0 none; + } + + &:last-child::before { + border-right: $connector-line; + border-radius: 0 $border-radius-lg 0 0; + } + + &:first-child::after { + border-radius: $border-radius-lg 0 0 0; + } + + //hovers + .plan-node:hover+ul::before { + border-color: $highlight-color; + } + + .plan-node:hover+ul li::after, + .plan-node:hover+ul li::before, + .plan-node:hover+ul ul::before{ + border-color: $highlight-color-dark; + } + } + } +} + +.plan-stats { + display: flex; + font-size: $font-size-base; + margin: 0 auto $padding-lg auto; + padding-bottom: $padding-lg; + border-bottom: 1px solid $line-color; + border-radius: $border-radius-lg*2; + width: 650px; + position: relative; + + div { + padding-right: $padding-lg; + flex-grow: 1; + } + + .stat-value { + display: block; + text-align: center; + font-size: $font-size-lg; + } + + .stat-label { + display: block; + text-align: center; + font-size: $font-size-sm; + } + + $triangle-size: 9px; + &:after { + content:''; + position: absolute; + top: 100%; + left: 50%; + margin-left: $triangle-size*-1; + width: 0; + height: 0; + border-top: solid $triangle-size $line-color; + border-left: solid $triangle-size transparent; + border-right: solid $triangle-size transparent; + } +} diff --git a/app/assets/sass/_table.scss b/app/assets/sass/_table.scss new file mode 100644 index 0000000..d3e6e63 --- /dev/null +++ b/app/assets/sass/_table.scss @@ -0,0 +1,12 @@ +.table { + width: 100%; + + td { + border-bottom: 1px solid $line-color; + padding: $padding-base; + } + + tr:hover { + background-color: $gray-lightest; + } +} diff --git a/app/assets/sass/_variables.scss b/app/assets/sass/_variables.scss new file mode 100644 index 0000000..cfdb166 --- /dev/null +++ b/app/assets/sass/_variables.scss @@ -0,0 +1,51 @@ +//vars +$page-width: 1000px; + +$padding-base: 6px; +$padding-sm: 3px; +$padding-lg: 10px; + +$font-size-base: 13px; +$font-size-xs: round($font-size-base * 0.7); +$font-size-sm: round($font-size-base * 0.9); +$font-size-lg: round($font-size-base * 1.3); +$font-size-xl: round($font-size-base * 1.7); + +$font-family-sans-serif: 'noto sans'; + +$line-height-base: 1.3; + +$gray-lightest: #f7f7f7; +$gray-light: darken($gray-lightest, 10%); +$gray: darken(#f7f7f7, 30%); +$gray-dark: darken(#f7f7f7, 50%); +$gray-darkest: darken($gray-lightest, 70%); + +$blue: #00B5E2; +$dark-blue: #008CAF; +$light-blue: #65DDFB; + +$red: #AF2F11; +$dark-red: #7C210C; +$light-red: #FB8165; + +$bg-color: $gray-lightest; + +$text-color: #4d525a; +$text-color-light: lighten($text-color, 30%); + +$line-color: $gray-light; +$line-color-light: lighten($gray-light, 20%); + +$link-color: $blue; + +$border-radius-base: 3px; +$border-radius-lg: 6px; + +$main-color: $blue; +$main-color-dark: $blue; + +$highlight-color: $blue; +$highlight-color-dark: $dark-blue; + +$alert-color: #FB4418; diff --git a/app/assets/sass/font-awesome/_animated.scss b/app/assets/sass/font-awesome/_animated.scss new file mode 100644 index 0000000..8a020db --- /dev/null +++ b/app/assets/sass/font-awesome/_animated.scss @@ -0,0 +1,34 @@ +// Spinning Icons +// -------------------------- + +.#{$fa-css-prefix}-spin { + -webkit-animation: fa-spin 2s infinite linear; + animation: fa-spin 2s infinite linear; +} + +.#{$fa-css-prefix}-pulse { + -webkit-animation: fa-spin 1s infinite steps(8); + animation: fa-spin 1s infinite steps(8); +} + +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} diff --git a/app/assets/sass/font-awesome/_bordered-pulled.scss b/app/assets/sass/font-awesome/_bordered-pulled.scss new file mode 100644 index 0000000..d4b85a0 --- /dev/null +++ b/app/assets/sass/font-awesome/_bordered-pulled.scss @@ -0,0 +1,25 @@ +// Bordered & Pulled +// ------------------------- + +.#{$fa-css-prefix}-border { + padding: .2em .25em .15em; + border: solid .08em $fa-border-color; + border-radius: .1em; +} + +.#{$fa-css-prefix}-pull-left { float: left; } +.#{$fa-css-prefix}-pull-right { float: right; } + +.#{$fa-css-prefix} { + &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } + &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } +} + +/* Deprecated as of 4.4.0 */ +.pull-right { float: right; } +.pull-left { float: left; } + +.#{$fa-css-prefix} { + &.pull-left { margin-right: .3em; } + &.pull-right { margin-left: .3em; } +} diff --git a/app/assets/sass/font-awesome/_core.scss b/app/assets/sass/font-awesome/_core.scss new file mode 100644 index 0000000..7425ef8 --- /dev/null +++ b/app/assets/sass/font-awesome/_core.scss @@ -0,0 +1,12 @@ +// Base Class Definition +// ------------------------- + +.#{$fa-css-prefix} { + display: inline-block; + font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration + font-size: inherit; // can't have font-size inherit on line above, so need to override + text-rendering: auto; // optimizelegibility throws things off #1094 + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + +} diff --git a/app/assets/sass/font-awesome/_fixed-width.scss b/app/assets/sass/font-awesome/_fixed-width.scss new file mode 100644 index 0000000..b221c98 --- /dev/null +++ b/app/assets/sass/font-awesome/_fixed-width.scss @@ -0,0 +1,6 @@ +// Fixed Width Icons +// ------------------------- +.#{$fa-css-prefix}-fw { + width: (18em / 14); + text-align: center; +} diff --git a/app/assets/sass/font-awesome/_icons.scss b/app/assets/sass/font-awesome/_icons.scss new file mode 100644 index 0000000..6f93759 --- /dev/null +++ b/app/assets/sass/font-awesome/_icons.scss @@ -0,0 +1,697 @@ +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen + readers do not read off random characters that represent icons */ + +.#{$fa-css-prefix}-glass:before { content: $fa-var-glass; } +.#{$fa-css-prefix}-music:before { content: $fa-var-music; } +.#{$fa-css-prefix}-search:before { content: $fa-var-search; } +.#{$fa-css-prefix}-envelope-o:before { content: $fa-var-envelope-o; } +.#{$fa-css-prefix}-heart:before { content: $fa-var-heart; } +.#{$fa-css-prefix}-star:before { content: $fa-var-star; } +.#{$fa-css-prefix}-star-o:before { content: $fa-var-star-o; } +.#{$fa-css-prefix}-user:before { content: $fa-var-user; } +.#{$fa-css-prefix}-film:before { content: $fa-var-film; } +.#{$fa-css-prefix}-th-large:before { content: $fa-var-th-large; } +.#{$fa-css-prefix}-th:before { content: $fa-var-th; } +.#{$fa-css-prefix}-th-list:before { content: $fa-var-th-list; } +.#{$fa-css-prefix}-check:before { content: $fa-var-check; } +.#{$fa-css-prefix}-remove:before, +.#{$fa-css-prefix}-close:before, +.#{$fa-css-prefix}-times:before { content: $fa-var-times; } +.#{$fa-css-prefix}-search-plus:before { content: $fa-var-search-plus; } +.#{$fa-css-prefix}-search-minus:before { content: $fa-var-search-minus; } +.#{$fa-css-prefix}-power-off:before { content: $fa-var-power-off; } +.#{$fa-css-prefix}-signal:before { content: $fa-var-signal; } +.#{$fa-css-prefix}-gear:before, +.#{$fa-css-prefix}-cog:before { content: $fa-var-cog; } +.#{$fa-css-prefix}-trash-o:before { content: $fa-var-trash-o; } +.#{$fa-css-prefix}-home:before { content: $fa-var-home; } +.#{$fa-css-prefix}-file-o:before { content: $fa-var-file-o; } +.#{$fa-css-prefix}-clock-o:before { content: $fa-var-clock-o; } +.#{$fa-css-prefix}-road:before { content: $fa-var-road; } +.#{$fa-css-prefix}-download:before { content: $fa-var-download; } +.#{$fa-css-prefix}-arrow-circle-o-down:before { content: $fa-var-arrow-circle-o-down; } +.#{$fa-css-prefix}-arrow-circle-o-up:before { content: $fa-var-arrow-circle-o-up; } +.#{$fa-css-prefix}-inbox:before { content: $fa-var-inbox; } +.#{$fa-css-prefix}-play-circle-o:before { content: $fa-var-play-circle-o; } +.#{$fa-css-prefix}-rotate-right:before, +.#{$fa-css-prefix}-repeat:before { content: $fa-var-repeat; } +.#{$fa-css-prefix}-refresh:before { content: $fa-var-refresh; } +.#{$fa-css-prefix}-list-alt:before { content: $fa-var-list-alt; } +.#{$fa-css-prefix}-lock:before { content: $fa-var-lock; } +.#{$fa-css-prefix}-flag:before { content: $fa-var-flag; } +.#{$fa-css-prefix}-headphones:before { content: $fa-var-headphones; } +.#{$fa-css-prefix}-volume-off:before { content: $fa-var-volume-off; } +.#{$fa-css-prefix}-volume-down:before { content: $fa-var-volume-down; } +.#{$fa-css-prefix}-volume-up:before { content: $fa-var-volume-up; } +.#{$fa-css-prefix}-qrcode:before { content: $fa-var-qrcode; } +.#{$fa-css-prefix}-barcode:before { content: $fa-var-barcode; } +.#{$fa-css-prefix}-tag:before { content: $fa-var-tag; } +.#{$fa-css-prefix}-tags:before { content: $fa-var-tags; } +.#{$fa-css-prefix}-book:before { content: $fa-var-book; } +.#{$fa-css-prefix}-bookmark:before { content: $fa-var-bookmark; } +.#{$fa-css-prefix}-print:before { content: $fa-var-print; } +.#{$fa-css-prefix}-camera:before { content: $fa-var-camera; } +.#{$fa-css-prefix}-font:before { content: $fa-var-font; } +.#{$fa-css-prefix}-bold:before { content: $fa-var-bold; } +.#{$fa-css-prefix}-italic:before { content: $fa-var-italic; } +.#{$fa-css-prefix}-text-height:before { content: $fa-var-text-height; } +.#{$fa-css-prefix}-text-width:before { content: $fa-var-text-width; } +.#{$fa-css-prefix}-align-left:before { content: $fa-var-align-left; } +.#{$fa-css-prefix}-align-center:before { content: $fa-var-align-center; } +.#{$fa-css-prefix}-align-right:before { content: $fa-var-align-right; } +.#{$fa-css-prefix}-align-justify:before { content: $fa-var-align-justify; } +.#{$fa-css-prefix}-list:before { content: $fa-var-list; } +.#{$fa-css-prefix}-dedent:before, +.#{$fa-css-prefix}-outdent:before { content: $fa-var-outdent; } +.#{$fa-css-prefix}-indent:before { content: $fa-var-indent; } +.#{$fa-css-prefix}-video-camera:before { content: $fa-var-video-camera; } +.#{$fa-css-prefix}-photo:before, +.#{$fa-css-prefix}-image:before, +.#{$fa-css-prefix}-picture-o:before { content: $fa-var-picture-o; } +.#{$fa-css-prefix}-pencil:before { content: $fa-var-pencil; } +.#{$fa-css-prefix}-map-marker:before { content: $fa-var-map-marker; } +.#{$fa-css-prefix}-adjust:before { content: $fa-var-adjust; } +.#{$fa-css-prefix}-tint:before { content: $fa-var-tint; } +.#{$fa-css-prefix}-edit:before, +.#{$fa-css-prefix}-pencil-square-o:before { content: $fa-var-pencil-square-o; } +.#{$fa-css-prefix}-share-square-o:before { content: $fa-var-share-square-o; } +.#{$fa-css-prefix}-check-square-o:before { content: $fa-var-check-square-o; } +.#{$fa-css-prefix}-arrows:before { content: $fa-var-arrows; } +.#{$fa-css-prefix}-step-backward:before { content: $fa-var-step-backward; } +.#{$fa-css-prefix}-fast-backward:before { content: $fa-var-fast-backward; } +.#{$fa-css-prefix}-backward:before { content: $fa-var-backward; } +.#{$fa-css-prefix}-play:before { content: $fa-var-play; } +.#{$fa-css-prefix}-pause:before { content: $fa-var-pause; } +.#{$fa-css-prefix}-stop:before { content: $fa-var-stop; } +.#{$fa-css-prefix}-forward:before { content: $fa-var-forward; } +.#{$fa-css-prefix}-fast-forward:before { content: $fa-var-fast-forward; } +.#{$fa-css-prefix}-step-forward:before { content: $fa-var-step-forward; } +.#{$fa-css-prefix}-eject:before { content: $fa-var-eject; } +.#{$fa-css-prefix}-chevron-left:before { content: $fa-var-chevron-left; } +.#{$fa-css-prefix}-chevron-right:before { content: $fa-var-chevron-right; } +.#{$fa-css-prefix}-plus-circle:before { content: $fa-var-plus-circle; } +.#{$fa-css-prefix}-minus-circle:before { content: $fa-var-minus-circle; } +.#{$fa-css-prefix}-times-circle:before { content: $fa-var-times-circle; } +.#{$fa-css-prefix}-check-circle:before { content: $fa-var-check-circle; } +.#{$fa-css-prefix}-question-circle:before { content: $fa-var-question-circle; } +.#{$fa-css-prefix}-info-circle:before { content: $fa-var-info-circle; } +.#{$fa-css-prefix}-crosshairs:before { content: $fa-var-crosshairs; } +.#{$fa-css-prefix}-times-circle-o:before { content: $fa-var-times-circle-o; } +.#{$fa-css-prefix}-check-circle-o:before { content: $fa-var-check-circle-o; } +.#{$fa-css-prefix}-ban:before { content: $fa-var-ban; } +.#{$fa-css-prefix}-arrow-left:before { content: $fa-var-arrow-left; } +.#{$fa-css-prefix}-arrow-right:before { content: $fa-var-arrow-right; } +.#{$fa-css-prefix}-arrow-up:before { content: $fa-var-arrow-up; } +.#{$fa-css-prefix}-arrow-down:before { content: $fa-var-arrow-down; } +.#{$fa-css-prefix}-mail-forward:before, +.#{$fa-css-prefix}-share:before { content: $fa-var-share; } +.#{$fa-css-prefix}-expand:before { content: $fa-var-expand; } +.#{$fa-css-prefix}-compress:before { content: $fa-var-compress; } +.#{$fa-css-prefix}-plus:before { content: $fa-var-plus; } +.#{$fa-css-prefix}-minus:before { content: $fa-var-minus; } +.#{$fa-css-prefix}-asterisk:before { content: $fa-var-asterisk; } +.#{$fa-css-prefix}-exclamation-circle:before { content: $fa-var-exclamation-circle; } +.#{$fa-css-prefix}-gift:before { content: $fa-var-gift; } +.#{$fa-css-prefix}-leaf:before { content: $fa-var-leaf; } +.#{$fa-css-prefix}-fire:before { content: $fa-var-fire; } +.#{$fa-css-prefix}-eye:before { content: $fa-var-eye; } +.#{$fa-css-prefix}-eye-slash:before { content: $fa-var-eye-slash; } +.#{$fa-css-prefix}-warning:before, +.#{$fa-css-prefix}-exclamation-triangle:before { content: $fa-var-exclamation-triangle; } +.#{$fa-css-prefix}-plane:before { content: $fa-var-plane; } +.#{$fa-css-prefix}-calendar:before { content: $fa-var-calendar; } +.#{$fa-css-prefix}-random:before { content: $fa-var-random; } +.#{$fa-css-prefix}-comment:before { content: $fa-var-comment; } +.#{$fa-css-prefix}-magnet:before { content: $fa-var-magnet; } +.#{$fa-css-prefix}-chevron-up:before { content: $fa-var-chevron-up; } +.#{$fa-css-prefix}-chevron-down:before { content: $fa-var-chevron-down; } +.#{$fa-css-prefix}-retweet:before { content: $fa-var-retweet; } +.#{$fa-css-prefix}-shopping-cart:before { content: $fa-var-shopping-cart; } +.#{$fa-css-prefix}-folder:before { content: $fa-var-folder; } +.#{$fa-css-prefix}-folder-open:before { content: $fa-var-folder-open; } +.#{$fa-css-prefix}-arrows-v:before { content: $fa-var-arrows-v; } +.#{$fa-css-prefix}-arrows-h:before { content: $fa-var-arrows-h; } +.#{$fa-css-prefix}-bar-chart-o:before, +.#{$fa-css-prefix}-bar-chart:before { content: $fa-var-bar-chart; } +.#{$fa-css-prefix}-twitter-square:before { content: $fa-var-twitter-square; } +.#{$fa-css-prefix}-facebook-square:before { content: $fa-var-facebook-square; } +.#{$fa-css-prefix}-camera-retro:before { content: $fa-var-camera-retro; } +.#{$fa-css-prefix}-key:before { content: $fa-var-key; } +.#{$fa-css-prefix}-gears:before, +.#{$fa-css-prefix}-cogs:before { content: $fa-var-cogs; } +.#{$fa-css-prefix}-comments:before { content: $fa-var-comments; } +.#{$fa-css-prefix}-thumbs-o-up:before { content: $fa-var-thumbs-o-up; } +.#{$fa-css-prefix}-thumbs-o-down:before { content: $fa-var-thumbs-o-down; } +.#{$fa-css-prefix}-star-half:before { content: $fa-var-star-half; } +.#{$fa-css-prefix}-heart-o:before { content: $fa-var-heart-o; } +.#{$fa-css-prefix}-sign-out:before { content: $fa-var-sign-out; } +.#{$fa-css-prefix}-linkedin-square:before { content: $fa-var-linkedin-square; } +.#{$fa-css-prefix}-thumb-tack:before { content: $fa-var-thumb-tack; } +.#{$fa-css-prefix}-external-link:before { content: $fa-var-external-link; } +.#{$fa-css-prefix}-sign-in:before { content: $fa-var-sign-in; } +.#{$fa-css-prefix}-trophy:before { content: $fa-var-trophy; } +.#{$fa-css-prefix}-github-square:before { content: $fa-var-github-square; } +.#{$fa-css-prefix}-upload:before { content: $fa-var-upload; } +.#{$fa-css-prefix}-lemon-o:before { content: $fa-var-lemon-o; } +.#{$fa-css-prefix}-phone:before { content: $fa-var-phone; } +.#{$fa-css-prefix}-square-o:before { content: $fa-var-square-o; } +.#{$fa-css-prefix}-bookmark-o:before { content: $fa-var-bookmark-o; } +.#{$fa-css-prefix}-phone-square:before { content: $fa-var-phone-square; } +.#{$fa-css-prefix}-twitter:before { content: $fa-var-twitter; } +.#{$fa-css-prefix}-facebook-f:before, +.#{$fa-css-prefix}-facebook:before { content: $fa-var-facebook; } +.#{$fa-css-prefix}-github:before { content: $fa-var-github; } +.#{$fa-css-prefix}-unlock:before { content: $fa-var-unlock; } +.#{$fa-css-prefix}-credit-card:before { content: $fa-var-credit-card; } +.#{$fa-css-prefix}-feed:before, +.#{$fa-css-prefix}-rss:before { content: $fa-var-rss; } +.#{$fa-css-prefix}-hdd-o:before { content: $fa-var-hdd-o; } +.#{$fa-css-prefix}-bullhorn:before { content: $fa-var-bullhorn; } +.#{$fa-css-prefix}-bell:before { content: $fa-var-bell; } +.#{$fa-css-prefix}-certificate:before { content: $fa-var-certificate; } +.#{$fa-css-prefix}-hand-o-right:before { content: $fa-var-hand-o-right; } +.#{$fa-css-prefix}-hand-o-left:before { content: $fa-var-hand-o-left; } +.#{$fa-css-prefix}-hand-o-up:before { content: $fa-var-hand-o-up; } +.#{$fa-css-prefix}-hand-o-down:before { content: $fa-var-hand-o-down; } +.#{$fa-css-prefix}-arrow-circle-left:before { content: $fa-var-arrow-circle-left; } +.#{$fa-css-prefix}-arrow-circle-right:before { content: $fa-var-arrow-circle-right; } +.#{$fa-css-prefix}-arrow-circle-up:before { content: $fa-var-arrow-circle-up; } +.#{$fa-css-prefix}-arrow-circle-down:before { content: $fa-var-arrow-circle-down; } +.#{$fa-css-prefix}-globe:before { content: $fa-var-globe; } +.#{$fa-css-prefix}-wrench:before { content: $fa-var-wrench; } +.#{$fa-css-prefix}-tasks:before { content: $fa-var-tasks; } +.#{$fa-css-prefix}-filter:before { content: $fa-var-filter; } +.#{$fa-css-prefix}-briefcase:before { content: $fa-var-briefcase; } +.#{$fa-css-prefix}-arrows-alt:before { content: $fa-var-arrows-alt; } +.#{$fa-css-prefix}-group:before, +.#{$fa-css-prefix}-users:before { content: $fa-var-users; } +.#{$fa-css-prefix}-chain:before, +.#{$fa-css-prefix}-link:before { content: $fa-var-link; } +.#{$fa-css-prefix}-cloud:before { content: $fa-var-cloud; } +.#{$fa-css-prefix}-flask:before { content: $fa-var-flask; } +.#{$fa-css-prefix}-cut:before, +.#{$fa-css-prefix}-scissors:before { content: $fa-var-scissors; } +.#{$fa-css-prefix}-copy:before, +.#{$fa-css-prefix}-files-o:before { content: $fa-var-files-o; } +.#{$fa-css-prefix}-paperclip:before { content: $fa-var-paperclip; } +.#{$fa-css-prefix}-save:before, +.#{$fa-css-prefix}-floppy-o:before { content: $fa-var-floppy-o; } +.#{$fa-css-prefix}-square:before { content: $fa-var-square; } +.#{$fa-css-prefix}-navicon:before, +.#{$fa-css-prefix}-reorder:before, +.#{$fa-css-prefix}-bars:before { content: $fa-var-bars; } +.#{$fa-css-prefix}-list-ul:before { content: $fa-var-list-ul; } +.#{$fa-css-prefix}-list-ol:before { content: $fa-var-list-ol; } +.#{$fa-css-prefix}-strikethrough:before { content: $fa-var-strikethrough; } +.#{$fa-css-prefix}-underline:before { content: $fa-var-underline; } +.#{$fa-css-prefix}-table:before { content: $fa-var-table; } +.#{$fa-css-prefix}-magic:before { content: $fa-var-magic; } +.#{$fa-css-prefix}-truck:before { content: $fa-var-truck; } +.#{$fa-css-prefix}-pinterest:before { content: $fa-var-pinterest; } +.#{$fa-css-prefix}-pinterest-square:before { content: $fa-var-pinterest-square; } +.#{$fa-css-prefix}-google-plus-square:before { content: $fa-var-google-plus-square; } +.#{$fa-css-prefix}-google-plus:before { content: $fa-var-google-plus; } +.#{$fa-css-prefix}-money:before { content: $fa-var-money; } +.#{$fa-css-prefix}-caret-down:before { content: $fa-var-caret-down; } +.#{$fa-css-prefix}-caret-up:before { content: $fa-var-caret-up; } +.#{$fa-css-prefix}-caret-left:before { content: $fa-var-caret-left; } +.#{$fa-css-prefix}-caret-right:before { content: $fa-var-caret-right; } +.#{$fa-css-prefix}-columns:before { content: $fa-var-columns; } +.#{$fa-css-prefix}-unsorted:before, +.#{$fa-css-prefix}-sort:before { content: $fa-var-sort; } +.#{$fa-css-prefix}-sort-down:before, +.#{$fa-css-prefix}-sort-desc:before { content: $fa-var-sort-desc; } +.#{$fa-css-prefix}-sort-up:before, +.#{$fa-css-prefix}-sort-asc:before { content: $fa-var-sort-asc; } +.#{$fa-css-prefix}-envelope:before { content: $fa-var-envelope; } +.#{$fa-css-prefix}-linkedin:before { content: $fa-var-linkedin; } +.#{$fa-css-prefix}-rotate-left:before, +.#{$fa-css-prefix}-undo:before { content: $fa-var-undo; } +.#{$fa-css-prefix}-legal:before, +.#{$fa-css-prefix}-gavel:before { content: $fa-var-gavel; } +.#{$fa-css-prefix}-dashboard:before, +.#{$fa-css-prefix}-tachometer:before { content: $fa-var-tachometer; } +.#{$fa-css-prefix}-comment-o:before { content: $fa-var-comment-o; } +.#{$fa-css-prefix}-comments-o:before { content: $fa-var-comments-o; } +.#{$fa-css-prefix}-flash:before, +.#{$fa-css-prefix}-bolt:before { content: $fa-var-bolt; } +.#{$fa-css-prefix}-sitemap:before { content: $fa-var-sitemap; } +.#{$fa-css-prefix}-umbrella:before { content: $fa-var-umbrella; } +.#{$fa-css-prefix}-paste:before, +.#{$fa-css-prefix}-clipboard:before { content: $fa-var-clipboard; } +.#{$fa-css-prefix}-lightbulb-o:before { content: $fa-var-lightbulb-o; } +.#{$fa-css-prefix}-exchange:before { content: $fa-var-exchange; } +.#{$fa-css-prefix}-cloud-download:before { content: $fa-var-cloud-download; } +.#{$fa-css-prefix}-cloud-upload:before { content: $fa-var-cloud-upload; } +.#{$fa-css-prefix}-user-md:before { content: $fa-var-user-md; } +.#{$fa-css-prefix}-stethoscope:before { content: $fa-var-stethoscope; } +.#{$fa-css-prefix}-suitcase:before { content: $fa-var-suitcase; } +.#{$fa-css-prefix}-bell-o:before { content: $fa-var-bell-o; } +.#{$fa-css-prefix}-coffee:before { content: $fa-var-coffee; } +.#{$fa-css-prefix}-cutlery:before { content: $fa-var-cutlery; } +.#{$fa-css-prefix}-file-text-o:before { content: $fa-var-file-text-o; } +.#{$fa-css-prefix}-building-o:before { content: $fa-var-building-o; } +.#{$fa-css-prefix}-hospital-o:before { content: $fa-var-hospital-o; } +.#{$fa-css-prefix}-ambulance:before { content: $fa-var-ambulance; } +.#{$fa-css-prefix}-medkit:before { content: $fa-var-medkit; } +.#{$fa-css-prefix}-fighter-jet:before { content: $fa-var-fighter-jet; } +.#{$fa-css-prefix}-beer:before { content: $fa-var-beer; } +.#{$fa-css-prefix}-h-square:before { content: $fa-var-h-square; } +.#{$fa-css-prefix}-plus-square:before { content: $fa-var-plus-square; } +.#{$fa-css-prefix}-angle-double-left:before { content: $fa-var-angle-double-left; } +.#{$fa-css-prefix}-angle-double-right:before { content: $fa-var-angle-double-right; } +.#{$fa-css-prefix}-angle-double-up:before { content: $fa-var-angle-double-up; } +.#{$fa-css-prefix}-angle-double-down:before { content: $fa-var-angle-double-down; } +.#{$fa-css-prefix}-angle-left:before { content: $fa-var-angle-left; } +.#{$fa-css-prefix}-angle-right:before { content: $fa-var-angle-right; } +.#{$fa-css-prefix}-angle-up:before { content: $fa-var-angle-up; } +.#{$fa-css-prefix}-angle-down:before { content: $fa-var-angle-down; } +.#{$fa-css-prefix}-desktop:before { content: $fa-var-desktop; } +.#{$fa-css-prefix}-laptop:before { content: $fa-var-laptop; } +.#{$fa-css-prefix}-tablet:before { content: $fa-var-tablet; } +.#{$fa-css-prefix}-mobile-phone:before, +.#{$fa-css-prefix}-mobile:before { content: $fa-var-mobile; } +.#{$fa-css-prefix}-circle-o:before { content: $fa-var-circle-o; } +.#{$fa-css-prefix}-quote-left:before { content: $fa-var-quote-left; } +.#{$fa-css-prefix}-quote-right:before { content: $fa-var-quote-right; } +.#{$fa-css-prefix}-spinner:before { content: $fa-var-spinner; } +.#{$fa-css-prefix}-circle:before { content: $fa-var-circle; } +.#{$fa-css-prefix}-mail-reply:before, +.#{$fa-css-prefix}-reply:before { content: $fa-var-reply; } +.#{$fa-css-prefix}-github-alt:before { content: $fa-var-github-alt; } +.#{$fa-css-prefix}-folder-o:before { content: $fa-var-folder-o; } +.#{$fa-css-prefix}-folder-open-o:before { content: $fa-var-folder-open-o; } +.#{$fa-css-prefix}-smile-o:before { content: $fa-var-smile-o; } +.#{$fa-css-prefix}-frown-o:before { content: $fa-var-frown-o; } +.#{$fa-css-prefix}-meh-o:before { content: $fa-var-meh-o; } +.#{$fa-css-prefix}-gamepad:before { content: $fa-var-gamepad; } +.#{$fa-css-prefix}-keyboard-o:before { content: $fa-var-keyboard-o; } +.#{$fa-css-prefix}-flag-o:before { content: $fa-var-flag-o; } +.#{$fa-css-prefix}-flag-checkered:before { content: $fa-var-flag-checkered; } +.#{$fa-css-prefix}-terminal:before { content: $fa-var-terminal; } +.#{$fa-css-prefix}-code:before { content: $fa-var-code; } +.#{$fa-css-prefix}-mail-reply-all:before, +.#{$fa-css-prefix}-reply-all:before { content: $fa-var-reply-all; } +.#{$fa-css-prefix}-star-half-empty:before, +.#{$fa-css-prefix}-star-half-full:before, +.#{$fa-css-prefix}-star-half-o:before { content: $fa-var-star-half-o; } +.#{$fa-css-prefix}-location-arrow:before { content: $fa-var-location-arrow; } +.#{$fa-css-prefix}-crop:before { content: $fa-var-crop; } +.#{$fa-css-prefix}-code-fork:before { content: $fa-var-code-fork; } +.#{$fa-css-prefix}-unlink:before, +.#{$fa-css-prefix}-chain-broken:before { content: $fa-var-chain-broken; } +.#{$fa-css-prefix}-question:before { content: $fa-var-question; } +.#{$fa-css-prefix}-info:before { content: $fa-var-info; } +.#{$fa-css-prefix}-exclamation:before { content: $fa-var-exclamation; } +.#{$fa-css-prefix}-superscript:before { content: $fa-var-superscript; } +.#{$fa-css-prefix}-subscript:before { content: $fa-var-subscript; } +.#{$fa-css-prefix}-eraser:before { content: $fa-var-eraser; } +.#{$fa-css-prefix}-puzzle-piece:before { content: $fa-var-puzzle-piece; } +.#{$fa-css-prefix}-microphone:before { content: $fa-var-microphone; } +.#{$fa-css-prefix}-microphone-slash:before { content: $fa-var-microphone-slash; } +.#{$fa-css-prefix}-shield:before { content: $fa-var-shield; } +.#{$fa-css-prefix}-calendar-o:before { content: $fa-var-calendar-o; } +.#{$fa-css-prefix}-fire-extinguisher:before { content: $fa-var-fire-extinguisher; } +.#{$fa-css-prefix}-rocket:before { content: $fa-var-rocket; } +.#{$fa-css-prefix}-maxcdn:before { content: $fa-var-maxcdn; } +.#{$fa-css-prefix}-chevron-circle-left:before { content: $fa-var-chevron-circle-left; } +.#{$fa-css-prefix}-chevron-circle-right:before { content: $fa-var-chevron-circle-right; } +.#{$fa-css-prefix}-chevron-circle-up:before { content: $fa-var-chevron-circle-up; } +.#{$fa-css-prefix}-chevron-circle-down:before { content: $fa-var-chevron-circle-down; } +.#{$fa-css-prefix}-html5:before { content: $fa-var-html5; } +.#{$fa-css-prefix}-css3:before { content: $fa-var-css3; } +.#{$fa-css-prefix}-anchor:before { content: $fa-var-anchor; } +.#{$fa-css-prefix}-unlock-alt:before { content: $fa-var-unlock-alt; } +.#{$fa-css-prefix}-bullseye:before { content: $fa-var-bullseye; } +.#{$fa-css-prefix}-ellipsis-h:before { content: $fa-var-ellipsis-h; } +.#{$fa-css-prefix}-ellipsis-v:before { content: $fa-var-ellipsis-v; } +.#{$fa-css-prefix}-rss-square:before { content: $fa-var-rss-square; } +.#{$fa-css-prefix}-play-circle:before { content: $fa-var-play-circle; } +.#{$fa-css-prefix}-ticket:before { content: $fa-var-ticket; } +.#{$fa-css-prefix}-minus-square:before { content: $fa-var-minus-square; } +.#{$fa-css-prefix}-minus-square-o:before { content: $fa-var-minus-square-o; } +.#{$fa-css-prefix}-level-up:before { content: $fa-var-level-up; } +.#{$fa-css-prefix}-level-down:before { content: $fa-var-level-down; } +.#{$fa-css-prefix}-check-square:before { content: $fa-var-check-square; } +.#{$fa-css-prefix}-pencil-square:before { content: $fa-var-pencil-square; } +.#{$fa-css-prefix}-external-link-square:before { content: $fa-var-external-link-square; } +.#{$fa-css-prefix}-share-square:before { content: $fa-var-share-square; } +.#{$fa-css-prefix}-compass:before { content: $fa-var-compass; } +.#{$fa-css-prefix}-toggle-down:before, +.#{$fa-css-prefix}-caret-square-o-down:before { content: $fa-var-caret-square-o-down; } +.#{$fa-css-prefix}-toggle-up:before, +.#{$fa-css-prefix}-caret-square-o-up:before { content: $fa-var-caret-square-o-up; } +.#{$fa-css-prefix}-toggle-right:before, +.#{$fa-css-prefix}-caret-square-o-right:before { content: $fa-var-caret-square-o-right; } +.#{$fa-css-prefix}-euro:before, +.#{$fa-css-prefix}-eur:before { content: $fa-var-eur; } +.#{$fa-css-prefix}-gbp:before { content: $fa-var-gbp; } +.#{$fa-css-prefix}-dollar:before, +.#{$fa-css-prefix}-usd:before { content: $fa-var-usd; } +.#{$fa-css-prefix}-rupee:before, +.#{$fa-css-prefix}-inr:before { content: $fa-var-inr; } +.#{$fa-css-prefix}-cny:before, +.#{$fa-css-prefix}-rmb:before, +.#{$fa-css-prefix}-yen:before, +.#{$fa-css-prefix}-jpy:before { content: $fa-var-jpy; } +.#{$fa-css-prefix}-ruble:before, +.#{$fa-css-prefix}-rouble:before, +.#{$fa-css-prefix}-rub:before { content: $fa-var-rub; } +.#{$fa-css-prefix}-won:before, +.#{$fa-css-prefix}-krw:before { content: $fa-var-krw; } +.#{$fa-css-prefix}-bitcoin:before, +.#{$fa-css-prefix}-btc:before { content: $fa-var-btc; } +.#{$fa-css-prefix}-file:before { content: $fa-var-file; } +.#{$fa-css-prefix}-file-text:before { content: $fa-var-file-text; } +.#{$fa-css-prefix}-sort-alpha-asc:before { content: $fa-var-sort-alpha-asc; } +.#{$fa-css-prefix}-sort-alpha-desc:before { content: $fa-var-sort-alpha-desc; } +.#{$fa-css-prefix}-sort-amount-asc:before { content: $fa-var-sort-amount-asc; } +.#{$fa-css-prefix}-sort-amount-desc:before { content: $fa-var-sort-amount-desc; } +.#{$fa-css-prefix}-sort-numeric-asc:before { content: $fa-var-sort-numeric-asc; } +.#{$fa-css-prefix}-sort-numeric-desc:before { content: $fa-var-sort-numeric-desc; } +.#{$fa-css-prefix}-thumbs-up:before { content: $fa-var-thumbs-up; } +.#{$fa-css-prefix}-thumbs-down:before { content: $fa-var-thumbs-down; } +.#{$fa-css-prefix}-youtube-square:before { content: $fa-var-youtube-square; } +.#{$fa-css-prefix}-youtube:before { content: $fa-var-youtube; } +.#{$fa-css-prefix}-xing:before { content: $fa-var-xing; } +.#{$fa-css-prefix}-xing-square:before { content: $fa-var-xing-square; } +.#{$fa-css-prefix}-youtube-play:before { content: $fa-var-youtube-play; } +.#{$fa-css-prefix}-dropbox:before { content: $fa-var-dropbox; } +.#{$fa-css-prefix}-stack-overflow:before { content: $fa-var-stack-overflow; } +.#{$fa-css-prefix}-instagram:before { content: $fa-var-instagram; } +.#{$fa-css-prefix}-flickr:before { content: $fa-var-flickr; } +.#{$fa-css-prefix}-adn:before { content: $fa-var-adn; } +.#{$fa-css-prefix}-bitbucket:before { content: $fa-var-bitbucket; } +.#{$fa-css-prefix}-bitbucket-square:before { content: $fa-var-bitbucket-square; } +.#{$fa-css-prefix}-tumblr:before { content: $fa-var-tumblr; } +.#{$fa-css-prefix}-tumblr-square:before { content: $fa-var-tumblr-square; } +.#{$fa-css-prefix}-long-arrow-down:before { content: $fa-var-long-arrow-down; } +.#{$fa-css-prefix}-long-arrow-up:before { content: $fa-var-long-arrow-up; } +.#{$fa-css-prefix}-long-arrow-left:before { content: $fa-var-long-arrow-left; } +.#{$fa-css-prefix}-long-arrow-right:before { content: $fa-var-long-arrow-right; } +.#{$fa-css-prefix}-apple:before { content: $fa-var-apple; } +.#{$fa-css-prefix}-windows:before { content: $fa-var-windows; } +.#{$fa-css-prefix}-android:before { content: $fa-var-android; } +.#{$fa-css-prefix}-linux:before { content: $fa-var-linux; } +.#{$fa-css-prefix}-dribbble:before { content: $fa-var-dribbble; } +.#{$fa-css-prefix}-skype:before { content: $fa-var-skype; } +.#{$fa-css-prefix}-foursquare:before { content: $fa-var-foursquare; } +.#{$fa-css-prefix}-trello:before { content: $fa-var-trello; } +.#{$fa-css-prefix}-female:before { content: $fa-var-female; } +.#{$fa-css-prefix}-male:before { content: $fa-var-male; } +.#{$fa-css-prefix}-gittip:before, +.#{$fa-css-prefix}-gratipay:before { content: $fa-var-gratipay; } +.#{$fa-css-prefix}-sun-o:before { content: $fa-var-sun-o; } +.#{$fa-css-prefix}-moon-o:before { content: $fa-var-moon-o; } +.#{$fa-css-prefix}-archive:before { content: $fa-var-archive; } +.#{$fa-css-prefix}-bug:before { content: $fa-var-bug; } +.#{$fa-css-prefix}-vk:before { content: $fa-var-vk; } +.#{$fa-css-prefix}-weibo:before { content: $fa-var-weibo; } +.#{$fa-css-prefix}-renren:before { content: $fa-var-renren; } +.#{$fa-css-prefix}-pagelines:before { content: $fa-var-pagelines; } +.#{$fa-css-prefix}-stack-exchange:before { content: $fa-var-stack-exchange; } +.#{$fa-css-prefix}-arrow-circle-o-right:before { content: $fa-var-arrow-circle-o-right; } +.#{$fa-css-prefix}-arrow-circle-o-left:before { content: $fa-var-arrow-circle-o-left; } +.#{$fa-css-prefix}-toggle-left:before, +.#{$fa-css-prefix}-caret-square-o-left:before { content: $fa-var-caret-square-o-left; } +.#{$fa-css-prefix}-dot-circle-o:before { content: $fa-var-dot-circle-o; } +.#{$fa-css-prefix}-wheelchair:before { content: $fa-var-wheelchair; } +.#{$fa-css-prefix}-vimeo-square:before { content: $fa-var-vimeo-square; } +.#{$fa-css-prefix}-turkish-lira:before, +.#{$fa-css-prefix}-try:before { content: $fa-var-try; } +.#{$fa-css-prefix}-plus-square-o:before { content: $fa-var-plus-square-o; } +.#{$fa-css-prefix}-space-shuttle:before { content: $fa-var-space-shuttle; } +.#{$fa-css-prefix}-slack:before { content: $fa-var-slack; } +.#{$fa-css-prefix}-envelope-square:before { content: $fa-var-envelope-square; } +.#{$fa-css-prefix}-wordpress:before { content: $fa-var-wordpress; } +.#{$fa-css-prefix}-openid:before { content: $fa-var-openid; } +.#{$fa-css-prefix}-institution:before, +.#{$fa-css-prefix}-bank:before, +.#{$fa-css-prefix}-university:before { content: $fa-var-university; } +.#{$fa-css-prefix}-mortar-board:before, +.#{$fa-css-prefix}-graduation-cap:before { content: $fa-var-graduation-cap; } +.#{$fa-css-prefix}-yahoo:before { content: $fa-var-yahoo; } +.#{$fa-css-prefix}-google:before { content: $fa-var-google; } +.#{$fa-css-prefix}-reddit:before { content: $fa-var-reddit; } +.#{$fa-css-prefix}-reddit-square:before { content: $fa-var-reddit-square; } +.#{$fa-css-prefix}-stumbleupon-circle:before { content: $fa-var-stumbleupon-circle; } +.#{$fa-css-prefix}-stumbleupon:before { content: $fa-var-stumbleupon; } +.#{$fa-css-prefix}-delicious:before { content: $fa-var-delicious; } +.#{$fa-css-prefix}-digg:before { content: $fa-var-digg; } +.#{$fa-css-prefix}-pied-piper:before { content: $fa-var-pied-piper; } +.#{$fa-css-prefix}-pied-piper-alt:before { content: $fa-var-pied-piper-alt; } +.#{$fa-css-prefix}-drupal:before { content: $fa-var-drupal; } +.#{$fa-css-prefix}-joomla:before { content: $fa-var-joomla; } +.#{$fa-css-prefix}-language:before { content: $fa-var-language; } +.#{$fa-css-prefix}-fax:before { content: $fa-var-fax; } +.#{$fa-css-prefix}-building:before { content: $fa-var-building; } +.#{$fa-css-prefix}-child:before { content: $fa-var-child; } +.#{$fa-css-prefix}-paw:before { content: $fa-var-paw; } +.#{$fa-css-prefix}-spoon:before { content: $fa-var-spoon; } +.#{$fa-css-prefix}-cube:before { content: $fa-var-cube; } +.#{$fa-css-prefix}-cubes:before { content: $fa-var-cubes; } +.#{$fa-css-prefix}-behance:before { content: $fa-var-behance; } +.#{$fa-css-prefix}-behance-square:before { content: $fa-var-behance-square; } +.#{$fa-css-prefix}-steam:before { content: $fa-var-steam; } +.#{$fa-css-prefix}-steam-square:before { content: $fa-var-steam-square; } +.#{$fa-css-prefix}-recycle:before { content: $fa-var-recycle; } +.#{$fa-css-prefix}-automobile:before, +.#{$fa-css-prefix}-car:before { content: $fa-var-car; } +.#{$fa-css-prefix}-cab:before, +.#{$fa-css-prefix}-taxi:before { content: $fa-var-taxi; } +.#{$fa-css-prefix}-tree:before { content: $fa-var-tree; } +.#{$fa-css-prefix}-spotify:before { content: $fa-var-spotify; } +.#{$fa-css-prefix}-deviantart:before { content: $fa-var-deviantart; } +.#{$fa-css-prefix}-soundcloud:before { content: $fa-var-soundcloud; } +.#{$fa-css-prefix}-database:before { content: $fa-var-database; } +.#{$fa-css-prefix}-file-pdf-o:before { content: $fa-var-file-pdf-o; } +.#{$fa-css-prefix}-file-word-o:before { content: $fa-var-file-word-o; } +.#{$fa-css-prefix}-file-excel-o:before { content: $fa-var-file-excel-o; } +.#{$fa-css-prefix}-file-powerpoint-o:before { content: $fa-var-file-powerpoint-o; } +.#{$fa-css-prefix}-file-photo-o:before, +.#{$fa-css-prefix}-file-picture-o:before, +.#{$fa-css-prefix}-file-image-o:before { content: $fa-var-file-image-o; } +.#{$fa-css-prefix}-file-zip-o:before, +.#{$fa-css-prefix}-file-archive-o:before { content: $fa-var-file-archive-o; } +.#{$fa-css-prefix}-file-sound-o:before, +.#{$fa-css-prefix}-file-audio-o:before { content: $fa-var-file-audio-o; } +.#{$fa-css-prefix}-file-movie-o:before, +.#{$fa-css-prefix}-file-video-o:before { content: $fa-var-file-video-o; } +.#{$fa-css-prefix}-file-code-o:before { content: $fa-var-file-code-o; } +.#{$fa-css-prefix}-vine:before { content: $fa-var-vine; } +.#{$fa-css-prefix}-codepen:before { content: $fa-var-codepen; } +.#{$fa-css-prefix}-jsfiddle:before { content: $fa-var-jsfiddle; } +.#{$fa-css-prefix}-life-bouy:before, +.#{$fa-css-prefix}-life-buoy:before, +.#{$fa-css-prefix}-life-saver:before, +.#{$fa-css-prefix}-support:before, +.#{$fa-css-prefix}-life-ring:before { content: $fa-var-life-ring; } +.#{$fa-css-prefix}-circle-o-notch:before { content: $fa-var-circle-o-notch; } +.#{$fa-css-prefix}-ra:before, +.#{$fa-css-prefix}-rebel:before { content: $fa-var-rebel; } +.#{$fa-css-prefix}-ge:before, +.#{$fa-css-prefix}-empire:before { content: $fa-var-empire; } +.#{$fa-css-prefix}-git-square:before { content: $fa-var-git-square; } +.#{$fa-css-prefix}-git:before { content: $fa-var-git; } +.#{$fa-css-prefix}-y-combinator-square:before, +.#{$fa-css-prefix}-yc-square:before, +.#{$fa-css-prefix}-hacker-news:before { content: $fa-var-hacker-news; } +.#{$fa-css-prefix}-tencent-weibo:before { content: $fa-var-tencent-weibo; } +.#{$fa-css-prefix}-qq:before { content: $fa-var-qq; } +.#{$fa-css-prefix}-wechat:before, +.#{$fa-css-prefix}-weixin:before { content: $fa-var-weixin; } +.#{$fa-css-prefix}-send:before, +.#{$fa-css-prefix}-paper-plane:before { content: $fa-var-paper-plane; } +.#{$fa-css-prefix}-send-o:before, +.#{$fa-css-prefix}-paper-plane-o:before { content: $fa-var-paper-plane-o; } +.#{$fa-css-prefix}-history:before { content: $fa-var-history; } +.#{$fa-css-prefix}-circle-thin:before { content: $fa-var-circle-thin; } +.#{$fa-css-prefix}-header:before { content: $fa-var-header; } +.#{$fa-css-prefix}-paragraph:before { content: $fa-var-paragraph; } +.#{$fa-css-prefix}-sliders:before { content: $fa-var-sliders; } +.#{$fa-css-prefix}-share-alt:before { content: $fa-var-share-alt; } +.#{$fa-css-prefix}-share-alt-square:before { content: $fa-var-share-alt-square; } +.#{$fa-css-prefix}-bomb:before { content: $fa-var-bomb; } +.#{$fa-css-prefix}-soccer-ball-o:before, +.#{$fa-css-prefix}-futbol-o:before { content: $fa-var-futbol-o; } +.#{$fa-css-prefix}-tty:before { content: $fa-var-tty; } +.#{$fa-css-prefix}-binoculars:before { content: $fa-var-binoculars; } +.#{$fa-css-prefix}-plug:before { content: $fa-var-plug; } +.#{$fa-css-prefix}-slideshare:before { content: $fa-var-slideshare; } +.#{$fa-css-prefix}-twitch:before { content: $fa-var-twitch; } +.#{$fa-css-prefix}-yelp:before { content: $fa-var-yelp; } +.#{$fa-css-prefix}-newspaper-o:before { content: $fa-var-newspaper-o; } +.#{$fa-css-prefix}-wifi:before { content: $fa-var-wifi; } +.#{$fa-css-prefix}-calculator:before { content: $fa-var-calculator; } +.#{$fa-css-prefix}-paypal:before { content: $fa-var-paypal; } +.#{$fa-css-prefix}-google-wallet:before { content: $fa-var-google-wallet; } +.#{$fa-css-prefix}-cc-visa:before { content: $fa-var-cc-visa; } +.#{$fa-css-prefix}-cc-mastercard:before { content: $fa-var-cc-mastercard; } +.#{$fa-css-prefix}-cc-discover:before { content: $fa-var-cc-discover; } +.#{$fa-css-prefix}-cc-amex:before { content: $fa-var-cc-amex; } +.#{$fa-css-prefix}-cc-paypal:before { content: $fa-var-cc-paypal; } +.#{$fa-css-prefix}-cc-stripe:before { content: $fa-var-cc-stripe; } +.#{$fa-css-prefix}-bell-slash:before { content: $fa-var-bell-slash; } +.#{$fa-css-prefix}-bell-slash-o:before { content: $fa-var-bell-slash-o; } +.#{$fa-css-prefix}-trash:before { content: $fa-var-trash; } +.#{$fa-css-prefix}-copyright:before { content: $fa-var-copyright; } +.#{$fa-css-prefix}-at:before { content: $fa-var-at; } +.#{$fa-css-prefix}-eyedropper:before { content: $fa-var-eyedropper; } +.#{$fa-css-prefix}-paint-brush:before { content: $fa-var-paint-brush; } +.#{$fa-css-prefix}-birthday-cake:before { content: $fa-var-birthday-cake; } +.#{$fa-css-prefix}-area-chart:before { content: $fa-var-area-chart; } +.#{$fa-css-prefix}-pie-chart:before { content: $fa-var-pie-chart; } +.#{$fa-css-prefix}-line-chart:before { content: $fa-var-line-chart; } +.#{$fa-css-prefix}-lastfm:before { content: $fa-var-lastfm; } +.#{$fa-css-prefix}-lastfm-square:before { content: $fa-var-lastfm-square; } +.#{$fa-css-prefix}-toggle-off:before { content: $fa-var-toggle-off; } +.#{$fa-css-prefix}-toggle-on:before { content: $fa-var-toggle-on; } +.#{$fa-css-prefix}-bicycle:before { content: $fa-var-bicycle; } +.#{$fa-css-prefix}-bus:before { content: $fa-var-bus; } +.#{$fa-css-prefix}-ioxhost:before { content: $fa-var-ioxhost; } +.#{$fa-css-prefix}-angellist:before { content: $fa-var-angellist; } +.#{$fa-css-prefix}-cc:before { content: $fa-var-cc; } +.#{$fa-css-prefix}-shekel:before, +.#{$fa-css-prefix}-sheqel:before, +.#{$fa-css-prefix}-ils:before { content: $fa-var-ils; } +.#{$fa-css-prefix}-meanpath:before { content: $fa-var-meanpath; } +.#{$fa-css-prefix}-buysellads:before { content: $fa-var-buysellads; } +.#{$fa-css-prefix}-connectdevelop:before { content: $fa-var-connectdevelop; } +.#{$fa-css-prefix}-dashcube:before { content: $fa-var-dashcube; } +.#{$fa-css-prefix}-forumbee:before { content: $fa-var-forumbee; } +.#{$fa-css-prefix}-leanpub:before { content: $fa-var-leanpub; } +.#{$fa-css-prefix}-sellsy:before { content: $fa-var-sellsy; } +.#{$fa-css-prefix}-shirtsinbulk:before { content: $fa-var-shirtsinbulk; } +.#{$fa-css-prefix}-simplybuilt:before { content: $fa-var-simplybuilt; } +.#{$fa-css-prefix}-skyatlas:before { content: $fa-var-skyatlas; } +.#{$fa-css-prefix}-cart-plus:before { content: $fa-var-cart-plus; } +.#{$fa-css-prefix}-cart-arrow-down:before { content: $fa-var-cart-arrow-down; } +.#{$fa-css-prefix}-diamond:before { content: $fa-var-diamond; } +.#{$fa-css-prefix}-ship:before { content: $fa-var-ship; } +.#{$fa-css-prefix}-user-secret:before { content: $fa-var-user-secret; } +.#{$fa-css-prefix}-motorcycle:before { content: $fa-var-motorcycle; } +.#{$fa-css-prefix}-street-view:before { content: $fa-var-street-view; } +.#{$fa-css-prefix}-heartbeat:before { content: $fa-var-heartbeat; } +.#{$fa-css-prefix}-venus:before { content: $fa-var-venus; } +.#{$fa-css-prefix}-mars:before { content: $fa-var-mars; } +.#{$fa-css-prefix}-mercury:before { content: $fa-var-mercury; } +.#{$fa-css-prefix}-intersex:before, +.#{$fa-css-prefix}-transgender:before { content: $fa-var-transgender; } +.#{$fa-css-prefix}-transgender-alt:before { content: $fa-var-transgender-alt; } +.#{$fa-css-prefix}-venus-double:before { content: $fa-var-venus-double; } +.#{$fa-css-prefix}-mars-double:before { content: $fa-var-mars-double; } +.#{$fa-css-prefix}-venus-mars:before { content: $fa-var-venus-mars; } +.#{$fa-css-prefix}-mars-stroke:before { content: $fa-var-mars-stroke; } +.#{$fa-css-prefix}-mars-stroke-v:before { content: $fa-var-mars-stroke-v; } +.#{$fa-css-prefix}-mars-stroke-h:before { content: $fa-var-mars-stroke-h; } +.#{$fa-css-prefix}-neuter:before { content: $fa-var-neuter; } +.#{$fa-css-prefix}-genderless:before { content: $fa-var-genderless; } +.#{$fa-css-prefix}-facebook-official:before { content: $fa-var-facebook-official; } +.#{$fa-css-prefix}-pinterest-p:before { content: $fa-var-pinterest-p; } +.#{$fa-css-prefix}-whatsapp:before { content: $fa-var-whatsapp; } +.#{$fa-css-prefix}-server:before { content: $fa-var-server; } +.#{$fa-css-prefix}-user-plus:before { content: $fa-var-user-plus; } +.#{$fa-css-prefix}-user-times:before { content: $fa-var-user-times; } +.#{$fa-css-prefix}-hotel:before, +.#{$fa-css-prefix}-bed:before { content: $fa-var-bed; } +.#{$fa-css-prefix}-viacoin:before { content: $fa-var-viacoin; } +.#{$fa-css-prefix}-train:before { content: $fa-var-train; } +.#{$fa-css-prefix}-subway:before { content: $fa-var-subway; } +.#{$fa-css-prefix}-medium:before { content: $fa-var-medium; } +.#{$fa-css-prefix}-yc:before, +.#{$fa-css-prefix}-y-combinator:before { content: $fa-var-y-combinator; } +.#{$fa-css-prefix}-optin-monster:before { content: $fa-var-optin-monster; } +.#{$fa-css-prefix}-opencart:before { content: $fa-var-opencart; } +.#{$fa-css-prefix}-expeditedssl:before { content: $fa-var-expeditedssl; } +.#{$fa-css-prefix}-battery-4:before, +.#{$fa-css-prefix}-battery-full:before { content: $fa-var-battery-full; } +.#{$fa-css-prefix}-battery-3:before, +.#{$fa-css-prefix}-battery-three-quarters:before { content: $fa-var-battery-three-quarters; } +.#{$fa-css-prefix}-battery-2:before, +.#{$fa-css-prefix}-battery-half:before { content: $fa-var-battery-half; } +.#{$fa-css-prefix}-battery-1:before, +.#{$fa-css-prefix}-battery-quarter:before { content: $fa-var-battery-quarter; } +.#{$fa-css-prefix}-battery-0:before, +.#{$fa-css-prefix}-battery-empty:before { content: $fa-var-battery-empty; } +.#{$fa-css-prefix}-mouse-pointer:before { content: $fa-var-mouse-pointer; } +.#{$fa-css-prefix}-i-cursor:before { content: $fa-var-i-cursor; } +.#{$fa-css-prefix}-object-group:before { content: $fa-var-object-group; } +.#{$fa-css-prefix}-object-ungroup:before { content: $fa-var-object-ungroup; } +.#{$fa-css-prefix}-sticky-note:before { content: $fa-var-sticky-note; } +.#{$fa-css-prefix}-sticky-note-o:before { content: $fa-var-sticky-note-o; } +.#{$fa-css-prefix}-cc-jcb:before { content: $fa-var-cc-jcb; } +.#{$fa-css-prefix}-cc-diners-club:before { content: $fa-var-cc-diners-club; } +.#{$fa-css-prefix}-clone:before { content: $fa-var-clone; } +.#{$fa-css-prefix}-balance-scale:before { content: $fa-var-balance-scale; } +.#{$fa-css-prefix}-hourglass-o:before { content: $fa-var-hourglass-o; } +.#{$fa-css-prefix}-hourglass-1:before, +.#{$fa-css-prefix}-hourglass-start:before { content: $fa-var-hourglass-start; } +.#{$fa-css-prefix}-hourglass-2:before, +.#{$fa-css-prefix}-hourglass-half:before { content: $fa-var-hourglass-half; } +.#{$fa-css-prefix}-hourglass-3:before, +.#{$fa-css-prefix}-hourglass-end:before { content: $fa-var-hourglass-end; } +.#{$fa-css-prefix}-hourglass:before { content: $fa-var-hourglass; } +.#{$fa-css-prefix}-hand-grab-o:before, +.#{$fa-css-prefix}-hand-rock-o:before { content: $fa-var-hand-rock-o; } +.#{$fa-css-prefix}-hand-stop-o:before, +.#{$fa-css-prefix}-hand-paper-o:before { content: $fa-var-hand-paper-o; } +.#{$fa-css-prefix}-hand-scissors-o:before { content: $fa-var-hand-scissors-o; } +.#{$fa-css-prefix}-hand-lizard-o:before { content: $fa-var-hand-lizard-o; } +.#{$fa-css-prefix}-hand-spock-o:before { content: $fa-var-hand-spock-o; } +.#{$fa-css-prefix}-hand-pointer-o:before { content: $fa-var-hand-pointer-o; } +.#{$fa-css-prefix}-hand-peace-o:before { content: $fa-var-hand-peace-o; } +.#{$fa-css-prefix}-trademark:before { content: $fa-var-trademark; } +.#{$fa-css-prefix}-registered:before { content: $fa-var-registered; } +.#{$fa-css-prefix}-creative-commons:before { content: $fa-var-creative-commons; } +.#{$fa-css-prefix}-gg:before { content: $fa-var-gg; } +.#{$fa-css-prefix}-gg-circle:before { content: $fa-var-gg-circle; } +.#{$fa-css-prefix}-tripadvisor:before { content: $fa-var-tripadvisor; } +.#{$fa-css-prefix}-odnoklassniki:before { content: $fa-var-odnoklassniki; } +.#{$fa-css-prefix}-odnoklassniki-square:before { content: $fa-var-odnoklassniki-square; } +.#{$fa-css-prefix}-get-pocket:before { content: $fa-var-get-pocket; } +.#{$fa-css-prefix}-wikipedia-w:before { content: $fa-var-wikipedia-w; } +.#{$fa-css-prefix}-safari:before { content: $fa-var-safari; } +.#{$fa-css-prefix}-chrome:before { content: $fa-var-chrome; } +.#{$fa-css-prefix}-firefox:before { content: $fa-var-firefox; } +.#{$fa-css-prefix}-opera:before { content: $fa-var-opera; } +.#{$fa-css-prefix}-internet-explorer:before { content: $fa-var-internet-explorer; } +.#{$fa-css-prefix}-tv:before, +.#{$fa-css-prefix}-television:before { content: $fa-var-television; } +.#{$fa-css-prefix}-contao:before { content: $fa-var-contao; } +.#{$fa-css-prefix}-500px:before { content: $fa-var-500px; } +.#{$fa-css-prefix}-amazon:before { content: $fa-var-amazon; } +.#{$fa-css-prefix}-calendar-plus-o:before { content: $fa-var-calendar-plus-o; } +.#{$fa-css-prefix}-calendar-minus-o:before { content: $fa-var-calendar-minus-o; } +.#{$fa-css-prefix}-calendar-times-o:before { content: $fa-var-calendar-times-o; } +.#{$fa-css-prefix}-calendar-check-o:before { content: $fa-var-calendar-check-o; } +.#{$fa-css-prefix}-industry:before { content: $fa-var-industry; } +.#{$fa-css-prefix}-map-pin:before { content: $fa-var-map-pin; } +.#{$fa-css-prefix}-map-signs:before { content: $fa-var-map-signs; } +.#{$fa-css-prefix}-map-o:before { content: $fa-var-map-o; } +.#{$fa-css-prefix}-map:before { content: $fa-var-map; } +.#{$fa-css-prefix}-commenting:before { content: $fa-var-commenting; } +.#{$fa-css-prefix}-commenting-o:before { content: $fa-var-commenting-o; } +.#{$fa-css-prefix}-houzz:before { content: $fa-var-houzz; } +.#{$fa-css-prefix}-vimeo:before { content: $fa-var-vimeo; } +.#{$fa-css-prefix}-black-tie:before { content: $fa-var-black-tie; } +.#{$fa-css-prefix}-fonticons:before { content: $fa-var-fonticons; } +.#{$fa-css-prefix}-reddit-alien:before { content: $fa-var-reddit-alien; } +.#{$fa-css-prefix}-edge:before { content: $fa-var-edge; } +.#{$fa-css-prefix}-credit-card-alt:before { content: $fa-var-credit-card-alt; } +.#{$fa-css-prefix}-codiepie:before { content: $fa-var-codiepie; } +.#{$fa-css-prefix}-modx:before { content: $fa-var-modx; } +.#{$fa-css-prefix}-fort-awesome:before { content: $fa-var-fort-awesome; } +.#{$fa-css-prefix}-usb:before { content: $fa-var-usb; } +.#{$fa-css-prefix}-product-hunt:before { content: $fa-var-product-hunt; } +.#{$fa-css-prefix}-mixcloud:before { content: $fa-var-mixcloud; } +.#{$fa-css-prefix}-scribd:before { content: $fa-var-scribd; } +.#{$fa-css-prefix}-pause-circle:before { content: $fa-var-pause-circle; } +.#{$fa-css-prefix}-pause-circle-o:before { content: $fa-var-pause-circle-o; } +.#{$fa-css-prefix}-stop-circle:before { content: $fa-var-stop-circle; } +.#{$fa-css-prefix}-stop-circle-o:before { content: $fa-var-stop-circle-o; } +.#{$fa-css-prefix}-shopping-bag:before { content: $fa-var-shopping-bag; } +.#{$fa-css-prefix}-shopping-basket:before { content: $fa-var-shopping-basket; } +.#{$fa-css-prefix}-hashtag:before { content: $fa-var-hashtag; } +.#{$fa-css-prefix}-bluetooth:before { content: $fa-var-bluetooth; } +.#{$fa-css-prefix}-bluetooth-b:before { content: $fa-var-bluetooth-b; } +.#{$fa-css-prefix}-percent:before { content: $fa-var-percent; } diff --git a/app/assets/sass/font-awesome/_larger.scss b/app/assets/sass/font-awesome/_larger.scss new file mode 100644 index 0000000..41e9a81 --- /dev/null +++ b/app/assets/sass/font-awesome/_larger.scss @@ -0,0 +1,13 @@ +// Icon Sizes +// ------------------------- + +/* makes the font 33% larger relative to the icon container */ +.#{$fa-css-prefix}-lg { + font-size: (4em / 3); + line-height: (3em / 4); + vertical-align: -15%; +} +.#{$fa-css-prefix}-2x { font-size: 2em; } +.#{$fa-css-prefix}-3x { font-size: 3em; } +.#{$fa-css-prefix}-4x { font-size: 4em; } +.#{$fa-css-prefix}-5x { font-size: 5em; } diff --git a/app/assets/sass/font-awesome/_list.scss b/app/assets/sass/font-awesome/_list.scss new file mode 100644 index 0000000..7d1e4d5 --- /dev/null +++ b/app/assets/sass/font-awesome/_list.scss @@ -0,0 +1,19 @@ +// List Icons +// ------------------------- + +.#{$fa-css-prefix}-ul { + padding-left: 0; + margin-left: $fa-li-width; + list-style-type: none; + > li { position: relative; } +} +.#{$fa-css-prefix}-li { + position: absolute; + left: -$fa-li-width; + width: $fa-li-width; + top: (2em / 14); + text-align: center; + &.#{$fa-css-prefix}-lg { + left: -$fa-li-width + (4em / 14); + } +} diff --git a/app/assets/sass/font-awesome/_mixins.scss b/app/assets/sass/font-awesome/_mixins.scss new file mode 100644 index 0000000..f96719b --- /dev/null +++ b/app/assets/sass/font-awesome/_mixins.scss @@ -0,0 +1,26 @@ +// Mixins +// -------------------------- + +@mixin fa-icon() { + display: inline-block; + font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration + font-size: inherit; // can't have font-size inherit on line above, so need to override + text-rendering: auto; // optimizelegibility throws things off #1094 + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + +} + +@mixin fa-icon-rotate($degrees, $rotation) { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}); + -webkit-transform: rotate($degrees); + -ms-transform: rotate($degrees); + transform: rotate($degrees); +} + +@mixin fa-icon-flip($horiz, $vert, $rotation) { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}); + -webkit-transform: scale($horiz, $vert); + -ms-transform: scale($horiz, $vert); + transform: scale($horiz, $vert); +} diff --git a/app/assets/sass/font-awesome/_path.scss b/app/assets/sass/font-awesome/_path.scss new file mode 100644 index 0000000..bb457c2 --- /dev/null +++ b/app/assets/sass/font-awesome/_path.scss @@ -0,0 +1,15 @@ +/* FONT PATH + * -------------------------- */ + +@font-face { + font-family: 'FontAwesome'; + src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); + src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), + url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), + url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), + url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), + url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); +// src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts + font-weight: normal; + font-style: normal; +} diff --git a/app/assets/sass/font-awesome/_rotated-flipped.scss b/app/assets/sass/font-awesome/_rotated-flipped.scss new file mode 100644 index 0000000..a3558fd --- /dev/null +++ b/app/assets/sass/font-awesome/_rotated-flipped.scss @@ -0,0 +1,20 @@ +// Rotated & Flipped Icons +// ------------------------- + +.#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } +.#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } +.#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } + +.#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } +.#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } + +// Hook for IE8-9 +// ------------------------- + +:root .#{$fa-css-prefix}-rotate-90, +:root .#{$fa-css-prefix}-rotate-180, +:root .#{$fa-css-prefix}-rotate-270, +:root .#{$fa-css-prefix}-flip-horizontal, +:root .#{$fa-css-prefix}-flip-vertical { + filter: none; +} diff --git a/app/assets/sass/font-awesome/_stacked.scss b/app/assets/sass/font-awesome/_stacked.scss new file mode 100644 index 0000000..aef7403 --- /dev/null +++ b/app/assets/sass/font-awesome/_stacked.scss @@ -0,0 +1,20 @@ +// Stacked Icons +// ------------------------- + +.#{$fa-css-prefix}-stack { + position: relative; + display: inline-block; + width: 2em; + height: 2em; + line-height: 2em; + vertical-align: middle; +} +.#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { + position: absolute; + left: 0; + width: 100%; + text-align: center; +} +.#{$fa-css-prefix}-stack-1x { line-height: inherit; } +.#{$fa-css-prefix}-stack-2x { font-size: 2em; } +.#{$fa-css-prefix}-inverse { color: $fa-inverse; } diff --git a/app/assets/sass/font-awesome/_styles.scss b/app/assets/sass/font-awesome/_styles.scss new file mode 100644 index 0000000..f4668a5 --- /dev/null +++ b/app/assets/sass/font-awesome/_styles.scss @@ -0,0 +1,17 @@ +/*! + * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */ + +@import "variables"; +@import "mixins"; +@import "path"; +@import "core"; +@import "larger"; +@import "fixed-width"; +@import "list"; +@import "bordered-pulled"; +@import "animated"; +@import "rotated-flipped"; +@import "stacked"; +@import "icons"; diff --git a/app/assets/sass/font-awesome/_variables.scss b/app/assets/sass/font-awesome/_variables.scss new file mode 100644 index 0000000..0a47110 --- /dev/null +++ b/app/assets/sass/font-awesome/_variables.scss @@ -0,0 +1,708 @@ +// Variables +// -------------------------- + +$fa-font-path: "../fonts" !default; +$fa-font-size-base: 14px !default; +$fa-line-height-base: 1 !default; +//$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.5.0/fonts" !default; // for referencing Bootstrap CDN font files directly +$fa-css-prefix: fa !default; +$fa-version: "4.5.0" !default; +$fa-border-color: #eee !default; +$fa-inverse: #fff !default; +$fa-li-width: (30em / 14) !default; + +$fa-var-500px: "\f26e"; +$fa-var-adjust: "\f042"; +$fa-var-adn: "\f170"; +$fa-var-align-center: "\f037"; +$fa-var-align-justify: "\f039"; +$fa-var-align-left: "\f036"; +$fa-var-align-right: "\f038"; +$fa-var-amazon: "\f270"; +$fa-var-ambulance: "\f0f9"; +$fa-var-anchor: "\f13d"; +$fa-var-android: "\f17b"; +$fa-var-angellist: "\f209"; +$fa-var-angle-double-down: "\f103"; +$fa-var-angle-double-left: "\f100"; +$fa-var-angle-double-right: "\f101"; +$fa-var-angle-double-up: "\f102"; +$fa-var-angle-down: "\f107"; +$fa-var-angle-left: "\f104"; +$fa-var-angle-right: "\f105"; +$fa-var-angle-up: "\f106"; +$fa-var-apple: "\f179"; +$fa-var-archive: "\f187"; +$fa-var-area-chart: "\f1fe"; +$fa-var-arrow-circle-down: "\f0ab"; +$fa-var-arrow-circle-left: "\f0a8"; +$fa-var-arrow-circle-o-down: "\f01a"; +$fa-var-arrow-circle-o-left: "\f190"; +$fa-var-arrow-circle-o-right: "\f18e"; +$fa-var-arrow-circle-o-up: "\f01b"; +$fa-var-arrow-circle-right: "\f0a9"; +$fa-var-arrow-circle-up: "\f0aa"; +$fa-var-arrow-down: "\f063"; +$fa-var-arrow-left: "\f060"; +$fa-var-arrow-right: "\f061"; +$fa-var-arrow-up: "\f062"; +$fa-var-arrows: "\f047"; +$fa-var-arrows-alt: "\f0b2"; +$fa-var-arrows-h: "\f07e"; +$fa-var-arrows-v: "\f07d"; +$fa-var-asterisk: "\f069"; +$fa-var-at: "\f1fa"; +$fa-var-automobile: "\f1b9"; +$fa-var-backward: "\f04a"; +$fa-var-balance-scale: "\f24e"; +$fa-var-ban: "\f05e"; +$fa-var-bank: "\f19c"; +$fa-var-bar-chart: "\f080"; +$fa-var-bar-chart-o: "\f080"; +$fa-var-barcode: "\f02a"; +$fa-var-bars: "\f0c9"; +$fa-var-battery-0: "\f244"; +$fa-var-battery-1: "\f243"; +$fa-var-battery-2: "\f242"; +$fa-var-battery-3: "\f241"; +$fa-var-battery-4: "\f240"; +$fa-var-battery-empty: "\f244"; +$fa-var-battery-full: "\f240"; +$fa-var-battery-half: "\f242"; +$fa-var-battery-quarter: "\f243"; +$fa-var-battery-three-quarters: "\f241"; +$fa-var-bed: "\f236"; +$fa-var-beer: "\f0fc"; +$fa-var-behance: "\f1b4"; +$fa-var-behance-square: "\f1b5"; +$fa-var-bell: "\f0f3"; +$fa-var-bell-o: "\f0a2"; +$fa-var-bell-slash: "\f1f6"; +$fa-var-bell-slash-o: "\f1f7"; +$fa-var-bicycle: "\f206"; +$fa-var-binoculars: "\f1e5"; +$fa-var-birthday-cake: "\f1fd"; +$fa-var-bitbucket: "\f171"; +$fa-var-bitbucket-square: "\f172"; +$fa-var-bitcoin: "\f15a"; +$fa-var-black-tie: "\f27e"; +$fa-var-bluetooth: "\f293"; +$fa-var-bluetooth-b: "\f294"; +$fa-var-bold: "\f032"; +$fa-var-bolt: "\f0e7"; +$fa-var-bomb: "\f1e2"; +$fa-var-book: "\f02d"; +$fa-var-bookmark: "\f02e"; +$fa-var-bookmark-o: "\f097"; +$fa-var-briefcase: "\f0b1"; +$fa-var-btc: "\f15a"; +$fa-var-bug: "\f188"; +$fa-var-building: "\f1ad"; +$fa-var-building-o: "\f0f7"; +$fa-var-bullhorn: "\f0a1"; +$fa-var-bullseye: "\f140"; +$fa-var-bus: "\f207"; +$fa-var-buysellads: "\f20d"; +$fa-var-cab: "\f1ba"; +$fa-var-calculator: "\f1ec"; +$fa-var-calendar: "\f073"; +$fa-var-calendar-check-o: "\f274"; +$fa-var-calendar-minus-o: "\f272"; +$fa-var-calendar-o: "\f133"; +$fa-var-calendar-plus-o: "\f271"; +$fa-var-calendar-times-o: "\f273"; +$fa-var-camera: "\f030"; +$fa-var-camera-retro: "\f083"; +$fa-var-car: "\f1b9"; +$fa-var-caret-down: "\f0d7"; +$fa-var-caret-left: "\f0d9"; +$fa-var-caret-right: "\f0da"; +$fa-var-caret-square-o-down: "\f150"; +$fa-var-caret-square-o-left: "\f191"; +$fa-var-caret-square-o-right: "\f152"; +$fa-var-caret-square-o-up: "\f151"; +$fa-var-caret-up: "\f0d8"; +$fa-var-cart-arrow-down: "\f218"; +$fa-var-cart-plus: "\f217"; +$fa-var-cc: "\f20a"; +$fa-var-cc-amex: "\f1f3"; +$fa-var-cc-diners-club: "\f24c"; +$fa-var-cc-discover: "\f1f2"; +$fa-var-cc-jcb: "\f24b"; +$fa-var-cc-mastercard: "\f1f1"; +$fa-var-cc-paypal: "\f1f4"; +$fa-var-cc-stripe: "\f1f5"; +$fa-var-cc-visa: "\f1f0"; +$fa-var-certificate: "\f0a3"; +$fa-var-chain: "\f0c1"; +$fa-var-chain-broken: "\f127"; +$fa-var-check: "\f00c"; +$fa-var-check-circle: "\f058"; +$fa-var-check-circle-o: "\f05d"; +$fa-var-check-square: "\f14a"; +$fa-var-check-square-o: "\f046"; +$fa-var-chevron-circle-down: "\f13a"; +$fa-var-chevron-circle-left: "\f137"; +$fa-var-chevron-circle-right: "\f138"; +$fa-var-chevron-circle-up: "\f139"; +$fa-var-chevron-down: "\f078"; +$fa-var-chevron-left: "\f053"; +$fa-var-chevron-right: "\f054"; +$fa-var-chevron-up: "\f077"; +$fa-var-child: "\f1ae"; +$fa-var-chrome: "\f268"; +$fa-var-circle: "\f111"; +$fa-var-circle-o: "\f10c"; +$fa-var-circle-o-notch: "\f1ce"; +$fa-var-circle-thin: "\f1db"; +$fa-var-clipboard: "\f0ea"; +$fa-var-clock-o: "\f017"; +$fa-var-clone: "\f24d"; +$fa-var-close: "\f00d"; +$fa-var-cloud: "\f0c2"; +$fa-var-cloud-download: "\f0ed"; +$fa-var-cloud-upload: "\f0ee"; +$fa-var-cny: "\f157"; +$fa-var-code: "\f121"; +$fa-var-code-fork: "\f126"; +$fa-var-codepen: "\f1cb"; +$fa-var-codiepie: "\f284"; +$fa-var-coffee: "\f0f4"; +$fa-var-cog: "\f013"; +$fa-var-cogs: "\f085"; +$fa-var-columns: "\f0db"; +$fa-var-comment: "\f075"; +$fa-var-comment-o: "\f0e5"; +$fa-var-commenting: "\f27a"; +$fa-var-commenting-o: "\f27b"; +$fa-var-comments: "\f086"; +$fa-var-comments-o: "\f0e6"; +$fa-var-compass: "\f14e"; +$fa-var-compress: "\f066"; +$fa-var-connectdevelop: "\f20e"; +$fa-var-contao: "\f26d"; +$fa-var-copy: "\f0c5"; +$fa-var-copyright: "\f1f9"; +$fa-var-creative-commons: "\f25e"; +$fa-var-credit-card: "\f09d"; +$fa-var-credit-card-alt: "\f283"; +$fa-var-crop: "\f125"; +$fa-var-crosshairs: "\f05b"; +$fa-var-css3: "\f13c"; +$fa-var-cube: "\f1b2"; +$fa-var-cubes: "\f1b3"; +$fa-var-cut: "\f0c4"; +$fa-var-cutlery: "\f0f5"; +$fa-var-dashboard: "\f0e4"; +$fa-var-dashcube: "\f210"; +$fa-var-database: "\f1c0"; +$fa-var-dedent: "\f03b"; +$fa-var-delicious: "\f1a5"; +$fa-var-desktop: "\f108"; +$fa-var-deviantart: "\f1bd"; +$fa-var-diamond: "\f219"; +$fa-var-digg: "\f1a6"; +$fa-var-dollar: "\f155"; +$fa-var-dot-circle-o: "\f192"; +$fa-var-download: "\f019"; +$fa-var-dribbble: "\f17d"; +$fa-var-dropbox: "\f16b"; +$fa-var-drupal: "\f1a9"; +$fa-var-edge: "\f282"; +$fa-var-edit: "\f044"; +$fa-var-eject: "\f052"; +$fa-var-ellipsis-h: "\f141"; +$fa-var-ellipsis-v: "\f142"; +$fa-var-empire: "\f1d1"; +$fa-var-envelope: "\f0e0"; +$fa-var-envelope-o: "\f003"; +$fa-var-envelope-square: "\f199"; +$fa-var-eraser: "\f12d"; +$fa-var-eur: "\f153"; +$fa-var-euro: "\f153"; +$fa-var-exchange: "\f0ec"; +$fa-var-exclamation: "\f12a"; +$fa-var-exclamation-circle: "\f06a"; +$fa-var-exclamation-triangle: "\f071"; +$fa-var-expand: "\f065"; +$fa-var-expeditedssl: "\f23e"; +$fa-var-external-link: "\f08e"; +$fa-var-external-link-square: "\f14c"; +$fa-var-eye: "\f06e"; +$fa-var-eye-slash: "\f070"; +$fa-var-eyedropper: "\f1fb"; +$fa-var-facebook: "\f09a"; +$fa-var-facebook-f: "\f09a"; +$fa-var-facebook-official: "\f230"; +$fa-var-facebook-square: "\f082"; +$fa-var-fast-backward: "\f049"; +$fa-var-fast-forward: "\f050"; +$fa-var-fax: "\f1ac"; +$fa-var-feed: "\f09e"; +$fa-var-female: "\f182"; +$fa-var-fighter-jet: "\f0fb"; +$fa-var-file: "\f15b"; +$fa-var-file-archive-o: "\f1c6"; +$fa-var-file-audio-o: "\f1c7"; +$fa-var-file-code-o: "\f1c9"; +$fa-var-file-excel-o: "\f1c3"; +$fa-var-file-image-o: "\f1c5"; +$fa-var-file-movie-o: "\f1c8"; +$fa-var-file-o: "\f016"; +$fa-var-file-pdf-o: "\f1c1"; +$fa-var-file-photo-o: "\f1c5"; +$fa-var-file-picture-o: "\f1c5"; +$fa-var-file-powerpoint-o: "\f1c4"; +$fa-var-file-sound-o: "\f1c7"; +$fa-var-file-text: "\f15c"; +$fa-var-file-text-o: "\f0f6"; +$fa-var-file-video-o: "\f1c8"; +$fa-var-file-word-o: "\f1c2"; +$fa-var-file-zip-o: "\f1c6"; +$fa-var-files-o: "\f0c5"; +$fa-var-film: "\f008"; +$fa-var-filter: "\f0b0"; +$fa-var-fire: "\f06d"; +$fa-var-fire-extinguisher: "\f134"; +$fa-var-firefox: "\f269"; +$fa-var-flag: "\f024"; +$fa-var-flag-checkered: "\f11e"; +$fa-var-flag-o: "\f11d"; +$fa-var-flash: "\f0e7"; +$fa-var-flask: "\f0c3"; +$fa-var-flickr: "\f16e"; +$fa-var-floppy-o: "\f0c7"; +$fa-var-folder: "\f07b"; +$fa-var-folder-o: "\f114"; +$fa-var-folder-open: "\f07c"; +$fa-var-folder-open-o: "\f115"; +$fa-var-font: "\f031"; +$fa-var-fonticons: "\f280"; +$fa-var-fort-awesome: "\f286"; +$fa-var-forumbee: "\f211"; +$fa-var-forward: "\f04e"; +$fa-var-foursquare: "\f180"; +$fa-var-frown-o: "\f119"; +$fa-var-futbol-o: "\f1e3"; +$fa-var-gamepad: "\f11b"; +$fa-var-gavel: "\f0e3"; +$fa-var-gbp: "\f154"; +$fa-var-ge: "\f1d1"; +$fa-var-gear: "\f013"; +$fa-var-gears: "\f085"; +$fa-var-genderless: "\f22d"; +$fa-var-get-pocket: "\f265"; +$fa-var-gg: "\f260"; +$fa-var-gg-circle: "\f261"; +$fa-var-gift: "\f06b"; +$fa-var-git: "\f1d3"; +$fa-var-git-square: "\f1d2"; +$fa-var-github: "\f09b"; +$fa-var-github-alt: "\f113"; +$fa-var-github-square: "\f092"; +$fa-var-gittip: "\f184"; +$fa-var-glass: "\f000"; +$fa-var-globe: "\f0ac"; +$fa-var-google: "\f1a0"; +$fa-var-google-plus: "\f0d5"; +$fa-var-google-plus-square: "\f0d4"; +$fa-var-google-wallet: "\f1ee"; +$fa-var-graduation-cap: "\f19d"; +$fa-var-gratipay: "\f184"; +$fa-var-group: "\f0c0"; +$fa-var-h-square: "\f0fd"; +$fa-var-hacker-news: "\f1d4"; +$fa-var-hand-grab-o: "\f255"; +$fa-var-hand-lizard-o: "\f258"; +$fa-var-hand-o-down: "\f0a7"; +$fa-var-hand-o-left: "\f0a5"; +$fa-var-hand-o-right: "\f0a4"; +$fa-var-hand-o-up: "\f0a6"; +$fa-var-hand-paper-o: "\f256"; +$fa-var-hand-peace-o: "\f25b"; +$fa-var-hand-pointer-o: "\f25a"; +$fa-var-hand-rock-o: "\f255"; +$fa-var-hand-scissors-o: "\f257"; +$fa-var-hand-spock-o: "\f259"; +$fa-var-hand-stop-o: "\f256"; +$fa-var-hashtag: "\f292"; +$fa-var-hdd-o: "\f0a0"; +$fa-var-header: "\f1dc"; +$fa-var-headphones: "\f025"; +$fa-var-heart: "\f004"; +$fa-var-heart-o: "\f08a"; +$fa-var-heartbeat: "\f21e"; +$fa-var-history: "\f1da"; +$fa-var-home: "\f015"; +$fa-var-hospital-o: "\f0f8"; +$fa-var-hotel: "\f236"; +$fa-var-hourglass: "\f254"; +$fa-var-hourglass-1: "\f251"; +$fa-var-hourglass-2: "\f252"; +$fa-var-hourglass-3: "\f253"; +$fa-var-hourglass-end: "\f253"; +$fa-var-hourglass-half: "\f252"; +$fa-var-hourglass-o: "\f250"; +$fa-var-hourglass-start: "\f251"; +$fa-var-houzz: "\f27c"; +$fa-var-html5: "\f13b"; +$fa-var-i-cursor: "\f246"; +$fa-var-ils: "\f20b"; +$fa-var-image: "\f03e"; +$fa-var-inbox: "\f01c"; +$fa-var-indent: "\f03c"; +$fa-var-industry: "\f275"; +$fa-var-info: "\f129"; +$fa-var-info-circle: "\f05a"; +$fa-var-inr: "\f156"; +$fa-var-instagram: "\f16d"; +$fa-var-institution: "\f19c"; +$fa-var-internet-explorer: "\f26b"; +$fa-var-intersex: "\f224"; +$fa-var-ioxhost: "\f208"; +$fa-var-italic: "\f033"; +$fa-var-joomla: "\f1aa"; +$fa-var-jpy: "\f157"; +$fa-var-jsfiddle: "\f1cc"; +$fa-var-key: "\f084"; +$fa-var-keyboard-o: "\f11c"; +$fa-var-krw: "\f159"; +$fa-var-language: "\f1ab"; +$fa-var-laptop: "\f109"; +$fa-var-lastfm: "\f202"; +$fa-var-lastfm-square: "\f203"; +$fa-var-leaf: "\f06c"; +$fa-var-leanpub: "\f212"; +$fa-var-legal: "\f0e3"; +$fa-var-lemon-o: "\f094"; +$fa-var-level-down: "\f149"; +$fa-var-level-up: "\f148"; +$fa-var-life-bouy: "\f1cd"; +$fa-var-life-buoy: "\f1cd"; +$fa-var-life-ring: "\f1cd"; +$fa-var-life-saver: "\f1cd"; +$fa-var-lightbulb-o: "\f0eb"; +$fa-var-line-chart: "\f201"; +$fa-var-link: "\f0c1"; +$fa-var-linkedin: "\f0e1"; +$fa-var-linkedin-square: "\f08c"; +$fa-var-linux: "\f17c"; +$fa-var-list: "\f03a"; +$fa-var-list-alt: "\f022"; +$fa-var-list-ol: "\f0cb"; +$fa-var-list-ul: "\f0ca"; +$fa-var-location-arrow: "\f124"; +$fa-var-lock: "\f023"; +$fa-var-long-arrow-down: "\f175"; +$fa-var-long-arrow-left: "\f177"; +$fa-var-long-arrow-right: "\f178"; +$fa-var-long-arrow-up: "\f176"; +$fa-var-magic: "\f0d0"; +$fa-var-magnet: "\f076"; +$fa-var-mail-forward: "\f064"; +$fa-var-mail-reply: "\f112"; +$fa-var-mail-reply-all: "\f122"; +$fa-var-male: "\f183"; +$fa-var-map: "\f279"; +$fa-var-map-marker: "\f041"; +$fa-var-map-o: "\f278"; +$fa-var-map-pin: "\f276"; +$fa-var-map-signs: "\f277"; +$fa-var-mars: "\f222"; +$fa-var-mars-double: "\f227"; +$fa-var-mars-stroke: "\f229"; +$fa-var-mars-stroke-h: "\f22b"; +$fa-var-mars-stroke-v: "\f22a"; +$fa-var-maxcdn: "\f136"; +$fa-var-meanpath: "\f20c"; +$fa-var-medium: "\f23a"; +$fa-var-medkit: "\f0fa"; +$fa-var-meh-o: "\f11a"; +$fa-var-mercury: "\f223"; +$fa-var-microphone: "\f130"; +$fa-var-microphone-slash: "\f131"; +$fa-var-minus: "\f068"; +$fa-var-minus-circle: "\f056"; +$fa-var-minus-square: "\f146"; +$fa-var-minus-square-o: "\f147"; +$fa-var-mixcloud: "\f289"; +$fa-var-mobile: "\f10b"; +$fa-var-mobile-phone: "\f10b"; +$fa-var-modx: "\f285"; +$fa-var-money: "\f0d6"; +$fa-var-moon-o: "\f186"; +$fa-var-mortar-board: "\f19d"; +$fa-var-motorcycle: "\f21c"; +$fa-var-mouse-pointer: "\f245"; +$fa-var-music: "\f001"; +$fa-var-navicon: "\f0c9"; +$fa-var-neuter: "\f22c"; +$fa-var-newspaper-o: "\f1ea"; +$fa-var-object-group: "\f247"; +$fa-var-object-ungroup: "\f248"; +$fa-var-odnoklassniki: "\f263"; +$fa-var-odnoklassniki-square: "\f264"; +$fa-var-opencart: "\f23d"; +$fa-var-openid: "\f19b"; +$fa-var-opera: "\f26a"; +$fa-var-optin-monster: "\f23c"; +$fa-var-outdent: "\f03b"; +$fa-var-pagelines: "\f18c"; +$fa-var-paint-brush: "\f1fc"; +$fa-var-paper-plane: "\f1d8"; +$fa-var-paper-plane-o: "\f1d9"; +$fa-var-paperclip: "\f0c6"; +$fa-var-paragraph: "\f1dd"; +$fa-var-paste: "\f0ea"; +$fa-var-pause: "\f04c"; +$fa-var-pause-circle: "\f28b"; +$fa-var-pause-circle-o: "\f28c"; +$fa-var-paw: "\f1b0"; +$fa-var-paypal: "\f1ed"; +$fa-var-pencil: "\f040"; +$fa-var-pencil-square: "\f14b"; +$fa-var-pencil-square-o: "\f044"; +$fa-var-percent: "\f295"; +$fa-var-phone: "\f095"; +$fa-var-phone-square: "\f098"; +$fa-var-photo: "\f03e"; +$fa-var-picture-o: "\f03e"; +$fa-var-pie-chart: "\f200"; +$fa-var-pied-piper: "\f1a7"; +$fa-var-pied-piper-alt: "\f1a8"; +$fa-var-pinterest: "\f0d2"; +$fa-var-pinterest-p: "\f231"; +$fa-var-pinterest-square: "\f0d3"; +$fa-var-plane: "\f072"; +$fa-var-play: "\f04b"; +$fa-var-play-circle: "\f144"; +$fa-var-play-circle-o: "\f01d"; +$fa-var-plug: "\f1e6"; +$fa-var-plus: "\f067"; +$fa-var-plus-circle: "\f055"; +$fa-var-plus-square: "\f0fe"; +$fa-var-plus-square-o: "\f196"; +$fa-var-power-off: "\f011"; +$fa-var-print: "\f02f"; +$fa-var-product-hunt: "\f288"; +$fa-var-puzzle-piece: "\f12e"; +$fa-var-qq: "\f1d6"; +$fa-var-qrcode: "\f029"; +$fa-var-question: "\f128"; +$fa-var-question-circle: "\f059"; +$fa-var-quote-left: "\f10d"; +$fa-var-quote-right: "\f10e"; +$fa-var-ra: "\f1d0"; +$fa-var-random: "\f074"; +$fa-var-rebel: "\f1d0"; +$fa-var-recycle: "\f1b8"; +$fa-var-reddit: "\f1a1"; +$fa-var-reddit-alien: "\f281"; +$fa-var-reddit-square: "\f1a2"; +$fa-var-refresh: "\f021"; +$fa-var-registered: "\f25d"; +$fa-var-remove: "\f00d"; +$fa-var-renren: "\f18b"; +$fa-var-reorder: "\f0c9"; +$fa-var-repeat: "\f01e"; +$fa-var-reply: "\f112"; +$fa-var-reply-all: "\f122"; +$fa-var-retweet: "\f079"; +$fa-var-rmb: "\f157"; +$fa-var-road: "\f018"; +$fa-var-rocket: "\f135"; +$fa-var-rotate-left: "\f0e2"; +$fa-var-rotate-right: "\f01e"; +$fa-var-rouble: "\f158"; +$fa-var-rss: "\f09e"; +$fa-var-rss-square: "\f143"; +$fa-var-rub: "\f158"; +$fa-var-ruble: "\f158"; +$fa-var-rupee: "\f156"; +$fa-var-safari: "\f267"; +$fa-var-save: "\f0c7"; +$fa-var-scissors: "\f0c4"; +$fa-var-scribd: "\f28a"; +$fa-var-search: "\f002"; +$fa-var-search-minus: "\f010"; +$fa-var-search-plus: "\f00e"; +$fa-var-sellsy: "\f213"; +$fa-var-send: "\f1d8"; +$fa-var-send-o: "\f1d9"; +$fa-var-server: "\f233"; +$fa-var-share: "\f064"; +$fa-var-share-alt: "\f1e0"; +$fa-var-share-alt-square: "\f1e1"; +$fa-var-share-square: "\f14d"; +$fa-var-share-square-o: "\f045"; +$fa-var-shekel: "\f20b"; +$fa-var-sheqel: "\f20b"; +$fa-var-shield: "\f132"; +$fa-var-ship: "\f21a"; +$fa-var-shirtsinbulk: "\f214"; +$fa-var-shopping-bag: "\f290"; +$fa-var-shopping-basket: "\f291"; +$fa-var-shopping-cart: "\f07a"; +$fa-var-sign-in: "\f090"; +$fa-var-sign-out: "\f08b"; +$fa-var-signal: "\f012"; +$fa-var-simplybuilt: "\f215"; +$fa-var-sitemap: "\f0e8"; +$fa-var-skyatlas: "\f216"; +$fa-var-skype: "\f17e"; +$fa-var-slack: "\f198"; +$fa-var-sliders: "\f1de"; +$fa-var-slideshare: "\f1e7"; +$fa-var-smile-o: "\f118"; +$fa-var-soccer-ball-o: "\f1e3"; +$fa-var-sort: "\f0dc"; +$fa-var-sort-alpha-asc: "\f15d"; +$fa-var-sort-alpha-desc: "\f15e"; +$fa-var-sort-amount-asc: "\f160"; +$fa-var-sort-amount-desc: "\f161"; +$fa-var-sort-asc: "\f0de"; +$fa-var-sort-desc: "\f0dd"; +$fa-var-sort-down: "\f0dd"; +$fa-var-sort-numeric-asc: "\f162"; +$fa-var-sort-numeric-desc: "\f163"; +$fa-var-sort-up: "\f0de"; +$fa-var-soundcloud: "\f1be"; +$fa-var-space-shuttle: "\f197"; +$fa-var-spinner: "\f110"; +$fa-var-spoon: "\f1b1"; +$fa-var-spotify: "\f1bc"; +$fa-var-square: "\f0c8"; +$fa-var-square-o: "\f096"; +$fa-var-stack-exchange: "\f18d"; +$fa-var-stack-overflow: "\f16c"; +$fa-var-star: "\f005"; +$fa-var-star-half: "\f089"; +$fa-var-star-half-empty: "\f123"; +$fa-var-star-half-full: "\f123"; +$fa-var-star-half-o: "\f123"; +$fa-var-star-o: "\f006"; +$fa-var-steam: "\f1b6"; +$fa-var-steam-square: "\f1b7"; +$fa-var-step-backward: "\f048"; +$fa-var-step-forward: "\f051"; +$fa-var-stethoscope: "\f0f1"; +$fa-var-sticky-note: "\f249"; +$fa-var-sticky-note-o: "\f24a"; +$fa-var-stop: "\f04d"; +$fa-var-stop-circle: "\f28d"; +$fa-var-stop-circle-o: "\f28e"; +$fa-var-street-view: "\f21d"; +$fa-var-strikethrough: "\f0cc"; +$fa-var-stumbleupon: "\f1a4"; +$fa-var-stumbleupon-circle: "\f1a3"; +$fa-var-subscript: "\f12c"; +$fa-var-subway: "\f239"; +$fa-var-suitcase: "\f0f2"; +$fa-var-sun-o: "\f185"; +$fa-var-superscript: "\f12b"; +$fa-var-support: "\f1cd"; +$fa-var-table: "\f0ce"; +$fa-var-tablet: "\f10a"; +$fa-var-tachometer: "\f0e4"; +$fa-var-tag: "\f02b"; +$fa-var-tags: "\f02c"; +$fa-var-tasks: "\f0ae"; +$fa-var-taxi: "\f1ba"; +$fa-var-television: "\f26c"; +$fa-var-tencent-weibo: "\f1d5"; +$fa-var-terminal: "\f120"; +$fa-var-text-height: "\f034"; +$fa-var-text-width: "\f035"; +$fa-var-th: "\f00a"; +$fa-var-th-large: "\f009"; +$fa-var-th-list: "\f00b"; +$fa-var-thumb-tack: "\f08d"; +$fa-var-thumbs-down: "\f165"; +$fa-var-thumbs-o-down: "\f088"; +$fa-var-thumbs-o-up: "\f087"; +$fa-var-thumbs-up: "\f164"; +$fa-var-ticket: "\f145"; +$fa-var-times: "\f00d"; +$fa-var-times-circle: "\f057"; +$fa-var-times-circle-o: "\f05c"; +$fa-var-tint: "\f043"; +$fa-var-toggle-down: "\f150"; +$fa-var-toggle-left: "\f191"; +$fa-var-toggle-off: "\f204"; +$fa-var-toggle-on: "\f205"; +$fa-var-toggle-right: "\f152"; +$fa-var-toggle-up: "\f151"; +$fa-var-trademark: "\f25c"; +$fa-var-train: "\f238"; +$fa-var-transgender: "\f224"; +$fa-var-transgender-alt: "\f225"; +$fa-var-trash: "\f1f8"; +$fa-var-trash-o: "\f014"; +$fa-var-tree: "\f1bb"; +$fa-var-trello: "\f181"; +$fa-var-tripadvisor: "\f262"; +$fa-var-trophy: "\f091"; +$fa-var-truck: "\f0d1"; +$fa-var-try: "\f195"; +$fa-var-tty: "\f1e4"; +$fa-var-tumblr: "\f173"; +$fa-var-tumblr-square: "\f174"; +$fa-var-turkish-lira: "\f195"; +$fa-var-tv: "\f26c"; +$fa-var-twitch: "\f1e8"; +$fa-var-twitter: "\f099"; +$fa-var-twitter-square: "\f081"; +$fa-var-umbrella: "\f0e9"; +$fa-var-underline: "\f0cd"; +$fa-var-undo: "\f0e2"; +$fa-var-university: "\f19c"; +$fa-var-unlink: "\f127"; +$fa-var-unlock: "\f09c"; +$fa-var-unlock-alt: "\f13e"; +$fa-var-unsorted: "\f0dc"; +$fa-var-upload: "\f093"; +$fa-var-usb: "\f287"; +$fa-var-usd: "\f155"; +$fa-var-user: "\f007"; +$fa-var-user-md: "\f0f0"; +$fa-var-user-plus: "\f234"; +$fa-var-user-secret: "\f21b"; +$fa-var-user-times: "\f235"; +$fa-var-users: "\f0c0"; +$fa-var-venus: "\f221"; +$fa-var-venus-double: "\f226"; +$fa-var-venus-mars: "\f228"; +$fa-var-viacoin: "\f237"; +$fa-var-video-camera: "\f03d"; +$fa-var-vimeo: "\f27d"; +$fa-var-vimeo-square: "\f194"; +$fa-var-vine: "\f1ca"; +$fa-var-vk: "\f189"; +$fa-var-volume-down: "\f027"; +$fa-var-volume-off: "\f026"; +$fa-var-volume-up: "\f028"; +$fa-var-warning: "\f071"; +$fa-var-wechat: "\f1d7"; +$fa-var-weibo: "\f18a"; +$fa-var-weixin: "\f1d7"; +$fa-var-whatsapp: "\f232"; +$fa-var-wheelchair: "\f193"; +$fa-var-wifi: "\f1eb"; +$fa-var-wikipedia-w: "\f266"; +$fa-var-windows: "\f17a"; +$fa-var-won: "\f159"; +$fa-var-wordpress: "\f19a"; +$fa-var-wrench: "\f0ad"; +$fa-var-xing: "\f168"; +$fa-var-xing-square: "\f169"; +$fa-var-y-combinator: "\f23b"; +$fa-var-y-combinator-square: "\f1d4"; +$fa-var-yahoo: "\f19e"; +$fa-var-yc: "\f23b"; +$fa-var-yc-square: "\f1d4"; +$fa-var-yelp: "\f1e9"; +$fa-var-yen: "\f157"; +$fa-var-youtube: "\f167"; +$fa-var-youtube-play: "\f16a"; +$fa-var-youtube-square: "\f166"; + diff --git a/app/assets/sass/styles.scss b/app/assets/sass/styles.scss new file mode 100644 index 0000000..4dec9e5 --- /dev/null +++ b/app/assets/sass/styles.scss @@ -0,0 +1,16 @@ +@import "compass/reset"; +@import "font-awesome/styles"; + +@import "variables"; +@import "common"; +@import "buttons"; +@import "forms"; +@import "nav"; + +@import "plan"; +@import "plan-node"; +@import "menu"; +@import "page"; +@import "table"; +@import "modal"; +@import "footer"; diff --git a/app/assets/styles.css b/app/assets/styles.css new file mode 100644 index 0000000..ec0a393 --- /dev/null +++ b/app/assets/styles.css @@ -0,0 +1,4 @@ +html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{margin:0;padding:0;border:0;font:inherit;font-size:100%;vertical-align:baseline}html{line-height:1}ol,ul{list-style:none}table{border-collapse:collapse;border-spacing:0}caption,th,td{text-align:left;font-weight:normal;vertical-align:middle}q,blockquote{quotes:none}q:before,q:after,blockquote:before,blockquote:after{content:"";content:none}a img{border:none}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}/*! + * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url("../fonts/fontawesome-webfont.eot?v=4.5.0");src:url("../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0") format("embedded-opentype"),url("../fonts/fontawesome-webfont.woff2?v=4.5.0") format("woff2"),url("../fonts/fontawesome-webfont.woff?v=4.5.0") format("woff"),url("../fonts/fontawesome-webfont.ttf?v=4.5.0") format("truetype"),url("../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before{content:""}.fa-check-circle:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before{content:""}.fa-arrow-circle-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-hotel:before,.fa-bed:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-yc:before,.fa-y-combinator:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery-full:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-tv:before,.fa-television:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}html{height:100%}body{font-size:13px;font-weight:300;color:#4d525a;height:100%;width:100%;background-color:#f7f7f7;line-height:1.3}strong{font-weight:600}body,input,a,button,textarea{font-family:"noto sans";font-weight:300}.text-muted{color:#999ea7}.hero-container{margin:30px;font-size:22px;text-align:center}.pull-right{float:right}.align-right{text-align:right}a{color:#00B5E2;text-decoration:none}.fa{margin-right:3px}.clickable{cursor:pointer}.btn{border-radius:3px;padding:6px 10px;font-size:13px;line-height:1.2;text-decoration:none;text-transform:uppercase}.btn-default{border:1px solid #00B5E2;color:#00B5E2;background-color:#fff}.btn-default:hover{background-color:#65DDFB}.btn-lg{padding:6px 20px;font-size:16px}.btn-danger{border:1px solid #AF2F11;color:#AF2F11;text-transform:uppercase;background-color:#fff}.btn-danger:hover{background-color:#FB8165}.btn-primary{border:0;background:#00B5E2;box-shadow:1px 1px 1px #ababab;color:#ffffff}.btn-primary:hover{background:#008CAF}.input-box:-moz-placeholder{color:#ababab;font-size:18px}.input-box::-moz-placeholder{color:#ababab;font-size:18px}.input-box:-ms-input-placeholder{color:#ababab;font-size:18px}.input-box::-webkit-input-placeholder{color:#ababab;font-size:18px}.input-box:focus{box-shadow:0 0 5px #51cbee}.input-box-main{font-size:18px;width:700px;border:0;border-bottom:2px solid #00B5E2;padding:10px;margin-top:10px;margin-bottom:10px}.input-box-lg{width:100%;height:280px;margin-bottom:6px;margin-bottom:10px;border-radius:3px;border:1px solid #dedede;padding:10px}nav{font-size:17px;background-color:#fff;padding:15px}nav .nav-container{width:1000px;margin:auto}.plan{padding-bottom:30px;overflow:auto;height:100%;width:100%}.plan ul{display:flex;padding-top:12px;position:relative;margin:auto;transition:all 0.5s;margin-top:-5px}.plan ul ul::before{content:'';position:absolute;top:0;left:50%;border-left:2px solid #c4c4c4;height:12px;width:0}.plan ul li{float:left;text-align:center;list-style-type:none;position:relative;padding:12px 3px 0 3px;transition:all 0.5s}.plan ul li:before,.plan ul li:after{content:'';position:absolute;top:0;right:50%;border-top:2px solid #c4c4c4;width:50%;height:12px}.plan ul li:after{right:auto;left:50%;border-left:2px solid #c4c4c4}.plan ul li:only-child{padding-top:0}.plan ul li:only-child:after,.plan ul li:only-child:before{display:none}.plan ul li:first-child::before,.plan ul li:last-child::after{border:0 none}.plan ul li:last-child::before{border-right:2px solid #c4c4c4;border-radius:0 6px 0 0}.plan ul li:first-child::after{border-radius:6px 0 0 0}.plan ul li .plan-node:hover+ul::before{border-color:#00B5E2}.plan ul li .plan-node:hover+ul li::after,.plan ul li .plan-node:hover+ul li::before,.plan ul li .plan-node:hover+ul ul::before{border-color:#008CAF}.plan-stats{display:flex;font-size:13px;margin:0 auto 10px auto;padding-bottom:10px;border-bottom:1px solid #dedede;border-radius:12px;width:650px;position:relative}.plan-stats div{padding-right:10px;flex-grow:1}.plan-stats .stat-value{display:block;text-align:center;font-size:17px}.plan-stats .stat-label{display:block;text-align:center;font-size:12px}.plan-stats:after{content:'';position:absolute;top:100%;left:50%;margin-left:-9px;width:0;height:0;border-top:solid 9px #dedede;border-left:solid 9px transparent;border-right:solid 9px transparent}.plan-node{text-decoration:none;color:#4d525a;display:inline-block;transition:all 0.5s;position:relative;padding:6px 10px;background-color:#fff;font-size:12px;border:2px solid #dedede;border-radius:3px;overflow:hidden;overflow-wrap:break-word;word-wrap:break-word;word-break:break-all;min-width:220px}.plan-node header{margin-bottom:6px;overflow:hidden;cursor:pointer}.plan-node header:hover{background-color:#f7f7f7}.plan-node header h4{font-size:13px;float:left;font-weight:600}.plan-node header .node-duration{float:right;margin-left:10px;font-size:13px}.plan-node .prop-list{float:left;text-align:left;width:400px;overflow-wrap:break-word;word-wrap:break-word;word-break:break-all;margin-top:10px;margin-bottom:6px}.plan-node .relation-name{text-align:left;max-width:220px}.plan-node .planner-estimate{float:left;clear:both;text-align:left;margin-top:6px;width:100%}.plan-node .tags{margin-top:6px;text-align:left}.plan-node .tags span{display:inline-block;background-color:#FB4418;color:#fff;font-size:10px;font-weight:600;margin-right:3px;padding:3px;border-radius:3px;line-height:1.1}.plan-node:hover{border-color:#00B5E2}.node-bar-container{float:left;height:5px;margin-top:6px;margin-left:auto;margin-right:auto;border:1px solid #dedede;border-radius:3px;color:#fff;background-color:#454545;position:relative}.node-bar-container .node-bar{height:100%;text-align:left;position:absolute;left:0;top:0}.node-bar-label{text-align:left;display:block}.menu{width:190px;height:200px;position:absolute;font-size:12px;top:115px;left:0;background-color:#777;box-shadow:1px 1px 2px 1px rgba(0,0,0,0.5);color:#fff;border-top-right-radius:3px;border-bottom-right-radius:3px;z-index:1;transition:all 0.3s}.menu header h3{padding-top:10px;margin-bottom:20px;font-size:16px;font-weight:600;line-height:2;text-align:right;padding-right:20px}.menu ul{margin-left:10px}.menu ul li{line-height:2.5}.menu-toggle{font-size:26px;float:left;padding-left:10px;line-height:2;cursor:pointer}.menu-hidden{width:50px;height:50px;border-top-right-radius:50%;border-bottom-right-radius:50%}.menu-hidden ul,.menu-hidden h3{visibility:hidden}.menu button{border:1px solid #fff;background-color:#544D4D;color:#fff;padding:2px;border-radius:3px}.page,.page-stretch{padding-top:10px;margin:auto;width:1000px;min-height:600px}.page h2,.page-stretch h2{font-size:26px;margin-bottom:6px}.page-stretch{margin:auto;width:95%}.table{width:100%}.table td{border-bottom:1px solid #dedede;padding:6px}.table tr:hover{background-color:#f7f7f7}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000;opacity:0.7}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050}.modal .modal-dialog{position:relative;transform:translate(0);margin:30px auto;width:500px;opacity:1}.modal .modal-dialog .modal-content{padding:30px;position:relative;background-color:#fff;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;outline:0;box-shadow:0 3px 9px rgba(0,0,0,0.5);display:block}.modal .modal-dialog .modal-content .modal-body{padding:3px}.modal .modal-dialog .modal-content .modal-footer{text-align:right}.modal .modal-dialog .modal-content .modal-footer button{margin-left:3px}footer{border-top:2px solid #dedede;margin:20px;padding:20px;color:#ababab;text-align:center;width:600px;margin:auto}footer .fa{font-size:17px;margin-left:6px} diff --git a/app/bootstrap.ts b/app/bootstrap.ts new file mode 100644 index 0000000..b3303f3 --- /dev/null +++ b/app/bootstrap.ts @@ -0,0 +1,9 @@ +import {provide} from 'angular2/core'; +import {bootstrap} from 'angular2/platform/browser'; +import {ROUTER_PROVIDERS, LocationStrategy, HashLocationStrategy} from 'angular2/router'; +import {App} from './components/app/app'; + +bootstrap(App, [ + ROUTER_PROVIDERS, + provide(LocationStrategy, { useClass: HashLocationStrategy }) +]); diff --git a/app/components/app/app.html b/app/components/app/app.html new file mode 100644 index 0000000..a8a1969 --- /dev/null +++ b/app/components/app/app.html @@ -0,0 +1,6 @@ + + diff --git a/app/components/app/app.ts b/app/components/app/app.ts new file mode 100644 index 0000000..f9c83e0 --- /dev/null +++ b/app/components/app/app.ts @@ -0,0 +1,22 @@ +import {Component, ViewEncapsulation} from 'angular2/core'; +import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router'; + +import {PlanView} from '../plan-view/plan-view'; +import {PlanList} from '../plan-list/plan-list'; +import {PlanNew} from '../plan-new/plan-new'; + +@Component({ + selector: 'app', + templateUrl: './components/app/app.html', + encapsulation: ViewEncapsulation.None, + directives: [ROUTER_DIRECTIVES] +}) + +@RouteConfig([ + { path: '/', redirectTo: ['/PlanList'] }, + { path: '/plans', component: PlanList, name: 'PlanList' }, + { path: '/plans/new', component: PlanNew, name: 'PlanNew' }, + { path: '/plans/:id', component: PlanView, name: 'PlanView' } +]) + +export class App { } diff --git a/app/components/plan-list/plan-list.html b/app/components/plan-list/plan-list.html new file mode 100644 index 0000000..a2f1a9d --- /dev/null +++ b/app/components/plan-list/plan-list.html @@ -0,0 +1,38 @@ + + +
+
+ Welcome to PEV! Please submit a plan for visualization +
+ + + + + + + +
{{plan.name}}created on {{plan.createdOn | momentDate }} + +
+ + + +
+
+
diff --git a/app/components/plan-list/plan-list.ts b/app/components/plan-list/plan-list.ts new file mode 100644 index 0000000..888e2e2 --- /dev/null +++ b/app/components/plan-list/plan-list.ts @@ -0,0 +1,47 @@ +import {Component, OnInit} from 'angular2/core'; +import {ROUTER_DIRECTIVES} from 'angular2/router'; + +import {IPlan} from '../../interfaces/iplan'; +import {PlanService} from '../../services/plan-service'; +import {PlanNew} from '../plan-new/plan-new'; + +import {MomentDatePipe} from '../../pipes'; + +@Component({ + selector: 'plan-list', + templateUrl: './components/plan-list/plan-list.html', + providers: [PlanService], + directives: [ROUTER_DIRECTIVES, PlanNew], + pipes: [MomentDatePipe] +}) +export class PlanList { + plans: Array; + newPlanName: string; + newPlanContent: any; + newPlanId: string; + openDialog: boolean = false; + + constructor(private _planService: PlanService) { } + + ngOnInit() { + this.plans = this._planService.getPlans(); + } + + requestDelete() { + this.openDialog = true; + } + + deletePlan(plan) { + this.openDialog = false; + this._planService.deletePlan(plan); + this.plans = this._planService.getPlans(); + } + + cancelDelete() { + this.openDialog = false; + } + + deleteAllPlans() { + this._planService.deleteAllPlans(); + } +} diff --git a/app/components/plan-new/plan-new.html b/app/components/plan-new/plan-new.html new file mode 100644 index 0000000..f9babb6 --- /dev/null +++ b/app/components/plan-new/plan-new.html @@ -0,0 +1,18 @@ + + +
+ For best results, use EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON) +
+ + +
+ + + +
diff --git a/app/components/plan-new/plan-new.ts b/app/components/plan-new/plan-new.ts new file mode 100644 index 0000000..4a748ea --- /dev/null +++ b/app/components/plan-new/plan-new.ts @@ -0,0 +1,26 @@ +import {Component, OnInit} from 'angular2/core'; +import {Router, ROUTER_DIRECTIVES} from 'angular2/router'; +import {IPlan} from '../../interfaces/iplan'; + +import {PlanService} from '../../services/plan-service'; + +@Component({ + selector: 'plan-new', + templateUrl: './components/plan-new/plan-new.html', + providers: [PlanService], + directives: [ROUTER_DIRECTIVES] +}) +export class PlanNew { + planIds: string[]; + newPlanName: string; + newPlanContent: string; + newPlanQuery: string; + newPlan: IPlan; + + constructor( private _router: Router, private _planService: PlanService) { } + + submitPlan() { + this.newPlan = this._planService.createPlan(this.newPlanName, this.newPlanContent, this.newPlanQuery); + this._router.navigate( ['PlanView', { id: this.newPlan.id }] ); + } +} diff --git a/app/components/plan-node/plan-node.html b/app/components/plan-node/plan-node.html new file mode 100644 index 0000000..49380a2 --- /dev/null +++ b/app/components/plan-node/plan-node.html @@ -0,0 +1,50 @@ +
+
+

{{node['Node Type'] | uppercase}}

+ {{duration}}{{durationUnit}} | + {{executionTimePercent}}% + +
+ +
on + {{node['Schema']}}.{{node['Relation Name']}} + ({{node['Alias']}}) +
+ +
by {{node['Group Key']}}
+
by {{node['Sort Key']}}
+
{{node['Join Type']}} join
+
using {{node['Index Name']}}
+ +
+ {{tag}} +
+ +
+
+ +
+ + {{viewOptions.highlightType}}: {{highlightValue | number:'.0-2'}} + +
+ +
+ over estimated rows + under estimated rows + by {{plannerRowEstimateValue}}x +
+ + + + + + +
{{prop.key}}{{prop.value}}
+
+ + diff --git a/app/components/plan-node/plan-node.ts b/app/components/plan-node/plan-node.ts new file mode 100644 index 0000000..3562ea6 --- /dev/null +++ b/app/components/plan-node/plan-node.ts @@ -0,0 +1,176 @@ +import {Component, OnInit} from 'angular2/core'; +import {HighlightType, EstimateDirection} from '../../enums'; +import {PlanService} from '../../services/plan-service'; +/// + +@Component({ + selector: 'plan-node', + inputs: ['node', 'planStats', 'viewOptions'], + templateUrl: './components/plan-node/plan-node.html', + directives: [PlanNode], + providers: [PlanService] +}) + +export class PlanNode { + // consts + MAX_WIDTH: number = 220; + MIN_ESTIMATE_MISS: number = 100; + COSTLY_TAG: string = 'costliest'; + SLOW_TAG: string = 'slowest'; + LARGE_TAG: string = 'largest'; + ESTIMATE_TAG: string = 'bad estimate'; + + // inputs + node: any; + planStats: any; + viewOptions: any; + + // calculated properties + duration: string; + durationUnit: string; + executionTimePercent: number; + backgroundColor: string; + highlightValue: number; + width: number; + props: Array; + tags: Array; + plannerRowEstimateValue: number; + plannerRowEstimateDirection: EstimateDirection; + currentHighlightType: string; + + // expose enum to view + estimateDirections = EstimateDirection; + highlightTypes = HighlightType; + + constructor(private _planService: PlanService) { } + + ngOnInit() { + this.currentHighlightType = this.viewOptions.highlightType; + this.calculateBar(); + this.calculateProps(); + this.calculateDuration(); + this.calculateTags(); + + this.plannerRowEstimateDirection = this.node[this._planService.PLANNER_ESIMATE_DIRECTION]; + this.plannerRowEstimateValue = _.round(this.node[this._planService.PLANNER_ESTIMATE_FACTOR]); + } + + ngDoCheck() { + // console.log("check", this.currentHighlightType, this.viewOptions.highlightType); + if (this.currentHighlightType !== this.viewOptions.highlightType) { + this.currentHighlightType = this.viewOptions.highlightType; + this.calculateBar(); + } + } + + calculateBar() { + switch (this.currentHighlightType) { + case HighlightType.DURATION: + this.highlightValue = (this.node[this._planService.ACTUAL_DURATION_PROP]); + this.width = Math.round((this.highlightValue / this.planStats.maxDuration) * this.MAX_WIDTH); + break; + case HighlightType.ROWS: + this.highlightValue = (this.node[this._planService.ACTUAL_ROWS_PROP]); + this.width = Math.round((this.highlightValue / this.planStats.maxRows) * this.MAX_WIDTH); + break; + case HighlightType.COST: + this.highlightValue = (this.node[this._planService.ACTUAL_COST_PROP]); + this.width = Math.round((this.highlightValue / this.planStats.maxCost) * this.MAX_WIDTH); + break; + } + + if (this.width < 1) { this.width = 1 } + this.backgroundColor = this.numberToColorHsl(1 - this.width / this.MAX_WIDTH); + } + + calculateDuration() { + var dur: number = _.round(this.node[this._planService.ACTUAL_DURATION_PROP]); + // convert duration into approriate units + if (dur < 1) { + this.duration = "<1"; + this.durationUnit = 'ms'; + } else if (dur > 1 && dur < 1000) { + this.duration = dur.toString(); + this.durationUnit = 'ms'; + } else { + this.duration = _.round(dur / 1000, 2).toString(); + this.durationUnit = 'mins'; + } + this.executionTimePercent = (_.round((dur / this.planStats.executionTime) * 100)); + } + + // create an array of node propeties so that they can be displayed in the view + calculateProps() { + this.props = _.chain(this.node) + .omit('Plans') + .map((value, key) => { + return { key: key, value: value }; + }) + .value(); + } + + calculateTags() { + this.tags = []; + if (this.node[this._planService.SLOWEST_NODE_PROP]) { + this.tags.push(this.SLOW_TAG); + } + if (this.node[this._planService.COSTLIEST_NODE_PROP]) { + this.tags.push(this.COSTLY_TAG); + } + if (this.node[this._planService.LARGEST_NODE_PROP]) { + this.tags.push(this.LARGE_TAG); + } + if (this.node[this._planService.PLANNER_ESTIMATE_FACTOR] >= this.MIN_ESTIMATE_MISS) { + this.tags.push(this.ESTIMATE_TAG); + } + } + + /** + * http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion + * + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + * + * @param Number h The hue + * @param Number s The saturation + * @param Number l The lightness + * @return Array The RGB representation + */ + hslToRgb(h, s, l) { + var r, g, b; + + if (s == 0) { + r = g = b = l; // achromatic + } else { + function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; + } + + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + + return [Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)]; + } + + // convert a number to a color using hsl + numberToColorHsl(i) { + // as the function expects a value between 0 and 1, and red = 0° and green = 120° + // we convert the input to the appropriate hue value + var hue = i * 100 * 1.2 / 360; + // we convert hsl to rgb (saturation 100%, lightness 50%) + var rgb = this.hslToRgb(hue, .9, .4); + // we format to css value and return + return 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')'; + } +} diff --git a/app/components/plan-view/plan-view.html b/app/components/plan-view/plan-view.html new file mode 100644 index 0000000..ab9b7dc --- /dev/null +++ b/app/components/plan-view/plan-view.html @@ -0,0 +1,72 @@ + + + + +
+
+
+ {{executionTime}} + execution time ({{executionTimeUnit}}) +
+
+ {{planStats.planningTime | number:'.0-2'}} + planning time (ms) +
+
+ {{planStats.maxDuration | number:'.0-2'}} + slowest node (ms) +
+
+ {{planStats.maxRows | number:'.0-2'}} + largest node (rows) +
+
+ {{planStats.maxCost | number:'.0-2'}} + costliest node +
+
+ +
+
    +
  • + +
  • +
+
+
diff --git a/app/components/plan-view/plan-view.ts b/app/components/plan-view/plan-view.ts new file mode 100644 index 0000000..ab4e6a3 --- /dev/null +++ b/app/components/plan-view/plan-view.ts @@ -0,0 +1,96 @@ +import {Component, OnInit} from 'angular2/core'; +import {RouteParams} from 'angular2/router'; +import {ROUTER_DIRECTIVES} from 'angular2/router'; + +import {IPlan} from '../../interfaces/iplan'; +import {PlanService} from '../../services/plan-service'; +import {HighlightType} from '../../enums'; +import {PlanNode} from '../plan-node/plan-node'; + +@Component({ + selector: 'plan-view', + templateUrl: './components/plan-view/plan-view.html', + directives: [ROUTER_DIRECTIVES, PlanNode], + providers: [PlanService] +}) +export class PlanView { + id: string; + plan: IPlan; + rootContainer: any; + executionTime: string; + executionTimeUnit: string; + hideMenu: boolean = true; + + planStats: any = { + executionTime: 0, + maxRows: 0, + maxCost: 0, + maxDuration: 0 + }; + + viewOptions: any = { + showPlanStats: true, + showHighlightBar: true, + showPlannerEstimate: false, + showTags: true, + highlightType: HighlightType.NONE + }; + + showPlannerEstimate: boolean = true; + showMenu: boolean = false; + + highlightTypes = HighlightType; // exposing the enum to the view + + constructor(private _planService: PlanService, routeParams: RouteParams) { + this.id = routeParams.get('id'); + } + + getPlan() { + if (!this.id) { + return; + } + + this.plan = this._planService.getPlan(this.id); + this.rootContainer = this.plan.content; + + var executionTime: number = this.rootContainer['Execution Time'] || this.rootContainer['Total Runtime']; + [this.executionTime, this.executionTimeUnit] = this.calculateDuration(executionTime); + + this.planStats = { + executionTime: executionTime, + planningTime: this.rootContainer['Planning Time'], + maxRows: this.rootContainer[this._planService.MAXIMUM_ROWS_PROP], + maxCost: this.rootContainer[this._planService.MAXIMUM_COSTS_PROP], + maxDuration: this.rootContainer[this._planService.MAXIMUM_DURATION_PROP] + } + } + + ngOnInit() { + this.getPlan(); + } + + toggleHighlight(type: HighlightType) { + this.viewOptions.highlightType = type; + } + + analyzePlan() { + this._planService.analyzePlan(this.plan); + } + + calculateDuration(originalValue: number) { + var duration: string = ''; + var unit: string = ''; + + if (originalValue < 1) { + duration = "<1"; + unit = 'ms'; + } else if (originalValue > 1 && originalValue < 1000) { + duration = originalValue.toString(); + unit = 'ms'; + } else { + duration = _.round(originalValue / 1000, 2).toString(); + unit = 'mins'; + } + return [duration, unit]; + } +} diff --git a/app/enums.ts b/app/enums.ts new file mode 100644 index 0000000..d7784d8 --- /dev/null +++ b/app/enums.ts @@ -0,0 +1,11 @@ +export class HighlightType { + static NONE: string = "none"; + static DURATION: string = "duration"; + static ROWS: string = "rows"; + static COST: string = "cost"; +} + +export enum EstimateDirection { + over, + under +} diff --git a/app/index.html b/app/index.html new file mode 100644 index 0000000..a43262e --- /dev/null +++ b/app/index.html @@ -0,0 +1,32 @@ + + + + + + <%= APP_TITLE %> + + + + + + + + Loading... + + + + + + + + + + + + + + + + diff --git a/app/interfaces/iplan.ts b/app/interfaces/iplan.ts new file mode 100644 index 0000000..bd08478 --- /dev/null +++ b/app/interfaces/iplan.ts @@ -0,0 +1,7 @@ +export interface IPlan { + id: string; + name: string; + content: any; + query: string; + createdOn: Date; +} diff --git a/app/pipes.ts b/app/pipes.ts new file mode 100644 index 0000000..d9f7ab5 --- /dev/null +++ b/app/pipes.ts @@ -0,0 +1,10 @@ +import {Pipe} from 'angular2/core'; +/// + +@Pipe({name: 'momentDate'}) + +export class MomentDatePipe { + transform(value:string, args:string[]) : any { + return moment(value).format('LLLL'); + } +} diff --git a/app/sample-plans/plan1.json b/app/sample-plans/plan1.json new file mode 100644 index 0000000..3be6615 --- /dev/null +++ b/app/sample-plans/plan1.json @@ -0,0 +1,334 @@ +[ + { + "Plan": { + "Node Type": "Limit", + "Startup Cost": 60970.08, + "Total Cost": 60970.11, + "Plan Rows": 10, + "Plan Width": 468, + "Actual Startup Time": 1941.751, + "Actual Total Time": 1941.780, + "Actual Rows": 10, + "Actual Loops": 1, + "Output": ["a.id", "(COALESCE(a.display_name, a.alert_name))", "a.external_alert_id", "a.venue_code", "altt.code", "altt.name", "altst.name", "a.stop_type", "a.deploy_mode", "a.estimated_cost", "(sum(s.total_alerts))", "(sum(s.total_followed))", "(sum(s.total_overridden))", "(sum(s.total_ignored))", "(sum(s.total_not_seen))", "(sum(s.total_unknown))", "(percenttotal((sum(s.total_followed)), (sum(s.total_alerts))))", "(percenttotal((sum(s.total_overridden)), (sum(s.total_alerts))))", "(percenttotal((sum(s.total_ignored)), (sum(s.total_alerts))))", "(percenttotal((sum(s.total_not_seen)), (sum(s.total_alerts))))", "(percenttotal((sum(s.total_unknown)), (sum(s.total_alerts))))", "(((sum(s.total_alerts)) * a.estimated_cost))"], + "Shared Hit Blocks": 260587, + "Shared Read Blocks": 0, + "Shared Dirtied Blocks": 0, + "Shared Written Blocks": 0, + "Local Hit Blocks": 0, + "Local Read Blocks": 0, + "Local Dirtied Blocks": 0, + "Local Written Blocks": 0, + "Temp Read Blocks": 0, + "Temp Written Blocks": 0, + "I/O Read Time": 0.000, + "I/O Write Time": 0.000, + "Plans": [ + { + "Node Type": "Sort", + "Parent Relationship": "Outer", + "Startup Cost": 60970.08, + "Total Cost": 60977.40, + "Plan Rows": 2927, + "Plan Width": 468, + "Actual Startup Time": 1941.747, + "Actual Total Time": 1941.757, + "Actual Rows": 10, + "Actual Loops": 1, + "Output": ["a.id", "(COALESCE(a.display_name, a.alert_name))", "a.external_alert_id", "a.venue_code", "altt.code", "altt.name", "altst.name", "a.stop_type", "a.deploy_mode", "a.estimated_cost", "(sum(s.total_alerts))", "(sum(s.total_followed))", "(sum(s.total_overridden))", "(sum(s.total_ignored))", "(sum(s.total_not_seen))", "(sum(s.total_unknown))", "(percenttotal((sum(s.total_followed)), (sum(s.total_alerts))))", "(percenttotal((sum(s.total_overridden)), (sum(s.total_alerts))))", "(percenttotal((sum(s.total_ignored)), (sum(s.total_alerts))))", "(percenttotal((sum(s.total_not_seen)), (sum(s.total_alerts))))", "(percenttotal((sum(s.total_unknown)), (sum(s.total_alerts))))", "(((sum(s.total_alerts)) * a.estimated_cost))"], + "Sort Key": ["(sum(s.total_alerts))"], + "Sort Method": "top-N heapsort", + "Sort Space Used": 27, + "Sort Space Type": "Memory", + "Shared Hit Blocks": 260587, + "Shared Read Blocks": 0, + "Shared Dirtied Blocks": 0, + "Shared Written Blocks": 0, + "Local Hit Blocks": 0, + "Local Read Blocks": 0, + "Local Dirtied Blocks": 0, + "Local Written Blocks": 0, + "Temp Read Blocks": 0, + "Temp Written Blocks": 0, + "I/O Read Time": 0.000, + "I/O Write Time": 0.000, + "Plans": [ + { + "Node Type": "Nested Loop", + "Parent Relationship": "Outer", + "Join Type": "Left", + "Startup Cost": 37552.36, + "Total Cost": 60906.83, + "Plan Rows": 2927, + "Plan Width": 468, + "Actual Startup Time": 522.250, + "Actual Total Time": 1865.979, + "Actual Rows": 32352, + "Actual Loops": 1, + "Output": ["a.id", "COALESCE(a.display_name, a.alert_name)", "a.external_alert_id", "a.venue_code", "altt.code", "altt.name", "altst.name", "a.stop_type", "a.deploy_mode", "a.estimated_cost", "(sum(s.total_alerts))", "(sum(s.total_followed))", "(sum(s.total_overridden))", "(sum(s.total_ignored))", "(sum(s.total_not_seen))", "(sum(s.total_unknown))", "percenttotal((sum(s.total_followed)), (sum(s.total_alerts)))", "percenttotal((sum(s.total_overridden)), (sum(s.total_alerts)))", "percenttotal((sum(s.total_ignored)), (sum(s.total_alerts)))", "percenttotal((sum(s.total_not_seen)), (sum(s.total_alerts)))", "percenttotal((sum(s.total_unknown)), (sum(s.total_alerts)))", "((sum(s.total_alerts)) * a.estimated_cost)"], + "Shared Hit Blocks": 260587, + "Shared Read Blocks": 0, + "Shared Dirtied Blocks": 0, + "Shared Written Blocks": 0, + "Local Hit Blocks": 0, + "Local Read Blocks": 0, + "Local Dirtied Blocks": 0, + "Local Written Blocks": 0, + "Temp Read Blocks": 0, + "Temp Written Blocks": 0, + "I/O Read Time": 0.000, + "I/O Write Time": 0.000, + "Plans": [ + { + "Node Type": "Nested Loop", + "Parent Relationship": "Outer", + "Join Type": "Left", + "Startup Cost": 37552.22, + "Total Cost": 56750.41, + "Plan Rows": 2927, + "Plan Width": 461, + "Actual Startup Time": 522.141, + "Actual Total Time": 1161.187, + "Actual Rows": 32352, + "Actual Loops": 1, + "Output": ["(sum(s.total_alerts))", "(sum(s.total_followed))", "(sum(s.total_overridden))", "(sum(s.total_ignored))", "(sum(s.total_not_seen))", "(sum(s.total_unknown))", "a.id", "a.display_name", "a.alert_name", "a.external_alert_id", "a.venue_code", "a.stop_type", "a.deploy_mode", "a.estimated_cost", "a.subtype_id", "altt.code", "altt.name"], + "Shared Hit Blocks": 197115, + "Shared Read Blocks": 0, + "Shared Dirtied Blocks": 0, + "Shared Written Blocks": 0, + "Local Hit Blocks": 0, + "Local Read Blocks": 0, + "Local Dirtied Blocks": 0, + "Local Written Blocks": 0, + "Temp Read Blocks": 0, + "Temp Written Blocks": 0, + "I/O Read Time": 0.000, + "I/O Write Time": 0.000, + "Plans": [ + { + "Node Type": "Nested Loop", + "Parent Relationship": "Outer", + "Join Type": "Inner", + "Startup Cost": 37552.07, + "Total Cost": 56230.56, + "Plan Rows": 2927, + "Plan Width": 405, + "Actual Startup Time": 522.131, + "Actual Total Time": 918.895, + "Actual Rows": 32352, + "Actual Loops": 1, + "Output": ["(sum(s.total_alerts))", "(sum(s.total_followed))", "(sum(s.total_overridden))", "(sum(s.total_ignored))", "(sum(s.total_not_seen))", "(sum(s.total_unknown))", "a.id", "a.display_name", "a.alert_name", "a.external_alert_id", "a.venue_code", "a.stop_type", "a.deploy_mode", "a.estimated_cost", "a.type_id", "a.subtype_id"], + "Shared Hit Blocks": 132411, + "Shared Read Blocks": 0, + "Shared Dirtied Blocks": 0, + "Shared Written Blocks": 0, + "Local Hit Blocks": 0, + "Local Read Blocks": 0, + "Local Dirtied Blocks": 0, + "Local Written Blocks": 0, + "Temp Read Blocks": 0, + "Temp Written Blocks": 0, + "I/O Read Time": 0.000, + "I/O Write Time": 0.000, + "Plans": [ + { + "Node Type": "Aggregate", + "Strategy": "Hashed", + "Parent Relationship": "Outer", + "Startup Cost": 37551.64, + "Total Cost": 37624.82, + "Plan Rows": 2927, + "Plan Width": 56, + "Actual Startup Time": 522.107, + "Actual Total Time": 587.536, + "Actual Rows": 32352, + "Actual Loops": 1, + "Output": ["s.alert_id", "sum(s.total_alerts)", "sum(s.total_followed)", "sum(s.total_overridden)", "sum(s.total_ignored)", "sum(s.total_not_seen)", "sum(s.total_unknown)"], + "Group Key": ["s.alert_id"], + "Shared Hit Blocks": 2784, + "Shared Read Blocks": 0, + "Shared Dirtied Blocks": 0, + "Shared Written Blocks": 0, + "Local Hit Blocks": 0, + "Local Read Blocks": 0, + "Local Dirtied Blocks": 0, + "Local Written Blocks": 0, + "Temp Read Blocks": 0, + "Temp Written Blocks": 0, + "I/O Read Time": 0.000, + "I/O Write Time": 0.000, + "Plans": [ + { + "Node Type": "Bitmap Heap Scan", + "Parent Relationship": "Outer", + "Relation Name": "alert_daily_summaries", + "Schema": "analytics", + "Alias": "s", + "Startup Cost": 3265.35, + "Total Cost": 35524.50, + "Plan Rows": 115837, + "Plan Width": 56, + "Actual Startup Time": 10.745, + "Actual Total Time": 165.318, + "Actual Rows": 140451, + "Actual Loops": 1, + "Output": ["s.id", "s.org_id", "s.alert_id", "s.alert_day", "s.total_alerts", "s.total_followed", "s.total_overridden", "s.total_ignored", "s.total_not_seen", "s.total_unknown", "s.override_comments"], + "Recheck Cond": "((s.org_id = 2) AND (s.alert_day >= '2015-10-01 00:00:00'::timestamp without time zone) AND (s.alert_day <= '2015-12-01 00:00:00'::timestamp without time zone))", + "Rows Removed by Index Recheck": 0, + "Exact Heap Blocks": 2243, + "Lossy Heap Blocks": 0, + "Shared Hit Blocks": 2784, + "Shared Read Blocks": 0, + "Shared Dirtied Blocks": 0, + "Shared Written Blocks": 0, + "Local Hit Blocks": 0, + "Local Read Blocks": 0, + "Local Dirtied Blocks": 0, + "Local Written Blocks": 0, + "Temp Read Blocks": 0, + "Temp Written Blocks": 0, + "I/O Read Time": 0.000, + "I/O Write Time": 0.000, + "Plans": [ + { + "Node Type": "Bitmap Index Scan", + "Parent Relationship": "Outer", + "Index Name": "alert_daily_summaries_org_id_alert_day_idx", + "Startup Cost": 0.00, + "Total Cost": 3236.39, + "Plan Rows": 115837, + "Plan Width": 0, + "Actual Startup Time": 10.445, + "Actual Total Time": 10.445, + "Actual Rows": 140451, + "Actual Loops": 1, + "Index Cond": "((s.org_id = 2) AND (s.alert_day >= '2015-10-01 00:00:00'::timestamp without time zone) AND (s.alert_day <= '2015-12-01 00:00:00'::timestamp without time zone))", + "Shared Hit Blocks": 541, + "Shared Read Blocks": 0, + "Shared Dirtied Blocks": 0, + "Shared Written Blocks": 0, + "Local Hit Blocks": 0, + "Local Read Blocks": 0, + "Local Dirtied Blocks": 0, + "Local Written Blocks": 0, + "Temp Read Blocks": 0, + "Temp Written Blocks": 0, + "I/O Read Time": 0.000, + "I/O Write Time": 0.000 + } + ] + } + ] + }, + { + "Node Type": "Index Scan", + "Parent Relationship": "Inner", + "Scan Direction": "Forward", + "Index Name": "alerts_pkey1", + "Relation Name": "alerts", + "Schema": "analytics", + "Alias": "a", + "Startup Cost": 0.42, + "Total Cost": 6.34, + "Plan Rows": 1, + "Plan Width": 213, + "Actual Startup Time": 0.004, + "Actual Total Time": 0.005, + "Actual Rows": 1, + "Actual Loops": 32352, + "Output": ["a.id", "a.org_id", "a.external_alert_id", "a.alert_name", "a.display_name", "a.estimated_cost", "a.description", "a.source", "a.venue_code", "a.action_expected", "a.cancel_expected", "a.responsible_provider_type", "a.status", "a.version", "a.created_by", "a.created_date", "a.modified_by", "a.modified_date", "a.tracking_id", "a.record_date", "a.released_date", "a.released", "a.comments", "a.default_lockout_hours", "a.deploy_mode", "a.stop_type", "a.importance_level", "a.type_id", "a.subtype_id"], + "Index Cond": "(a.id = s.alert_id)", + "Rows Removed by Index Recheck": 0, + "Shared Hit Blocks": 129627, + "Shared Read Blocks": 0, + "Shared Dirtied Blocks": 0, + "Shared Written Blocks": 0, + "Local Hit Blocks": 0, + "Local Read Blocks": 0, + "Local Dirtied Blocks": 0, + "Local Written Blocks": 0, + "Temp Read Blocks": 0, + "Temp Written Blocks": 0, + "I/O Read Time": 0.000, + "I/O Write Time": 0.000 + } + ] + }, + { + "Node Type": "Index Scan", + "Parent Relationship": "Inner", + "Scan Direction": "Forward", + "Index Name": "alert_types_id_org_id_key", + "Relation Name": "alert_types", + "Schema": "analytics", + "Alias": "altt", + "Startup Cost": 0.15, + "Total Cost": 0.17, + "Plan Rows": 1, + "Plan Width": 72, + "Actual Startup Time": 0.002, + "Actual Total Time": 0.003, + "Actual Rows": 1, + "Actual Loops": 32352, + "Output": ["altt.id", "altt.version", "altt.code", "altt.name", "altt.org_id"], + "Index Cond": "(a.type_id = altt.id)", + "Rows Removed by Index Recheck": 0, + "Shared Hit Blocks": 64704, + "Shared Read Blocks": 0, + "Shared Dirtied Blocks": 0, + "Shared Written Blocks": 0, + "Local Hit Blocks": 0, + "Local Read Blocks": 0, + "Local Dirtied Blocks": 0, + "Local Written Blocks": 0, + "Temp Read Blocks": 0, + "Temp Written Blocks": 0, + "I/O Read Time": 0.000, + "I/O Write Time": 0.000 + } + ] + }, + { + "Node Type": "Index Scan", + "Parent Relationship": "Inner", + "Scan Direction": "Forward", + "Index Name": "alert_subtypes_id_org_id_key", + "Relation Name": "alert_subtypes", + "Schema": "analytics", + "Alias": "altst", + "Startup Cost": 0.14, + "Total Cost": 0.16, + "Plan Rows": 1, + "Plan Width": 23, + "Actual Startup Time": 0.002, + "Actual Total Time": 0.003, + "Actual Rows": 1, + "Actual Loops": 32352, + "Output": ["altst.id", "altst.version", "altst.code", "altst.name", "altst.org_id"], + "Index Cond": "(a.subtype_id = altst.id)", + "Rows Removed by Index Recheck": 0, + "Shared Hit Blocks": 63472, + "Shared Read Blocks": 0, + "Shared Dirtied Blocks": 0, + "Shared Written Blocks": 0, + "Local Hit Blocks": 0, + "Local Read Blocks": 0, + "Local Dirtied Blocks": 0, + "Local Written Blocks": 0, + "Temp Read Blocks": 0, + "Temp Written Blocks": 0, + "I/O Read Time": 0.000, + "I/O Write Time": 0.000 + } + ] + } + ] + } + ] + }, + "Planning Time": 0.507, + "Triggers": [ + ], + "Execution Time": 1953.684 + } +] diff --git a/app/services/plan-service.ts b/app/services/plan-service.ts new file mode 100644 index 0000000..5d7dc3c --- /dev/null +++ b/app/services/plan-service.ts @@ -0,0 +1,166 @@ +import {IPlan} from '../interfaces/iplan'; +import {HighlightType, EstimateDirection} from '../enums'; +/// +/// + +export class PlanService { + // plan property keys + NODE_TYPE_PROP: string = 'Node Type'; + ACTUAL_ROWS_PROP: string = 'Actual Rows'; + PLAN_ROWS_PROP: string = 'Plan Rows'; + ACTUAL_TOTAL_TIME_PROP: string = 'Actual Total Time'; + ACTUAL_LOOPS_PROP: string = 'Actual Loops'; + TOTAL_COST_PROP: string = 'Total Cost'; + PLANS_PROP: string = 'Plans'; + + + // computed by pev + COMPUTED_TAGS_PROP: string = "*Tags"; + + COSTLIEST_NODE_PROP: string = "*Costiest Node (by cost)"; + LARGEST_NODE_PROP: string = "*Largest Node (by rows)"; + SLOWEST_NODE_PROP: string = "*Slowest Node (by duration)"; + + MAXIMUM_COSTS_PROP: string = '*Most Expensive Node (cost)'; + MAXIMUM_ROWS_PROP: string = '*Largest Node (rows)'; + MAXIMUM_DURATION_PROP: string = '*Slowest Node (time)'; + ACTUAL_DURATION_PROP: string = '*Actual Duration'; + ACTUAL_COST_PROP: string = '*Actual Cost'; + PLANNER_ESTIMATE_FACTOR: string = '*Planner Row Estimate Factor'; + PLANNER_ESIMATE_DIRECTION: string = '*Planner Row Estimate Direction'; + + ARRAY_INDEX_KEY: string = 'arrayIndex'; + + private _maxRows: number = 0; + private _maxCost: number = 0; + private _maxDuration: number = 0; + + getPlans(): Array { + var plans: Array = []; + + for (var i in localStorage) { + plans.push(JSON.parse(localStorage[i])); + } + + return plans; + } + + getPlan(id: string): IPlan { + return JSON.parse(localStorage.getItem(id)); + } + + createPlan(planName: string, planContent: string, planQuery): IPlan { + var plan: IPlan = { + id: 'plan_' + new Date().getTime().toString(), + name: planName || 'plan created on ' + moment().format('LLL'), + createdOn: new Date(), + content: JSON.parse(planContent)[0], + query: planQuery + }; + + this.analyzePlan(plan); + return plan; + } + + analyzePlan(plan: IPlan) { + this.processNode(plan.content.Plan); + plan.content[this.MAXIMUM_ROWS_PROP] = this._maxRows; + plan.content[this.MAXIMUM_COSTS_PROP] = this._maxCost; + plan.content[this.MAXIMUM_DURATION_PROP] = this._maxDuration; + + this.findOutlierNodes(plan.content.Plan); + + localStorage.setItem(plan.id, JSON.stringify(plan)); + } + + deletePlan(plan: IPlan) { + localStorage.removeItem(plan.id); + } + + deleteAllPlans() { + localStorage.clear(); + } + + // recursively walk down the plan to compute various metrics + processNode(node) { + this.calculatePlannerEstimate(node); + this.calculateActuals(node); + + _.each(node, (value, key) => { + this.calculateMaximums(node, key, value); + + if (key === this.PLANS_PROP) { + _.each(value, (value) => { + this.processNode(value); + }) + } + }); + } + + calculateMaximums(node, key, value) { + if (key === this.ACTUAL_ROWS_PROP && this._maxRows < value) { + this._maxRows = value; + } + if (key === this.ACTUAL_COST_PROP && this._maxCost < value) { + this._maxCost = value; + } + + if (key === this.ACTUAL_DURATION_PROP && this._maxDuration < value) { + this._maxDuration = value; + } + } + + findOutlierNodes(node) { + node[this.SLOWEST_NODE_PROP] = false; + node[this.LARGEST_NODE_PROP] = false; + node[this.COSTLIEST_NODE_PROP] = false; + + if (node[this.ACTUAL_COST_PROP] === this._maxCost) { + node[this.COSTLIEST_NODE_PROP] = true; + } + if (node[this.ACTUAL_ROWS_PROP] === this._maxRows) { + node[this.LARGEST_NODE_PROP] = true; + } + if (node[this.ACTUAL_DURATION_PROP] === this._maxDuration) { + node[this.SLOWEST_NODE_PROP] = true; + } + + _.each(node, (value, key) => { + if (key === this.PLANS_PROP) { + _.each(value, (value) => { + this.findOutlierNodes(value); + }) + } + }); + } + + // actual duration and actual cost are calculated by subtracting child values from the total + calculateActuals(node) { + node[this.ACTUAL_DURATION_PROP] = node[this.ACTUAL_TOTAL_TIME_PROP]; + node[this.ACTUAL_COST_PROP] = node[this.TOTAL_COST_PROP]; + + _.each(node.Plans, subPlan => { + node[this.ACTUAL_DURATION_PROP] = node[this.ACTUAL_DURATION_PROP] - subPlan[this.ACTUAL_TOTAL_TIME_PROP]; + node[this.ACTUAL_COST_PROP] = node[this.ACTUAL_COST_PROP] - subPlan[this.TOTAL_COST_PROP]; + }); + + if (node[this.ACTUAL_COST_PROP] < 0) { + node[this.ACTUAL_COST_PROP] = 0; + } + + // since time is reported for an invidual loop, actual duration must be adjusted by number of loops + node[this.ACTUAL_DURATION_PROP] = node[this.ACTUAL_DURATION_PROP] * node[this.ACTUAL_LOOPS_PROP]; + } + + // figure out order of magnitude by which the planner mis-estimated how many rows would be + // invloved in this node + calculatePlannerEstimate(node) { + node[this.PLANNER_ESTIMATE_FACTOR] = node[this.ACTUAL_ROWS_PROP] / node[this.PLAN_ROWS_PROP]; + node[this.PLANNER_ESIMATE_DIRECTION] = EstimateDirection.under; + + if (node[this.PLANNER_ESTIMATE_FACTOR] < 1) { + node[this.PLANNER_ESIMATE_DIRECTION] = EstimateDirection.over; + node[this.PLANNER_ESTIMATE_FACTOR] = node[this.PLAN_ROWS_PROP] / node[this.ACTUAL_ROWS_PROP]; + } + } +} diff --git a/gulpfile.ts b/gulpfile.ts new file mode 100644 index 0000000..673804c --- /dev/null +++ b/gulpfile.ts @@ -0,0 +1,83 @@ +import * as gulp from 'gulp'; +import {runSequence, task} from './tools/utils'; + +// -------------- +// Clean (override). +gulp.task('clean', task('clean', 'all')); +gulp.task('clean.dist', task('clean', 'dist')); +gulp.task('clean.test', task('clean', 'test')); +gulp.task('clean.tmp', task('clean', 'tmp')); + +gulp.task('check.versions', task('check.versions')); + +// -------------- +// Postinstall. +gulp.task('postinstall', done => + runSequence('clean', + 'npm', + done)); + +// -------------- +// Build dev. +gulp.task('build.dev', done => + runSequence('clean.dist', + 'tslint', + 'build.sass.dev', + 'build.img.dev', + 'build.fonts.dev', + 'build.js.dev', + 'build.index', + done)); + +// -------------- +// Build prod. +gulp.task('build.prod', done => + runSequence('clean.dist', + 'clean.tmp', + 'tslint', + 'build.sass.dev', + 'build.img.dev', + 'build.fonts.dev', + 'build.html_css.prod', + 'build.deps', + 'build.js.prod', + 'build.bundles', + 'build.index', + done)); + +// -------------- +// Watch. +gulp.task('build.dev.watch', done => + runSequence('build.dev', + 'watch.dev', + done)); + +gulp.task('build.test.watch', done => + runSequence('build.test', + 'watch.test', + done)); + +// -------------- +// Test. +gulp.task('test', done => + runSequence('clean.test', + 'tslint', + 'build.test', + 'karma.start', + done)); + +// -------------- +// Serve. +gulp.task('serve', done => + runSequence('build.dev', + 'server.start', + 'watch.serve', + done)); + +// -------------- +// Docs +// Disabled until https://github.com/sebastian-lenz/typedoc/issues/162 gets resolved +// gulp.task('docs', done => +// runSequence('build.docs', +// 'serve.docs', +// done)); diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..7f7e5cc --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,103 @@ +// Karma configuration +// Generated on Wed Jul 15 2015 09:44:02 GMT+0200 (Romance Daylight Time) +'use strict'; + +module.exports = function(config) { + config.set({ + + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: './', + + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['jasmine'], + + + // list of files / patterns to load in the browser + files: [ + 'node_modules/zone.js/dist/zone-microtask.js', + 'node_modules/zone.js/dist/long-stack-trace-zone.js', + 'node_modules/zone.js/dist/jasmine-patch.js', + 'node_modules/es6-module-loader/dist/es6-module-loader.js', + 'node_modules/traceur/bin/traceur-runtime.js', // Required by PhantomJS2, otherwise it shouts ReferenceError: Can't find variable: require + 'node_modules/traceur/bin/traceur.js', + 'node_modules/systemjs/dist/system.src.js', + 'node_modules/reflect-metadata/Reflect.js', + + { pattern: 'node_modules/angular2/**/*.js', included: false, watched: false }, + { pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false }, + { pattern: 'test/**/*.js', included: false, watched: true }, + { pattern: 'node_modules/systemjs/dist/system-polyfills.js', included: false, watched: false }, // PhantomJS2 (and possibly others) might require it + + 'test-main.js' + ], + + + // list of files to exclude + exclude: [ + 'node_modules/angular2/**/*_spec.js' + ], + + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + }, + + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['mocha'], + + + // web server port + port: 9876, + + + // enable / disable colors in the output (reporters and logs) + colors: true, + + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: true, + + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: [ + 'PhantomJS2', + 'Chrome' + ], + + + customLaunchers: { + Chrome_travis_ci: { + base: 'Chrome', + flags: ['--no-sandbox'] + } + }, + + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: false + }); + + if (process.env.APPVEYOR) { + config.browsers = ['IE']; + config.singleRun = true; + config.browserNoActivityTimeout = 90000; // Note: default value (10000) is not enough + } + + if (process.env.TRAVIS || process.env.CIRCLECI) { + config.browsers = ['Chrome_travis_ci']; + config.singleRun = true; + } +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..2f2334b --- /dev/null +++ b/package.json @@ -0,0 +1,93 @@ +{ + "name": "pev", + "version": "0.0.0", + "description": "Postgres Explain Visualizer", + "repository": { + "url": "http://www.tatiyants.com" + }, + "scripts": { + "build.dev": "gulp build.dev", + "build.dev.watch": "gulp build.dev.watch", + "build.prod": "gulp build.prod --env prod", + "build.test": "gulp build.test", + "build.test.watch": "gulp build.test.watch", + "docs": "npm run gulp -- build.docs && npm run gulp -- serve.docs", + "gulp": "gulp", + "karma": "karma", + "karma.start": "karma start", + "lint": "gulp tslint", + "postinstall": "tsd reinstall --clean && tsd link && tsd rebundle && gulp check.versions && gulp postinstall", + "reinstall": "rimraf node_modules && npm cache clean && npm install", + "start": "gulp serve --env dev", + "serve.dev": "gulp serve --env dev", + "tasks.list": "gulp --tasks-simple", + "test": "gulp test", + "tsd": "tsd" + }, + "author": "Alex Tatiyants", + "license": "MIT", + "devDependencies": { + "async": "^1.4.2", + "chalk": "^1.1.1", + "connect-livereload": "^0.5.3", + "del": "^1.1.1", + "express": "~4.13.1", + "extend": "^3.0.0", + "gulp": "^3.9.0", + "gulp-compass": "^2.1.0", + "gulp-concat": "^2.5.2", + "gulp-filter": "^2.0.2", + "gulp-inject": "^1.3.1", + "gulp-inline-ng2-template": "^0.0.7", + "gulp-load-plugins": "^0.10.0", + "gulp-minify-css": "^1.1.6", + "gulp-minify-html": "^1.0.3", + "gulp-plumber": "~1.0.1", + "gulp-sass": "^2.0.4", + "gulp-shell": "~0.4.3", + "gulp-sourcemaps": "~1.5.2", + "gulp-template": "^3.0.0", + "gulp-tslint": "^3.3.0", + "gulp-tslint-stylish": "^1.0.4", + "gulp-typescript": "~2.8.2", + "gulp-uglify": "^1.2.0", + "gulp-util": "^3.0.7", + "gulp-watch": "^4.2.4", + "jasmine-core": "~2.3.4", + "karma": "~0.13.15", + "karma-chrome-launcher": "~0.2.0", + "karma-ie-launcher": "^0.2.0", + "karma-jasmine": "~0.3.6", + "karma-mocha-reporter": "^1.1.1", + "karma-phantomjs2-launcher": "^0.3.2", + "merge-stream": "^1.0.0", + "open": "0.0.5", + "rimraf": "^2.4.3", + "run-sequence": "^1.1.0", + "semver": "^5.0.3", + "serve-static": "^1.9.2", + "slash": "~1.0.0", + "stream-series": "^0.1.1", + "systemjs-builder": "^0.14.8", + "tiny-lr": "^0.2.1", + "traceur": "^0.0.91", + "ts-node": "^0.5.4", + "tsd": "^0.6.4", + "typedoc": "^0.3.12", + "typescript": "~1.7.3", + "typescript-register": "^1.1.0", + "yargs": "^3.25.0" + }, + "dependencies": { + "angular2": "2.0.0-beta.0", + "bootstrap": "^3.3.5", + "es6-module-loader": "^0.17.8", + "es6-shim": "^0.33.3", + "lodash": "^3.10.1", + "moment": "^2.10.6", + "reflect-metadata": "^0.1.2", + "rxjs": "5.0.0-beta.0", + "systemjs": "^0.19.4", + "zone.js": "0.5.10" + } +} diff --git a/test-main.js b/test-main.js new file mode 100644 index 0000000..457ee32 --- /dev/null +++ b/test-main.js @@ -0,0 +1,53 @@ +// Turn on full stack traces in errors to help debugging +Error.stackTraceLimit=Infinity; + +jasmine.DEFAULT_TIMEOUT_INTERVAL = 100; + +// Cancel Karma's synchronous start, +// we will call `__karma__.start()` later, once all the specs are loaded. +__karma__.loaded = function() {}; + +System.config({ + baseURL: '/base/', + defaultJSExtensions: true, + paths: { + 'angular2/*': 'node_modules/angular2/*.js', + 'rxjs/*': 'node_modules/rxjs/*.js' + } +}); + +System.import('angular2/src/platform/browser/browser_adapter').then(function(browser_adapter) { + browser_adapter.BrowserDomAdapter.makeCurrent(); +}).then(function() { + return Promise.all( + Object.keys(window.__karma__.files) // All files served by Karma. + .filter(onlySpecFiles) + .map(file2moduleName) + .map(function(path) { + return System.import(path).then(function(module) { + if (module.hasOwnProperty('main')) { + module.main(); + } else { + throw new Error('Module ' + path + ' does not implement main() method.'); + } + }); + })); +}) +.then(function() { + __karma__.start(); +}, function(error) { + console.error(error.stack || error); + __karma__.start(); +}); + + +function onlySpecFiles(path) { + return /[\.|_]spec\.js$/.test(path); +} + +// Normalize paths to module names. +function file2moduleName(filePath) { + return filePath.replace(/\\/g, '/') + .replace(/^\/base\//, '') + .replace(/\.js/, ''); +} diff --git a/tools/config.ts b/tools/config.ts new file mode 100644 index 0000000..9c5559a --- /dev/null +++ b/tools/config.ts @@ -0,0 +1,101 @@ +import {readFileSync} from 'fs'; +import {argv} from 'yargs'; + + +// -------------- +// Configuration. +export const ENV = argv['env'] || 'dev'; +export const DEBUG = argv['debug'] || false; +export const PORT = argv['port'] || 5555; +export const LIVE_RELOAD_PORT = argv['reload-port'] || 4002; +export const DOCS_PORT = argv['docs-port'] || 4003; +export const APP_BASE = argv['base'] || '/'; + +export const APP_TITLE = 'My Angular2 App'; + +export const APP_SRC = 'app'; +export const ASSETS_SRC = `${APP_SRC}/assets`; + +export const TOOLS_DIR = 'tools'; +export const TMP_DIR = 'tmp'; +export const TEST_DEST = 'test'; +export const DOCS_DEST = 'docs'; +export const APP_DEST = `dist/${ENV}`; +export const ASSETS_DEST = `${APP_DEST}/assets`; +export const BUNDLES_DEST = `${APP_DEST}/bundles`; +export const CSS_DEST = `${APP_DEST}/css`; +export const FONTS_DEST = `${APP_DEST}/fonts`; +export const LIB_DEST = `${APP_DEST}/lib`; +export const APP_ROOT = ENV === 'dev' ? `${APP_BASE}${APP_DEST}/` : `${APP_BASE}`; +export const VERSION = appVersion(); + +export const VERSION_NPM = '2.14.7'; +export const VERSION_NODE = '4.0.0'; + +// Declare NPM dependencies (Note that globs should not be injected). +export const NPM_DEPENDENCIES = [ + { src: 'systemjs/dist/system-polyfills.js', dest: LIB_DEST }, + + { src: 'es6-shim/es6-shim.min.js', inject: 'shims', dest: LIB_DEST }, + { src: 'reflect-metadata/Reflect.js', inject: 'shims', dest: LIB_DEST }, + { src: 'systemjs/dist/system.src.js', inject: 'shims', dest: LIB_DEST }, + { src: 'angular2/bundles/angular2-polyfills.js', inject: 'shims', dest: LIB_DEST }, + + // Faster dev page load + { src: 'rxjs/bundles/Rx.min.js', inject: 'libs', dest: LIB_DEST }, + { src: 'angular2/bundles/angular2.min.js', inject: 'libs', dest: LIB_DEST }, + { src: 'angular2/bundles/router.js', inject: 'libs', dest: LIB_DEST }, // use router.min.js with alpha47 + { src: 'angular2/bundles/http.min.js', inject: 'libs', dest: LIB_DEST }, + + { src: 'lodash/index.js', inject: 'libs', dest: LIB_DEST }, + { src: 'moment/moment.js', inject: 'libs', dest: LIB_DEST } +]; + +// Declare local files that needs to be injected +export const APP_ASSETS = [ + { src: `${ASSETS_SRC}/css/styles.css`, inject: true, dest: CSS_DEST} +]; + +NPM_DEPENDENCIES + .filter(d => !/\*/.test(d.src)) // Skip globs + .forEach(d => d.src = require.resolve(d.src)); + +export const DEPENDENCIES = NPM_DEPENDENCIES.concat(APP_ASSETS); + + +// ---------------- +// SystemsJS Configuration. +const SYSTEM_CONFIG_DEV = { + defaultJSExtensions: true, + paths: { + 'bootstrap': `${APP_ROOT}bootstrap`, + '*': `${APP_BASE}node_modules/*` + } +}; + +const SYSTEM_CONFIG_PROD = { + defaultJSExtensions: true, + bundles: { + 'bundles/app': ['bootstrap'] + } +}; + +export const SYSTEM_CONFIG = ENV === 'dev' ? SYSTEM_CONFIG_DEV : SYSTEM_CONFIG_PROD; + +// This is important to keep clean module names as 'module name == module uri'. +export const SYSTEM_CONFIG_BUILDER = { + defaultJSExtensions: true, + paths: { + '*': `${TMP_DIR}/*`, + 'angular2/*': 'node_modules/angular2/*', + 'rxjs/*': 'node_modules/rxjs/*' + } +}; + + +// -------------- +// Private. +function appVersion(): number|string { + var pkg = JSON.parse(readFileSync('package.json').toString()); + return pkg.version; +} diff --git a/tools/tasks/build.bundles.ts b/tools/tasks/build.bundles.ts new file mode 100644 index 0000000..52eb381 --- /dev/null +++ b/tools/tasks/build.bundles.ts @@ -0,0 +1,26 @@ +import {parallel} from 'async'; +import {join} from 'path'; +import * as Builder from 'systemjs-builder'; +import {BUNDLES_DEST, SYSTEM_CONFIG_BUILDER} from '../config'; + +const BUNDLE_OPTS = { + minify: true, + sourceMaps: true, + format: 'cjs' +}; + +export = function bundles(gulp, plugins) { + return function (done) { + let builder = new Builder(SYSTEM_CONFIG_BUILDER); + + parallel([ + bundleApp + ], () => done()); + + function bundleApp(done) { + builder.bundle( + 'bootstrap - angular2/*', + join(BUNDLES_DEST, 'app.js'), BUNDLE_OPTS).then(done); + } + }; +}; diff --git a/tools/tasks/build.deps.ts b/tools/tasks/build.deps.ts new file mode 100644 index 0000000..071f4cd --- /dev/null +++ b/tools/tasks/build.deps.ts @@ -0,0 +1,20 @@ +import * as merge from 'merge-stream'; +import {DEPENDENCIES} from '../config'; + +export = function buildDepsProd(gulp, plugins) { + return function () { + let stream = merge(); + + DEPENDENCIES.forEach(dep => { + stream.add(addStream(dep)); + }); + + return stream; + + function addStream(dep) { + let stream = gulp.src(dep.src); + stream.pipe(gulp.dest(dep.dest)); + return stream; + } + }; +}; diff --git a/tools/tasks/build.docs.ts b/tools/tasks/build.docs.ts new file mode 100644 index 0000000..a464c67 --- /dev/null +++ b/tools/tasks/build.docs.ts @@ -0,0 +1,27 @@ +import {join} from 'path'; +import {APP_SRC, APP_TITLE, DOCS_DEST} from '../config'; + +export = function buildDocs(gulp, plugins, option) { + return function() { + + let src = [ + join(APP_SRC, '**/*.ts'), + '!' + join(APP_SRC, '**/*_spec.ts') + ]; + + return gulp.src(src) + .pipe(plugins.typedoc({ + // TypeScript options (see typescript docs) + module: 'commonjs', + target: 'es5', + includeDeclarations: true, + // Output options (see typedoc docs) + out: DOCS_DEST, + json: join(DOCS_DEST , 'data/docs.json' ), + name: APP_TITLE, + ignoreCompilerErrors: false, + experimentalDecorators: true, + version: true + })); + }; +} diff --git a/tools/tasks/build.fonts.dev.ts b/tools/tasks/build.fonts.dev.ts new file mode 100644 index 0000000..6aa0b92 --- /dev/null +++ b/tools/tasks/build.fonts.dev.ts @@ -0,0 +1,15 @@ +import {join} from 'path'; +import {APP_SRC, APP_DEST} from '../config'; + +export = function buildFontsDev(gulp, plugins) { + return function () { + return gulp.src([ + join(APP_SRC, '**/*.eot'), + join(APP_SRC, '**/*.ttf'), + join(APP_SRC, '**/*.woff'), + join(APP_SRC, '**/*.woff2'), + join(APP_SRC, '**/*.otf') + ]) + .pipe(gulp.dest(APP_DEST)); + }; +} diff --git a/tools/tasks/build.html_css.prod.ts b/tools/tasks/build.html_css.prod.ts new file mode 100644 index 0000000..93d0aa9 --- /dev/null +++ b/tools/tasks/build.html_css.prod.ts @@ -0,0 +1,24 @@ +import * as merge from 'merge-stream'; +import {join} from 'path'; +import {APP_SRC, TMP_DIR} from '../config'; + +// const HTML_MINIFIER_OPTS = { empty: true }; + +export = function buildJSDev(gulp, plugins) { + return function () { + + return merge(minifyHtml(), minifyCss()); + + function minifyHtml() { + return gulp.src(join(APP_SRC, '**/*.html')) + // .pipe(plugins.minifyHtml(HTML_MINIFIER_OPTS)) + .pipe(gulp.dest(TMP_DIR)); + } + + function minifyCss() { + return gulp.src(join(APP_SRC, '**/*.css')) + .pipe(plugins.minifyCss()) + .pipe(gulp.dest(TMP_DIR)); + } + }; +}; diff --git a/tools/tasks/build.img.dev.ts b/tools/tasks/build.img.dev.ts new file mode 100644 index 0000000..28a7897 --- /dev/null +++ b/tools/tasks/build.img.dev.ts @@ -0,0 +1,14 @@ +import {join} from 'path'; +import {APP_SRC, APP_DEST} from '../config'; + +export = function buildImagesDev(gulp, plugins) { + return function () { + return gulp.src([ + join(APP_SRC, '**/*.gif'), + join(APP_SRC, '**/*.jpg'), + join(APP_SRC, '**/*.png'), + join(APP_SRC, '**/*.svg') + ]) + .pipe(gulp.dest(APP_DEST)); + }; +} diff --git a/tools/tasks/build.index.ts b/tools/tasks/build.index.ts new file mode 100644 index 0000000..bbf98ee --- /dev/null +++ b/tools/tasks/build.index.ts @@ -0,0 +1,34 @@ +import {join, sep} from 'path'; +import {APP_SRC, APP_DEST, DEPENDENCIES, ENV} from '../config'; +import {transformPath, templateLocals} from '../utils'; + +export = function buildIndexDev(gulp, plugins) { + return function () { + return gulp.src(join(APP_SRC, 'index.html')) + // NOTE: There might be a way to pipe in loop. + .pipe(inject('shims')) + .pipe(inject('libs')) + .pipe(inject()) + .pipe(plugins.template(templateLocals())) + .pipe(gulp.dest(APP_DEST)); + }; + + + function inject(name?: string) { + return plugins.inject(gulp.src(getInjectablesDependenciesRef(name), { read: false }), { + name, + transform: transformPath(plugins, 'dev') + }); + } + + function getInjectablesDependenciesRef(name?: string) { + return DEPENDENCIES + .filter(dep => dep['inject'] && dep['inject'] === (name || true)) + .map(mapPath); + } + + function mapPath(dep) { + let prodPath = join(dep.dest, dep.src.split(sep).pop()); + return ('prod' === ENV ? prodPath : dep.src ); + } +}; diff --git a/tools/tasks/build.js.dev.ts b/tools/tasks/build.js.dev.ts new file mode 100644 index 0000000..dfe9539 --- /dev/null +++ b/tools/tasks/build.js.dev.ts @@ -0,0 +1,25 @@ +import {join} from 'path'; +import {APP_SRC, APP_DEST} from '../config'; +import {templateLocals, tsProjectFn} from '../utils'; + +export = function buildJSDev(gulp, plugins) { + let tsProject = tsProjectFn(plugins); + return function () { + let src = [ + join(APP_SRC, '**/*.ts'), + '!' + join(APP_SRC, '**/*_spec.ts') + ]; + + let result = gulp.src(src) + .pipe(plugins.plumber()) + // Won't be required for non-production build after the change + .pipe(plugins.inlineNg2Template({ base: APP_SRC })) + .pipe(plugins.sourcemaps.init()) + .pipe(plugins.typescript(tsProject)); + + return result.js + .pipe(plugins.sourcemaps.write()) + .pipe(plugins.template(templateLocals())) + .pipe(gulp.dest(APP_DEST)); + }; +}; diff --git a/tools/tasks/build.js.prod.ts b/tools/tasks/build.js.prod.ts new file mode 100644 index 0000000..54ed21a --- /dev/null +++ b/tools/tasks/build.js.prod.ts @@ -0,0 +1,22 @@ +import {join} from 'path'; +import {APP_SRC, TMP_DIR} from '../config'; +import {templateLocals, tsProjectFn} from '../utils'; + +export = function buildJSDev(gulp, plugins) { + return function () { + let tsProject = tsProjectFn(plugins); + let src = [ + join(APP_SRC, '**/*.ts'), + '!' + join(APP_SRC, '**/*_spec.ts') + ]; + + let result = gulp.src(src) + .pipe(plugins.plumber()) + .pipe(plugins.inlineNg2Template({ base: TMP_DIR })) + .pipe(plugins.typescript(tsProject)); + + return result.js + .pipe(plugins.template(templateLocals())) + .pipe(gulp.dest(TMP_DIR)); + }; +}; diff --git a/tools/tasks/build.sass.dev.ts b/tools/tasks/build.sass.dev.ts new file mode 100644 index 0000000..a2127be --- /dev/null +++ b/tools/tasks/build.sass.dev.ts @@ -0,0 +1,22 @@ +import {join} from 'path'; +import {APP_SRC} from '../config'; + +export = function buildSassDev(gulp, plugins, option) { + return function() { + return gulp.src(join(APP_SRC, '**', '*.scss')) + .pipe(plugins.plumber({ + // this allows gulp not to crash on sass compilation errors + errorHandler: function(error) { + console.log(error.message); + this.emit('end'); + } + })) + .pipe(plugins.compass({ + // config_file: './config.rb', + style: 'compressed', + css: 'app/assets/css', + sass: join(APP_SRC, 'assets/sass'), + })) + .pipe(gulp.dest(join(APP_SRC, 'assets'))); + }; +} diff --git a/tools/tasks/build.test.ts b/tools/tasks/build.test.ts new file mode 100644 index 0000000..3e089cd --- /dev/null +++ b/tools/tasks/build.test.ts @@ -0,0 +1,21 @@ +import {join} from 'path'; +import {APP_SRC, TEST_DEST} from '../config'; +import {tsProjectFn} from '../utils'; + +export = function buildTest(gulp, plugins) { + return function () { + let tsProject = tsProjectFn(plugins); + let src = [ + join(APP_SRC, '**/*.ts'), + '!' + join(APP_SRC, 'bootstrap.ts') + ]; + + let result = gulp.src(src) + .pipe(plugins.plumber()) + .pipe(plugins.inlineNg2Template({ base: APP_SRC })) + .pipe(plugins.typescript(tsProject)); + + return result.js + .pipe(gulp.dest(TEST_DEST)); + }; +}; diff --git a/tools/tasks/check.versions.ts b/tools/tasks/check.versions.ts new file mode 100644 index 0000000..211d5ed --- /dev/null +++ b/tools/tasks/check.versions.ts @@ -0,0 +1,35 @@ +import {VERSION_NPM, VERSION_NODE} from '../config'; + +function reportError(message: string) { + console.error(require('chalk').white.bgRed.bold(message)); + process.exit(1); +} + +module.exports = function check(gulp, plugins) { + return function () { + let exec = require('child_process').exec; + let semver = require('semver'); + + exec('npm --version', + function (error, stdout, stderr) { + if (error !== null) { + reportError('npm preinstall error: ' + error + stderr); + } + + if (!semver.gte(stdout, VERSION_NPM)) { + reportError('NPM is not in required version! Required is ' + VERSION_NPM + ' and you\'re using ' + stdout); + } + }); + + exec('node --version', + function (error, stdout, stderr) { + if (error !== null) { + reportError('npm preinstall error: ' + error + stderr); + } + + if (!semver.gte(stdout, VERSION_NODE)) { + reportError('NODE is not in required version! Required is ' + VERSION_NODE + ' and you\'re using ' + stdout); + } + }); + }; +}; diff --git a/tools/tasks/clean.ts b/tools/tasks/clean.ts new file mode 100644 index 0000000..9f0ebf2 --- /dev/null +++ b/tools/tasks/clean.ts @@ -0,0 +1,34 @@ +import * as async from 'async'; +import * as del from 'del'; +import {APP_DEST, TEST_DEST, TMP_DIR} from '../config'; + +export = function clean(gulp, plugins, option) { + return function (done) { + + switch(option) { + case 'all' : cleanAll(done); break; + case 'dist' : cleanDist(done); break; + case 'test' : cleanTest(done); break; + case 'tmp' : cleanTmp(done); break; + default: done(); + } + + }; +}; + +function cleanAll(done) { + async.parallel([ + cleanDist, + cleanTest, + cleanTmp + ], done); +} +function cleanDist(done) { + del(APP_DEST, done); +} +function cleanTest(done) { + del(TEST_DEST, done); +} +function cleanTmp(done) { + del(TMP_DIR, done); +} diff --git a/tools/tasks/karma.start.ts b/tools/tasks/karma.start.ts new file mode 100644 index 0000000..313aacd --- /dev/null +++ b/tools/tasks/karma.start.ts @@ -0,0 +1,11 @@ +import * as karma from 'karma'; +import {join} from 'path'; + +export = function karmaStart() { + return function (done) { + new (karma).Server({ + configFile: join(process.cwd(), 'karma.conf.js'), + singleRun: true + }).start(done); + }; +}; diff --git a/tools/tasks/npm.ts b/tools/tasks/npm.ts new file mode 100644 index 0000000..2ab5e76 --- /dev/null +++ b/tools/tasks/npm.ts @@ -0,0 +1,5 @@ +export = function npm(gulp, plugins) { + return plugins.shell.task([ + 'npm prune' + ]); +}; diff --git a/tools/tasks/serve.docs.ts b/tools/tasks/serve.docs.ts new file mode 100644 index 0000000..2aa5f05 --- /dev/null +++ b/tools/tasks/serve.docs.ts @@ -0,0 +1,7 @@ +import {serveDocs} from '../utils'; + +export = function serverStart(gulp, plugins) { + return function () { + serveDocs(); + }; +}; diff --git a/tools/tasks/server.start.ts b/tools/tasks/server.start.ts new file mode 100644 index 0000000..e733e92 --- /dev/null +++ b/tools/tasks/server.start.ts @@ -0,0 +1,7 @@ +import {serveSPA} from '../utils'; + +export = function serverStart(gulp, plugins) { + return function () { + serveSPA(); + }; +}; diff --git a/tools/tasks/tsd.ts b/tools/tasks/tsd.ts new file mode 100644 index 0000000..bf38ad0 --- /dev/null +++ b/tools/tasks/tsd.ts @@ -0,0 +1,7 @@ +export = function tsd(gulp, plugins) { + return plugins.shell.task([ + 'tsd reinstall --clean', + 'tsd link', + 'tsd rebundle' + ]); +}; diff --git a/tools/tasks/tslint.ts b/tools/tasks/tslint.ts new file mode 100644 index 0000000..ccea264 --- /dev/null +++ b/tools/tasks/tslint.ts @@ -0,0 +1,21 @@ +import {join} from 'path'; +import {APP_SRC, TOOLS_DIR} from '../config'; + +export = function tslint(gulp, plugins) { + return function () { + let src = [ + join(APP_SRC, '**/*.ts'), + '!' + join(APP_SRC, '**/*.d.ts'), + join(TOOLS_DIR, '**/*.ts'), + '!' + join(TOOLS_DIR, '**/*.d.ts') + ]; + + return gulp.src(src) + .pipe(plugins.tslint()) + .pipe(plugins.tslint.report(plugins.tslintStylish, { + emitError: false, + sort: true, + bell: true + })); + }; +}; diff --git a/tools/tasks/watch.dev.ts b/tools/tasks/watch.dev.ts new file mode 100644 index 0000000..bfa37a3 --- /dev/null +++ b/tools/tasks/watch.dev.ts @@ -0,0 +1,8 @@ +import {join} from 'path'; +import {APP_SRC} from '../config'; + +export = function watchDev(gulp, plugins) { + return function () { + plugins.watch(join(APP_SRC, '**/*'), () => gulp.start('build.dev')); + }; +}; diff --git a/tools/tasks/watch.serve.ts b/tools/tasks/watch.serve.ts new file mode 100644 index 0000000..2bb18f1 --- /dev/null +++ b/tools/tasks/watch.serve.ts @@ -0,0 +1,12 @@ +import * as runSequence from 'run-sequence'; +import {join} from 'path'; +import {APP_SRC} from '../config'; +import {notifyLiveReload} from '../utils'; + +export = function watchServe(gulp, plugins) { + return function () { + plugins.watch(join(APP_SRC, '**'), e => + runSequence('build.dev', () => notifyLiveReload(e)) + ); + }; +}; diff --git a/tools/tasks/watch.test.ts b/tools/tasks/watch.test.ts new file mode 100644 index 0000000..2a8b55b --- /dev/null +++ b/tools/tasks/watch.test.ts @@ -0,0 +1,8 @@ +import {join} from 'path'; +import {APP_SRC} from '../config'; + +export = function watchTest(gulp, plugins) { + return function () { + plugins.watch(join(APP_SRC, '**/*.ts'), () => gulp.start('build.test')); + }; +}; diff --git a/tools/typings/connect-livereload.d.ts b/tools/typings/connect-livereload.d.ts new file mode 100644 index 0000000..1b1ef21 --- /dev/null +++ b/tools/typings/connect-livereload.d.ts @@ -0,0 +1,5 @@ +declare module 'connect-livereload' { + function connectLivereload(options?: any): any; + module connectLivereload {} + export = connectLivereload; +} diff --git a/tools/typings/gulp-load-plugins.d.ts b/tools/typings/gulp-load-plugins.d.ts new file mode 100644 index 0000000..809c305 --- /dev/null +++ b/tools/typings/gulp-load-plugins.d.ts @@ -0,0 +1,42 @@ +// Type definitions for gulp-load-plugins +// Project: https://github.com/jackfranklin/gulp-load-plugins +// Definitions by: Joe Skeen +// Definitions: https://github.com/borisyankov/DefinitelyTyped + +// Does not support ES2015 import (import * as open from 'open'). + +/** Loads in any gulp plugins and attaches them to an object, freeing you up from having to manually require each gulp plugin. */ +declare module 'gulp-load-plugins' { + + interface IOptions { + /** the glob(s) to search for, default ['gulp-*', 'gulp.*'] */ + pattern?: string[]; + /** where to find the plugins, searched up from process.cwd(), default 'package.json' */ + config?: string; + /** which keys in the config to look within, default ['dependencies', 'devDependencies', 'peerDependencies'] */ + scope?: string[]; + /** what to remove from the name of the module when adding it to the context, default /^gulp(-|\.)/ */ + replaceString?: RegExp; + /** if true, transforms hyphenated plugin names to camel case, default true */ + camelize?: boolean; + /** whether the plugins should be lazy loaded on demand, default true */ + lazy?: boolean; + /** a mapping of plugins to rename, the key being the NPM name of the package, and the value being an alias you define */ + rename?: IPluginNameMappings; + } + + interface IPluginNameMappings { + [npmPackageName: string]: string; + } + + /** Loads in any gulp plugins and attaches them to an object, freeing you up from having to manually require each gulp plugin. */ + function gulpLoadPlugins(options?: IOptions): T; + module gulpLoadPlugins {} + export = gulpLoadPlugins; +} + +/** + * Extend this interface to use Gulp plugins in your gulpfile.js + */ +interface IGulpPlugins { +} diff --git a/tools/typings/karma.d.ts b/tools/typings/karma.d.ts new file mode 100644 index 0000000..5d2cd42 --- /dev/null +++ b/tools/typings/karma.d.ts @@ -0,0 +1,12 @@ +// Use this minimalistic definition file as bluebird dependency +// generate a lot of errors. + +declare module 'karma' { + var karma: IKarma; + export = karma; + interface IKarma { + server: { + start(options: any, callback: Function): void + }; + } +} diff --git a/tools/typings/merge-stream.d.ts b/tools/typings/merge-stream.d.ts new file mode 100644 index 0000000..7425d35 --- /dev/null +++ b/tools/typings/merge-stream.d.ts @@ -0,0 +1,8 @@ +declare module 'merge-stream' { + function mergeStream(...streams: NodeJS.ReadWriteStream[]): MergeStream; + interface MergeStream extends NodeJS.ReadWriteStream { + add(stream: NodeJS.ReadWriteStream): MergeStream; + } + module mergeStream {} + export = mergeStream; +} \ No newline at end of file diff --git a/tools/typings/open.d.ts b/tools/typings/open.d.ts new file mode 100644 index 0000000..89d4b76 --- /dev/null +++ b/tools/typings/open.d.ts @@ -0,0 +1,8 @@ +// https://github.com/borisyankov/DefinitelyTyped/tree/master/open +// Does not support ES2015 import (import * as open from 'open'). + +declare module 'open' { + function open(target: string, app?: string): void; + module open {} + export = open; +} diff --git a/tools/typings/run-sequence.d.ts b/tools/typings/run-sequence.d.ts new file mode 100644 index 0000000..c4fada3 --- /dev/null +++ b/tools/typings/run-sequence.d.ts @@ -0,0 +1,5 @@ +declare module 'run-sequence' { + function runSequence(...args: any[]): void; + module runSequence {} + export = runSequence; +} diff --git a/tools/typings/slash.d.ts b/tools/typings/slash.d.ts new file mode 100644 index 0000000..10ec0b5 --- /dev/null +++ b/tools/typings/slash.d.ts @@ -0,0 +1,5 @@ +declare module 'slash' { + function slash(path: string): string; + module slash {} + export = slash; +} diff --git a/tools/typings/systemjs-builder.d.ts b/tools/typings/systemjs-builder.d.ts new file mode 100644 index 0000000..4644816 --- /dev/null +++ b/tools/typings/systemjs-builder.d.ts @@ -0,0 +1,10 @@ +declare module 'systemjs-builder' { + class Builder { + constructor(configObject?: any, baseUrl?: string, configPath?: string); + bundle(source: string, target: string, options?: any): Promise; + buildStatic(source: string, target: string, options?: any): Promise; + } + + module Builder {} + export = Builder; +} diff --git a/tools/typings/tiny-lr.d.ts b/tools/typings/tiny-lr.d.ts new file mode 100644 index 0000000..8a492d5 --- /dev/null +++ b/tools/typings/tiny-lr.d.ts @@ -0,0 +1,10 @@ +declare module 'tiny-lr' { + function tinylr(): ITinylr; + module tinylr {} + export = tinylr; + + interface ITinylr { + changed(options: any): void; + listen(port: number): void; + } +} diff --git a/tools/typings/yargs.d.ts b/tools/typings/yargs.d.ts new file mode 100644 index 0000000..8351942 --- /dev/null +++ b/tools/typings/yargs.d.ts @@ -0,0 +1,9 @@ +declare module 'yargs' { + var yargs: IYargs; + export = yargs; + + // Minimalistic but serves the usage in the seed. + interface IYargs { + argv: any; + } +} diff --git a/tools/utils.ts b/tools/utils.ts new file mode 100644 index 0000000..81e3bb2 --- /dev/null +++ b/tools/utils.ts @@ -0,0 +1,11 @@ +export * from './utils/template-injectables'; +export * from './utils/template-locals'; +export * from './utils/server'; +export * from './utils/tasks_tools'; + + +export function tsProjectFn(plugins) { + return plugins.typescript.createProject('tsconfig.json', { + typescript: require('typescript') + }); +} diff --git a/tools/utils/server.ts b/tools/utils/server.ts new file mode 100644 index 0000000..9e185d3 --- /dev/null +++ b/tools/utils/server.ts @@ -0,0 +1,45 @@ +import * as connectLivereload from 'connect-livereload'; +import * as express from 'express'; +import * as tinylrFn from 'tiny-lr'; +import * as openResource from 'open'; +import * as serveStatic from 'serve-static'; +import {resolve} from 'path'; +import {APP_BASE, APP_DEST, DOCS_DEST, LIVE_RELOAD_PORT, DOCS_PORT, PORT} from '../config'; + +let tinylr = tinylrFn(); + + +export function serveSPA() { + let server = express(); + tinylr.listen(LIVE_RELOAD_PORT); + + server.use( + APP_BASE, + connectLivereload({ port: LIVE_RELOAD_PORT }), + express.static(process.cwd()) + ); + + server.listen(PORT, () => + openResource('http://localhost:' + PORT + APP_BASE + APP_DEST) + ); +} + +export function notifyLiveReload(e) { + let fileName = e.path; + tinylr.changed({ + body: { files: [fileName] } + }); +} + +export function serveDocs() { + let server = express(); + + server.use( + APP_BASE, + serveStatic(resolve(process.cwd(), DOCS_DEST)) + ); + + server.listen(DOCS_PORT, () => + openResource('http://localhost:' + DOCS_PORT + APP_BASE) + ); +} diff --git a/tools/utils/tasks_tools.ts b/tools/utils/tasks_tools.ts new file mode 100644 index 0000000..af1a069 --- /dev/null +++ b/tools/utils/tasks_tools.ts @@ -0,0 +1,64 @@ +import * as gulp from 'gulp'; +import * as util from 'gulp-util'; +import * as chalk from 'chalk'; +import * as gulpLoadPlugins from 'gulp-load-plugins'; +import * as _runSequence from 'run-sequence'; +import {readdirSync, existsSync, lstatSync} from 'fs'; +import {join} from 'path'; +import {TOOLS_DIR} from '../config'; + +const TASKS_PATH = join(TOOLS_DIR, 'tasks'); + +// NOTE: Remove if no issues with runSequence function below. +// export function loadTasks(): void { +// scanDir(TASKS_PATH, (taskname) => registerTask(taskname)); +// } + +export function task(taskname: string, option?: string) { + util.log('Loading task', chalk.yellow(taskname, option || '')); + return require(join('..', 'tasks', taskname))(gulp, gulpLoadPlugins(), option); +} + +export function runSequence(...sequence: any[]) { + let tasks = []; + let _sequence = sequence.slice(0); + sequence.pop(); + + scanDir(TASKS_PATH, taskname => tasks.push(taskname)); + + sequence.forEach(task => { + if (tasks.indexOf(task) > -1) { registerTask(task); } + }); + + return _runSequence(..._sequence); +} + +// ---------- +// Private. + +function registerTask(taskname: string, filename?: string, option: string = ''): void { + gulp.task(taskname, task(filename || taskname, option)); +} + +// TODO: add recursive lookup ? or enforce pattern file + folder (ie ./tools/utils & ./tools/utils.ts ) +function scanDir(root: string, cb: (taskname: string) => void) { + if (!existsSync(root)) return; + + walk(root); + + function walk(path) { + let files = readdirSync(path); + for (let i = 0; i < files.length; i += 1) { + let file = files[i]; + let curPath = join(path, file); + // if (lstatSync(curPath).isDirectory()) { // recurse + // path = file; + // walk(curPath); + // } + if (lstatSync(curPath).isFile() && /\.ts$/.test(file)) { + let taskname = file.replace(/(\.ts)/, ''); + cb(taskname); + } + } + } +} diff --git a/tools/utils/template-injectables.ts b/tools/utils/template-injectables.ts new file mode 100644 index 0000000..83ac315 --- /dev/null +++ b/tools/utils/template-injectables.ts @@ -0,0 +1,25 @@ +import * as slash from 'slash'; +import {join} from 'path'; +import {APP_BASE, APP_DEST, ENV} from '../config'; + +let injectables: string[] = []; + +export function injectableAssetsRef() { + return injectables; +} + +export function registerInjectableAssetsRef(paths: string[], target: string = '') { + injectables = injectables.concat( + paths + .filter(path => !/(\.map)$/.test(path)) + .map(path => join(target, slash(path).split('/').pop())) + ); +} + +export function transformPath(plugins, env) { + return function (filepath) { + filepath = ENV === 'prod' ? filepath.replace(`/${APP_DEST}`, '') : filepath; + arguments[0] = join(APP_BASE, filepath); + return slash(plugins.inject.transform.apply(plugins.inject.transform, arguments)); + }; +} diff --git a/tools/utils/template-locals.ts b/tools/utils/template-locals.ts new file mode 100644 index 0000000..be6b669 --- /dev/null +++ b/tools/utils/template-locals.ts @@ -0,0 +1,13 @@ +import {APP_BASE, APP_DEST, APP_ROOT, APP_TITLE, SYSTEM_CONFIG, VERSION} from '../config'; + +// TODO: Add an interface to register more template locals. +export function templateLocals() { + return { + APP_BASE, + APP_DEST, + APP_ROOT, + APP_TITLE, + SYSTEM_CONFIG, + VERSION + }; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..999d975 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "declaration": false, + "noImplicitAny": false, + "removeComments": true, + "noLib": false, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "sourceMap": true + }, + "exclude": [ + "node_modules" + ], + "compileOnSave": false +} diff --git a/tsd.json b/tsd.json new file mode 100644 index 0000000..4fae99b --- /dev/null +++ b/tsd.json @@ -0,0 +1,63 @@ +{ + "version": "v4", + "repo": "DefinitelyTyped/DefinitelyTyped", + "ref": "master", + "path": "tools/typings/tsd", + "bundle": "tools/typings/tsd/tsd.d.ts", + "installed": { + "systemjs/systemjs.d.ts": { + "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f" + }, + "gulp/gulp.d.ts": { + "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f" + }, + "q/Q.d.ts": { + "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f" + }, + "orchestrator/orchestrator.d.ts": { + "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f" + }, + "gulp-shell/gulp-shell.d.ts": { + "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f" + }, + "mime/mime.d.ts": { + "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f" + }, + "express/express.d.ts": { + "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f" + }, + "serve-static/serve-static.d.ts": { + "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f" + }, + "del/del.d.ts": { + "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f" + }, + "glob/glob.d.ts": { + "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f" + }, + "minimatch/minimatch.d.ts": { + "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f" + }, + "async/async.d.ts": { + "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f" + }, + "es6-promise/es6-promise.d.ts": { + "commit": "923c5431d9447db9d5cf41adc5914e3c94c1ff10" + }, + "node/node.d.ts": { + "commit": "5a8fc5ee71701431e4fdbb80c506e3c13f85a9ff" + }, + "gulp-util/gulp-util.d.ts": { + "commit": "5a8fc5ee71701431e4fdbb80c506e3c13f85a9ff" + }, + "vinyl/vinyl.d.ts": { + "commit": "5a8fc5ee71701431e4fdbb80c506e3c13f85a9ff" + }, + "through2/through2.d.ts": { + "commit": "5a8fc5ee71701431e4fdbb80c506e3c13f85a9ff" + }, + "chalk/chalk.d.ts": { + "commit": "5a8fc5ee71701431e4fdbb80c506e3c13f85a9ff" + } + } +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..b2cd447 --- /dev/null +++ b/tslint.json @@ -0,0 +1,36 @@ +{ + "rules": { + "class-name": true, + "curly": false, + "eofline": true, + "indent": "spaces", + "max-line-length": [true, 140], + "member-ordering": [true, + "public-before-private", + "static-before-instance", + "variables-before-functions" + ], + "no-arg": true, + "no-construct": true, + "no-duplicate-key": true, + "no-duplicate-variable": true, + "no-empty": true, + "no-eval": true, + "no-trailing-comma": true, + "no-trailing-whitespace": true, + "no-unused-expression": true, + "no-unused-variable": true, + "no-unreachable": true, + "no-use-before-declare": true, + "one-line": [true, + "check-open-brace", + "check-catch", + "check-else", + "check-whitespace" + ], + "quotemark": [true, "single"], + "semicolon": true, + "triple-equals": true, + "variable-name": false + } +} -- cgit v1.2.3