The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .editorconfig
├── .gitignore
├── .jshintrc
├── Dockerfile
├── LICENSE
├── README.md
├── app
    ├── assets
    │   ├── css
    │   │   └── styles.css
    │   ├── fonts
    │   │   ├── FontAwesome.otf
    │   │   ├── fontawesome-webfont.eot
    │   │   ├── fontawesome-webfont.ttf
    │   │   ├── fontawesome-webfont.woff
    │   │   ├── fontawesome-webfont.woff2
    │   │   ├── noto-sans-700.ttf
    │   │   ├── noto-sans-700italic.ttf
    │   │   ├── noto-sans-italic.ttf
    │   │   ├── noto-sans-regular.ttf
    │   │   ├── source-code-pro-300.ttf
    │   │   └── source-code-pro-500.ttf
    │   ├── img
    │   │   └── pev-logo.png
    │   ├── sass
    │   │   ├── _buttons.scss
    │   │   ├── _common.scss
    │   │   ├── _fonts.scss
    │   │   ├── _footer.scss
    │   │   ├── _forms.scss
    │   │   ├── _highlight.scss
    │   │   ├── _menu.scss
    │   │   ├── _modal.scss
    │   │   ├── _nav.scss
    │   │   ├── _page.scss
    │   │   ├── _plan-node.scss
    │   │   ├── _plan.scss
    │   │   ├── _table.scss
    │   │   ├── _variables.scss
    │   │   ├── font-awesome
    │   │   │   ├── _animated.scss
    │   │   │   ├── _bordered-pulled.scss
    │   │   │   ├── _core.scss
    │   │   │   ├── _fixed-width.scss
    │   │   │   ├── _icons.scss
    │   │   │   ├── _larger.scss
    │   │   │   ├── _list.scss
    │   │   │   ├── _mixins.scss
    │   │   │   ├── _path.scss
    │   │   │   ├── _rotated-flipped.scss
    │   │   │   ├── _stacked.scss
    │   │   │   ├── _styles.scss
    │   │   │   └── _variables.scss
    │   │   └── styles.scss
    │   └── styles.css
    ├── bootstrap.ts
    ├── components
    │   ├── about
    │   │   ├── about.html
    │   │   └── about.ts
    │   ├── app
    │   │   ├── app.html
    │   │   └── app.ts
    │   ├── plan-list
    │   │   ├── plan-list.html
    │   │   └── plan-list.ts
    │   ├── plan-new
    │   │   ├── plan-new.html
    │   │   └── plan-new.ts
    │   ├── plan-node
    │   │   ├── plan-node.html
    │   │   └── plan-node.ts
    │   └── plan-view
    │   │   ├── plan-view.html
    │   │   └── plan-view.ts
    ├── enums.ts
    ├── index.html
    ├── interfaces
    │   └── iplan.ts
    ├── pipes.ts
    ├── sample-plans
    │   └── plan1.json
    └── services
    │   ├── color-service.ts
    │   ├── help-service.ts
    │   ├── plan-service.ts
    │   └── syntax-highlight-service.ts
├── docker-compose.yml
├── gulpfile.ts
├── karma.conf.js
├── logo.ai
├── package.json
├── test-main.js
├── tools
    ├── config.ts
    ├── tasks
    │   ├── build.bundles.ts
    │   ├── build.deps.ts
    │   ├── build.docs.ts
    │   ├── build.fonts.dev.ts
    │   ├── build.html_css.prod.ts
    │   ├── build.img.dev.ts
    │   ├── build.index.ts
    │   ├── build.js.dev.ts
    │   ├── build.js.prod.ts
    │   ├── build.sass.dev.ts
    │   ├── build.test.ts
    │   ├── check.versions.ts
    │   ├── clean.ts
    │   ├── karma.start.ts
    │   ├── npm.ts
    │   ├── serve.docs.ts
    │   ├── server.start.ts
    │   ├── tsd.ts
    │   ├── tslint.ts
    │   ├── watch.dev.ts
    │   ├── watch.serve.ts
    │   └── watch.test.ts
    ├── typings
    │   ├── connect-livereload.d.ts
    │   ├── gulp-load-plugins.d.ts
    │   ├── karma.d.ts
    │   ├── merge-stream.d.ts
    │   ├── open.d.ts
    │   ├── run-sequence.d.ts
    │   ├── slash.d.ts
    │   ├── systemjs-builder.d.ts
    │   ├── tiny-lr.d.ts
    │   └── yargs.d.ts
    ├── utils.ts
    └── utils
    │   ├── server.ts
    │   ├── tasks_tools.ts
    │   ├── template-injectables.ts
    │   └── template-locals.ts
├── tsconfig.json
├── tsd.json
├── tsdrc
└── tslint.json


/.editorconfig:
--------------------------------------------------------------------------------
 1 | # http://editorconfig.org
 2 | 
 3 | root = true
 4 | 
 5 | [*]
 6 | charset = utf-8
 7 | indent_style = space
 8 | indent_size = 2
 9 | end_of_line = lf
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 | 
13 | [*.md]
14 | insert_final_newline = false
15 | trim_trailing_whitespace = false
16 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | # Logs
 2 | logs
 3 | *.log
 4 | 
 5 | # Runtime data
 6 | pids
 7 | *.pid
 8 | *.seed
 9 | 
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 | 
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 | 
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 | 
19 | # Compiled binary addons (http://nodejs.org/api/addons.html)
20 | build/Release
21 | 
22 | node_modules
23 | .sass-cache
24 | 
25 | # Users Environment Variables
26 | .lock-wscript
27 | .tsdrc
28 | 
29 | #IDE configuration files
30 | .idea
31 | .vscode
32 | 
33 | dist
34 | dev
35 | docs
36 | lib
37 | test
38 | tools/typings/tsd
39 | tmp
40 | 


--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
 1 | {
 2 |   "bitwise": true,
 3 |   "immed": true,
 4 |   "newcap": true,
 5 |   "noarg": true,
 6 |   "noempty": true,
 7 |   "nonew": true,
 8 |   "trailing": true,
 9 |   "maxlen": 200,
10 |   "boss": true,
11 |   "eqnull": true,
12 |   "expr": true,
13 |   "globalstrict": true,
14 |   "laxbreak": true,
15 |   "loopfunc": true,
16 |   "sub": true,
17 |   "undef": true,
18 |   "indent": 2,
19 |   "unused": true,
20 | 
21 |   "node": true,
22 |   "globals": {
23 |     "System": true
24 |   }
25 | }
26 | 


--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
 1 | FROM debian:jessie
 2 | 
 3 | RUN apt-get update && apt-get -y install apt-transport-https curl g++ make python ruby
 4 | 
 5 | RUN curl -sL https://deb.nodesource.com/setup_4.x | bash -
 6 | 
 7 | RUN apt-get -y install nodejs
 8 | 
 9 | COPY tsdrc ~/.tsdrc
10 | 
11 | COPY . /var/www/pev
12 | 
13 | WORKDIR /var/www/pev
14 | 
15 | RUN npm install -g gulp
16 | 
17 | RUN npm install
18 | 
19 | CMD cd /var/www/pev && npm start
20 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2016 Alex Tatiyants
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | # Postgres Explain Visualizer (pev)
 2 | 
 3 | Postgres Explain Visualizer (dev) is designed to make [EXPLAIN](http://www.postgresql.org/docs/current/static/sql-explain.html) output easier to grok. It creates a graphical representation of the plan. You can see it in action at [tatiyants.com/pev](http://tatiyants.com/pev/), or read about it [on my blog](http://tatiyants.com/postgres-query-plan-visualization/).
 4 | 
 5 | Pev is heavily influenced by the excellent [explain.depesz.com](http://explain.depesz.com/).
 6 | 
 7 | Pev is written in [angular 2](https://angular.io/) with [TypeScript](http://www.typescriptlang.org/). The project is based on [angular2 seed](https://github.com/mgechev/angular2-seed). It requires [npm](https://www.npmjs.com/), [gulp](http://gulpjs.com/), [tsd](http://definitelytyped.org/tsd/), and [compass](http://compass-style.org/).
 8 | 
 9 | 
10 | ## Installation
11 | 
12 | ```
13 | npm install
14 | npm start
15 | ```
16 | 
17 | You may also need to install tsd and compass:
18 | 
19 | ```
20 | npm install tsd -g
21 | gem install compass
22 | ```
23 | 
24 | ## Build
25 | To build, run the build command for a specific environment. For example, the following will create a production distribution:
26 | 
27 | ```
28 | npm start build.prod
29 | ```
30 | 


--------------------------------------------------------------------------------
/app/assets/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexTatiyants/pev/6d31cdd75f557761d7581da6c46586792e5f2dad/app/assets/fonts/FontAwesome.otf


--------------------------------------------------------------------------------
/app/assets/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexTatiyants/pev/6d31cdd75f557761d7581da6c46586792e5f2dad/app/assets/fonts/fontawesome-webfont.eot


--------------------------------------------------------------------------------
/app/assets/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexTatiyants/pev/6d31cdd75f557761d7581da6c46586792e5f2dad/app/assets/fonts/fontawesome-webfont.ttf


--------------------------------------------------------------------------------
/app/assets/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexTatiyants/pev/6d31cdd75f557761d7581da6c46586792e5f2dad/app/assets/fonts/fontawesome-webfont.woff


--------------------------------------------------------------------------------
/app/assets/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexTatiyants/pev/6d31cdd75f557761d7581da6c46586792e5f2dad/app/assets/fonts/fontawesome-webfont.woff2


--------------------------------------------------------------------------------
/app/assets/fonts/noto-sans-700.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexTatiyants/pev/6d31cdd75f557761d7581da6c46586792e5f2dad/app/assets/fonts/noto-sans-700.ttf


--------------------------------------------------------------------------------
/app/assets/fonts/noto-sans-700italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexTatiyants/pev/6d31cdd75f557761d7581da6c46586792e5f2dad/app/assets/fonts/noto-sans-700italic.ttf


--------------------------------------------------------------------------------
/app/assets/fonts/noto-sans-italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexTatiyants/pev/6d31cdd75f557761d7581da6c46586792e5f2dad/app/assets/fonts/noto-sans-italic.ttf


--------------------------------------------------------------------------------
/app/assets/fonts/noto-sans-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexTatiyants/pev/6d31cdd75f557761d7581da6c46586792e5f2dad/app/assets/fonts/noto-sans-regular.ttf


--------------------------------------------------------------------------------
/app/assets/fonts/source-code-pro-300.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexTatiyants/pev/6d31cdd75f557761d7581da6c46586792e5f2dad/app/assets/fonts/source-code-pro-300.ttf


--------------------------------------------------------------------------------
/app/assets/fonts/source-code-pro-500.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexTatiyants/pev/6d31cdd75f557761d7581da6c46586792e5f2dad/app/assets/fonts/source-code-pro-500.ttf


--------------------------------------------------------------------------------
/app/assets/img/pev-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexTatiyants/pev/6d31cdd75f557761d7581da6c46586792e5f2dad/app/assets/img/pev-logo.png


--------------------------------------------------------------------------------
/app/assets/sass/_buttons.scss:
--------------------------------------------------------------------------------
 1 | .btn {
 2 |    border-radius: $border-radius-base;
 3 |    padding: $padding-base $padding-lg;
 4 |    font-size: $font-size-base;
 5 |    line-height: 1.2;
 6 |    text-decoration: none;
 7 |    text-transform: uppercase;
 8 |    cursor: pointer;
 9 |    margin-left: $padding-base;
10 | 
11 |    &-default {
12 |    border: 1px solid $blue;
13 |    color: $blue;
14 |    background-color: #fff;
15 | 
16 |       &:hover {
17 |          background-color: $light-blue;
18 |          color: #fff;
19 |       }
20 |    }
21 | 
22 |    &-lg {
23 |       padding: $padding-base $padding-lg*2;
24 |       font-size: round($font-size-base * 1.2);
25 |    }
26 | 
27 |    &-sm {
28 |       padding: $padding-base $padding-base;
29 |       font-size: round($font-size-base * 0.9);
30 |    }
31 | 
32 |    &-slim {
33 |       .fa { margin: 0};
34 |    }
35 | 
36 |    &-link {
37 |       border: 0;
38 |       background-color: transparent;
39 |       color: $link-color;
40 |       padding: 0;
41 |    }
42 | 
43 |    &-danger {
44 |       border: 1px solid $red;
45 |       color: $red;
46 |       text-transform: uppercase;
47 |       background-color: #fff;
48 | 
49 |       &:hover {
50 |          background-color: $light-red;
51 |       }
52 |    }
53 | 
54 |    &-primary {
55 |       border: 0;
56 |       background: $blue;
57 |       box-shadow: 1px 1px 1px $gray;
58 |       color: #ffffff;
59 | 
60 |       &:hover {
61 |          background: $dark-blue;
62 |       }
63 |    }
64 | 
65 |    &-close {
66 |       @extend .btn-slim;
67 |          border-radius: 50%;
68 |          box-shadow: 0;
69 |          line-height: 1.5;
70 |          border: 1px solid $line-color;
71 |          background-color: #fff;
72 |       }
73 |    }
74 | 
75 | .button-group {
76 |    button {
77 |       margin: 0;
78 |       background-color: $gray-lightest;
79 |       border-radius: 0;
80 |       float: left;
81 |       border: 1px solid $line-color;
82 |       cursor: pointer;
83 | 
84 |       &:first-of-type {
85 |          border-top-left-radius: $border-radius-lg;
86 |          border-bottom-left-radius: $border-radius-lg;
87 |       }
88 |       &:last-of-type {
89 |          border-top-right-radius: $border-radius-lg;
90 |          border-bottom-right-radius: $border-radius-lg;
91 |       }
92 |    }
93 |    
94 |    .selected {
95 |       background-color: $gray-light;
96 |    }
97 | }
98 | 


--------------------------------------------------------------------------------
/app/assets/sass/_common.scss:
--------------------------------------------------------------------------------
 1 | html {
 2 | 	height: 100%;
 3 | }
 4 | 
 5 | body {
 6 | 	font-size: $font-size-base;
 7 | 	font-weight: 300;
 8 | 	color: $text-color;
 9 | 	height: 100%;
10 | 	width: 100%;
11 | 	background-color: $bg-color;
12 | 	line-height: $line-height-base;
13 | }
14 | 
15 | strong {
16 | 	font-weight: 600;
17 | }
18 | 
19 | body, input, a, button, textarea {
20 | 	font-family: $font-family-sans-serif;
21 | 	font-weight: 300;
22 | }
23 | 
24 | .text-muted {
25 | 	color: $text-color-light;
26 | }
27 | 
28 | .text-warning {
29 |    color: $alert-color;
30 | }
31 | 
32 | .hero-container {
33 |    margin: $padding-lg * 3;
34 |    font-size: $font-size-xl;
35 |    text-align: center;
36 | }
37 | 
38 | .pull-right {
39 | 	float: right;
40 | }
41 | 
42 | .align-right {
43 |    text-align: right;
44 | }
45 | 
46 | a {
47 | 	color: $link-color;
48 | 	text-decoration: none;
49 | }
50 | 
51 | .fa {
52 | 	margin-right: $padding-sm;
53 | }
54 | 
55 | .clickable {
56 |    cursor: pointer;
57 | }
58 | 
59 | code, .code {
60 |    font-family: $font-family-mono;
61 |    font-weight: 600;
62 | }
63 | 
64 | .pad-left {
65 |    margin-left: $padding-lg;
66 | }
67 | 
68 | .pad-top {
69 |    margin-top: $padding-lg;
70 | }
71 | 
72 | .pad-bottom {
73 |    margin-bottom: $padding-lg;
74 | }
75 | 
76 | [tooltip]:before {
77 |     width: 150px;
78 |     text-transform: none;
79 |     text-align: left;
80 |     content: attr(tooltip);
81 |     position: absolute;
82 |     opacity: 0;
83 |     transition: all 0.15s ease;
84 |     padding: $padding-lg;
85 |     color: #fff;
86 |     border-radius: $border-radius-lg;
87 |     margin-top: -10px;
88 |     margin-left: 20px;
89 |     z-index: 5;
90 |     pointer-events: none;
91 | }
92 | 
93 | [tooltip]:hover:before {
94 |     opacity: 1;
95 |     background: $dark-blue;
96 | }
97 | 


--------------------------------------------------------------------------------
/app/assets/sass/_fonts.scss:
--------------------------------------------------------------------------------
 1 | @font-face {
 2 |   font-family: 'noto';
 3 |   src: url('../fonts/noto-sans-regular.ttf') format('truetype');
 4 |   font-weight: 300;
 5 |   font-style: normal;
 6 | }
 7 | 
 8 | @font-face {
 9 |   font-family: 'noto';
10 |   src: url('../fonts/noto-sans-italic.ttf') format('truetype');
11 |   font-weight: 300;
12 |   font-style: italic;
13 | }
14 | 
15 | @font-face {
16 |   font-family: 'noto';
17 |   src: url('../fonts/noto-sans-700.ttf') format('truetype');
18 |   font-weight: 600;
19 |   font-style: normal;
20 | }
21 | 
22 | @font-face {
23 |   font-family: 'noto';
24 |   src: url('../fonts/noto-sans-700italic.ttf') format('truetype');
25 |   font-weight: 600;
26 |   font-style: italic;
27 | }
28 | 
29 | @font-face {
30 |   font-family: 'source code';
31 |   src: url('../fonts/source-code-pro-300.ttf') format('truetype');
32 |   font-weight: 300;
33 |   font-style: normal;
34 | }
35 | 
36 | @font-face {
37 |   font-family: 'source code';
38 |   src: url('../fonts/source-code-pro-500.ttf') format('truetype');
39 |   font-weight: 600;
40 |   font-style: normal;
41 | }
42 | 


--------------------------------------------------------------------------------
/app/assets/sass/_footer.scss:
--------------------------------------------------------------------------------
 1 | footer {
 2 |    border-top: 2px solid $gray-light;
 3 |    margin: round($padding-lg * 2);
 4 |    padding: round($padding-lg * 2);
 5 |    color: $gray;
 6 |    text-align: center;
 7 |    width: 600px;
 8 |    margin: auto;
 9 | 
10 |    .fa {
11 |       font-size: $font-size-lg;
12 |       margin-left: $padding-base;
13 |    }
14 | }
15 | 


--------------------------------------------------------------------------------
/app/assets/sass/_forms.scss:
--------------------------------------------------------------------------------
 1 | @import "compass/css3/user-interface";
 2 | 
 3 | .input-box {
 4 |   @include input-placeholder {
 5 |     color: $gray;
 6 |     font-size: round($font-size-base * 1.4);
 7 |   }
 8 | 
 9 |   &:focus {
10 |     box-shadow: 0 0 5px rgba(81, 203, 238, 1);
11 |   }
12 | 
13 |   &-main {
14 |     font-size: round($font-size-base * 1.4);
15 |     width: 700px;
16 |     border: 0;
17 |     border-bottom: 2px solid $blue;
18 |     padding: $padding-lg;
19 |     margin-top: $padding-lg;
20 |     margin-bottom: $padding-lg;
21 |   }
22 | 
23 |     &-lg {
24 |       width: 98%;
25 |       height: 210px;
26 |       margin-bottom: $padding-base;
27 |       margin-bottom: $padding-lg;
28 |       border-radius: $border-radius-base;
29 |       border: 1px solid $line-color;
30 |       padding: $padding-lg;
31 |     }
32 | }
33 | 
34 | .error-message {
35 |    background-color: $alert-color;
36 |    padding: $padding-base;
37 |    color: #fff;
38 | }
39 | 


--------------------------------------------------------------------------------
/app/assets/sass/_highlight.scss:
--------------------------------------------------------------------------------
  1 | .plan-query-container {
  2 |    border: 1px solid $line-color;
  3 |    padding: $padding-xl;
  4 |    background-color: #fff;
  5 |    position: absolute;
  6 |    box-shadow: 0px 0px 10px 2px rgba(0,0,0,0.3);
  7 |    border-radius: $border-radius-base;
  8 |    margin-bottom: $padding-xl;
  9 |    z-index: 6;
 10 |    left: 0;
 11 | 
 12 |    code {
 13 |       font-weight: 300;
 14 |    }
 15 | 
 16 |    .plan-query-text {
 17 |       background-color: #fff;
 18 |       font-family: $font-family-mono;
 19 |       text-align: left;
 20 | 
 21 |       .code-key-item {
 22 |          background-color: $yellow;
 23 |          font-weight: 600;
 24 |          padding: 1px;
 25 |       }
 26 |    }
 27 | 
 28 |    h3 {
 29 |       font-size: $font-size-lg;
 30 |       width: 93%;
 31 |       text-align: left;
 32 |       border-bottom: 1px solid $line-color;
 33 |       padding-bottom: $padding-base;
 34 |       margin-bottom: $padding-lg;
 35 |    }
 36 | }
 37 | 
 38 | .hljs, .hljs-subst {
 39 |   color: $text-color;
 40 | }
 41 | 
 42 | .hljs-keyword,
 43 | .hljs-attribute,
 44 | .hljs-selector-tag,
 45 | .hljs-meta-keyword,
 46 | .hljs-doctag,
 47 | .hljs-name {
 48 |    color: $dark-blue;
 49 |    font-weight: bold;
 50 | }
 51 | 
 52 | .hljs-built_in,
 53 | .hljs-literal,
 54 | .hljs-bullet,
 55 | .hljs-code,
 56 | .hljs-addition {
 57 |    color: $dark-blue;
 58 |    font-weight: bold;
 59 | }
 60 | 
 61 | .hljs-regexp,
 62 | .hljs-symbol,
 63 | .hljs-variable,
 64 | .hljs-template-variable,
 65 | .hljs-link,
 66 | .hljs-selector-attr,
 67 | .hljs-selector-pseudo {
 68 |    color: $dark-blue;
 69 |    font-weight: bold;
 70 | }
 71 | 
 72 | .hljs-type,
 73 | .hljs-string,
 74 | .hljs-number,
 75 | .hljs-selector-id,
 76 | .hljs-selector-class,
 77 | .hljs-quote,
 78 | .hljs-template-tag,
 79 | .hljs-deletion {
 80 |   color: $green;
 81 |   font-weight: 600;
 82 | }
 83 | 
 84 | .hljs-title,
 85 | .hljs-section {
 86 |   color: $dark-blue;
 87 |   font-weight: bold;
 88 | }
 89 | 
 90 | .hljs-comment {
 91 |   color: $text-color-light;
 92 |   font-style: italic;
 93 | }
 94 | 
 95 | .hljs-meta {
 96 |   color: $text-color-light;
 97 | }
 98 | 
 99 | .hljs-emphasis {
100 |   font-style: italic;
101 | }
102 | 
103 | .hljs-strong {
104 |   font-weight: 600;
105 | }
106 | 


--------------------------------------------------------------------------------
/app/assets/sass/_menu.scss:
--------------------------------------------------------------------------------
 1 | $menu-offset: 120px;
 2 | $menu-toggle-height: 45px;
 3 | 
 4 | .menu {
 5 |    width: 190px;
 6 |    height: 260px;
 7 |    position: absolute;
 8 |    font-size: $font-size-sm;
 9 |    top: $menu-offset - 5;
10 |    left: 0;
11 |    background-color: $gray-dark;
12 |    box-shadow: 1px 1px 2px 1px rgba(0,0,0,0.2);
13 |    color: #fff;
14 |    border-top-right-radius: $border-radius-base;
15 |    border-bottom-right-radius: $border-radius-base;
16 |    z-index: 1;
17 | 
18 |    header {
19 |       h3 {
20 |          padding-top: $padding-lg;
21 |          margin-bottom: $padding-base;
22 |          font-size: round($font-size-base * 1.2);
23 |          font-weight: 600;
24 |          line-height: 2;
25 |          text-align: right;
26 |          padding-right: $padding-lg * 2;
27 |       }
28 |    }
29 | 
30 |    ul {
31 |       margin-left: $padding-lg;
32 | 
33 |       li {
34 |          line-height: 2.5;
35 |       }
36 |    }
37 | 
38 |    &-toggle {
39 |       font-size: round($font-size-lg * 1.3);
40 |       float: left;
41 |       padding-left: $padding-base;
42 |       line-height: 2;
43 |       cursor: pointer;
44 |    }
45 | 
46 |    &-hidden {
47 |       width: $menu-toggle-height;
48 |       height: $menu-toggle-height;
49 |       border-top-right-radius: 50%;
50 |       border-bottom-right-radius: 50%;
51 | 
52 |       ul, h3 {
53 |          visibility: hidden;
54 |       }
55 |    }
56 | 
57 |    .button-group {
58 |       display: flex;
59 |       margin-bottom: $padding-base;
60 |    }
61 | }
62 | 


--------------------------------------------------------------------------------
/app/assets/sass/_modal.scss:
--------------------------------------------------------------------------------
 1 | .modal-backdrop {
 2 |     position: fixed;
 3 |     top: 0;
 4 |     right: 0;
 5 |     bottom: 0;
 6 |     left: 0;
 7 |     z-index: 1040;
 8 |     background-color: #000;
 9 |     opacity: 0.1;
10 | }
11 | 
12 | .modal {
13 |    position: fixed;
14 |    top: 0;
15 |    right: 0;
16 |    bottom: 0;
17 |    left: 0;
18 |    z-index: 1050;
19 |    transition: all 1s;
20 | 
21 |    .modal-dialog {
22 |       position: relative;
23 |       transform: translate(0);
24 |       margin: 100px auto;
25 |       width: 500px;
26 |       opacity: 1;
27 | 
28 |       .modal-content {
29 |          padding: 30px;
30 |          position: relative;
31 |          background-color: #fff;
32 |          background-clip: padding-box;
33 |          border: 1px solid #999;
34 |          border: 1px solid rgba(0,0,0,.2);
35 |          border-radius: 6px;
36 |          outline: 0;
37 |          box-shadow: 0 2px 2px rgba(0,0,0,.5);
38 |          display: block;
39 | 
40 |          .modal-body {
41 |             padding: $padding-sm;
42 |             margin-bottom: $padding-xl;
43 |             text-align: left;
44 |             line-height: 1.5;
45 |          }
46 | 
47 |          .modal-footer {
48 |             text-align: right;
49 | 
50 |             button {
51 |                margin-left: $padding-sm;
52 |             }
53 |          }
54 |       }
55 |    }
56 | }
57 | 


--------------------------------------------------------------------------------
/app/assets/sass/_nav.scss:
--------------------------------------------------------------------------------
 1 | nav {
 2 |   font-size: round($font-size-base * 1.3);
 3 |   background-color: #fff;
 4 |   padding: round($padding-lg * 1.5);
 5 | 
 6 |   .nav-container {
 7 |     width: $page-width;
 8 |     margin: auto;
 9 |   }
10 | 
11 |   .about {
12 |      float: right;
13 |      line-height: 2;
14 |      margin-left: $padding-xl;
15 |  }
16 | }
17 | 


--------------------------------------------------------------------------------
/app/assets/sass/_page.scss:
--------------------------------------------------------------------------------
 1 | .page {
 2 |   padding-top: $padding-lg;
 3 |   margin: auto;
 4 |   width: $page-width;
 5 |   min-height: 600px;
 6 | 
 7 |    em {
 8 |       font-style: italic;
 9 |    }
10 | }
11 | 
12 | .page-content {
13 |     h2 {
14 |       font-size: round($font-size-base * 2);
15 |       line-height: 2;
16 |     }
17 |     h3 {
18 |       font-size: round($font-size-base * 1.7);
19 |       margin-top: $padding-xl;
20 |       line-height: 2;
21 |    }
22 | 
23 |    p, ul {
24 |       font-size: round($font-size-base * 1.2);
25 |       line-height: 1.5;
26 |       margin-bottom: $padding-xl;
27 |    }
28 | 
29 |    ul {
30 |       list-style-type: disc;
31 |       position: relative;
32 |       left: $padding-xl;
33 |    }
34 | }
35 | 
36 | .page-stretch {
37 |   @extend .page;
38 |   margin: auto;
39 |   width: 95%;
40 | 
41 |   h2 {
42 |      text-align: left;
43 |      font-size: $font-size-lg;
44 |      max-width: $page-width;
45 |      margin: auto;
46 |      margin-bottom: $padding-lg*2;
47 | 
48 |      .sub-title {
49 |         font-size: $font-size-base;
50 |         font-style: italic;
51 |      }
52 |  }
53 | }
54 | 


--------------------------------------------------------------------------------
/app/assets/sass/_plan-node.scss:
--------------------------------------------------------------------------------
  1 | .plan-node {
  2 |    text-decoration: none;
  3 |    color: $text-color;
  4 |    display: inline-block;
  5 |    transition: all 0.1s;
  6 |    position: relative;
  7 |    padding: $padding-base $padding-lg;
  8 | 	background-color: #fff;
  9 | 	font-size: $font-size-sm;
 10 | 	border: 1px solid $line-color;
 11 |    margin-bottom: 4px;
 12 | 	border-radius: $border-radius-base;
 13 |    overflow-wrap: break-word;
 14 |    word-wrap: break-word;
 15 |    word-break: break-all;
 16 |    width: 240px;
 17 |    box-shadow: 1px 1px 3px 0px rgba(0,0,0,0.1);
 18 | 
 19 |    header {
 20 |       margin-bottom: $padding-base;
 21 |       overflow: hidden;
 22 |       cursor: pointer;
 23 | 
 24 |       &:hover {
 25 |          background-color: $gray-lightest;
 26 |       }
 27 | 
 28 |       h4 {
 29 |          font-size: $font-size-base;
 30 |          float: left;
 31 |          font-weight: 600;
 32 |       }
 33 | 
 34 |       .node-duration {
 35 |          float: right;
 36 |          margin-left: $padding-lg;
 37 |          font-size: $font-size-base;
 38 |       }
 39 |    }
 40 | 
 41 |    .prop-list {
 42 |       float: left;
 43 |       text-align: left;
 44 |       overflow-wrap: break-word;
 45 |       word-wrap: break-word;
 46 |       word-break: break-all;
 47 |       margin-top: $padding-lg;
 48 |       margin-bottom: $padding-base;
 49 |    }
 50 | 
 51 |    .relation-name {
 52 |      text-align: left;
 53 |    }
 54 | 
 55 |    .planner-estimate {
 56 |       border-top: 1px solid $line-color;
 57 |    	text-align: left;
 58 |       padding-top: $padding-sm;
 59 |    	margin-top: $padding-base;
 60 |    	width: 100%;
 61 |    }
 62 | 
 63 |    .tags {
 64 |       margin-top: $padding-base;
 65 |       text-align: left;
 66 | 
 67 |       span {
 68 |          display: inline-block;
 69 |          background-color: $alert-color;
 70 |          color: #fff;
 71 |          font-size: round($font-size-sm * 0.8);
 72 |          font-weight: 600;
 73 |          margin-right: $padding-sm;
 74 |          margin-bottom: $padding-sm;
 75 |          padding: $padding-sm;
 76 |          border-radius: $border-radius-base;
 77 |          line-height: 1.1;
 78 |       }
 79 |    }
 80 | 
 81 |    //hovers
 82 |    &:hover {
 83 |      border-color: $highlight-color;
 84 |    }
 85 | 
 86 |    .node-description {
 87 |       text-align: left;
 88 |       font-style: italic;
 89 |       padding-top: $padding-lg;
 90 |       word-break: normal;
 91 | 
 92 |       .node-type {
 93 |          font-weight: 600;
 94 |          background-color: $blue;
 95 |          color: #fff;
 96 |          padding: 0 $padding-base;
 97 |       }
 98 |    }
 99 | 
100 |    .btn-default {
101 |       border: 0;
102 |    }
103 | }
104 | 
105 | .node-bar-container {
106 |    height: 5px;
107 |    margin-top: $padding-lg;
108 |    margin-bottom: $padding-sm;
109 |    border-radius: $border-radius-lg;
110 |    background-color: $gray-light;
111 |    position: relative;
112 | 
113 |    .node-bar {
114 |       border-radius: $border-radius-lg;
115 |       height: 100%;
116 |       text-align: left;
117 |       position: absolute;
118 |       left: 0;
119 |       top: 0;
120 |    }
121 | }
122 | 
123 | .node-bar-label {
124 |    text-align: left;
125 |    display: block;
126 | }
127 | 
128 | .expanded {
129 |    width: 400px !important;
130 |    overflow: visible !important;
131 |    padding: $padding-base $padding-lg !important;
132 |    .tags span {
133 |       margin-right: $padding-sm !important;
134 |    }
135 | }
136 | 
137 | .compact {
138 |    width: 140px;
139 | }
140 | 
141 | .dot {
142 |    width: 30px;
143 |    overflow: hidden;
144 |    padding: $padding-sm;
145 | 
146 |    .tags span {
147 |       margin-right: 1px;
148 |    }
149 | 
150 |    .node-bar-container {
151 |       margin-top: $padding-sm;
152 |    }
153 | }
154 | 


--------------------------------------------------------------------------------
/app/assets/sass/_plan.scss:
--------------------------------------------------------------------------------
  1 | $connector-height: 12px;
  2 | $connector-line: 2px solid darken($line-color, 10%);
  3 | 
  4 | .plan {
  5 | 	padding-bottom: $padding-lg * 3;
  6 |    margin-left: 100px;
  7 | 
  8 | 	ul {
  9 | 		display: flex;
 10 | 		padding-top: $connector-height;
 11 | 		position: relative;
 12 | 		margin: auto;
 13 | 		transition: all 0.5s;
 14 |       margin-top: -5px;
 15 | 
 16 | 		// vertical connector
 17 | 		ul::before {
 18 | 			content: '';
 19 | 			position: absolute; top: 0; left: 50%;
 20 | 			border-left: $connector-line;
 21 | 			height: $connector-height;
 22 | 			width: 0;
 23 | 		}
 24 | 
 25 | 		li {
 26 | 			float: left; text-align: center;
 27 | 			list-style-type: none;
 28 | 			position: relative;
 29 | 			padding: $connector-height $padding-sm 0 $padding-sm;
 30 | 			transition: all 0.5s;
 31 | 
 32 | 			// connectors
 33 | 			&:before, &:after {
 34 | 				content: '';
 35 | 				position: absolute; top: 0; right: 50%;
 36 | 				border-top: $connector-line;
 37 | 				width: 50%; height: $connector-height;
 38 | 			}
 39 | 
 40 | 			&:after {
 41 | 				right: auto; left: 50%;
 42 | 				border-left: $connector-line;
 43 | 			}
 44 | 
 45 | 			&:only-child {
 46 | 				padding-top: 0;
 47 | 				&:after, &:before {
 48 | 					display: none;
 49 | 				}
 50 | 			}
 51 | 
 52 | 			&:first-child::before, &:last-child::after {
 53 | 				border: 0 none;
 54 | 			}
 55 | 
 56 | 			&:last-child::before {
 57 | 				border-right: $connector-line;
 58 | 				border-radius: 0 $border-radius-lg 0 0;
 59 | 			}
 60 | 
 61 | 			&:first-child::after {
 62 | 				border-radius: $border-radius-lg 0 0 0;
 63 | 			}
 64 | 
 65 | 			//hovers
 66 | 			.plan-node:hover+ul::before {
 67 | 				border-color: $highlight-color;
 68 | 			}
 69 | 
 70 | 			.plan-node:hover+ul li::after,
 71 | 			.plan-node:hover+ul li::before,
 72 | 			.plan-node:hover+ul ul::before{
 73 | 					border-color:  $highlight-color-dark;
 74 | 				}
 75 | 		}
 76 | 	}
 77 | }
 78 | 
 79 | .plan-stats {
 80 |    display: flex;
 81 |    font-size: $font-size-base;
 82 |    margin: $padding-lg auto $padding-lg auto;
 83 |    padding-bottom: $padding-lg;
 84 |    border-bottom: 1px solid $line-color;
 85 |    border-radius: $border-radius-lg*2;
 86 |    width: 650px;
 87 |    position: relative;
 88 | 
 89 |    div {
 90 |       padding-right: $padding-lg;
 91 |       flex-grow: 1;
 92 |    }
 93 | 
 94 |    .stat-value {
 95 |       display: block;
 96 |       text-align: center;
 97 |       font-size: $font-size-lg;
 98 |    }
 99 | 
100 |    .stat-label {
101 |       display: block;
102 |       text-align: center;
103 |       font-size: $font-size-sm;
104 |    }
105 | 
106 |    $triangle-size: 9px;
107 |    &:after {
108 |       content:'';
109 |       position: absolute;
110 |       top: 100%;
111 |       left: 50%;
112 |       margin-left: $triangle-size*-1;
113 |       width: 0;
114 |       height: 0;
115 |       border-top: solid $triangle-size $line-color;
116 |       border-left: solid $triangle-size transparent;
117 |       border-right: solid $triangle-size transparent;
118 |    }
119 | 
120 |    .btn-close {
121 |       padding: $padding-base;
122 |       background-color: transparent;
123 |       font-size: $font-size-lg;
124 |       text-align: center;
125 |       @extend .text-muted;
126 |       margin-left: $padding-base;
127 |       cursor: pointer;
128 |       border: 0;
129 |    }
130 | }
131 | 


--------------------------------------------------------------------------------
/app/assets/sass/_table.scss:
--------------------------------------------------------------------------------
 1 | .table {
 2 |   width: 100%;
 3 | 
 4 |   td {
 5 |     border-bottom: 1px solid $line-color;
 6 |     padding: $padding-base;
 7 |   }
 8 | 
 9 |   tr:hover {
10 |     background-color: $gray-lightest;
11 |   }
12 | }
13 | 


--------------------------------------------------------------------------------
/app/assets/sass/_variables.scss:
--------------------------------------------------------------------------------
 1 | //vars
 2 | $page-width: 1000px;
 3 | 
 4 | $padding-base: 6px;
 5 | $padding-sm: 3px;
 6 | $padding-lg: 10px;
 7 | $padding-xl: 18px;
 8 | 
 9 | $font-size-base: 13px;
10 | $font-size-xs: round($font-size-base * 0.7);
11 | $font-size-sm: round($font-size-base * 0.9);
12 | $font-size-lg: round($font-size-base * 1.3);
13 | $font-size-xl: round($font-size-base * 1.7);
14 | 
15 | $font-family-sans-serif: 'noto';
16 | $font-family-mono: 'source code';
17 | 
18 | $line-height-base: 1.3;
19 | 
20 | $gray-lightest: #f7f7f7;
21 | $gray-light: darken($gray-lightest, 10%);
22 | $gray: darken(#f7f7f7, 30%);
23 | $gray-dark: darken(#f7f7f7, 50%);
24 | $gray-darkest: darken($gray-lightest, 70%);
25 | 
26 | $blue: #00B5E2;
27 | $dark-blue: #008CAF;
28 | $light-blue: #65DDFB;
29 | 
30 | $red: #AF2F11;
31 | $dark-red: #7C210C;
32 | $light-red: #FB8165;
33 | 
34 | $green: #279404;
35 | $yellow: #F8E400;
36 | 
37 | $bg-color: $gray-lightest;
38 | 
39 | $text-color: #4d525a;
40 | $text-color-light: lighten($text-color, 30%);
41 | 
42 | $line-color: $gray-light;
43 | $line-color-light: lighten($gray-light, 10%);
44 | 
45 | $link-color: $blue;
46 | 
47 | $border-radius-base: 3px;
48 | $border-radius-lg: 6px;
49 | 
50 | $main-color: $blue;
51 | $main-color-dark: $blue;
52 | 
53 | $highlight-color: $blue;
54 | $highlight-color-dark: $dark-blue;
55 | 
56 | $alert-color: #FB4418;
57 | 


--------------------------------------------------------------------------------
/app/assets/sass/font-awesome/_animated.scss:
--------------------------------------------------------------------------------
 1 | // Spinning Icons
 2 | // --------------------------
 3 | 
 4 | .#{$fa-css-prefix}-spin {
 5 |   -webkit-animation: fa-spin 2s infinite linear;
 6 |           animation: fa-spin 2s infinite linear;
 7 | }
 8 | 
 9 | .#{$fa-css-prefix}-pulse {
10 |   -webkit-animation: fa-spin 1s infinite steps(8);
11 |           animation: fa-spin 1s infinite steps(8);
12 | }
13 | 
14 | @-webkit-keyframes fa-spin {
15 |   0% {
16 |     -webkit-transform: rotate(0deg);
17 |             transform: rotate(0deg);
18 |   }
19 |   100% {
20 |     -webkit-transform: rotate(359deg);
21 |             transform: rotate(359deg);
22 |   }
23 | }
24 | 
25 | @keyframes fa-spin {
26 |   0% {
27 |     -webkit-transform: rotate(0deg);
28 |             transform: rotate(0deg);
29 |   }
30 |   100% {
31 |     -webkit-transform: rotate(359deg);
32 |             transform: rotate(359deg);
33 |   }
34 | }
35 | 


--------------------------------------------------------------------------------
/app/assets/sass/font-awesome/_bordered-pulled.scss:
--------------------------------------------------------------------------------
 1 | // Bordered & Pulled
 2 | // -------------------------
 3 | 
 4 | .#{$fa-css-prefix}-border {
 5 |   padding: .2em .25em .15em;
 6 |   border: solid .08em $fa-border-color;
 7 |   border-radius: .1em;
 8 | }
 9 | 
10 | .#{$fa-css-prefix}-pull-left { float: left; }
11 | .#{$fa-css-prefix}-pull-right { float: right; }
12 | 
13 | .#{$fa-css-prefix} {
14 |   &.#{$fa-css-prefix}-pull-left { margin-right: .3em; }
15 |   &.#{$fa-css-prefix}-pull-right { margin-left: .3em; }
16 | }
17 | 
18 | /* Deprecated as of 4.4.0 */
19 | .pull-right { float: right; }
20 | .pull-left { float: left; }
21 | 
22 | .#{$fa-css-prefix} {
23 |   &.pull-left { margin-right: .3em; }
24 |   &.pull-right { margin-left: .3em; }
25 | }
26 | 


--------------------------------------------------------------------------------
/app/assets/sass/font-awesome/_core.scss:
--------------------------------------------------------------------------------
 1 | // Base Class Definition
 2 | // -------------------------
 3 | 
 4 | .#{$fa-css-prefix} {
 5 |   display: inline-block;
 6 |   font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration
 7 |   font-size: inherit; // can't have font-size inherit on line above, so need to override
 8 |   text-rendering: auto; // optimizelegibility throws things off #1094
 9 |   -webkit-font-smoothing: antialiased;
10 |   -moz-osx-font-smoothing: grayscale;
11 | 
12 | }
13 | 


--------------------------------------------------------------------------------
/app/assets/sass/font-awesome/_fixed-width.scss:
--------------------------------------------------------------------------------
1 | // Fixed Width Icons
2 | // -------------------------
3 | .#{$fa-css-prefix}-fw {
4 |   width: (18em / 14);
5 |   text-align: center;
6 | }
7 | 


--------------------------------------------------------------------------------
/app/assets/sass/font-awesome/_larger.scss:
--------------------------------------------------------------------------------
 1 | // Icon Sizes
 2 | // -------------------------
 3 | 
 4 | /* makes the font 33% larger relative to the icon container */
 5 | .#{$fa-css-prefix}-lg {
 6 |   font-size: (4em / 3);
 7 |   line-height: (3em / 4);
 8 |   vertical-align: -15%;
 9 | }
10 | .#{$fa-css-prefix}-2x { font-size: 2em; }
11 | .#{$fa-css-prefix}-3x { font-size: 3em; }
12 | .#{$fa-css-prefix}-4x { font-size: 4em; }
13 | .#{$fa-css-prefix}-5x { font-size: 5em; }
14 | 


--------------------------------------------------------------------------------
/app/assets/sass/font-awesome/_list.scss:
--------------------------------------------------------------------------------
 1 | // List Icons
 2 | // -------------------------
 3 | 
 4 | .#{$fa-css-prefix}-ul {
 5 |   padding-left: 0;
 6 |   margin-left: $fa-li-width;
 7 |   list-style-type: none;
 8 |   > li { position: relative; }
 9 | }
10 | .#{$fa-css-prefix}-li {
11 |   position: absolute;
12 |   left: -$fa-li-width;
13 |   width: $fa-li-width;
14 |   top: (2em / 14);
15 |   text-align: center;
16 |   &.#{$fa-css-prefix}-lg {
17 |     left: -$fa-li-width + (4em / 14);
18 |   }
19 | }
20 | 


--------------------------------------------------------------------------------
/app/assets/sass/font-awesome/_mixins.scss:
--------------------------------------------------------------------------------
 1 | // Mixins
 2 | // --------------------------
 3 | 
 4 | @mixin fa-icon() {
 5 |   display: inline-block;
 6 |   font: normal normal normal #{$fa-font-size-base}/#{$fa-line-height-base} FontAwesome; // shortening font declaration
 7 |   font-size: inherit; // can't have font-size inherit on line above, so need to override
 8 |   text-rendering: auto; // optimizelegibility throws things off #1094
 9 |   -webkit-font-smoothing: antialiased;
10 |   -moz-osx-font-smoothing: grayscale;
11 | 
12 | }
13 | 
14 | @mixin fa-icon-rotate($degrees, $rotation) {
15 |   filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
16 |   -webkit-transform: rotate($degrees);
17 |       -ms-transform: rotate($degrees);
18 |           transform: rotate($degrees);
19 | }
20 | 
21 | @mixin fa-icon-flip($horiz, $vert, $rotation) {
22 |   filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
23 |   -webkit-transform: scale($horiz, $vert);
24 |       -ms-transform: scale($horiz, $vert);
25 |           transform: scale($horiz, $vert);
26 | }
27 | 


--------------------------------------------------------------------------------
/app/assets/sass/font-awesome/_path.scss:
--------------------------------------------------------------------------------
 1 | /* FONT PATH
 2 |  * -------------------------- */
 3 | 
 4 | @font-face {
 5 |   font-family: 'FontAwesome';
 6 |   src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}');
 7 |   src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'),
 8 |     url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'),
 9 |     url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'),
10 |     url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'),
11 |     url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg');
12 | //  src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
13 |   font-weight: normal;
14 |   font-style: normal;
15 | }
16 | 


--------------------------------------------------------------------------------
/app/assets/sass/font-awesome/_rotated-flipped.scss:
--------------------------------------------------------------------------------
 1 | // Rotated & Flipped Icons
 2 | // -------------------------
 3 | 
 4 | .#{$fa-css-prefix}-rotate-90  { @include fa-icon-rotate(90deg, 1);  }
 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); }
 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); }
 7 | 
 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); }
 9 | .#{$fa-css-prefix}-flip-vertical   { @include fa-icon-flip(1, -1, 2); }
10 | 
11 | // Hook for IE8-9
12 | // -------------------------
13 | 
14 | :root .#{$fa-css-prefix}-rotate-90,
15 | :root .#{$fa-css-prefix}-rotate-180,
16 | :root .#{$fa-css-prefix}-rotate-270,
17 | :root .#{$fa-css-prefix}-flip-horizontal,
18 | :root .#{$fa-css-prefix}-flip-vertical {
19 |   filter: none;
20 | }
21 | 


--------------------------------------------------------------------------------
/app/assets/sass/font-awesome/_stacked.scss:
--------------------------------------------------------------------------------
 1 | // Stacked Icons
 2 | // -------------------------
 3 | 
 4 | .#{$fa-css-prefix}-stack {
 5 |   position: relative;
 6 |   display: inline-block;
 7 |   width: 2em;
 8 |   height: 2em;
 9 |   line-height: 2em;
10 |   vertical-align: middle;
11 | }
12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x {
13 |   position: absolute;
14 |   left: 0;
15 |   width: 100%;
16 |   text-align: center;
17 | }
18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; }
19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; }
20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; }
21 | 


--------------------------------------------------------------------------------
/app/assets/sass/font-awesome/_styles.scss:
--------------------------------------------------------------------------------
 1 | /*!
 2 |  *  Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome
 3 |  *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
 4 |  */
 5 | 
 6 | @import "variables";
 7 | @import "mixins";
 8 | @import "path";
 9 | @import "core";
10 | @import "larger";
11 | @import "fixed-width";
12 | @import "list";
13 | @import "bordered-pulled";
14 | @import "animated";
15 | @import "rotated-flipped";
16 | @import "stacked";
17 | @import "icons";
18 | 


--------------------------------------------------------------------------------
/app/assets/sass/font-awesome/_variables.scss:
--------------------------------------------------------------------------------
  1 | // Variables
  2 | // --------------------------
  3 | 
  4 | $fa-font-path:        "../fonts" !default;
  5 | $fa-font-size-base:   14px !default;
  6 | $fa-line-height-base: 1 !default;
  7 | //$fa-font-path:        "//netdna.bootstrapcdn.com/font-awesome/4.5.0/fonts" !default; // for referencing Bootstrap CDN font files directly
  8 | $fa-css-prefix:       fa !default;
  9 | $fa-version:          "4.5.0" !default;
 10 | $fa-border-color:     #eee !default;
 11 | $fa-inverse:          #fff !default;
 12 | $fa-li-width:         (30em / 14) !default;
 13 | 
 14 | $fa-var-500px: "\f26e";
 15 | $fa-var-adjust: "\f042";
 16 | $fa-var-adn: "\f170";
 17 | $fa-var-align-center: "\f037";
 18 | $fa-var-align-justify: "\f039";
 19 | $fa-var-align-left: "\f036";
 20 | $fa-var-align-right: "\f038";
 21 | $fa-var-amazon: "\f270";
 22 | $fa-var-ambulance: "\f0f9";
 23 | $fa-var-anchor: "\f13d";
 24 | $fa-var-android: "\f17b";
 25 | $fa-var-angellist: "\f209";
 26 | $fa-var-angle-double-down: "\f103";
 27 | $fa-var-angle-double-left: "\f100";
 28 | $fa-var-angle-double-right: "\f101";
 29 | $fa-var-angle-double-up: "\f102";
 30 | $fa-var-angle-down: "\f107";
 31 | $fa-var-angle-left: "\f104";
 32 | $fa-var-angle-right: "\f105";
 33 | $fa-var-angle-up: "\f106";
 34 | $fa-var-apple: "\f179";
 35 | $fa-var-archive: "\f187";
 36 | $fa-var-area-chart: "\f1fe";
 37 | $fa-var-arrow-circle-down: "\f0ab";
 38 | $fa-var-arrow-circle-left: "\f0a8";
 39 | $fa-var-arrow-circle-o-down: "\f01a";
 40 | $fa-var-arrow-circle-o-left: "\f190";
 41 | $fa-var-arrow-circle-o-right: "\f18e";
 42 | $fa-var-arrow-circle-o-up: "\f01b";
 43 | $fa-var-arrow-circle-right: "\f0a9";
 44 | $fa-var-arrow-circle-up: "\f0aa";
 45 | $fa-var-arrow-down: "\f063";
 46 | $fa-var-arrow-left: "\f060";
 47 | $fa-var-arrow-right: "\f061";
 48 | $fa-var-arrow-up: "\f062";
 49 | $fa-var-arrows: "\f047";
 50 | $fa-var-arrows-alt: "\f0b2";
 51 | $fa-var-arrows-h: "\f07e";
 52 | $fa-var-arrows-v: "\f07d";
 53 | $fa-var-asterisk: "\f069";
 54 | $fa-var-at: "\f1fa";
 55 | $fa-var-automobile: "\f1b9";
 56 | $fa-var-backward: "\f04a";
 57 | $fa-var-balance-scale: "\f24e";
 58 | $fa-var-ban: "\f05e";
 59 | $fa-var-bank: "\f19c";
 60 | $fa-var-bar-chart: "\f080";
 61 | $fa-var-bar-chart-o: "\f080";
 62 | $fa-var-barcode: "\f02a";
 63 | $fa-var-bars: "\f0c9";
 64 | $fa-var-battery-0: "\f244";
 65 | $fa-var-battery-1: "\f243";
 66 | $fa-var-battery-2: "\f242";
 67 | $fa-var-battery-3: "\f241";
 68 | $fa-var-battery-4: "\f240";
 69 | $fa-var-battery-empty: "\f244";
 70 | $fa-var-battery-full: "\f240";
 71 | $fa-var-battery-half: "\f242";
 72 | $fa-var-battery-quarter: "\f243";
 73 | $fa-var-battery-three-quarters: "\f241";
 74 | $fa-var-bed: "\f236";
 75 | $fa-var-beer: "\f0fc";
 76 | $fa-var-behance: "\f1b4";
 77 | $fa-var-behance-square: "\f1b5";
 78 | $fa-var-bell: "\f0f3";
 79 | $fa-var-bell-o: "\f0a2";
 80 | $fa-var-bell-slash: "\f1f6";
 81 | $fa-var-bell-slash-o: "\f1f7";
 82 | $fa-var-bicycle: "\f206";
 83 | $fa-var-binoculars: "\f1e5";
 84 | $fa-var-birthday-cake: "\f1fd";
 85 | $fa-var-bitbucket: "\f171";
 86 | $fa-var-bitbucket-square: "\f172";
 87 | $fa-var-bitcoin: "\f15a";
 88 | $fa-var-black-tie: "\f27e";
 89 | $fa-var-bluetooth: "\f293";
 90 | $fa-var-bluetooth-b: "\f294";
 91 | $fa-var-bold: "\f032";
 92 | $fa-var-bolt: "\f0e7";
 93 | $fa-var-bomb: "\f1e2";
 94 | $fa-var-book: "\f02d";
 95 | $fa-var-bookmark: "\f02e";
 96 | $fa-var-bookmark-o: "\f097";
 97 | $fa-var-briefcase: "\f0b1";
 98 | $fa-var-btc: "\f15a";
 99 | $fa-var-bug: "\f188";
100 | $fa-var-building: "\f1ad";
101 | $fa-var-building-o: "\f0f7";
102 | $fa-var-bullhorn: "\f0a1";
103 | $fa-var-bullseye: "\f140";
104 | $fa-var-bus: "\f207";
105 | $fa-var-buysellads: "\f20d";
106 | $fa-var-cab: "\f1ba";
107 | $fa-var-calculator: "\f1ec";
108 | $fa-var-calendar: "\f073";
109 | $fa-var-calendar-check-o: "\f274";
110 | $fa-var-calendar-minus-o: "\f272";
111 | $fa-var-calendar-o: "\f133";
112 | $fa-var-calendar-plus-o: "\f271";
113 | $fa-var-calendar-times-o: "\f273";
114 | $fa-var-camera: "\f030";
115 | $fa-var-camera-retro: "\f083";
116 | $fa-var-car: "\f1b9";
117 | $fa-var-caret-down: "\f0d7";
118 | $fa-var-caret-left: "\f0d9";
119 | $fa-var-caret-right: "\f0da";
120 | $fa-var-caret-square-o-down: "\f150";
121 | $fa-var-caret-square-o-left: "\f191";
122 | $fa-var-caret-square-o-right: "\f152";
123 | $fa-var-caret-square-o-up: "\f151";
124 | $fa-var-caret-up: "\f0d8";
125 | $fa-var-cart-arrow-down: "\f218";
126 | $fa-var-cart-plus: "\f217";
127 | $fa-var-cc: "\f20a";
128 | $fa-var-cc-amex: "\f1f3";
129 | $fa-var-cc-diners-club: "\f24c";
130 | $fa-var-cc-discover: "\f1f2";
131 | $fa-var-cc-jcb: "\f24b";
132 | $fa-var-cc-mastercard: "\f1f1";
133 | $fa-var-cc-paypal: "\f1f4";
134 | $fa-var-cc-stripe: "\f1f5";
135 | $fa-var-cc-visa: "\f1f0";
136 | $fa-var-certificate: "\f0a3";
137 | $fa-var-chain: "\f0c1";
138 | $fa-var-chain-broken: "\f127";
139 | $fa-var-check: "\f00c";
140 | $fa-var-check-circle: "\f058";
141 | $fa-var-check-circle-o: "\f05d";
142 | $fa-var-check-square: "\f14a";
143 | $fa-var-check-square-o: "\f046";
144 | $fa-var-chevron-circle-down: "\f13a";
145 | $fa-var-chevron-circle-left: "\f137";
146 | $fa-var-chevron-circle-right: "\f138";
147 | $fa-var-chevron-circle-up: "\f139";
148 | $fa-var-chevron-down: "\f078";
149 | $fa-var-chevron-left: "\f053";
150 | $fa-var-chevron-right: "\f054";
151 | $fa-var-chevron-up: "\f077";
152 | $fa-var-child: "\f1ae";
153 | $fa-var-chrome: "\f268";
154 | $fa-var-circle: "\f111";
155 | $fa-var-circle-o: "\f10c";
156 | $fa-var-circle-o-notch: "\f1ce";
157 | $fa-var-circle-thin: "\f1db";
158 | $fa-var-clipboard: "\f0ea";
159 | $fa-var-clock-o: "\f017";
160 | $fa-var-clone: "\f24d";
161 | $fa-var-close: "\f00d";
162 | $fa-var-cloud: "\f0c2";
163 | $fa-var-cloud-download: "\f0ed";
164 | $fa-var-cloud-upload: "\f0ee";
165 | $fa-var-cny: "\f157";
166 | $fa-var-code: "\f121";
167 | $fa-var-code-fork: "\f126";
168 | $fa-var-codepen: "\f1cb";
169 | $fa-var-codiepie: "\f284";
170 | $fa-var-coffee: "\f0f4";
171 | $fa-var-cog: "\f013";
172 | $fa-var-cogs: "\f085";
173 | $fa-var-columns: "\f0db";
174 | $fa-var-comment: "\f075";
175 | $fa-var-comment-o: "\f0e5";
176 | $fa-var-commenting: "\f27a";
177 | $fa-var-commenting-o: "\f27b";
178 | $fa-var-comments: "\f086";
179 | $fa-var-comments-o: "\f0e6";
180 | $fa-var-compass: "\f14e";
181 | $fa-var-compress: "\f066";
182 | $fa-var-connectdevelop: "\f20e";
183 | $fa-var-contao: "\f26d";
184 | $fa-var-copy: "\f0c5";
185 | $fa-var-copyright: "\f1f9";
186 | $fa-var-creative-commons: "\f25e";
187 | $fa-var-credit-card: "\f09d";
188 | $fa-var-credit-card-alt: "\f283";
189 | $fa-var-crop: "\f125";
190 | $fa-var-crosshairs: "\f05b";
191 | $fa-var-css3: "\f13c";
192 | $fa-var-cube: "\f1b2";
193 | $fa-var-cubes: "\f1b3";
194 | $fa-var-cut: "\f0c4";
195 | $fa-var-cutlery: "\f0f5";
196 | $fa-var-dashboard: "\f0e4";
197 | $fa-var-dashcube: "\f210";
198 | $fa-var-database: "\f1c0";
199 | $fa-var-dedent: "\f03b";
200 | $fa-var-delicious: "\f1a5";
201 | $fa-var-desktop: "\f108";
202 | $fa-var-deviantart: "\f1bd";
203 | $fa-var-diamond: "\f219";
204 | $fa-var-digg: "\f1a6";
205 | $fa-var-dollar: "\f155";
206 | $fa-var-dot-circle-o: "\f192";
207 | $fa-var-download: "\f019";
208 | $fa-var-dribbble: "\f17d";
209 | $fa-var-dropbox: "\f16b";
210 | $fa-var-drupal: "\f1a9";
211 | $fa-var-edge: "\f282";
212 | $fa-var-edit: "\f044";
213 | $fa-var-eject: "\f052";
214 | $fa-var-ellipsis-h: "\f141";
215 | $fa-var-ellipsis-v: "\f142";
216 | $fa-var-empire: "\f1d1";
217 | $fa-var-envelope: "\f0e0";
218 | $fa-var-envelope-o: "\f003";
219 | $fa-var-envelope-square: "\f199";
220 | $fa-var-eraser: "\f12d";
221 | $fa-var-eur: "\f153";
222 | $fa-var-euro: "\f153";
223 | $fa-var-exchange: "\f0ec";
224 | $fa-var-exclamation: "\f12a";
225 | $fa-var-exclamation-circle: "\f06a";
226 | $fa-var-exclamation-triangle: "\f071";
227 | $fa-var-expand: "\f065";
228 | $fa-var-expeditedssl: "\f23e";
229 | $fa-var-external-link: "\f08e";
230 | $fa-var-external-link-square: "\f14c";
231 | $fa-var-eye: "\f06e";
232 | $fa-var-eye-slash: "\f070";
233 | $fa-var-eyedropper: "\f1fb";
234 | $fa-var-facebook: "\f09a";
235 | $fa-var-facebook-f: "\f09a";
236 | $fa-var-facebook-official: "\f230";
237 | $fa-var-facebook-square: "\f082";
238 | $fa-var-fast-backward: "\f049";
239 | $fa-var-fast-forward: "\f050";
240 | $fa-var-fax: "\f1ac";
241 | $fa-var-feed: "\f09e";
242 | $fa-var-female: "\f182";
243 | $fa-var-fighter-jet: "\f0fb";
244 | $fa-var-file: "\f15b";
245 | $fa-var-file-archive-o: "\f1c6";
246 | $fa-var-file-audio-o: "\f1c7";
247 | $fa-var-file-code-o: "\f1c9";
248 | $fa-var-file-excel-o: "\f1c3";
249 | $fa-var-file-image-o: "\f1c5";
250 | $fa-var-file-movie-o: "\f1c8";
251 | $fa-var-file-o: "\f016";
252 | $fa-var-file-pdf-o: "\f1c1";
253 | $fa-var-file-photo-o: "\f1c5";
254 | $fa-var-file-picture-o: "\f1c5";
255 | $fa-var-file-powerpoint-o: "\f1c4";
256 | $fa-var-file-sound-o: "\f1c7";
257 | $fa-var-file-text: "\f15c";
258 | $fa-var-file-text-o: "\f0f6";
259 | $fa-var-file-video-o: "\f1c8";
260 | $fa-var-file-word-o: "\f1c2";
261 | $fa-var-file-zip-o: "\f1c6";
262 | $fa-var-files-o: "\f0c5";
263 | $fa-var-film: "\f008";
264 | $fa-var-filter: "\f0b0";
265 | $fa-var-fire: "\f06d";
266 | $fa-var-fire-extinguisher: "\f134";
267 | $fa-var-firefox: "\f269";
268 | $fa-var-flag: "\f024";
269 | $fa-var-flag-checkered: "\f11e";
270 | $fa-var-flag-o: "\f11d";
271 | $fa-var-flash: "\f0e7";
272 | $fa-var-flask: "\f0c3";
273 | $fa-var-flickr: "\f16e";
274 | $fa-var-floppy-o: "\f0c7";
275 | $fa-var-folder: "\f07b";
276 | $fa-var-folder-o: "\f114";
277 | $fa-var-folder-open: "\f07c";
278 | $fa-var-folder-open-o: "\f115";
279 | $fa-var-font: "\f031";
280 | $fa-var-fonticons: "\f280";
281 | $fa-var-fort-awesome: "\f286";
282 | $fa-var-forumbee: "\f211";
283 | $fa-var-forward: "\f04e";
284 | $fa-var-foursquare: "\f180";
285 | $fa-var-frown-o: "\f119";
286 | $fa-var-futbol-o: "\f1e3";
287 | $fa-var-gamepad: "\f11b";
288 | $fa-var-gavel: "\f0e3";
289 | $fa-var-gbp: "\f154";
290 | $fa-var-ge: "\f1d1";
291 | $fa-var-gear: "\f013";
292 | $fa-var-gears: "\f085";
293 | $fa-var-genderless: "\f22d";
294 | $fa-var-get-pocket: "\f265";
295 | $fa-var-gg: "\f260";
296 | $fa-var-gg-circle: "\f261";
297 | $fa-var-gift: "\f06b";
298 | $fa-var-git: "\f1d3";
299 | $fa-var-git-square: "\f1d2";
300 | $fa-var-github: "\f09b";
301 | $fa-var-github-alt: "\f113";
302 | $fa-var-github-square: "\f092";
303 | $fa-var-gittip: "\f184";
304 | $fa-var-glass: "\f000";
305 | $fa-var-globe: "\f0ac";
306 | $fa-var-google: "\f1a0";
307 | $fa-var-google-plus: "\f0d5";
308 | $fa-var-google-plus-square: "\f0d4";
309 | $fa-var-google-wallet: "\f1ee";
310 | $fa-var-graduation-cap: "\f19d";
311 | $fa-var-gratipay: "\f184";
312 | $fa-var-group: "\f0c0";
313 | $fa-var-h-square: "\f0fd";
314 | $fa-var-hacker-news: "\f1d4";
315 | $fa-var-hand-grab-o: "\f255";
316 | $fa-var-hand-lizard-o: "\f258";
317 | $fa-var-hand-o-down: "\f0a7";
318 | $fa-var-hand-o-left: "\f0a5";
319 | $fa-var-hand-o-right: "\f0a4";
320 | $fa-var-hand-o-up: "\f0a6";
321 | $fa-var-hand-paper-o: "\f256";
322 | $fa-var-hand-peace-o: "\f25b";
323 | $fa-var-hand-pointer-o: "\f25a";
324 | $fa-var-hand-rock-o: "\f255";
325 | $fa-var-hand-scissors-o: "\f257";
326 | $fa-var-hand-spock-o: "\f259";
327 | $fa-var-hand-stop-o: "\f256";
328 | $fa-var-hashtag: "\f292";
329 | $fa-var-hdd-o: "\f0a0";
330 | $fa-var-header: "\f1dc";
331 | $fa-var-headphones: "\f025";
332 | $fa-var-heart: "\f004";
333 | $fa-var-heart-o: "\f08a";
334 | $fa-var-heartbeat: "\f21e";
335 | $fa-var-history: "\f1da";
336 | $fa-var-home: "\f015";
337 | $fa-var-hospital-o: "\f0f8";
338 | $fa-var-hotel: "\f236";
339 | $fa-var-hourglass: "\f254";
340 | $fa-var-hourglass-1: "\f251";
341 | $fa-var-hourglass-2: "\f252";
342 | $fa-var-hourglass-3: "\f253";
343 | $fa-var-hourglass-end: "\f253";
344 | $fa-var-hourglass-half: "\f252";
345 | $fa-var-hourglass-o: "\f250";
346 | $fa-var-hourglass-start: "\f251";
347 | $fa-var-houzz: "\f27c";
348 | $fa-var-html5: "\f13b";
349 | $fa-var-i-cursor: "\f246";
350 | $fa-var-ils: "\f20b";
351 | $fa-var-image: "\f03e";
352 | $fa-var-inbox: "\f01c";
353 | $fa-var-indent: "\f03c";
354 | $fa-var-industry: "\f275";
355 | $fa-var-info: "\f129";
356 | $fa-var-info-circle: "\f05a";
357 | $fa-var-inr: "\f156";
358 | $fa-var-instagram: "\f16d";
359 | $fa-var-institution: "\f19c";
360 | $fa-var-internet-explorer: "\f26b";
361 | $fa-var-intersex: "\f224";
362 | $fa-var-ioxhost: "\f208";
363 | $fa-var-italic: "\f033";
364 | $fa-var-joomla: "\f1aa";
365 | $fa-var-jpy: "\f157";
366 | $fa-var-jsfiddle: "\f1cc";
367 | $fa-var-key: "\f084";
368 | $fa-var-keyboard-o: "\f11c";
369 | $fa-var-krw: "\f159";
370 | $fa-var-language: "\f1ab";
371 | $fa-var-laptop: "\f109";
372 | $fa-var-lastfm: "\f202";
373 | $fa-var-lastfm-square: "\f203";
374 | $fa-var-leaf: "\f06c";
375 | $fa-var-leanpub: "\f212";
376 | $fa-var-legal: "\f0e3";
377 | $fa-var-lemon-o: "\f094";
378 | $fa-var-level-down: "\f149";
379 | $fa-var-level-up: "\f148";
380 | $fa-var-life-bouy: "\f1cd";
381 | $fa-var-life-buoy: "\f1cd";
382 | $fa-var-life-ring: "\f1cd";
383 | $fa-var-life-saver: "\f1cd";
384 | $fa-var-lightbulb-o: "\f0eb";
385 | $fa-var-line-chart: "\f201";
386 | $fa-var-link: "\f0c1";
387 | $fa-var-linkedin: "\f0e1";
388 | $fa-var-linkedin-square: "\f08c";
389 | $fa-var-linux: "\f17c";
390 | $fa-var-list: "\f03a";
391 | $fa-var-list-alt: "\f022";
392 | $fa-var-list-ol: "\f0cb";
393 | $fa-var-list-ul: "\f0ca";
394 | $fa-var-location-arrow: "\f124";
395 | $fa-var-lock: "\f023";
396 | $fa-var-long-arrow-down: "\f175";
397 | $fa-var-long-arrow-left: "\f177";
398 | $fa-var-long-arrow-right: "\f178";
399 | $fa-var-long-arrow-up: "\f176";
400 | $fa-var-magic: "\f0d0";
401 | $fa-var-magnet: "\f076";
402 | $fa-var-mail-forward: "\f064";
403 | $fa-var-mail-reply: "\f112";
404 | $fa-var-mail-reply-all: "\f122";
405 | $fa-var-male: "\f183";
406 | $fa-var-map: "\f279";
407 | $fa-var-map-marker: "\f041";
408 | $fa-var-map-o: "\f278";
409 | $fa-var-map-pin: "\f276";
410 | $fa-var-map-signs: "\f277";
411 | $fa-var-mars: "\f222";
412 | $fa-var-mars-double: "\f227";
413 | $fa-var-mars-stroke: "\f229";
414 | $fa-var-mars-stroke-h: "\f22b";
415 | $fa-var-mars-stroke-v: "\f22a";
416 | $fa-var-maxcdn: "\f136";
417 | $fa-var-meanpath: "\f20c";
418 | $fa-var-medium: "\f23a";
419 | $fa-var-medkit: "\f0fa";
420 | $fa-var-meh-o: "\f11a";
421 | $fa-var-mercury: "\f223";
422 | $fa-var-microphone: "\f130";
423 | $fa-var-microphone-slash: "\f131";
424 | $fa-var-minus: "\f068";
425 | $fa-var-minus-circle: "\f056";
426 | $fa-var-minus-square: "\f146";
427 | $fa-var-minus-square-o: "\f147";
428 | $fa-var-mixcloud: "\f289";
429 | $fa-var-mobile: "\f10b";
430 | $fa-var-mobile-phone: "\f10b";
431 | $fa-var-modx: "\f285";
432 | $fa-var-money: "\f0d6";
433 | $fa-var-moon-o: "\f186";
434 | $fa-var-mortar-board: "\f19d";
435 | $fa-var-motorcycle: "\f21c";
436 | $fa-var-mouse-pointer: "\f245";
437 | $fa-var-music: "\f001";
438 | $fa-var-navicon: "\f0c9";
439 | $fa-var-neuter: "\f22c";
440 | $fa-var-newspaper-o: "\f1ea";
441 | $fa-var-object-group: "\f247";
442 | $fa-var-object-ungroup: "\f248";
443 | $fa-var-odnoklassniki: "\f263";
444 | $fa-var-odnoklassniki-square: "\f264";
445 | $fa-var-opencart: "\f23d";
446 | $fa-var-openid: "\f19b";
447 | $fa-var-opera: "\f26a";
448 | $fa-var-optin-monster: "\f23c";
449 | $fa-var-outdent: "\f03b";
450 | $fa-var-pagelines: "\f18c";
451 | $fa-var-paint-brush: "\f1fc";
452 | $fa-var-paper-plane: "\f1d8";
453 | $fa-var-paper-plane-o: "\f1d9";
454 | $fa-var-paperclip: "\f0c6";
455 | $fa-var-paragraph: "\f1dd";
456 | $fa-var-paste: "\f0ea";
457 | $fa-var-pause: "\f04c";
458 | $fa-var-pause-circle: "\f28b";
459 | $fa-var-pause-circle-o: "\f28c";
460 | $fa-var-paw: "\f1b0";
461 | $fa-var-paypal: "\f1ed";
462 | $fa-var-pencil: "\f040";
463 | $fa-var-pencil-square: "\f14b";
464 | $fa-var-pencil-square-o: "\f044";
465 | $fa-var-percent: "\f295";
466 | $fa-var-phone: "\f095";
467 | $fa-var-phone-square: "\f098";
468 | $fa-var-photo: "\f03e";
469 | $fa-var-picture-o: "\f03e";
470 | $fa-var-pie-chart: "\f200";
471 | $fa-var-pied-piper: "\f1a7";
472 | $fa-var-pied-piper-alt: "\f1a8";
473 | $fa-var-pinterest: "\f0d2";
474 | $fa-var-pinterest-p: "\f231";
475 | $fa-var-pinterest-square: "\f0d3";
476 | $fa-var-plane: "\f072";
477 | $fa-var-play: "\f04b";
478 | $fa-var-play-circle: "\f144";
479 | $fa-var-play-circle-o: "\f01d";
480 | $fa-var-plug: "\f1e6";
481 | $fa-var-plus: "\f067";
482 | $fa-var-plus-circle: "\f055";
483 | $fa-var-plus-square: "\f0fe";
484 | $fa-var-plus-square-o: "\f196";
485 | $fa-var-power-off: "\f011";
486 | $fa-var-print: "\f02f";
487 | $fa-var-product-hunt: "\f288";
488 | $fa-var-puzzle-piece: "\f12e";
489 | $fa-var-qq: "\f1d6";
490 | $fa-var-qrcode: "\f029";
491 | $fa-var-question: "\f128";
492 | $fa-var-question-circle: "\f059";
493 | $fa-var-quote-left: "\f10d";
494 | $fa-var-quote-right: "\f10e";
495 | $fa-var-ra: "\f1d0";
496 | $fa-var-random: "\f074";
497 | $fa-var-rebel: "\f1d0";
498 | $fa-var-recycle: "\f1b8";
499 | $fa-var-reddit: "\f1a1";
500 | $fa-var-reddit-alien: "\f281";
501 | $fa-var-reddit-square: "\f1a2";
502 | $fa-var-refresh: "\f021";
503 | $fa-var-registered: "\f25d";
504 | $fa-var-remove: "\f00d";
505 | $fa-var-renren: "\f18b";
506 | $fa-var-reorder: "\f0c9";
507 | $fa-var-repeat: "\f01e";
508 | $fa-var-reply: "\f112";
509 | $fa-var-reply-all: "\f122";
510 | $fa-var-retweet: "\f079";
511 | $fa-var-rmb: "\f157";
512 | $fa-var-road: "\f018";
513 | $fa-var-rocket: "\f135";
514 | $fa-var-rotate-left: "\f0e2";
515 | $fa-var-rotate-right: "\f01e";
516 | $fa-var-rouble: "\f158";
517 | $fa-var-rss: "\f09e";
518 | $fa-var-rss-square: "\f143";
519 | $fa-var-rub: "\f158";
520 | $fa-var-ruble: "\f158";
521 | $fa-var-rupee: "\f156";
522 | $fa-var-safari: "\f267";
523 | $fa-var-save: "\f0c7";
524 | $fa-var-scissors: "\f0c4";
525 | $fa-var-scribd: "\f28a";
526 | $fa-var-search: "\f002";
527 | $fa-var-search-minus: "\f010";
528 | $fa-var-search-plus: "\f00e";
529 | $fa-var-sellsy: "\f213";
530 | $fa-var-send: "\f1d8";
531 | $fa-var-send-o: "\f1d9";
532 | $fa-var-server: "\f233";
533 | $fa-var-share: "\f064";
534 | $fa-var-share-alt: "\f1e0";
535 | $fa-var-share-alt-square: "\f1e1";
536 | $fa-var-share-square: "\f14d";
537 | $fa-var-share-square-o: "\f045";
538 | $fa-var-shekel: "\f20b";
539 | $fa-var-sheqel: "\f20b";
540 | $fa-var-shield: "\f132";
541 | $fa-var-ship: "\f21a";
542 | $fa-var-shirtsinbulk: "\f214";
543 | $fa-var-shopping-bag: "\f290";
544 | $fa-var-shopping-basket: "\f291";
545 | $fa-var-shopping-cart: "\f07a";
546 | $fa-var-sign-in: "\f090";
547 | $fa-var-sign-out: "\f08b";
548 | $fa-var-signal: "\f012";
549 | $fa-var-simplybuilt: "\f215";
550 | $fa-var-sitemap: "\f0e8";
551 | $fa-var-skyatlas: "\f216";
552 | $fa-var-skype: "\f17e";
553 | $fa-var-slack: "\f198";
554 | $fa-var-sliders: "\f1de";
555 | $fa-var-slideshare: "\f1e7";
556 | $fa-var-smile-o: "\f118";
557 | $fa-var-soccer-ball-o: "\f1e3";
558 | $fa-var-sort: "\f0dc";
559 | $fa-var-sort-alpha-asc: "\f15d";
560 | $fa-var-sort-alpha-desc: "\f15e";
561 | $fa-var-sort-amount-asc: "\f160";
562 | $fa-var-sort-amount-desc: "\f161";
563 | $fa-var-sort-asc: "\f0de";
564 | $fa-var-sort-desc: "\f0dd";
565 | $fa-var-sort-down: "\f0dd";
566 | $fa-var-sort-numeric-asc: "\f162";
567 | $fa-var-sort-numeric-desc: "\f163";
568 | $fa-var-sort-up: "\f0de";
569 | $fa-var-soundcloud: "\f1be";
570 | $fa-var-space-shuttle: "\f197";
571 | $fa-var-spinner: "\f110";
572 | $fa-var-spoon: "\f1b1";
573 | $fa-var-spotify: "\f1bc";
574 | $fa-var-square: "\f0c8";
575 | $fa-var-square-o: "\f096";
576 | $fa-var-stack-exchange: "\f18d";
577 | $fa-var-stack-overflow: "\f16c";
578 | $fa-var-star: "\f005";
579 | $fa-var-star-half: "\f089";
580 | $fa-var-star-half-empty: "\f123";
581 | $fa-var-star-half-full: "\f123";
582 | $fa-var-star-half-o: "\f123";
583 | $fa-var-star-o: "\f006";
584 | $fa-var-steam: "\f1b6";
585 | $fa-var-steam-square: "\f1b7";
586 | $fa-var-step-backward: "\f048";
587 | $fa-var-step-forward: "\f051";
588 | $fa-var-stethoscope: "\f0f1";
589 | $fa-var-sticky-note: "\f249";
590 | $fa-var-sticky-note-o: "\f24a";
591 | $fa-var-stop: "\f04d";
592 | $fa-var-stop-circle: "\f28d";
593 | $fa-var-stop-circle-o: "\f28e";
594 | $fa-var-street-view: "\f21d";
595 | $fa-var-strikethrough: "\f0cc";
596 | $fa-var-stumbleupon: "\f1a4";
597 | $fa-var-stumbleupon-circle: "\f1a3";
598 | $fa-var-subscript: "\f12c";
599 | $fa-var-subway: "\f239";
600 | $fa-var-suitcase: "\f0f2";
601 | $fa-var-sun-o: "\f185";
602 | $fa-var-superscript: "\f12b";
603 | $fa-var-support: "\f1cd";
604 | $fa-var-table: "\f0ce";
605 | $fa-var-tablet: "\f10a";
606 | $fa-var-tachometer: "\f0e4";
607 | $fa-var-tag: "\f02b";
608 | $fa-var-tags: "\f02c";
609 | $fa-var-tasks: "\f0ae";
610 | $fa-var-taxi: "\f1ba";
611 | $fa-var-television: "\f26c";
612 | $fa-var-tencent-weibo: "\f1d5";
613 | $fa-var-terminal: "\f120";
614 | $fa-var-text-height: "\f034";
615 | $fa-var-text-width: "\f035";
616 | $fa-var-th: "\f00a";
617 | $fa-var-th-large: "\f009";
618 | $fa-var-th-list: "\f00b";
619 | $fa-var-thumb-tack: "\f08d";
620 | $fa-var-thumbs-down: "\f165";
621 | $fa-var-thumbs-o-down: "\f088";
622 | $fa-var-thumbs-o-up: "\f087";
623 | $fa-var-thumbs-up: "\f164";
624 | $fa-var-ticket: "\f145";
625 | $fa-var-times: "\f00d";
626 | $fa-var-times-circle: "\f057";
627 | $fa-var-times-circle-o: "\f05c";
628 | $fa-var-tint: "\f043";
629 | $fa-var-toggle-down: "\f150";
630 | $fa-var-toggle-left: "\f191";
631 | $fa-var-toggle-off: "\f204";
632 | $fa-var-toggle-on: "\f205";
633 | $fa-var-toggle-right: "\f152";
634 | $fa-var-toggle-up: "\f151";
635 | $fa-var-trademark: "\f25c";
636 | $fa-var-train: "\f238";
637 | $fa-var-transgender: "\f224";
638 | $fa-var-transgender-alt: "\f225";
639 | $fa-var-trash: "\f1f8";
640 | $fa-var-trash-o: "\f014";
641 | $fa-var-tree: "\f1bb";
642 | $fa-var-trello: "\f181";
643 | $fa-var-tripadvisor: "\f262";
644 | $fa-var-trophy: "\f091";
645 | $fa-var-truck: "\f0d1";
646 | $fa-var-try: "\f195";
647 | $fa-var-tty: "\f1e4";
648 | $fa-var-tumblr: "\f173";
649 | $fa-var-tumblr-square: "\f174";
650 | $fa-var-turkish-lira: "\f195";
651 | $fa-var-tv: "\f26c";
652 | $fa-var-twitch: "\f1e8";
653 | $fa-var-twitter: "\f099";
654 | $fa-var-twitter-square: "\f081";
655 | $fa-var-umbrella: "\f0e9";
656 | $fa-var-underline: "\f0cd";
657 | $fa-var-undo: "\f0e2";
658 | $fa-var-university: "\f19c";
659 | $fa-var-unlink: "\f127";
660 | $fa-var-unlock: "\f09c";
661 | $fa-var-unlock-alt: "\f13e";
662 | $fa-var-unsorted: "\f0dc";
663 | $fa-var-upload: "\f093";
664 | $fa-var-usb: "\f287";
665 | $fa-var-usd: "\f155";
666 | $fa-var-user: "\f007";
667 | $fa-var-user-md: "\f0f0";
668 | $fa-var-user-plus: "\f234";
669 | $fa-var-user-secret: "\f21b";
670 | $fa-var-user-times: "\f235";
671 | $fa-var-users: "\f0c0";
672 | $fa-var-venus: "\f221";
673 | $fa-var-venus-double: "\f226";
674 | $fa-var-venus-mars: "\f228";
675 | $fa-var-viacoin: "\f237";
676 | $fa-var-video-camera: "\f03d";
677 | $fa-var-vimeo: "\f27d";
678 | $fa-var-vimeo-square: "\f194";
679 | $fa-var-vine: "\f1ca";
680 | $fa-var-vk: "\f189";
681 | $fa-var-volume-down: "\f027";
682 | $fa-var-volume-off: "\f026";
683 | $fa-var-volume-up: "\f028";
684 | $fa-var-warning: "\f071";
685 | $fa-var-wechat: "\f1d7";
686 | $fa-var-weibo: "\f18a";
687 | $fa-var-weixin: "\f1d7";
688 | $fa-var-whatsapp: "\f232";
689 | $fa-var-wheelchair: "\f193";
690 | $fa-var-wifi: "\f1eb";
691 | $fa-var-wikipedia-w: "\f266";
692 | $fa-var-windows: "\f17a";
693 | $fa-var-won: "\f159";
694 | $fa-var-wordpress: "\f19a";
695 | $fa-var-wrench: "\f0ad";
696 | $fa-var-xing: "\f168";
697 | $fa-var-xing-square: "\f169";
698 | $fa-var-y-combinator: "\f23b";
699 | $fa-var-y-combinator-square: "\f1d4";
700 | $fa-var-yahoo: "\f19e";
701 | $fa-var-yc: "\f23b";
702 | $fa-var-yc-square: "\f1d4";
703 | $fa-var-yelp: "\f1e9";
704 | $fa-var-yen: "\f157";
705 | $fa-var-youtube: "\f167";
706 | $fa-var-youtube-play: "\f16a";
707 | $fa-var-youtube-square: "\f166";
708 | 
709 | 


--------------------------------------------------------------------------------
/app/assets/sass/styles.scss:
--------------------------------------------------------------------------------
 1 | @import "compass/reset";
 2 | @import "font-awesome/styles";
 3 | 
 4 | @import "variables";
 5 | @import "fonts";
 6 | @import "common";
 7 | @import "buttons";
 8 | @import "forms";
 9 | @import "nav";
10 | 
11 | @import "plan";
12 | @import "plan-node";
13 | @import "menu";
14 | @import "page";
15 | @import "table";
16 | @import "modal";
17 | @import "footer";
18 | @import "highlight";
19 | 


--------------------------------------------------------------------------------
/app/bootstrap.ts:
--------------------------------------------------------------------------------
 1 | import {provide} from 'angular2/core';
 2 | import {bootstrap} from 'angular2/platform/browser';
 3 | import {ROUTER_PROVIDERS, LocationStrategy, HashLocationStrategy} from 'angular2/router';
 4 | import {App} from './components/app/app';
 5 | 
 6 | bootstrap(App, [
 7 |   ROUTER_PROVIDERS,
 8 |   provide(LocationStrategy, { useClass: HashLocationStrategy })
 9 | ]);
10 | 


--------------------------------------------------------------------------------
/app/components/about/about.html:
--------------------------------------------------------------------------------
 1 | <div class="page page-content">
 2 |    <h2>Postgres EXPLAIN Visualizer (Pev)</h2>
 3 |    <p>
 4 |       Pev is designed to make <a href="http://www.postgresql.org/docs/current/static/sql-explain.html">
 5 |       Postgres query plans</a> easier to grok. It displays a plan as a tree, with each node representing a step that takes in a row set
 6 |       and produces another. Pev can show you a number of useful things:</p>
 7 |       <ul>
 8 |          <li>overall plan stats</li>
 9 |          <li>individual node stats (duration, row size, cost)</li>
10 |          <li>explanation of what each node does</li>
11 |          <li>outlier nodes</li>
12 |          <li>graph of a specific metric (like cost) for all nodes</li>
13 |          <li>for some nodes, highlighted part of the query which corresponds to the node</li>
14 |       </ul>
15 | 
16 |    <p>You can tweak display options using the menu on the right.</p>
17 | 
18 |    <h3>Usage tips</h3>
19 |    <p>Pev currently accepts only JSON formatted plans. In fact, the get the most out of it,
20 |       I recommend generating a query plan using the following line:
21 |       <code>EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON)</code>. I also recommend submitting a (decently formatted)
22 |       SQL query that generated the plan. Doing so will make Pev more useful.
23 |    </p>
24 | 
25 |    <p>Pev will remember the plans you analyzed. They are stored locally and are not submitted to me. This is good
26 |    because no one but you will see your queries. It's also bad because you can't share them with others.</p>
27 | 
28 |    <h3>Acknowledgements</h3>
29 |    <p>Pev was inspired and heavily influenced by the excellent <a href="http://explain.depesz.com/">explain.depesz.com</a>. Both the
30 |       tool and the corresponding help files are a great resource to learn about Postgres and its planner.
31 |    </p>
32 | 
33 |    <h3>Help me improve Pev</h3>
34 |    <p>If you want to help, there are multiple ways to contribute:</p>
35 |    <ul>
36 |       <li>give me your plans - I need more test cases for the layout</li>
37 |       <li>add descriptions for missing node types (especially ones from newer versions of Postgres)</li>
38 |       <li>contribute to the code on <a href="https://github.com/AlexTatiyants/pev">github</a></li>
39 |    </ul>
40 | 
41 | </div>
42 | 


--------------------------------------------------------------------------------
/app/components/about/about.ts:
--------------------------------------------------------------------------------
1 | import {Component} from 'angular2/core';
2 | 
3 | @Component({
4 |     selector: 'about',
5 |     templateUrl: './components/about/about.html'
6 | })
7 | export class About {}
8 | 


--------------------------------------------------------------------------------
/app/components/app/app.html:
--------------------------------------------------------------------------------
 1 | <nav>
 2 |    <div class="nav-container">
 3 |       <a class="about"[routerLink]="['About']">about</a>
 4 |       <a class="btn btn-primary btn-lg pull-right" [routerLink]="['PlanNew']">new plan</a>
 5 |       <a [routerLink]="['PlanList']">plans</a>
 6 |    </div>
 7 | </nav>
 8 | 
 9 | <router-outlet></router-outlet>
10 | 
11 | <footer><strong>pev</strong> is made by
12 |    <a href="http://tatiyants.com/">Alex Tatiyants</a>
13 |    <a href="https://twitter.com/AlexTatiyants"><i class="fa fa-twitter"></i></a>
14 |    <a href="https://github.com/AlexTatiyants"><i class="fa fa-github"></i></a>
15 | </footer>
16 | 


--------------------------------------------------------------------------------
/app/components/app/app.ts:
--------------------------------------------------------------------------------
 1 | import {Component, ViewEncapsulation} from 'angular2/core';
 2 | import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
 3 | 
 4 | import {PlanView} from '../plan-view/plan-view';
 5 | import {PlanList} from '../plan-list/plan-list';
 6 | import {PlanNew} from '../plan-new/plan-new';
 7 | import {About} from '../about/about';
 8 | 
 9 | @Component({
10 |     selector: 'app',
11 |     templateUrl: './components/app/app.html',
12 |     encapsulation: ViewEncapsulation.None,
13 |     directives: [ROUTER_DIRECTIVES]
14 | })
15 | 
16 | @RouteConfig([
17 |     { path: '/', redirectTo: ['/PlanList'] },
18 |     { path: '/plans', component: PlanList, name: 'PlanList' },
19 |     { path: '/plans/new', component: PlanNew, name: 'PlanNew' },
20 |     { path: '/plans/:id', component: PlanView, name: 'PlanView' },
21 |     { path: '/about', component: About, name: 'About'}
22 | ])
23 | 
24 | export class App { }
25 | 


--------------------------------------------------------------------------------
/app/components/plan-list/plan-list.html:
--------------------------------------------------------------------------------
 1 | <div class="page">
 2 |    <div class="hero-container" *ngIf="plans.length === 0">
 3 |       Welcome to PEV! Please <a [routerLink]="['/PlanNew']">submit</a> a plan for visualization
 4 |    </div>
 5 | 
 6 |   <table class="table pad-bottom">
 7 |     <tr *ngFor="#plan of plans">
 8 |       <!-- this is a hack that should be converted to a proper dialog once that is available in angular 2-->
 9 |       <div *ngIf="openDialog">
10 |           <div class="modal-backdrop"></div>
11 |           <div class="modal">
12 |              <div class="modal-dialog">
13 |                 <div class="modal-content">
14 |                   <div class="modal-body">You're about to delete plan "{{planToDelete.name}}". Are you sure?</div>
15 |                   <div class="modal-footer">
16 |                      <button class="btn btn-primary" (click)="deletePlan()">Yes</button>
17 |                      <button class="btn btn-default" (click)="cancelDelete()">No</button>
18 |                   </div>
19 |               </div>
20 |              </div>
21 |           </div>
22 |       </div>
23 | 
24 |       <td width="30%"><a [routerLink]="['/PlanView', {id: plan.id}]">{{plan.name}}</a></td>
25 |       <td>created on {{plan.createdOn | momentDate }}</td>
26 |       <td class="align-right"><button class="btn btn-danger" (click)="requestDelete(plan)">
27 |         <i class="fa fa-trash"></i>delete</button>
28 |      </td>
29 |     </tr>
30 |   </table>
31 | </div>
32 | 


--------------------------------------------------------------------------------
/app/components/plan-list/plan-list.ts:
--------------------------------------------------------------------------------
 1 | import {Component, OnInit} from 'angular2/core';
 2 | import {ROUTER_DIRECTIVES} from 'angular2/router';
 3 | 
 4 | import {IPlan} from '../../interfaces/iplan';
 5 | import {PlanService} from '../../services/plan-service';
 6 | import {PlanNew} from '../plan-new/plan-new';
 7 | 
 8 | import {MomentDatePipe} from '../../pipes';
 9 | 
10 | @Component({
11 |     selector: 'plan-list',
12 |     templateUrl: './components/plan-list/plan-list.html',
13 |     providers: [PlanService],
14 |     directives: [ROUTER_DIRECTIVES, PlanNew],
15 |     pipes: [MomentDatePipe]
16 | })
17 | export class PlanList {
18 |     plans: Array<IPlan>;
19 |     newPlanName: string;
20 |     newPlanContent: any;
21 |     newPlanId: string;
22 |     openDialog: boolean = false;
23 |     planToDelete: IPlan;
24 | 
25 |     constructor(private _planService: PlanService) { }
26 | 
27 |     ngOnInit() {
28 |         this.plans = this._planService.getPlans();
29 |     }
30 | 
31 |     requestDelete(plan) {
32 |         this.openDialog = true;
33 |         this.planToDelete = plan;
34 |     }
35 | 
36 |     deletePlan(plan) {
37 |         this.openDialog = false;
38 |         console.log(this.planToDelete);
39 |         this._planService.deletePlan(this.planToDelete);
40 |         this.plans = this._planService.getPlans();
41 |     }
42 | 
43 |     cancelDelete() {
44 |         this.openDialog = false;
45 |     }
46 | 
47 |     deleteAllPlans() {
48 |         this._planService.deleteAllPlans();
49 |     }
50 | }
51 | 


--------------------------------------------------------------------------------
/app/components/plan-new/plan-new.html:
--------------------------------------------------------------------------------
 1 | <div class="page">
 2 |    <button class="pull-right btn btn-link" (click)="prefill()">create a sample plan</button>
 3 | 
 4 |    <span class="text-muted">For best results, use <code>EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON)</code><br>
 5 |       Psql users can export the plan to a file using <code>psql -qAt -f explain.sql > analyze.json</code></span>
 6 |    <p class="pad-top">DISCLAIMER: Pev stores your plans locally (localStorage) and will not send them anywhere.</p>
 7 |   <div>
 8 |     <input placeholder="name (optional)" class="input-box input-box-main" type="text" [(ngModel)]="newPlanName">
 9 |     <button class="btn btn-default btn-lg pad-top pull-right" (click)="submitPlan()">submit</button>
10 |   </div>
11 | 
12 |   <p *ngIf="validationMessage" class="error-message">{{validationMessage}}</p>
13 |   <textarea placeholder="paste execution plan" class="input-box input-box-lg code" [(ngModel)]="newPlanContent"></textarea>
14 |   <textarea placeholder="paste corresponding SQL query" class="input-box input-box-lg code" [(ngModel)]="newPlanQuery"></textarea>
15 | </div>
16 | 


--------------------------------------------------------------------------------
/app/components/plan-new/plan-new.ts:
--------------------------------------------------------------------------------
  1 | import {Component, OnInit} from 'angular2/core';
  2 | import {Router, ROUTER_DIRECTIVES} from 'angular2/router';
  3 | import {IPlan} from '../../interfaces/iplan';
  4 | 
  5 | import {PlanService} from '../../services/plan-service';
  6 | 
  7 | @Component({
  8 |     selector: 'plan-new',
  9 |     templateUrl: './components/plan-new/plan-new.html',
 10 |     providers: [PlanService],
 11 |     directives: [ROUTER_DIRECTIVES]
 12 | })
 13 | export class PlanNew {
 14 |     planIds: string[];
 15 |     newPlanName: string;
 16 |     newPlanContent: string;
 17 |     newPlanQuery: string;
 18 |     newPlan: IPlan;
 19 |     validationMessage: string;
 20 | 
 21 |     constructor(private _router: Router, private _planService: PlanService) { }
 22 | 
 23 |     submitPlan() {
 24 |         // remove psql generated header
 25 |         this.newPlanContent = this.newPlanContent.replace('QUERY PLAN', '');
 26 | 
 27 |         if (!this._planService.isJsonString(this.newPlanContent)) {
 28 |             this.validationMessage = 'The string you submitted is not valid JSON'
 29 |             return;
 30 |         }
 31 | 
 32 |         this.newPlan = this._planService.createPlan(this.newPlanName, this.newPlanContent, this.newPlanQuery);
 33 |         this._router.navigate(['PlanView', { id: this.newPlan.id }]);
 34 |     }
 35 | 
 36 |     prefill() {
 37 |         this.newPlanName = 'Sample plan';
 38 |         this.newPlanContent = SAMPLE_JSON;
 39 |         this.newPlanQuery = SAMPLE_QUERY;
 40 |     }
 41 | }
 42 | export var SAMPLE_JSON = `[
 43 |   {
 44 |     "Plan": {
 45 |       "Node Type": "Limit",
 46 |       "Startup Cost": 17024.84,
 47 |       "Total Cost": 17024.87,
 48 |       "Plan Rows": 10,
 49 |       "Plan Width": 133,
 50 |       "Actual Startup Time": 725.773,
 51 |       "Actual Total Time": 725.775,
 52 |       "Actual Rows": 10,
 53 |       "Actual Loops": 1,
 54 |       "Output": ["c.state", "cat.categoryname", "(sum(o.netamount))", "(sum(o.totalamount))"],
 55 |       "Shared Hit Blocks": 23,
 56 |       "Shared Read Blocks": 1392,
 57 |       "Shared Dirtied Blocks": 0,
 58 |       "Shared Written Blocks": 0,
 59 |       "Local Hit Blocks": 0,
 60 |       "Local Read Blocks": 0,
 61 |       "Local Dirtied Blocks": 0,
 62 |       "Local Written Blocks": 0,
 63 |       "Temp Read Blocks": 0,
 64 |       "Temp Written Blocks": 0,
 65 |       "I/O Read Time": 0.000,
 66 |       "I/O Write Time": 0.000,
 67 |       "Plans": [
 68 |         {
 69 |           "Node Type": "Sort",
 70 |           "Parent Relationship": "Outer",
 71 |           "Startup Cost": 17024.84,
 72 |           "Total Cost": 17026.88,
 73 |           "Plan Rows": 816,
 74 |           "Plan Width": 133,
 75 |           "Actual Startup Time": 725.771,
 76 |           "Actual Total Time": 725.772,
 77 |           "Actual Rows": 11,
 78 |           "Actual Loops": 1,
 79 |           "Output": ["c.state", "cat.categoryname", "(sum(o.netamount))", "(sum(o.totalamount))"],
 80 |           "Sort Key": ["c.state", "(sum(o.totalamount))"],
 81 |           "Sort Method": "top-N heapsort",
 82 |           "Sort Space Used": 25,
 83 |           "Sort Space Type": "Memory",
 84 |           "Shared Hit Blocks": 23,
 85 |           "Shared Read Blocks": 1392,
 86 |           "Shared Dirtied Blocks": 0,
 87 |           "Shared Written Blocks": 0,
 88 |           "Local Hit Blocks": 0,
 89 |           "Local Read Blocks": 0,
 90 |           "Local Dirtied Blocks": 0,
 91 |           "Local Written Blocks": 0,
 92 |           "Temp Read Blocks": 0,
 93 |           "Temp Written Blocks": 0,
 94 |           "I/O Read Time": 0.000,
 95 |           "I/O Write Time": 0.000,
 96 |           "Plans": [
 97 |             {
 98 |               "Node Type": "Aggregate",
 99 |               "Strategy": "Hashed",
100 |               "Parent Relationship": "Outer",
101 |               "Startup Cost": 16994.41,
102 |               "Total Cost": 17006.65,
103 |               "Plan Rows": 816,
104 |               "Plan Width": 133,
105 |               "Actual Startup Time": 723.877,
106 |               "Actual Total Time": 724.417,
107 |               "Actual Rows": 832,
108 |               "Actual Loops": 1,
109 |               "Output": ["c.state", "cat.categoryname", "sum(o.netamount)", "sum(o.totalamount)"],
110 |               "Group Key": ["c.state", "cat.categoryname"],
111 |               "Shared Hit Blocks": 13,
112 |               "Shared Read Blocks": 1392,
113 |               "Shared Dirtied Blocks": 0,
114 |               "Shared Written Blocks": 0,
115 |               "Local Hit Blocks": 0,
116 |               "Local Read Blocks": 0,
117 |               "Local Dirtied Blocks": 0,
118 |               "Local Written Blocks": 0,
119 |               "Temp Read Blocks": 0,
120 |               "Temp Written Blocks": 0,
121 |               "I/O Read Time": 0.000,
122 |               "I/O Write Time": 0.000,
123 |               "Plans": [
124 |                 {
125 |                   "Node Type": "Hash Join",
126 |                   "Parent Relationship": "Outer",
127 |                   "Join Type": "Inner",
128 |                   "Startup Cost": 4966.48,
129 |                   "Total Cost": 13742.65,
130 |                   "Plan Rows": 325176,
131 |                   "Plan Width": 133,
132 |                   "Actual Startup Time": 118.314,
133 |                   "Actual Total Time": 354.285,
134 |                   "Actual Rows": 383270,
135 |                   "Actual Loops": 1,
136 |                   "Output": ["c.state", "o.netamount", "o.totalamount", "cat.categoryname"],
137 |                   "Hash Cond": "(o.orderid = ch.orderid)",
138 |                   "Shared Hit Blocks": 13,
139 |                   "Shared Read Blocks": 1392,
140 |                   "Shared Dirtied Blocks": 0,
141 |                   "Shared Written Blocks": 0,
142 |                   "Local Hit Blocks": 0,
143 |                   "Local Read Blocks": 0,
144 |                   "Local Dirtied Blocks": 0,
145 |                   "Local Written Blocks": 0,
146 |                   "Temp Read Blocks": 0,
147 |                   "Temp Written Blocks": 0,
148 |                   "I/O Read Time": 0.000,
149 |                   "I/O Write Time": 0.000,
150 |                   "Plans": [
151 |                     {
152 |                       "Node Type": "Hash Join",
153 |                       "Parent Relationship": "Outer",
154 |                       "Join Type": "Inner",
155 |                       "Startup Cost": 834.86,
156 |                       "Total Cost": 4539.11,
157 |                       "Plan Rows": 60350,
158 |                       "Plan Width": 138,
159 |                       "Actual Startup Time": 22.651,
160 |                       "Actual Total Time": 133.484,
161 |                       "Actual Rows": 60350,
162 |                       "Actual Loops": 1,
163 |                       "Output": ["o.netamount", "o.totalamount", "o.orderid", "ol.orderid", "cat.categoryname"],
164 |                       "Hash Cond": "(ol.orderid = o.orderid)",
165 |                       "Shared Hit Blocks": 9,
166 |                       "Shared Read Blocks": 581,
167 |                       "Shared Dirtied Blocks": 0,
168 |                       "Shared Written Blocks": 0,
169 |                       "Local Hit Blocks": 0,
170 |                       "Local Read Blocks": 0,
171 |                       "Local Dirtied Blocks": 0,
172 |                       "Local Written Blocks": 0,
173 |                       "Temp Read Blocks": 0,
174 |                       "Temp Written Blocks": 0,
175 |                       "I/O Read Time": 0.000,
176 |                       "I/O Write Time": 0.000,
177 |                       "Plans": [
178 |                         {
179 |                           "Node Type": "Hash Join",
180 |                           "Parent Relationship": "Outer",
181 |                           "Join Type": "Inner",
182 |                           "Startup Cost": 464.86,
183 |                           "Total Cost": 2962.11,
184 |                           "Plan Rows": 60350,
185 |                           "Plan Width": 122,
186 |                           "Actual Startup Time": 12.467,
187 |                           "Actual Total Time": 85.647,
188 |                           "Actual Rows": 60350,
189 |                           "Actual Loops": 1,
190 |                           "Output": ["ol.orderid", "cat.categoryname"],
191 |                           "Hash Cond": "(ol.prod_id = p.prod_id)",
192 |                           "Shared Hit Blocks": 4,
193 |                           "Shared Read Blocks": 483,
194 |                           "Shared Dirtied Blocks": 0,
195 |                           "Shared Written Blocks": 0,
196 |                           "Local Hit Blocks": 0,
197 |                           "Local Read Blocks": 0,
198 |                           "Local Dirtied Blocks": 0,
199 |                           "Local Written Blocks": 0,
200 |                           "Temp Read Blocks": 0,
201 |                           "Temp Written Blocks": 0,
202 |                           "I/O Read Time": 0.000,
203 |                           "I/O Write Time": 0.000,
204 |                           "Plans": [
205 |                             {
206 |                               "Node Type": "Seq Scan",
207 |                               "Parent Relationship": "Outer",
208 |                               "Relation Name": "orderlines",
209 |                               "Schema": "public",
210 |                               "Alias": "ol",
211 |                               "Startup Cost": 0.00,
212 |                               "Total Cost": 988.50,
213 |                               "Plan Rows": 60350,
214 |                               "Plan Width": 8,
215 |                               "Actual Startup Time": 0.005,
216 |                               "Actual Total Time": 14.054,
217 |                               "Actual Rows": 60350,
218 |                               "Actual Loops": 1,
219 |                               "Output": ["ol.orderlineid", "ol.orderid", "ol.prod_id", "ol.quantity", "ol.orderdate"],
220 |                               "Shared Hit Blocks": 2,
221 |                               "Shared Read Blocks": 383,
222 |                               "Shared Dirtied Blocks": 0,
223 |                               "Shared Written Blocks": 0,
224 |                               "Local Hit Blocks": 0,
225 |                               "Local Read Blocks": 0,
226 |                               "Local Dirtied Blocks": 0,
227 |                               "Local Written Blocks": 0,
228 |                               "Temp Read Blocks": 0,
229 |                               "Temp Written Blocks": 0,
230 |                               "I/O Read Time": 0.000,
231 |                               "I/O Write Time": 0.000
232 |                             },
233 |                             {
234 |                               "Node Type": "Hash",
235 |                               "Parent Relationship": "Inner",
236 |                               "Startup Cost": 339.86,
237 |                               "Total Cost": 339.86,
238 |                               "Plan Rows": 10000,
239 |                               "Plan Width": 122,
240 |                               "Actual Startup Time": 12.446,
241 |                               "Actual Total Time": 12.446,
242 |                               "Actual Rows": 10000,
243 |                               "Actual Loops": 1,
244 |                               "Output": ["p.prod_id", "cat.categoryname"],
245 |                               "Hash Buckets": 1024,
246 |                               "Hash Batches": 1,
247 |                               "Original Hash Batches": 1,
248 |                               "Peak Memory Usage": 425,
249 |                               "Shared Hit Blocks": 2,
250 |                               "Shared Read Blocks": 100,
251 |                               "Shared Dirtied Blocks": 0,
252 |                               "Shared Written Blocks": 0,
253 |                               "Local Hit Blocks": 0,
254 |                               "Local Read Blocks": 0,
255 |                               "Local Dirtied Blocks": 0,
256 |                               "Local Written Blocks": 0,
257 |                               "Temp Read Blocks": 0,
258 |                               "Temp Written Blocks": 0,
259 |                               "I/O Read Time": 0.000,
260 |                               "I/O Write Time": 0.000,
261 |                               "Plans": [
262 |                                 {
263 |                                   "Node Type": "Hash Join",
264 |                                   "Parent Relationship": "Outer",
265 |                                   "Join Type": "Inner",
266 |                                   "Startup Cost": 1.36,
267 |                                   "Total Cost": 339.86,
268 |                                   "Plan Rows": 10000,
269 |                                   "Plan Width": 122,
270 |                                   "Actual Startup Time": 0.283,
271 |                                   "Actual Total Time": 9.015,
272 |                                   "Actual Rows": 10000,
273 |                                   "Actual Loops": 1,
274 |                                   "Output": ["p.prod_id", "cat.categoryname"],
275 |                                   "Hash Cond": "(p.category = cat.category)",
276 |                                   "Shared Hit Blocks": 2,
277 |                                   "Shared Read Blocks": 100,
278 |                                   "Shared Dirtied Blocks": 0,
279 |                                   "Shared Written Blocks": 0,
280 |                                   "Local Hit Blocks": 0,
281 |                                   "Local Read Blocks": 0,
282 |                                   "Local Dirtied Blocks": 0,
283 |                                   "Local Written Blocks": 0,
284 |                                   "Temp Read Blocks": 0,
285 |                                   "Temp Written Blocks": 0,
286 |                                   "I/O Read Time": 0.000,
287 |                                   "I/O Write Time": 0.000,
288 |                                   "Plans": [
289 |                                     {
290 |                                       "Node Type": "Seq Scan",
291 |                                       "Parent Relationship": "Outer",
292 |                                       "Relation Name": "products",
293 |                                       "Schema": "public",
294 |                                       "Alias": "p",
295 |                                       "Startup Cost": 0.00,
296 |                                       "Total Cost": 201.00,
297 |                                       "Plan Rows": 10000,
298 |                                       "Plan Width": 8,
299 |                                       "Actual Startup Time": 0.003,
300 |                                       "Actual Total Time": 4.330,
301 |                                       "Actual Rows": 10000,
302 |                                       "Actual Loops": 1,
303 |                                       "Output": ["p.prod_id", "p.category", "p.title", "p.actor", "p.price", "p.special", "p.common_prod_id"],
304 |                                       "Shared Hit Blocks": 2,
305 |                                       "Shared Read Blocks": 99,
306 |                                       "Shared Dirtied Blocks": 0,
307 |                                       "Shared Written Blocks": 0,
308 |                                       "Local Hit Blocks": 0,
309 |                                       "Local Read Blocks": 0,
310 |                                       "Local Dirtied Blocks": 0,
311 |                                       "Local Written Blocks": 0,
312 |                                       "Temp Read Blocks": 0,
313 |                                       "Temp Written Blocks": 0,
314 |                                       "I/O Read Time": 0.000,
315 |                                       "I/O Write Time": 0.000
316 |                                     },
317 |                                     {
318 |                                       "Node Type": "Hash",
319 |                                       "Parent Relationship": "Inner",
320 |                                       "Startup Cost": 1.16,
321 |                                       "Total Cost": 1.16,
322 |                                       "Plan Rows": 16,
323 |                                       "Plan Width": 122,
324 |                                       "Actual Startup Time": 0.265,
325 |                                       "Actual Total Time": 0.265,
326 |                                       "Actual Rows": 16,
327 |                                       "Actual Loops": 1,
328 |                                       "Output": ["cat.categoryname", "cat.category"],
329 |                                       "Hash Buckets": 1024,
330 |                                       "Hash Batches": 1,
331 |                                       "Original Hash Batches": 1,
332 |                                       "Peak Memory Usage": 1,
333 |                                       "Shared Hit Blocks": 0,
334 |                                       "Shared Read Blocks": 1,
335 |                                       "Shared Dirtied Blocks": 0,
336 |                                       "Shared Written Blocks": 0,
337 |                                       "Local Hit Blocks": 0,
338 |                                       "Local Read Blocks": 0,
339 |                                       "Local Dirtied Blocks": 0,
340 |                                       "Local Written Blocks": 0,
341 |                                       "Temp Read Blocks": 0,
342 |                                       "Temp Written Blocks": 0,
343 |                                       "I/O Read Time": 0.000,
344 |                                       "I/O Write Time": 0.000,
345 |                                       "Plans": [
346 |                                         {
347 |                                           "Node Type": "Seq Scan",
348 |                                           "Parent Relationship": "Outer",
349 |                                           "Relation Name": "categories",
350 |                                           "Schema": "public",
351 |                                           "Alias": "cat",
352 |                                           "Startup Cost": 0.00,
353 |                                           "Total Cost": 1.16,
354 |                                           "Plan Rows": 16,
355 |                                           "Plan Width": 122,
356 |                                           "Actual Startup Time": 0.250,
357 |                                           "Actual Total Time": 0.252,
358 |                                           "Actual Rows": 16,
359 |                                           "Actual Loops": 1,
360 |                                           "Output": ["cat.categoryname", "cat.category"],
361 |                                           "Shared Hit Blocks": 0,
362 |                                           "Shared Read Blocks": 1,
363 |                                           "Shared Dirtied Blocks": 0,
364 |                                           "Shared Written Blocks": 0,
365 |                                           "Local Hit Blocks": 0,
366 |                                           "Local Read Blocks": 0,
367 |                                           "Local Dirtied Blocks": 0,
368 |                                           "Local Written Blocks": 0,
369 |                                           "Temp Read Blocks": 0,
370 |                                           "Temp Written Blocks": 0,
371 |                                           "I/O Read Time": 0.000,
372 |                                           "I/O Write Time": 0.000
373 |                                         }
374 |                                       ]
375 |                                     }
376 |                                   ]
377 |                                 }
378 |                               ]
379 |                             }
380 |                           ]
381 |                         },
382 |                         {
383 |                           "Node Type": "Hash",
384 |                           "Parent Relationship": "Inner",
385 |                           "Startup Cost": 220.00,
386 |                           "Total Cost": 220.00,
387 |                           "Plan Rows": 12000,
388 |                           "Plan Width": 16,
389 |                           "Actual Startup Time": 10.159,
390 |                           "Actual Total Time": 10.159,
391 |                           "Actual Rows": 12000,
392 |                           "Actual Loops": 1,
393 |                           "Output": ["o.netamount", "o.totalamount", "o.orderid"],
394 |                           "Hash Buckets": 2048,
395 |                           "Hash Batches": 1,
396 |                           "Original Hash Batches": 1,
397 |                           "Peak Memory Usage": 609,
398 |                           "Shared Hit Blocks": 2,
399 |                           "Shared Read Blocks": 98,
400 |                           "Shared Dirtied Blocks": 0,
401 |                           "Shared Written Blocks": 0,
402 |                           "Local Hit Blocks": 0,
403 |                           "Local Read Blocks": 0,
404 |                           "Local Dirtied Blocks": 0,
405 |                           "Local Written Blocks": 0,
406 |                           "Temp Read Blocks": 0,
407 |                           "Temp Written Blocks": 0,
408 |                           "I/O Read Time": 0.000,
409 |                           "I/O Write Time": 0.000,
410 |                           "Plans": [
411 |                             {
412 |                               "Node Type": "Seq Scan",
413 |                               "Parent Relationship": "Outer",
414 |                               "Relation Name": "orders",
415 |                               "Schema": "public",
416 |                               "Alias": "o",
417 |                               "Startup Cost": 0.00,
418 |                               "Total Cost": 220.00,
419 |                               "Plan Rows": 12000,
420 |                               "Plan Width": 16,
421 |                               "Actual Startup Time": 0.008,
422 |                               "Actual Total Time": 5.548,
423 |                               "Actual Rows": 12000,
424 |                               "Actual Loops": 1,
425 |                               "Output": ["o.netamount", "o.totalamount", "o.orderid"],
426 |                               "Shared Hit Blocks": 2,
427 |                               "Shared Read Blocks": 98,
428 |                               "Shared Dirtied Blocks": 0,
429 |                               "Shared Written Blocks": 0,
430 |                               "Local Hit Blocks": 0,
431 |                               "Local Read Blocks": 0,
432 |                               "Local Dirtied Blocks": 0,
433 |                               "Local Written Blocks": 0,
434 |                               "Temp Read Blocks": 0,
435 |                               "Temp Written Blocks": 0,
436 |                               "I/O Read Time": 0.000,
437 |                               "I/O Write Time": 0.000
438 |                             }
439 |                           ]
440 |                         }
441 |                       ]
442 |                     },
443 |                     {
444 |                       "Node Type": "Hash",
445 |                       "Parent Relationship": "Inner",
446 |                       "Startup Cost": 3377.25,
447 |                       "Total Cost": 3377.25,
448 |                       "Plan Rows": 60350,
449 |                       "Plan Width": 7,
450 |                       "Actual Startup Time": 95.610,
451 |                       "Actual Total Time": 95.610,
452 |                       "Actual Rows": 60350,
453 |                       "Actual Loops": 1,
454 |                       "Output": ["c.state", "ch.orderid"],
455 |                       "Hash Buckets": 8192,
456 |                       "Hash Batches": 1,
457 |                       "Original Hash Batches": 1,
458 |                       "Peak Memory Usage": 2239,
459 |                       "Shared Hit Blocks": 4,
460 |                       "Shared Read Blocks": 811,
461 |                       "Shared Dirtied Blocks": 0,
462 |                       "Shared Written Blocks": 0,
463 |                       "Local Hit Blocks": 0,
464 |                       "Local Read Blocks": 0,
465 |                       "Local Dirtied Blocks": 0,
466 |                       "Local Written Blocks": 0,
467 |                       "Temp Read Blocks": 0,
468 |                       "Temp Written Blocks": 0,
469 |                       "I/O Read Time": 0.000,
470 |                       "I/O Write Time": 0.000,
471 |                       "Plans": [
472 |                         {
473 |                           "Node Type": "Hash Join",
474 |                           "Parent Relationship": "Outer",
475 |                           "Join Type": "Inner",
476 |                           "Startup Cost": 938.00,
477 |                           "Total Cost": 3377.25,
478 |                           "Plan Rows": 60350,
479 |                           "Plan Width": 7,
480 |                           "Actual Startup Time": 24.115,
481 |                           "Actual Total Time": 74.639,
482 |                           "Actual Rows": 60350,
483 |                           "Actual Loops": 1,
484 |                           "Output": ["c.state", "ch.orderid"],
485 |                           "Hash Cond": "(ch.customerid = c.customerid)",
486 |                           "Shared Hit Blocks": 4,
487 |                           "Shared Read Blocks": 811,
488 |                           "Shared Dirtied Blocks": 0,
489 |                           "Shared Written Blocks": 0,
490 |                           "Local Hit Blocks": 0,
491 |                           "Local Read Blocks": 0,
492 |                           "Local Dirtied Blocks": 0,
493 |                           "Local Written Blocks": 0,
494 |                           "Temp Read Blocks": 0,
495 |                           "Temp Written Blocks": 0,
496 |                           "I/O Read Time": 0.000,
497 |                           "I/O Write Time": 0.000,
498 |                           "Plans": [
499 |                             {
500 |                               "Node Type": "Seq Scan",
501 |                               "Parent Relationship": "Outer",
502 |                               "Relation Name": "cust_hist",
503 |                               "Schema": "public",
504 |                               "Alias": "ch",
505 |                               "Startup Cost": 0.00,
506 |                               "Total Cost": 930.50,
507 |                               "Plan Rows": 60350,
508 |                               "Plan Width": 8,
509 |                               "Actual Startup Time": 0.294,
510 |                               "Actual Total Time": 11.812,
511 |                               "Actual Rows": 60350,
512 |                               "Actual Loops": 1,
513 |                               "Output": ["ch.customerid", "ch.orderid", "ch.prod_id"],
514 |                               "Shared Hit Blocks": 2,
515 |                               "Shared Read Blocks": 325,
516 |                               "Shared Dirtied Blocks": 0,
517 |                               "Shared Written Blocks": 0,
518 |                               "Local Hit Blocks": 0,
519 |                               "Local Read Blocks": 0,
520 |                               "Local Dirtied Blocks": 0,
521 |                               "Local Written Blocks": 0,
522 |                               "Temp Read Blocks": 0,
523 |                               "Temp Written Blocks": 0,
524 |                               "I/O Read Time": 0.000,
525 |                               "I/O Write Time": 0.000
526 |                             },
527 |                             {
528 |                               "Node Type": "Hash",
529 |                               "Parent Relationship": "Inner",
530 |                               "Startup Cost": 688.00,
531 |                               "Total Cost": 688.00,
532 |                               "Plan Rows": 20000,
533 |                               "Plan Width": 7,
534 |                               "Actual Startup Time": 23.786,
535 |                               "Actual Total Time": 23.786,
536 |                               "Actual Rows": 20000,
537 |                               "Actual Loops": 1,
538 |                               "Output": ["c.state", "c.customerid"],
539 |                               "Hash Buckets": 2048,
540 |                               "Hash Batches": 1,
541 |                               "Original Hash Batches": 1,
542 |                               "Peak Memory Usage": 743,
543 |                               "Shared Hit Blocks": 2,
544 |                               "Shared Read Blocks": 486,
545 |                               "Shared Dirtied Blocks": 0,
546 |                               "Shared Written Blocks": 0,
547 |                               "Local Hit Blocks": 0,
548 |                               "Local Read Blocks": 0,
549 |                               "Local Dirtied Blocks": 0,
550 |                               "Local Written Blocks": 0,
551 |                               "Temp Read Blocks": 0,
552 |                               "Temp Written Blocks": 0,
553 |                               "I/O Read Time": 0.000,
554 |                               "I/O Write Time": 0.000,
555 |                               "Plans": [
556 |                                 {
557 |                                   "Node Type": "Seq Scan",
558 |                                   "Parent Relationship": "Outer",
559 |                                   "Relation Name": "customers",
560 |                                   "Schema": "public",
561 |                                   "Alias": "c",
562 |                                   "Startup Cost": 0.00,
563 |                                   "Total Cost": 688.00,
564 |                                   "Plan Rows": 20000,
565 |                                   "Plan Width": 7,
566 |                                   "Actual Startup Time": 0.005,
567 |                                   "Actual Total Time": 16.771,
568 |                                   "Actual Rows": 20000,
569 |                                   "Actual Loops": 1,
570 |                                   "Output": ["c.state", "c.customerid"],
571 |                                   "Shared Hit Blocks": 2,
572 |                                   "Shared Read Blocks": 486,
573 |                                   "Shared Dirtied Blocks": 0,
574 |                                   "Shared Written Blocks": 0,
575 |                                   "Local Hit Blocks": 0,
576 |                                   "Local Read Blocks": 0,
577 |                                   "Local Dirtied Blocks": 0,
578 |                                   "Local Written Blocks": 0,
579 |                                   "Temp Read Blocks": 0,
580 |                                   "Temp Written Blocks": 0,
581 |                                   "I/O Read Time": 0.000,
582 |                                   "I/O Write Time": 0.000
583 |                                 }
584 |                               ]
585 |                             }
586 |                           ]
587 |                         }
588 |                       ]
589 |                     }
590 |                   ]
591 |                 }
592 |               ]
593 |             }
594 |           ]
595 |         }
596 |       ]
597 |     },
598 |     "Planning Time": 26.171,
599 |     "Triggers": [
600 |     ],
601 |     "Execution Time": 726.800
602 |   }
603 | ]`;
604 | 
605 | export var SAMPLE_QUERY = `SELECT c.state,
606 |   cat.categoryname,
607 |   sum(o.netamount),
608 |   sum(o.totalamount)
609 | FROM customers c
610 |   INNER JOIN cust_hist ch ON c.customerid = ch.customerid
611 |   INNER JOIN orders o ON ch.orderid = o.orderid
612 |   INNER JOIN orderlines ol ON ol.orderid = o.orderid
613 |   INNER JOIN products p ON ol.prod_id = p.prod_id
614 |   INNER JOIN categories cat ON p.category = cat.category
615 | GROUP BY c.state, cat.categoryname
616 | ORDER BY c.state, sum(o.totalamount) DESC
617 | LIMIT 10 OFFSET 1`;
618 | 


--------------------------------------------------------------------------------
/app/components/plan-node/plan-node.html:
--------------------------------------------------------------------------------
 1 | <div class="plan-node"
 2 |    [class.expanded]="showDetails"
 3 |    [class.compact]="viewOptions.viewMode === viewModes.COMPACT"
 4 |    [class.dot]="viewOptions.viewMode === viewModes.DOT">
 5 | 
 6 |    <header (click)="showDetails = !showDetails" tooltip="view node details">
 7 |       <h4>{{getNodeName()}}</h4>
 8 |       <span *ngIf="viewOptions.viewMode === viewModes.FULL || showDetails">
 9 |          <span class="node-duration">{{node[_planService.ACTUAL_DURATION_PROP] | duration}}<span class="text-muted">{{node[_planService.ACTUAL_DURATION_PROP] | durationUnit}}
10 |              | </span><strong>{{executionTimePercent}}</strong>
11 |             <span class="text-muted">%</span>
12 |          </span>
13 |       </span>
14 |    </header>
15 | 
16 |    <button *ngIf="plan.query && viewOptions.viewMode === viewModes.FULL" tooltip="view corresponding query"
17 |       class="btn btn-sm btn-default btn-slim pull-right" (click)="showQuery = !showQuery">
18 |       <i class="fa fa-database"></i>
19 |    </button>
20 | 
21 |    <div *ngIf="viewOptions.viewMode === viewModes.FULL">
22 |       <div class="relation-name" *ngIf="node[_planService.RELATION_NAME_PROP]">
23 |          <span class="text-muted">on </span>
24 |          <span *ngIf="node[_planService.SCHEMA_PROP]">{{node[_planService.SCHEMA_PROP]}}.</span>{{node[_planService.RELATION_NAME_PROP]}}
25 |          <span *ngIf="node[_planService.ALIAS_PROP]"> ({{node[_planService.ALIAS_PROP]}})</span>
26 |       </div>
27 | 
28 |       <div class="relation-name" *ngIf="node[_planService.GROUP_KEY_PROP]">
29 |          <span class="text-muted">by</span> {{node[_planService.GROUP_KEY_PROP]}}</div>
30 |       <div class="relation-name" *ngIf="node[_planService.SORT_KEY_PROP]">
31 |          <span class="text-muted">by</span> {{node[_planService.SORT_KEY_PROP]}}</div>
32 |       <div class="relation-name" *ngIf="node[_planService.JOIN_TYPE_PROP]">{{node[_planService.JOIN_TYPE_PROP]}}
33 |          <span class="text-muted">join</span></div>
34 |       <div class="relation-name" *ngIf="node[_planService.INDEX_NAME_PROP]"><span class="text-muted">
35 |          using</span> {{node[_planService.INDEX_NAME_PROP]}}</div>
36 |       <div class="relation-name" *ngIf="node[_planService.HASH_CONDITION_PROP]"><span class="text-muted">
37 |          on</span> {{node[_planService.HASH_CONDITION_PROP]}}</div>
38 |       <div class="relation-name" *ngIf="node[_planService.CTE_NAME_PROP]">
39 |             <span class="text-muted">CTE</span> {{node[_planService.CTE_NAME_PROP]}}
40 |       </div>
41 |    </div>
42 | 
43 |    <div class="tags" *ngIf="viewOptions.showTags && tags.length > 0">
44 |       <span *ngFor="#tag of tags">{{getTagName(tag)}}</span>
45 |    </div>
46 | 
47 |    <div *ngIf="currentHighlightType !== highlightTypes.NONE">
48 |       <div class="node-bar-container">
49 |          <span class="node-bar" [style.width]="barWidth+'px'" [style.backgroundColor]="backgroundColor"></span>
50 |       </div>
51 |       <span class="node-bar-label" *ngIf="shouldShowNodeBarLabel()">
52 |          <span class="text-muted">{{viewOptions.highlightType}}:</span> {{highlightValue | number:'.0-2'}}
53 |       </span>
54 |    </div>
55 | 
56 |    <div class="planner-estimate" *ngIf="shouldShowPlannerEstimate()">
57 |       <span *ngIf="plannerRowEstimateDirection === estimateDirections.over"><strong>over</strong> estimated rows</span>
58 |       <span *ngIf="plannerRowEstimateDirection === estimateDirections.under"><strong>under</strong> estimated rows</span>
59 |       <span> by <strong>{{plannerRowEstimateValue | number}}</strong>x</span>
60 |    </div>
61 | 
62 |    <div *ngIf="showDetails">
63 |       <div *ngIf="getNodeTypeDescription()" class="node-description">
64 |          <span class="node-type">{{node[_planService.NODE_TYPE_PROP]}} Node</span> <span [innerHtml]="getNodeTypeDescription()"></span>
65 |       </div>
66 | 
67 |       <table class="table prop-list">
68 |          <tr *ngFor="#prop of props">
69 |             <td width="40%">{{prop.key}}</td>
70 |             <td>{{prop.value}}</td>
71 |          <tr>
72 |       </table>
73 |       <div class="text-muted pad-top align-right"><em>*Pev calculated value</em></div>
74 |    </div>
75 | 
76 |    <div *ngIf="showQuery" class="plan-query-container">
77 |       <button class="btn btn-close pull-right" (click)="showQuery = false">
78 |          <i class="fa fa-close"></i>
79 |       </button>
80 |       <h3>query</h3>
81 |       <pre class="plan-query-text"><code [innerHTML]="getFormattedQuery()"></code></pre>
82 |    </div>
83 | </div>
84 | 
85 | <ul *ngIf="node.Plans">
86 |   <li *ngFor="#subNode of node.Plans">
87 |     <plan-node [plan]="plan" [node]="subNode" [viewOptions]="viewOptions" [planStats]="planStats"></plan-node>
88 |   </li>
89 | </ul>
90 | 


--------------------------------------------------------------------------------
/app/components/plan-node/plan-node.ts:
--------------------------------------------------------------------------------
  1 | import {IPlan} from '../../interfaces/iplan';
  2 | import {Component, OnInit} from 'angular2/core';
  3 | import {HighlightType, EstimateDirection, ViewMode} from '../../enums';
  4 | import {DurationPipe, DurationUnitPipe} from '../../pipes';
  5 | 
  6 | import {PlanService} from '../../services/plan-service';
  7 | import {SyntaxHighlightService} from '../../services/syntax-highlight-service';
  8 | import {HelpService} from '../../services/help-service';
  9 | import {ColorService} from '../../services/color-service';
 10 | 
 11 | /// <reference path="lodash.d.ts" />
 12 | 
 13 | @Component({
 14 |     selector: 'plan-node',
 15 |     inputs: ['plan', 'node', 'viewOptions'],
 16 |     templateUrl: './components/plan-node/plan-node.html',
 17 |     directives: [PlanNode],
 18 |     providers: [PlanService, SyntaxHighlightService, HelpService, ColorService],
 19 |     pipes: [DurationPipe, DurationUnitPipe]
 20 | })
 21 | 
 22 | export class PlanNode {
 23 |     // consts
 24 |     NORMAL_WIDTH: number = 220;
 25 |     COMPACT_WIDTH: number = 140;
 26 |     DOT_WIDTH: number = 30;
 27 |     EXPANDED_WIDTH: number = 400;
 28 | 
 29 |     MIN_ESTIMATE_MISS: number = 100;
 30 |     COSTLY_TAG: string = 'costliest';
 31 |     SLOW_TAG: string = 'slowest';
 32 |     LARGE_TAG: string = 'largest';
 33 |     ESTIMATE_TAG: string = 'bad estimate';
 34 | 
 35 |     // inputs
 36 |     plan: IPlan;
 37 |     node: any;
 38 |     viewOptions: any;
 39 | 
 40 |     // UI flags
 41 |     showDetails: boolean;
 42 | 
 43 |     // calculated properties
 44 |     executionTimePercent: number;
 45 |     backgroundColor: string;
 46 |     highlightValue: number;
 47 |     barContainerWidth: number;
 48 |     barWidth: number;
 49 |     props: Array<any>;
 50 |     tags: Array<string>;
 51 |     plannerRowEstimateValue: number;
 52 |     plannerRowEstimateDirection: EstimateDirection;
 53 | 
 54 |     // required for custom change detection
 55 |     currentHighlightType: string;
 56 |     currentCompactView: boolean;
 57 |     currentExpandedView: boolean;
 58 | 
 59 |     // expose enum to view
 60 |     estimateDirections = EstimateDirection;
 61 |     highlightTypes = HighlightType;
 62 |     viewModes = ViewMode;
 63 | 
 64 |     constructor(private _planService: PlanService,
 65 |         private _syntaxHighlightService: SyntaxHighlightService,
 66 |         private _helpService: HelpService,
 67 |         private _colorService: ColorService) { }
 68 | 
 69 |     ngOnInit() {
 70 |         this.currentHighlightType = this.viewOptions.highlightType;
 71 |         this.calculateBar();
 72 |         this.calculateProps();
 73 |         this.calculateDuration();
 74 |         this.calculateTags();
 75 | 
 76 |         this.plannerRowEstimateDirection = this.node[this._planService.PLANNER_ESIMATE_DIRECTION];
 77 |         this.plannerRowEstimateValue = _.round(this.node[this._planService.PLANNER_ESTIMATE_FACTOR]);
 78 |     }
 79 | 
 80 |     ngDoCheck() {
 81 |         if (this.currentHighlightType !== this.viewOptions.highlightType) {
 82 |             this.currentHighlightType = this.viewOptions.highlightType;
 83 |             this.calculateBar();
 84 |         }
 85 | 
 86 |         if (this.currentCompactView !== this.viewOptions.showCompactView) {
 87 |             this.currentCompactView = this.viewOptions.showCompactView;
 88 |             this.calculateBar();
 89 |         }
 90 | 
 91 |         if (this.currentExpandedView !== this.showDetails) {
 92 |             this.currentExpandedView = this.showDetails;
 93 |             this.calculateBar();
 94 |         }
 95 |     }
 96 | 
 97 |     getFormattedQuery() {
 98 |         var keyItems: Array<string> = [];
 99 | 
100 |         // relation name will be highlighted for SCAN nodes
101 |         var relationName: string = this.node[this._planService.RELATION_NAME_PROP];
102 |         if (relationName) {
103 |             keyItems.push(this.node[this._planService.SCHEMA_PROP] + '.' + relationName);
104 |             keyItems.push(' ' + relationName);
105 |             keyItems.push(' ' + this.node[this._planService.ALIAS_PROP] + ' ');
106 |         }
107 | 
108 |         // group key will be highlighted for AGGREGATE nodes
109 |         var groupKey: Array<string> = this.node[this._planService.GROUP_KEY_PROP];
110 |         if (groupKey) {
111 |             keyItems.push('GROUP BY ' + groupKey.join(','));
112 |         }
113 | 
114 |         // hash condition will be highlighted for HASH JOIN nodes
115 |         var hashCondition: string = this.node[this._planService.HASH_CONDITION_PROP];
116 |         if (hashCondition) {
117 |             keyItems.push(hashCondition.replace('(', '').replace(')', ''));
118 |         }
119 | 
120 |         if (this.node[this._planService.NODE_TYPE_PROP].toUpperCase() === 'LIMIT') {
121 |             keyItems.push('LIMIT');
122 |         }
123 |         return this._syntaxHighlightService.highlight(this.plan.query, keyItems);
124 |     }
125 | 
126 |     calculateBar() {
127 |         switch (this.viewOptions.viewMode) {
128 |             case ViewMode.DOT:
129 |                 this.barContainerWidth = this.DOT_WIDTH;
130 |                 break;
131 |             case ViewMode.COMPACT:
132 |                 this.barContainerWidth = this.COMPACT_WIDTH;
133 |                 break;
134 |             default:
135 |                 this.barContainerWidth = this.NORMAL_WIDTH;
136 |                 break;
137 |         }
138 | 
139 |         // expanded view width trumps others
140 |         if (this.currentExpandedView) {
141 |             this.barContainerWidth = this.EXPANDED_WIDTH;
142 |         }
143 | 
144 |         switch (this.currentHighlightType) {
145 |             case HighlightType.DURATION:
146 |                 this.highlightValue = (this.node[this._planService.ACTUAL_DURATION_PROP]);
147 |                 this.barWidth = Math.round((this.highlightValue / this.plan.planStats.maxDuration) * this.barContainerWidth);
148 |                 break;
149 |             case HighlightType.ROWS:
150 |                 this.highlightValue = (this.node[this._planService.ACTUAL_ROWS_PROP]);
151 |                 this.barWidth = Math.round((this.highlightValue / this.plan.planStats.maxRows) * this.barContainerWidth);
152 |                 break;
153 |             case HighlightType.COST:
154 |                 this.highlightValue = (this.node[this._planService.ACTUAL_COST_PROP]);
155 |                 this.barWidth = Math.round((this.highlightValue / this.plan.planStats.maxCost) * this.barContainerWidth);
156 |                 break;
157 |         }
158 | 
159 |         if (this.barWidth < 1) {
160 |             this.barWidth = 1;
161 |         }
162 | 
163 |         this.backgroundColor = this._colorService.numberToColorHsl(1 - this.barWidth / this.barContainerWidth);
164 |     }
165 | 
166 |     calculateDuration() {
167 |         this.executionTimePercent = (_.round((this.node[this._planService.ACTUAL_DURATION_PROP] / this.plan.planStats.executionTime) * 100));
168 |     }
169 | 
170 |     // create an array of node propeties so that they can be displayed in the view
171 |     calculateProps() {
172 |         this.props = _.chain(this.node)
173 |             .omit(this._planService.PLANS_PROP)
174 |             .map((value, key) => {
175 |                 return { key: key, value: value };
176 |             })
177 |             .value();
178 |     }
179 | 
180 |     calculateTags() {
181 |         this.tags = [];
182 |         if (this.node[this._planService.SLOWEST_NODE_PROP]) {
183 |             this.tags.push(this.SLOW_TAG);
184 |         }
185 |         if (this.node[this._planService.COSTLIEST_NODE_PROP]) {
186 |             this.tags.push(this.COSTLY_TAG);
187 |         }
188 |         if (this.node[this._planService.LARGEST_NODE_PROP]) {
189 |             this.tags.push(this.LARGE_TAG);
190 |         }
191 |         if (this.node[this._planService.PLANNER_ESTIMATE_FACTOR] >= this.MIN_ESTIMATE_MISS) {
192 |             this.tags.push(this.ESTIMATE_TAG);
193 |         }
194 |     }
195 | 
196 |     getNodeTypeDescription() {
197 |         return this._helpService.getNodeTypeDescription(this.node[this._planService.NODE_TYPE_PROP]);
198 |     }
199 | 
200 |     getNodeName() {
201 |         if (this.viewOptions.viewMode === ViewMode.DOT && !this.showDetails) {
202 |             return this.node[this._planService.NODE_TYPE_PROP].replace(/[^A-Z]/g, '').toUpperCase();
203 |         }
204 | 
205 |         return (this.node[this._planService.NODE_TYPE_PROP]).toUpperCase();
206 |     }
207 | 
208 |     getTagName(tagName: String) {
209 |         if (this.viewOptions.viewMode === ViewMode.DOT && !this.showDetails) {
210 |             return tagName.charAt(0);
211 |         }
212 |         return tagName;
213 |     }
214 | 
215 |     shouldShowPlannerEstimate() {
216 |         if (this.viewOptions.showPlannerEstimate && this.showDetails) {
217 |             return true;
218 |         }
219 | 
220 |         if (this.viewOptions.viewMode === ViewMode.DOT) {
221 |             return false;
222 |         }
223 | 
224 |         return this.viewOptions.showPlannerEstimate;
225 |     }
226 | 
227 |     shouldShowNodeBarLabel() {
228 |       if (this.showDetails) {
229 |          return true;
230 |       }
231 | 
232 |       if (this.viewOptions.viewMode === ViewMode.DOT) {
233 |          return false;
234 |       }
235 | 
236 |       return true;
237 |    }
238 | }
239 | 


--------------------------------------------------------------------------------
/app/components/plan-view/plan-view.html:
--------------------------------------------------------------------------------
 1 | <div class="menu" [class.menu-hidden]="hideMenu">
 2 |    <header>
 3 |       <i class="fa fa-cogs menu-toggle" (click)="hideMenu = !hideMenu"></i>
 4 |       <h3>display options</h3>
 5 |    </header>
 6 | 
 7 |    <ul>
 8 |       <li>
 9 |          <input id="showPlanStats" type="checkbox" [(ngModel)]="viewOptions.showPlanStats">
10 |          <label class="clickable" for="showPlanStats"> show plan stats</label>
11 |       </li>
12 |       <li>
13 |          <input id="showPlannerEstimate" type="checkbox" [(ngModel)]="viewOptions.showPlannerEstimate">
14 |          <label class="clickable" for="showPlannerEstimate"> show planner estimate</label>
15 |       </li>
16 |       <li>
17 |          <input id="showTags" type="checkbox" [(ngModel)]="viewOptions.showTags">
18 |          <label class="clickable" for="showTags"> show analysis tags</label>
19 |       </li>
20 |       <li>
21 |          <label>view mode: </label>
22 |          <div class="button-group">
23 |             <button [class.selected]="viewOptions.viewMode == viewModes.FULL" (click)="viewOptions.viewMode = viewModes.FULL">full</button>
24 |             <button [class.selected]="viewOptions.viewMode == viewModes.COMPACT" (click)="viewOptions.viewMode = viewModes.COMPACT">compact</button>
25 |             <button [class.selected]="viewOptions.viewMode == viewModes.DOT" (click)="viewOptions.viewMode = viewModes.DOT">dot</button>
26 |          </div>
27 |       </li>
28 | 
29 |       <li>
30 |          <label>graph metric: </label>
31 |          <div class="button-group">
32 |             <button [class.selected]="viewOptions.highlightType === highlightTypes.NONE" (click)="viewOptions.highlightType = highlightTypes.NONE">none</button>
33 |             <button [class.selected]="viewOptions.highlightType === highlightTypes.DURATION" (click)="viewOptions.highlightType = highlightTypes.DURATION">duration</button>
34 |             <button [class.selected]="viewOptions.highlightType === highlightTypes.ROWS" (click)="viewOptions.highlightType = highlightTypes.ROWS">rows</button>
35 |             <button [class.selected]="viewOptions.highlightType === highlightTypes.COST" (click)="viewOptions.highlightType = highlightTypes.COST">cost</button>
36 |          </div>
37 |       </li>
38 |    </ul>
39 | </div>
40 | 
41 | <div class="page page-stretch">
42 |    <h2><span *ngIf="!editName">{{plan.name}}</span>
43 |       <input *ngIf="editName" class="input-box input-box-main" type="text" [(ngModel)]="plan.name">
44 |       <button class="btn btn-link btn-lg" (click) = "editName = !editName">
45 |          <i class="fa fa-pencil"></i>
46 |       </button>
47 |    </h2>
48 | 
49 |    <div *ngIf="viewOptions.showPlanStats" class="plan-stats">
50 |       <div>
51 |          <span class="stat-value">{{plan.planStats.executionTime | duration}}</span>
52 |          <span class="stat-label">execution time ({{plan.planStats.executionTime | durationUnit}})</span>
53 |       </div>
54 |       <div *ngIf="plan.planStats.planningTime">
55 |          <span class="stat-value">{{plan.planStats.planningTime | number:'.0-2'}}</span>
56 |          <span class="stat-label">planning time (ms)</span>
57 |       </div>
58 |       <div *ngIf="plan.planStats.maxDuration">
59 |          <span class="stat-value">{{plan.planStats.maxDuration | duration}}</span>
60 |          <span class="stat-label">slowest node ({{plan.planStats.maxDuration | durationUnit}})</span>
61 |       </div>
62 |       <div *ngIf="plan.planStats.maxRows">
63 |          <span class="stat-value">{{plan.planStats.maxRows | number:'.0-2'}}</span>
64 |          <span class="stat-label">largest node (rows)</span>
65 |       </div>
66 |       <div *ngIf="plan.planStats.maxCost">
67 |          <span class="stat-value">{{plan.planStats.maxCost | number:'.0-2'}}</span>
68 |          <span class="stat-label">costliest node</span>
69 |       </div>
70 |       <div class="btn-close" (click)="viewOptions.showPlanStats = false"><i class="fa fa-close"></i></div>
71 |    </div>
72 | 
73 |    <div class="plan">
74 |       <ul>
75 |          <li>
76 |             <plan-node [plan]="plan" [node]="rootContainer.Plan" [viewOptions]="viewOptions"></plan-node>
77 |          </li>
78 |       </ul>
79 |    </div>
80 | </div>
81 | 


--------------------------------------------------------------------------------
/app/components/plan-view/plan-view.ts:
--------------------------------------------------------------------------------
 1 | import {Component, OnInit} from 'angular2/core';
 2 | import {ROUTER_DIRECTIVES, RouteParams} from 'angular2/router';
 3 | 
 4 | import {IPlan} from '../../interfaces/iplan';
 5 | import {HighlightType, ViewMode} from '../../enums';
 6 | import {PlanNode} from '../plan-node/plan-node';
 7 | import {PlanService} from '../../services/plan-service';
 8 | import {SyntaxHighlightService} from '../../services/syntax-highlight-service';
 9 | import {DurationPipe, DurationUnitPipe} from '../../pipes';
10 | 
11 | @Component({
12 |     selector: 'plan-view',
13 |     templateUrl: './components/plan-view/plan-view.html',
14 |     directives: [ROUTER_DIRECTIVES, PlanNode],
15 |     providers: [PlanService, SyntaxHighlightService],
16 |     pipes: [DurationPipe, DurationUnitPipe]
17 | })
18 | export class PlanView {
19 |     id: string;
20 |     plan: IPlan;
21 |     rootContainer: any;
22 |     hideMenu: boolean = true;
23 | 
24 |     viewOptions: any = {
25 |         showPlanStats: true,
26 |         showHighlightBar: true,
27 |         showPlannerEstimate: false,
28 |         showTags: true,
29 |         highlightType: HighlightType.NONE,
30 |         viewMode: ViewMode.FULL
31 |     };
32 | 
33 |     showPlannerEstimate: boolean = true;
34 |     showMenu: boolean = false;
35 | 
36 |     highlightTypes = HighlightType; // exposing the enum to the view
37 |     viewModes = ViewMode;
38 | 
39 |     constructor(private _planService: PlanService, routeParams: RouteParams) {
40 |         this.id = routeParams.get('id');
41 |     }
42 | 
43 |     getPlan() {
44 |         if (!this.id) {
45 |             return;
46 |         }
47 | 
48 |         this.plan = this._planService.getPlan(this.id);
49 |         this.rootContainer = this.plan.content;
50 |         this.plan.planStats = {
51 |             executionTime: this.rootContainer['Execution Time'] || this.rootContainer['Total Runtime'],
52 |             planningTime: this.rootContainer['Planning Time'] || 0,
53 |             maxRows: this.rootContainer[this._planService.MAXIMUM_ROWS_PROP] || 0,
54 |             maxCost: this.rootContainer[this._planService.MAXIMUM_COSTS_PROP] || 0,
55 |             maxDuration: this.rootContainer[this._planService.MAXIMUM_DURATION_PROP] || 0
56 |         };
57 |     }
58 | 
59 |     ngOnInit() {
60 |         this.getPlan();
61 |     }
62 | 
63 |     toggleHighlight(type: HighlightType) {
64 |         this.viewOptions.highlightType = type;
65 |     }
66 | 
67 |     analyzePlan() {
68 |         this._planService.analyzePlan(this.plan);
69 |     }
70 | }
71 | 


--------------------------------------------------------------------------------
/app/enums.ts:
--------------------------------------------------------------------------------
 1 | export class HighlightType {
 2 |     static NONE: string = 'none';
 3 |     static DURATION: string = 'duration';
 4 |     static ROWS: string = 'rows';
 5 |     static COST: string = 'cost';
 6 | }
 7 | 
 8 | export enum EstimateDirection {
 9 |     over,
10 |     under
11 | }
12 | 
13 | export class ViewMode {
14 |     static FULL: string = 'full';
15 |     static COMPACT: string = 'compact';
16 |     static DOT: string = 'dot';
17 | }
18 | 


--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
 1 | <!DOCTYPE html>
 2 | <html lang="en">
 3 | <head>
 4 |   <meta charset="utf-8">
 5 |   <meta http-equiv="X-UA-Compatible" content="IE=edge">
 6 |   <title><%= APP_TITLE %></title>
 7 |   <meta name="description" content="">
 8 |   <meta name="viewport" content="width=device-width, initial-scale=1">
 9 |   <!-- inject:css -->
10 |   <!-- endinject -->
11 | </head>
12 | <body>
13 | 
14 |   <app>Loading...</app>
15 |   <!-- shims:js -->
16 |   <!-- endinject -->
17 | 
18 |   <script>
19 |    System.config(<%= JSON.stringify(SYSTEM_CONFIG) %>)
20 |   </script>
21 | 
22 |   <!-- libs:js -->
23 |   <!-- endinject -->
24 | 
25 |   <script>
26 |     System.import('bootstrap');
27 |   </script>
28 | 
29 |   <!-- inject:js -->
30 |   <!-- endinject -->
31 | 
32 | </body>
33 | </html>
34 | 


--------------------------------------------------------------------------------
/app/interfaces/iplan.ts:
--------------------------------------------------------------------------------
 1 | export interface IPlan {
 2 |   id: string;
 3 |   name: string;
 4 |   content: any;
 5 |   query: string;
 6 |   createdOn: Date;
 7 |   planStats: any;
 8 |   formattedQuery: string;
 9 | }
10 | 


--------------------------------------------------------------------------------
/app/pipes.ts:
--------------------------------------------------------------------------------
 1 | import {Pipe} from 'angular2/core';
 2 | /// <reference path="moment.d.ts" />
 3 | 
 4 | @Pipe({ name: 'momentDate' })
 5 | export class MomentDatePipe {
 6 |     transform(value: string, args: string[]): any {
 7 |         return moment(value).format('LLL');
 8 |     }
 9 | }
10 | 
11 | @Pipe({ name: 'duration' })
12 | export class DurationPipe {
13 |     transform(value: number): string {
14 |         var duration: string = '';
15 | 
16 |         if (value < 1) {
17 |             duration = '<1';
18 |         } else if (value > 1 && value < 1000) {
19 |             duration = _.round(value, 2).toString();
20 |         } else if (value >= 1000 && value < 60000) {
21 |             duration = _.round(value / 1000, 2).toString();
22 |         } else if (value >= 60000) {
23 |             duration = _.round(value / 60000, 2).toString();
24 |         }
25 |         return duration;
26 |     }
27 | }
28 | 
29 | @Pipe({ name: 'durationUnit' })
30 | export class DurationUnitPipe {
31 |     transform(value: number) {
32 |         var unit: string = '';
33 | 
34 |         if (value < 1) {
35 |             unit = 'ms';
36 |         } else if (value > 1 && value < 1000) {
37 |             unit = 'ms';
38 |         } else if (value >= 1000 && value < 60000) {
39 |             unit = 's';
40 |         } else if (value >= 60000) {
41 |             unit = 'min';
42 |         }
43 |         return unit;
44 |     }
45 | }
46 | 


--------------------------------------------------------------------------------
/app/sample-plans/plan1.json:
--------------------------------------------------------------------------------
  1 | [
  2 |   {
  3 |     "Plan": {
  4 |       "Node Type": "Limit",
  5 |       "Startup Cost": 60970.08,
  6 |       "Total Cost": 60970.11,
  7 |       "Plan Rows": 10,
  8 |       "Plan Width": 468,
  9 |       "Actual Startup Time": 1941.751,
 10 |       "Actual Total Time": 1941.780,
 11 |       "Actual Rows": 10,
 12 |       "Actual Loops": 1,
 13 |       "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))"],
 14 |       "Shared Hit Blocks": 260587,
 15 |       "Shared Read Blocks": 0,
 16 |       "Shared Dirtied Blocks": 0,
 17 |       "Shared Written Blocks": 0,
 18 |       "Local Hit Blocks": 0,
 19 |       "Local Read Blocks": 0,
 20 |       "Local Dirtied Blocks": 0,
 21 |       "Local Written Blocks": 0,
 22 |       "Temp Read Blocks": 0,
 23 |       "Temp Written Blocks": 0,
 24 |       "I/O Read Time": 0.000,
 25 |       "I/O Write Time": 0.000,
 26 |       "Plans": [
 27 |         {
 28 |           "Node Type": "Sort",
 29 |           "Parent Relationship": "Outer",
 30 |           "Startup Cost": 60970.08,
 31 |           "Total Cost": 60977.40,
 32 |           "Plan Rows": 2927,
 33 |           "Plan Width": 468,
 34 |           "Actual Startup Time": 1941.747,
 35 |           "Actual Total Time": 1941.757,
 36 |           "Actual Rows": 10,
 37 |           "Actual Loops": 1,
 38 |           "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))"],
 39 |           "Sort Key": ["(sum(s.total_alerts))"],
 40 |           "Sort Method": "top-N heapsort",
 41 |           "Sort Space Used": 27,
 42 |           "Sort Space Type": "Memory",
 43 |           "Shared Hit Blocks": 260587,
 44 |           "Shared Read Blocks": 0,
 45 |           "Shared Dirtied Blocks": 0,
 46 |           "Shared Written Blocks": 0,
 47 |           "Local Hit Blocks": 0,
 48 |           "Local Read Blocks": 0,
 49 |           "Local Dirtied Blocks": 0,
 50 |           "Local Written Blocks": 0,
 51 |           "Temp Read Blocks": 0,
 52 |           "Temp Written Blocks": 0,
 53 |           "I/O Read Time": 0.000,
 54 |           "I/O Write Time": 0.000,
 55 |           "Plans": [
 56 |             {
 57 |               "Node Type": "Nested Loop",
 58 |               "Parent Relationship": "Outer",
 59 |               "Join Type": "Left",
 60 |               "Startup Cost": 37552.36,
 61 |               "Total Cost": 60906.83,
 62 |               "Plan Rows": 2927,
 63 |               "Plan Width": 468,
 64 |               "Actual Startup Time": 522.250,
 65 |               "Actual Total Time": 1865.979,
 66 |               "Actual Rows": 32352,
 67 |               "Actual Loops": 1,
 68 |               "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)"],
 69 |               "Shared Hit Blocks": 260587,
 70 |               "Shared Read Blocks": 0,
 71 |               "Shared Dirtied Blocks": 0,
 72 |               "Shared Written Blocks": 0,
 73 |               "Local Hit Blocks": 0,
 74 |               "Local Read Blocks": 0,
 75 |               "Local Dirtied Blocks": 0,
 76 |               "Local Written Blocks": 0,
 77 |               "Temp Read Blocks": 0,
 78 |               "Temp Written Blocks": 0,
 79 |               "I/O Read Time": 0.000,
 80 |               "I/O Write Time": 0.000,
 81 |               "Plans": [
 82 |                 {
 83 |                   "Node Type": "Nested Loop",
 84 |                   "Parent Relationship": "Outer",
 85 |                   "Join Type": "Left",
 86 |                   "Startup Cost": 37552.22,
 87 |                   "Total Cost": 56750.41,
 88 |                   "Plan Rows": 2927,
 89 |                   "Plan Width": 461,
 90 |                   "Actual Startup Time": 522.141,
 91 |                   "Actual Total Time": 1161.187,
 92 |                   "Actual Rows": 32352,
 93 |                   "Actual Loops": 1,
 94 |                   "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"],
 95 |                   "Shared Hit Blocks": 197115,
 96 |                   "Shared Read Blocks": 0,
 97 |                   "Shared Dirtied Blocks": 0,
 98 |                   "Shared Written Blocks": 0,
 99 |                   "Local Hit Blocks": 0,
100 |                   "Local Read Blocks": 0,
101 |                   "Local Dirtied Blocks": 0,
102 |                   "Local Written Blocks": 0,
103 |                   "Temp Read Blocks": 0,
104 |                   "Temp Written Blocks": 0,
105 |                   "I/O Read Time": 0.000,
106 |                   "I/O Write Time": 0.000,
107 |                   "Plans": [
108 |                     {
109 |                       "Node Type": "Nested Loop",
110 |                       "Parent Relationship": "Outer",
111 |                       "Join Type": "Inner",
112 |                       "Startup Cost": 37552.07,
113 |                       "Total Cost": 56230.56,
114 |                       "Plan Rows": 2927,
115 |                       "Plan Width": 405,
116 |                       "Actual Startup Time": 522.131,
117 |                       "Actual Total Time": 918.895,
118 |                       "Actual Rows": 32352,
119 |                       "Actual Loops": 1,
120 |                       "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"],
121 |                       "Shared Hit Blocks": 132411,
122 |                       "Shared Read Blocks": 0,
123 |                       "Shared Dirtied Blocks": 0,
124 |                       "Shared Written Blocks": 0,
125 |                       "Local Hit Blocks": 0,
126 |                       "Local Read Blocks": 0,
127 |                       "Local Dirtied Blocks": 0,
128 |                       "Local Written Blocks": 0,
129 |                       "Temp Read Blocks": 0,
130 |                       "Temp Written Blocks": 0,
131 |                       "I/O Read Time": 0.000,
132 |                       "I/O Write Time": 0.000,
133 |                       "Plans": [
134 |                         {
135 |                           "Node Type": "Aggregate",
136 |                           "Strategy": "Hashed",
137 |                           "Parent Relationship": "Outer",
138 |                           "Startup Cost": 37551.64,
139 |                           "Total Cost": 37624.82,
140 |                           "Plan Rows": 2927,
141 |                           "Plan Width": 56,
142 |                           "Actual Startup Time": 522.107,
143 |                           "Actual Total Time": 587.536,
144 |                           "Actual Rows": 32352,
145 |                           "Actual Loops": 1,
146 |                           "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)"],
147 |                           "Group Key": ["s.alert_id"],
148 |                           "Shared Hit Blocks": 2784,
149 |                           "Shared Read Blocks": 0,
150 |                           "Shared Dirtied Blocks": 0,
151 |                           "Shared Written Blocks": 0,
152 |                           "Local Hit Blocks": 0,
153 |                           "Local Read Blocks": 0,
154 |                           "Local Dirtied Blocks": 0,
155 |                           "Local Written Blocks": 0,
156 |                           "Temp Read Blocks": 0,
157 |                           "Temp Written Blocks": 0,
158 |                           "I/O Read Time": 0.000,
159 |                           "I/O Write Time": 0.000,
160 |                           "Plans": [
161 |                             {
162 |                               "Node Type": "Bitmap Heap Scan",
163 |                               "Parent Relationship": "Outer",
164 |                               "Relation Name": "alert_daily_summaries",
165 |                               "Schema": "analytics",
166 |                               "Alias": "s",
167 |                               "Startup Cost": 3265.35,
168 |                               "Total Cost": 35524.50,
169 |                               "Plan Rows": 115837,
170 |                               "Plan Width": 56,
171 |                               "Actual Startup Time": 10.745,
172 |                               "Actual Total Time": 165.318,
173 |                               "Actual Rows": 140451,
174 |                               "Actual Loops": 1,
175 |                               "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"],
176 |                               "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))",
177 |                               "Rows Removed by Index Recheck": 0,
178 |                               "Exact Heap Blocks": 2243,
179 |                               "Lossy Heap Blocks": 0,
180 |                               "Shared Hit Blocks": 2784,
181 |                               "Shared Read Blocks": 0,
182 |                               "Shared Dirtied Blocks": 0,
183 |                               "Shared Written Blocks": 0,
184 |                               "Local Hit Blocks": 0,
185 |                               "Local Read Blocks": 0,
186 |                               "Local Dirtied Blocks": 0,
187 |                               "Local Written Blocks": 0,
188 |                               "Temp Read Blocks": 0,
189 |                               "Temp Written Blocks": 0,
190 |                               "I/O Read Time": 0.000,
191 |                               "I/O Write Time": 0.000,
192 |                               "Plans": [
193 |                                 {
194 |                                   "Node Type": "Bitmap Index Scan",
195 |                                   "Parent Relationship": "Outer",
196 |                                   "Index Name": "alert_daily_summaries_org_id_alert_day_idx",
197 |                                   "Startup Cost": 0.00,
198 |                                   "Total Cost": 3236.39,
199 |                                   "Plan Rows": 115837,
200 |                                   "Plan Width": 0,
201 |                                   "Actual Startup Time": 10.445,
202 |                                   "Actual Total Time": 10.445,
203 |                                   "Actual Rows": 140451,
204 |                                   "Actual Loops": 1,
205 |                                   "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))",
206 |                                   "Shared Hit Blocks": 541,
207 |                                   "Shared Read Blocks": 0,
208 |                                   "Shared Dirtied Blocks": 0,
209 |                                   "Shared Written Blocks": 0,
210 |                                   "Local Hit Blocks": 0,
211 |                                   "Local Read Blocks": 0,
212 |                                   "Local Dirtied Blocks": 0,
213 |                                   "Local Written Blocks": 0,
214 |                                   "Temp Read Blocks": 0,
215 |                                   "Temp Written Blocks": 0,
216 |                                   "I/O Read Time": 0.000,
217 |                                   "I/O Write Time": 0.000
218 |                                 }
219 |                               ]
220 |                             }
221 |                           ]
222 |                         },
223 |                         {
224 |                           "Node Type": "Index Scan",
225 |                           "Parent Relationship": "Inner",
226 |                           "Scan Direction": "Forward",
227 |                           "Index Name": "alerts_pkey1",
228 |                           "Relation Name": "alerts",
229 |                           "Schema": "analytics",
230 |                           "Alias": "a",
231 |                           "Startup Cost": 0.42,
232 |                           "Total Cost": 6.34,
233 |                           "Plan Rows": 1,
234 |                           "Plan Width": 213,
235 |                           "Actual Startup Time": 0.004,
236 |                           "Actual Total Time": 0.005,
237 |                           "Actual Rows": 1,
238 |                           "Actual Loops": 32352,
239 |                           "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"],
240 |                           "Index Cond": "(a.id = s.alert_id)",
241 |                           "Rows Removed by Index Recheck": 0,
242 |                           "Shared Hit Blocks": 129627,
243 |                           "Shared Read Blocks": 0,
244 |                           "Shared Dirtied Blocks": 0,
245 |                           "Shared Written Blocks": 0,
246 |                           "Local Hit Blocks": 0,
247 |                           "Local Read Blocks": 0,
248 |                           "Local Dirtied Blocks": 0,
249 |                           "Local Written Blocks": 0,
250 |                           "Temp Read Blocks": 0,
251 |                           "Temp Written Blocks": 0,
252 |                           "I/O Read Time": 0.000,
253 |                           "I/O Write Time": 0.000
254 |                         }
255 |                       ]
256 |                     },
257 |                     {
258 |                       "Node Type": "Index Scan",
259 |                       "Parent Relationship": "Inner",
260 |                       "Scan Direction": "Forward",
261 |                       "Index Name": "alert_types_id_org_id_key",
262 |                       "Relation Name": "alert_types",
263 |                       "Schema": "analytics",
264 |                       "Alias": "altt",
265 |                       "Startup Cost": 0.15,
266 |                       "Total Cost": 0.17,
267 |                       "Plan Rows": 1,
268 |                       "Plan Width": 72,
269 |                       "Actual Startup Time": 0.002,
270 |                       "Actual Total Time": 0.003,
271 |                       "Actual Rows": 1,
272 |                       "Actual Loops": 32352,
273 |                       "Output": ["altt.id", "altt.version", "altt.code", "altt.name", "altt.org_id"],
274 |                       "Index Cond": "(a.type_id = altt.id)",
275 |                       "Rows Removed by Index Recheck": 0,
276 |                       "Shared Hit Blocks": 64704,
277 |                       "Shared Read Blocks": 0,
278 |                       "Shared Dirtied Blocks": 0,
279 |                       "Shared Written Blocks": 0,
280 |                       "Local Hit Blocks": 0,
281 |                       "Local Read Blocks": 0,
282 |                       "Local Dirtied Blocks": 0,
283 |                       "Local Written Blocks": 0,
284 |                       "Temp Read Blocks": 0,
285 |                       "Temp Written Blocks": 0,
286 |                       "I/O Read Time": 0.000,
287 |                       "I/O Write Time": 0.000
288 |                     }
289 |                   ]
290 |                 },
291 |                 {
292 |                   "Node Type": "Index Scan",
293 |                   "Parent Relationship": "Inner",
294 |                   "Scan Direction": "Forward",
295 |                   "Index Name": "alert_subtypes_id_org_id_key",
296 |                   "Relation Name": "alert_subtypes",
297 |                   "Schema": "analytics",
298 |                   "Alias": "altst",
299 |                   "Startup Cost": 0.14,
300 |                   "Total Cost": 0.16,
301 |                   "Plan Rows": 1,
302 |                   "Plan Width": 23,
303 |                   "Actual Startup Time": 0.002,
304 |                   "Actual Total Time": 0.003,
305 |                   "Actual Rows": 1,
306 |                   "Actual Loops": 32352,
307 |                   "Output": ["altst.id", "altst.version", "altst.code", "altst.name", "altst.org_id"],
308 |                   "Index Cond": "(a.subtype_id = altst.id)",
309 |                   "Rows Removed by Index Recheck": 0,
310 |                   "Shared Hit Blocks": 63472,
311 |                   "Shared Read Blocks": 0,
312 |                   "Shared Dirtied Blocks": 0,
313 |                   "Shared Written Blocks": 0,
314 |                   "Local Hit Blocks": 0,
315 |                   "Local Read Blocks": 0,
316 |                   "Local Dirtied Blocks": 0,
317 |                   "Local Written Blocks": 0,
318 |                   "Temp Read Blocks": 0,
319 |                   "Temp Written Blocks": 0,
320 |                   "I/O Read Time": 0.000,
321 |                   "I/O Write Time": 0.000
322 |                 }
323 |               ]
324 |             }
325 |           ]
326 |         }
327 |       ]
328 |     },
329 |     "Planning Time": 0.507,
330 |     "Triggers": [
331 |     ],
332 |     "Execution Time": 1953.684
333 |   }
334 | ]
335 | 


--------------------------------------------------------------------------------
/app/services/color-service.ts:
--------------------------------------------------------------------------------
 1 | export class ColorService {
 2 |        /**
 3 |         * http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
 4 |         *
 5 |         * Converts an HSL color value to RGB. Conversion formula
 6 |         * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
 7 |         * Assumes h, s, and l are contained in the set [0, 1] and
 8 |         * returns r, g, and b in the set [0, 255].
 9 |         *
10 |         * @param   Number  h       The hue
11 |         * @param   Number  s       The saturation
12 |         * @param   Number  l       The lightness
13 |         * @return  Array           The RGB representation
14 |         */
15 |        hslToRgb(h, s, l) {
16 |            var r, g, b;
17 | 
18 |            if (s === 0) {
19 |                r = g = b = l; // achromatic
20 |            } else {
21 |                function hue2rgb(p, q, t) {
22 |                    if (t < 0) t += 1;
23 |                    if (t > 1) t -= 1;
24 |                    if (t < 1 / 6) return p + (q - p) * 6 * t;
25 |                    if (t < 1 / 2) return q;
26 |                    if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
27 |                    return p;
28 |                }
29 | 
30 |                var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
31 |                var p = 2 * l - q;
32 |                r = hue2rgb(p, q, h + 1 / 3);
33 |                g = hue2rgb(p, q, h);
34 |                b = hue2rgb(p, q, h - 1 / 3);
35 |            }
36 | 
37 |            return [Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)];
38 |        }
39 | 
40 |        // convert a number to a color using hsl
41 |        numberToColorHsl(i) {
42 |            // as the function expects a value between 0 and 1, and red = 0° and green = 120°
43 |            // we convert the input to the appropriate hue value
44 |            var hue = i * 100 * 1.2 / 360;
45 |            // we convert hsl to rgb (saturation 100%, lightness 50%)
46 |            var rgb = this.hslToRgb(hue, .9, .4);
47 |            // we format to css value and return
48 |            return 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')';
49 |        }
50 | }
51 | 


--------------------------------------------------------------------------------
/app/services/help-service.ts:
--------------------------------------------------------------------------------
 1 | /// <reference path="lodash.d.ts" />
 2 | 
 3 | export class HelpService {
 4 |    getNodeTypeDescription(nodeType: string) {
 5 |       return NODE_DESCRIPTIONS[nodeType.toUpperCase()];
 6 |    }
 7 | }
 8 | 
 9 | export var NODE_DESCRIPTIONS = {
10 |    'LIMIT':'returns a specified number of rows from a record set.',
11 |    'SORT': 'sorts a record set based on the specified sort key.',
12 |    'NESTED LOOP': `merges two record sets by looping through every record in the first set and
13 |    trying to find a match in the second set. All matching records are returned.`,
14 |    'MERGE JOIN': `merges two record sets by first sorting them on a <strong>join key</strong>.`,
15 |    'HASH': `generates a hash table from the records in the input recordset. Hash is used by
16 |    <strong>Hash Join</strong>.`,
17 |    'HASH JOIN': `joins to record sets by hashing one of them (using a <strong>Hash Scan</scan>).`,
18 |    'AGGREGATE': `groups records together based on a GROUP BY or aggregate function (like <code>sum()</code>).`,
19 |    'HASHAGGREGATE': `groups records together based on a GROUP BY or aggregate function (like sum()). Hash Aggregate uses
20 |    a hash to first organize the records by a key.`,
21 |    'SEQ SCAN': `finds relevant records by sequentially scanning the input record set. When reading from a table,
22 |    Seq Scans (unlike Index Scans) perform a single read operation (only the table is read).`,
23 |    'INDEX SCAN': `finds relevant records based on an <strong>Index</strong>. Index Scans perform 2 read operations: one to
24 |    read the index and another to read the actual value from the table.`,
25 |    'INDEX ONLY SCAN': `finds relevant records based on an <strong>Index</strong>. Index Only Scans perform a single read operation
26 |    from the index and do not read from the corresponding table.`,
27 |    'BITMAP HEAP SCAN': 'searches through the pages returned by the <strong>Bitmap Index Scan</strong> for relevant rows.',
28 |    'BITMAP INDEX SCAN': `uses a <strong>Bitmap Index</strong> (index which uses 1 bit per page) to find all relevant pages.
29 |    Results of this node are fed to the <strong>Bitmap Heap Scan</strong>.`,
30 |    'CTE SCAN': `performs a sequential scan of <strong>Common Table Expression (CTE) query</strong> results. Note that
31 |    results of a CTE are materialized (calculated and temporarily stored).`
32 | 
33 | };
34 | 


--------------------------------------------------------------------------------
/app/services/plan-service.ts:
--------------------------------------------------------------------------------
  1 | import {IPlan} from '../interfaces/iplan';
  2 | import {EstimateDirection} from '../enums';
  3 | /// <reference path="moment.d.ts" />
  4 | /// <reference path="lodash.d.ts" />
  5 | 
  6 | export class PlanService {
  7 |     // plan property keys
  8 |     NODE_TYPE_PROP: string = 'Node Type';
  9 |     ACTUAL_ROWS_PROP: string = 'Actual Rows';
 10 |     PLAN_ROWS_PROP: string = 'Plan Rows';
 11 |     ACTUAL_TOTAL_TIME_PROP: string = 'Actual Total Time';
 12 |     ACTUAL_LOOPS_PROP: string = 'Actual Loops';
 13 |     TOTAL_COST_PROP: string = 'Total Cost';
 14 |     PLANS_PROP: string = 'Plans';
 15 |     RELATION_NAME_PROP: string = 'Relation Name';
 16 |     SCHEMA_PROP: string = 'Schema';
 17 |     ALIAS_PROP: string = 'Alias';
 18 |     GROUP_KEY_PROP: string = 'Group Key';
 19 |     SORT_KEY_PROP: string = 'Sort Key';
 20 |     JOIN_TYPE_PROP: string = 'Join Type';
 21 |     INDEX_NAME_PROP: string = 'Index Name';
 22 |     HASH_CONDITION_PROP: string = 'Hash Cond';
 23 | 
 24 |     // computed by pev
 25 |     COMPUTED_TAGS_PROP: string = '*Tags';
 26 | 
 27 |     COSTLIEST_NODE_PROP: string = '*Costiest Node (by cost)';
 28 |     LARGEST_NODE_PROP: string = '*Largest Node (by rows)';
 29 |     SLOWEST_NODE_PROP: string = '*Slowest Node (by duration)';
 30 | 
 31 |     MAXIMUM_COSTS_PROP: string = '*Most Expensive Node (cost)';
 32 |     MAXIMUM_ROWS_PROP: string = '*Largest Node (rows)';
 33 |     MAXIMUM_DURATION_PROP: string = '*Slowest Node (time)';
 34 |     ACTUAL_DURATION_PROP: string = '*Actual Duration';
 35 |     ACTUAL_COST_PROP: string = '*Actual Cost';
 36 |     PLANNER_ESTIMATE_FACTOR: string = '*Planner Row Estimate Factor';
 37 |     PLANNER_ESIMATE_DIRECTION: string = '*Planner Row Estimate Direction';
 38 | 
 39 |     CTE_SCAN_PROP = 'CTE Scan';
 40 |     CTE_NAME_PROP = 'CTE Name';
 41 | 
 42 |     ARRAY_INDEX_KEY: string = 'arrayIndex';
 43 | 
 44 |     PEV_PLAN_TAG: string = 'plan_';
 45 | 
 46 |     private _maxRows: number = 0;
 47 |     private _maxCost: number = 0;
 48 |     private _maxDuration: number = 0;
 49 | 
 50 |     getPlans(): Array<IPlan> {
 51 |         var plans: Array<IPlan> = [];
 52 | 
 53 |         for (var i in localStorage) {
 54 |             if (_.startsWith(i, this.PEV_PLAN_TAG)) {
 55 |                 plans.push(JSON.parse(localStorage[i]));
 56 |             }
 57 |         }
 58 | 
 59 |         return _.chain(plans)
 60 |         .sortBy('createdOn')
 61 |         .reverse()
 62 |         .value();
 63 |     }
 64 | 
 65 |     getPlan(id: string): IPlan {
 66 |         return JSON.parse(localStorage.getItem(id));
 67 |     }
 68 | 
 69 |     createPlan(planName: string, planContent: string, planQuery): IPlan {
 70 |         var plan: IPlan = {
 71 |             id: this.PEV_PLAN_TAG + new Date().getTime().toString(),
 72 |             name: planName || 'plan created on ' + moment().format('LLL'),
 73 |             createdOn: new Date(),
 74 |             content: JSON.parse(planContent)[0],
 75 |             query: planQuery
 76 |         };
 77 | 
 78 |         this.analyzePlan(plan);
 79 |         return plan;
 80 |     }
 81 | 
 82 |     isJsonString(str) {
 83 |         try {
 84 |             JSON.parse(str);
 85 |         } catch (e) {
 86 |             return false;
 87 |         }
 88 |         return true;
 89 |     }
 90 | 
 91 |     analyzePlan(plan: IPlan) {
 92 |         this.processNode(plan.content.Plan);
 93 |         plan.content[this.MAXIMUM_ROWS_PROP] = this._maxRows;
 94 |         plan.content[this.MAXIMUM_COSTS_PROP] = this._maxCost;
 95 |         plan.content[this.MAXIMUM_DURATION_PROP] = this._maxDuration;
 96 | 
 97 |         this.findOutlierNodes(plan.content.Plan);
 98 | 
 99 |         localStorage.setItem(plan.id, JSON.stringify(plan));
100 |     }
101 | 
102 |     deletePlan(plan: IPlan) {
103 |         localStorage.removeItem(plan.id);
104 |     }
105 | 
106 |     deleteAllPlans() {
107 |         localStorage.clear();
108 |     }
109 | 
110 |     // recursively walk down the plan to compute various metrics
111 |     processNode(node) {
112 |         this.calculatePlannerEstimate(node);
113 |         this.calculateActuals(node);
114 | 
115 |         _.each(node, (value, key) => {
116 |             this.calculateMaximums(node, key, value);
117 | 
118 |             if (key === this.PLANS_PROP) {
119 |                 _.each(value, (value) => {
120 |                     this.processNode(value);
121 |                 });
122 |             }
123 |         });
124 |     }
125 | 
126 |     calculateMaximums(node, key, value) {
127 |         if (key === this.ACTUAL_ROWS_PROP && this._maxRows < value) {
128 |             this._maxRows = value;
129 |         }
130 |         if (key === this.ACTUAL_COST_PROP && this._maxCost < value) {
131 |             this._maxCost = value;
132 |         }
133 | 
134 |         if (key === this.ACTUAL_DURATION_PROP && this._maxDuration < value) {
135 |             this._maxDuration = value;
136 |         }
137 |     }
138 | 
139 |     findOutlierNodes(node) {
140 |         node[this.SLOWEST_NODE_PROP] = false;
141 |         node[this.LARGEST_NODE_PROP] = false;
142 |         node[this.COSTLIEST_NODE_PROP] = false;
143 | 
144 |         if (node[this.ACTUAL_COST_PROP] === this._maxCost) {
145 |             node[this.COSTLIEST_NODE_PROP] = true;
146 |         }
147 |         if (node[this.ACTUAL_ROWS_PROP] === this._maxRows) {
148 |             node[this.LARGEST_NODE_PROP] = true;
149 |         }
150 |         if (node[this.ACTUAL_DURATION_PROP] === this._maxDuration) {
151 |             node[this.SLOWEST_NODE_PROP] = true;
152 |         }
153 | 
154 |         _.each(node, (value, key) => {
155 |             if (key === this.PLANS_PROP) {
156 |                 _.each(value, (value) => {
157 |                     this.findOutlierNodes(value);
158 |                 });
159 |             }
160 |         });
161 |     }
162 | 
163 |     // actual duration and actual cost are calculated by subtracting child values from the total
164 |     calculateActuals(node) {
165 |         node[this.ACTUAL_DURATION_PROP] = node[this.ACTUAL_TOTAL_TIME_PROP];
166 |         node[this.ACTUAL_COST_PROP] = node[this.TOTAL_COST_PROP];
167 | 
168 |         console.log (node);
169 |         _.each(node.Plans, subPlan => {
170 |            console.log('processing chldren', subPlan)
171 |            // since CTE scan duration is already included in its subnodes, it should be be
172 |            // subtracted from the duration of this node
173 |             if (subPlan[this.NODE_TYPE_PROP] !== this.CTE_SCAN_PROP) {
174 |                node[this.ACTUAL_DURATION_PROP] = node[this.ACTUAL_DURATION_PROP] - subPlan[this.ACTUAL_TOTAL_TIME_PROP];
175 |                node[this.ACTUAL_COST_PROP] = node[this.ACTUAL_COST_PROP] - subPlan[this.TOTAL_COST_PROP];
176 |             }
177 |         });
178 | 
179 |         if (node[this.ACTUAL_COST_PROP] < 0) {
180 |             node[this.ACTUAL_COST_PROP] = 0;
181 |         }
182 | 
183 |         // since time is reported for an invidual loop, actual duration must be adjusted by number of loops
184 |         node[this.ACTUAL_DURATION_PROP] = node[this.ACTUAL_DURATION_PROP] * node[this.ACTUAL_LOOPS_PROP];
185 |     }
186 | 
187 |     // figure out order of magnitude by which the planner mis-estimated how many rows would be
188 |     // invloved in this node
189 |     calculatePlannerEstimate(node) {
190 |         node[this.PLANNER_ESTIMATE_FACTOR] = node[this.ACTUAL_ROWS_PROP] / node[this.PLAN_ROWS_PROP];
191 |         node[this.PLANNER_ESIMATE_DIRECTION] = EstimateDirection.under;
192 | 
193 |         if (node[this.PLANNER_ESTIMATE_FACTOR] < 1) {
194 |             node[this.PLANNER_ESIMATE_DIRECTION] = EstimateDirection.over;
195 |             node[this.PLANNER_ESTIMATE_FACTOR] = node[this.PLAN_ROWS_PROP] / node[this.ACTUAL_ROWS_PROP];
196 |         }
197 |     }
198 | }
199 | 


--------------------------------------------------------------------------------
/app/services/syntax-highlight-service.ts:
--------------------------------------------------------------------------------
  1 | /// <reference path="highlight.d.ts" />
  2 | /// <reference path="lodash.d.ts" />
  3 | 
  4 | export class SyntaxHighlightService {
  5 |     OPEN_TAG: string = ' _OPEN_TAG_';
  6 |     CLOSE_TAG: string = '_CLOSE_TAG_';
  7 | 
  8 |     highlight(code: string, keyItems: Array<string>) {
  9 |         hljs.registerLanguage('sql', LANG_SQL);
 10 |         hljs.configure({
 11 |             tabReplace: '    '
 12 |         });
 13 | 
 14 |         // prior to syntax highlighting, we want to tag key items in the raw code. making the
 15 |         // query upper case and ensuring that all comma separated values have a space
 16 |         // makes it simpler to find the items we're looing for
 17 |         var result: string = code.toUpperCase().replace(', ', ',');
 18 |         _.each(keyItems, (keyItem: string) => {
 19 |             result = result.replace(keyItem.toUpperCase(), `${this.OPEN_TAG}${keyItem}${this.CLOSE_TAG}`);
 20 |         });
 21 | 
 22 |         result = hljs.highlightAuto(result).value;
 23 |         result = result.replace(new RegExp(this.OPEN_TAG, 'g'), `<span class='code-key-item'>`);
 24 |         result = result.replace(new RegExp(this.CLOSE_TAG, 'g'), '</span>');
 25 | 
 26 |         return result;
 27 |     }
 28 | }
 29 | 
 30 | export var LANG_SQL = function(hljs) {
 31 |     var COMMENT_MODE = hljs.COMMENT('--', '
#39;);
 32 |     return {
 33 |         case_insensitive: true,
 34 |         illegal: /[<>{}*]/,
 35 |         contains: [
 36 |             {
 37 |                 beginKeywords:
 38 |                 'begin end start commit rollback savepoint lock alter create drop rename call ' +
 39 |                 'delete do handler insert load replace select truncate update set show pragma grant ' +
 40 |                 'merge describe use explain help declare prepare execute deallocate release ' +
 41 |                 'unlock purge reset change stop analyze cache flush optimize repair kill ' +
 42 |                 'install uninstall checksum restore check backup revoke',
 43 |                 end: /;/, endsWithParent: true,
 44 |                 keywords: {
 45 |                     keyword:
 46 |                     'abort abs absolute acc acce accep accept access accessed accessible account acos action activate add ' +
 47 |                     'addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias ' +
 48 |                     'allocate allow alter always analyze ancillary and any anydata anydataset anyschema anytype apply ' +
 49 |                     'archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan ' +
 50 |                     'atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid ' +
 51 |                     'authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile ' +
 52 |                     'before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float ' +
 53 |                     'binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound ' +
 54 |                     'buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel ' +
 55 |                     'capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base ' +
 56 |                     'char_length character_length characters characterset charindex charset charsetform charsetid check ' +
 57 |                     'checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close ' +
 58 |                     'cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation ' +
 59 |                     'collect colu colum column column_value columns columns_updated comment commit compact compatibility ' +
 60 |                     'compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn ' +
 61 |                     'connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection ' +
 62 |                     'consider consistent constant constraint constraints constructor container content contents context ' +
 63 |                     'contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost ' +
 64 |                     'count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation ' +
 65 |                     'critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user ' +
 66 |                     'cursor curtime customdatum cycle d data database databases datafile datafiles datalength date_add ' +
 67 |                     'date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts ' +
 68 |                     'day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate ' +
 69 |                     'declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults ' +
 70 |                     'deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank ' +
 71 |                     'depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor ' +
 72 |                     'deterministic diagnostics difference dimension direct_load directory disable disable_all ' +
 73 |                     'disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div ' +
 74 |                     'do document domain dotnet double downgrade drop dumpfile duplicate duration e each edition editionable ' +
 75 |                     'editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt ' +
 76 |                     'end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors ' +
 77 |                     'escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding ' +
 78 |                     'execu execut execute exempt exists exit exp expire explain export export_set extended extent external ' +
 79 |                     'external_1 external_2 externally extract f failed failed_login_attempts failover failure far fast ' +
 80 |                     'feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final ' +
 81 |                     'finish first first_value fixed flash_cache flashback floor flush following follows for forall force ' +
 82 |                     'form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ' +
 83 |                     'ftp full function g general generated get get_format get_lock getdate getutcdate global global_name ' +
 84 |                     'globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups ' +
 85 |                     'gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex ' +
 86 |                     'hierarchy high high_priority hosts hour http i id ident_current ident_incr ident_seed identified ' +
 87 |                     'identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment ' +
 88 |                     'index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile ' +
 89 |                     'initial initialized initially initrans inmemory inner innodb input insert install instance instantiable ' +
 90 |                     'instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat ' +
 91 |                     'is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists ' +
 92 |                     'k keep keep_duplicates key keys kill l language large last last_day last_insert_id last_value lax lcase ' +
 93 |                     'lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit ' +
 94 |                     'lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate ' +
 95 |                     'locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call ' +
 96 |                     'logoff logon logs long loop low low_priority lower lpad lrtrim ltrim m main make_set makedate maketime ' +
 97 |                     'managed management manual map mapping mask master master_pos_wait match matched materialized max ' +
 98 |                     'maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans ' +
 99 |                     'md5 measures median medium member memcompress memory merge microsecond mid migration min minextents ' +
100 |                     'minimum mining minus minute minvalue missing mod mode model modification modify module monitoring month ' +
101 |                     'months mount move movement multiset mutex n name name_const names nan national native natural nav nchar ' +
102 |                     'nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile ' +
103 |                     'nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile ' +
104 |                     'nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder ' +
105 |                     'nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck ' +
106 |                     'noswitch not nothing notice notrim novalidate now nowait nth_value nullif nulls num numb numbe ' +
107 |                     'nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ' +
108 |                     'ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old ' +
109 |                     'on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date ' +
110 |                     'oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary ' +
111 |                     'out outer outfile outline output over overflow overriding p package pad parallel parallel_enable ' +
112 |                     'parameters parent parse partial partition partitions pascal passing password password_grace_time ' +
113 |                     'password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex ' +
114 |                     'pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc ' +
115 |                     'performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin ' +
116 |                     'policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction ' +
117 |                     'prediction_cost prediction_details prediction_probability prediction_set prepare present preserve ' +
118 |                     'prior priority private private_sga privileges procedural procedure procedure_analyze processlist ' +
119 |                     'profiles project prompt protection public publishingservername purge quarter query quick quiesce quota ' +
120 |                     'quotename radians raise rand range rank raw read reads readsize rebuild record records ' +
121 |                     'recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh ' +
122 |                     'regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy ' +
123 |                     'reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename ' +
124 |                     'repair repeat replace replicate replication required reset resetlogs resize resource respect restore ' +
125 |                     'restricted result result_cache resumable resume retention return returning returns reuse reverse revoke ' +
126 |                     'right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows ' +
127 |                     'rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll ' +
128 |                     'sdo_georaster sdo_topo_geometry search sec_to_time second section securefile security seed segment select ' +
129 |                     'self sequence sequential serializable server servererror session session_user sessions_per_user set ' +
130 |                     'sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor ' +
131 |                     'si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin ' +
132 |                     'size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex ' +
133 |                     'source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows ' +
134 |                     'sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone ' +
135 |                     'standby start starting startup statement static statistics stats_binomial_test stats_crosstab ' +
136 |                     'stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep ' +
137 |                     'stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev ' +
138 |                     'stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate ' +
139 |                     'subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum ' +
140 |                     'suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate ' +
141 |                     'sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime t table tables tablespace tan tdo ' +
142 |                     'template temporary terminated tertiary_weights test than then thread through tier ties time time_format ' +
143 |                     'time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr ' +
144 |                     'timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking ' +
145 |                     'transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate ' +
146 |                     'try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress ' +
147 |                     'under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unpivot ' +
148 |                     'unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert ' +
149 |                     'url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date ' +
150 |                     'utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var ' +
151 |                     'var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray ' +
152 |                     'verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear ' +
153 |                     'wellformed when whene whenev wheneve whenever where while whitespace with within without work wrapped ' +
154 |                     'xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces ' +
155 |                     'xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek',
156 |                     literal:
157 |                     'true false null',
158 |                     built_in:
159 |                     'array bigint binary bit blob boolean char character date dec decimal float int int8 integer interval number ' +
160 |                     'numeric real record serial serial8 smallint text varchar varying void'
161 |                 },
162 |                 contains: [
163 |                     {
164 |                         className: 'string',
165 |                         begin: '\'', end: '\'',
166 |                         contains: [hljs.BACKSLASH_ESCAPE, { begin: '\'\'' }]
167 |                     },
168 |                     {
169 |                         className: 'string',
170 |                         begin: '"', end: '"',
171 |                         contains: [hljs.BACKSLASH_ESCAPE, { begin: '""' }]
172 |                     },
173 |                     {
174 |                         className: 'string',
175 |                         begin: '`', end: '`',
176 |                         contains: [hljs.BACKSLASH_ESCAPE]
177 |                     },
178 |                     hljs.C_NUMBER_MODE,
179 |                     hljs.C_BLOCK_COMMENT_MODE,
180 |                     COMMENT_MODE
181 |                 ]
182 |             },
183 |             hljs.C_BLOCK_COMMENT_MODE,
184 |             COMMENT_MODE
185 |         ]
186 |     };
187 | };
188 | 


--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | pev:
2 |   build: .
3 |   ports:
4 |     - "8000:8000"
5 |   volumes:
6 |     - ./:/var/www/pev/
7 | 


--------------------------------------------------------------------------------
/gulpfile.ts:
--------------------------------------------------------------------------------
 1 | import * as gulp from 'gulp';
 2 | import {runSequence, task} from './tools/utils';
 3 | 
 4 | // --------------
 5 | // Clean (override).
 6 | gulp.task('clean',       task('clean', 'all'));
 7 | gulp.task('clean.dist',  task('clean', 'dist'));
 8 | gulp.task('clean.test',  task('clean', 'test'));
 9 | gulp.task('clean.tmp',   task('clean', 'tmp'));
10 | 
11 | gulp.task('check.versions', task('check.versions'));
12 | 
13 | // --------------
14 | // Postinstall.
15 | gulp.task('postinstall', done =>
16 |   runSequence('clean',
17 |               'npm',
18 |               done));
19 | 
20 | // --------------
21 | // Build dev.
22 | gulp.task('build.dev', done =>
23 |   runSequence('clean.dist',
24 |               'tslint',
25 |               'build.sass.dev',
26 |               'build.img.dev',
27 |               'build.fonts.dev',
28 |               'build.js.dev',
29 |               'build.index',
30 |               done));
31 | 
32 | // --------------
33 | // Build prod.
34 | gulp.task('build.prod', done =>
35 |   runSequence('clean.dist',
36 |               'clean.tmp',
37 |               'tslint',
38 |               'build.sass.dev',
39 |               'build.img.dev',
40 |               'build.fonts.dev',
41 |               'build.html_css.prod',
42 |               'build.deps',
43 |               'build.js.prod',
44 |               'build.bundles',
45 |               'build.index',
46 |               done));
47 | 
48 | // --------------
49 | // Watch.
50 | gulp.task('build.dev.watch', done =>
51 |   runSequence('build.dev',
52 |               'watch.dev',
53 |               done));
54 | 
55 | gulp.task('build.test.watch', done =>
56 |   runSequence('build.test',
57 |               'watch.test',
58 |               done));
59 | 
60 | // --------------
61 | // Test.
62 | gulp.task('test', done =>
63 |   runSequence('clean.test',
64 |               'tslint',
65 |               'build.test',
66 |               'karma.start',
67 |               done));
68 | 
69 | // --------------
70 | // Serve.
71 | gulp.task('serve', done =>
72 |   runSequence('build.dev',
73 |               'server.start',
74 |               'watch.serve',
75 |               done));
76 | 
77 | // --------------
78 | // Docs
79 | // Disabled until https://github.com/sebastian-lenz/typedoc/issues/162 gets resolved
80 | // gulp.task('docs', done =>
81 | //   runSequence('build.docs',
82 | //               'serve.docs',
83 | //               done));
84 | 


--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
  1 | // Karma configuration
  2 | // Generated on Wed Jul 15 2015 09:44:02 GMT+0200 (Romance Daylight Time)
  3 | 'use strict';
  4 | 
  5 | module.exports = function(config) {
  6 |   config.set({
  7 | 
  8 |     // base path that will be used to resolve all patterns (eg. files, exclude)
  9 |     basePath: './',
 10 | 
 11 | 
 12 |     // frameworks to use
 13 |     // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
 14 |     frameworks: ['jasmine'],
 15 | 
 16 | 
 17 |     // list of files / patterns to load in the browser
 18 |     files: [
 19 |       'node_modules/zone.js/dist/zone-microtask.js',
 20 |       'node_modules/zone.js/dist/long-stack-trace-zone.js',
 21 |       'node_modules/zone.js/dist/jasmine-patch.js',
 22 |       'node_modules/es6-module-loader/dist/es6-module-loader.js',
 23 |       'node_modules/traceur/bin/traceur-runtime.js', // Required by PhantomJS2, otherwise it shouts ReferenceError: Can't find variable: require
 24 |       'node_modules/traceur/bin/traceur.js',
 25 |       'node_modules/systemjs/dist/system.src.js',
 26 |       'node_modules/reflect-metadata/Reflect.js',
 27 | 
 28 |       { pattern: 'node_modules/angular2/**/*.js', included: false, watched: false },
 29 |       { pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false },
 30 |       { pattern: 'test/**/*.js', included: false, watched: true },
 31 |       { pattern: 'node_modules/systemjs/dist/system-polyfills.js', included: false, watched: false }, // PhantomJS2 (and possibly others) might require it
 32 | 
 33 |       'test-main.js'
 34 |     ],
 35 | 
 36 | 
 37 |     // list of files to exclude
 38 |     exclude: [
 39 |       'node_modules/angular2/**/*_spec.js'
 40 |     ],
 41 | 
 42 | 
 43 |     // preprocess matching files before serving them to the browser
 44 |     // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
 45 |     preprocessors: {
 46 |     },
 47 | 
 48 | 
 49 |     // test results reporter to use
 50 |     // possible values: 'dots', 'progress'
 51 |     // available reporters: https://npmjs.org/browse/keyword/karma-reporter
 52 |     reporters: ['mocha'],
 53 | 
 54 | 
 55 |     // web server port
 56 |     port: 9876,
 57 | 
 58 | 
 59 |     // enable / disable colors in the output (reporters and logs)
 60 |     colors: true,
 61 | 
 62 | 
 63 |     // level of logging
 64 |     // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
 65 |     logLevel: config.LOG_INFO,
 66 | 
 67 | 
 68 |     // enable / disable watching file and executing tests whenever any file changes
 69 |     autoWatch: true,
 70 | 
 71 | 
 72 |     // start these browsers
 73 |     // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
 74 |     browsers: [
 75 |       'PhantomJS2',
 76 |       'Chrome'
 77 |     ],
 78 | 
 79 | 
 80 |     customLaunchers: {
 81 |       Chrome_travis_ci: {
 82 |         base: 'Chrome',
 83 |         flags: ['--no-sandbox']
 84 |       }
 85 |     },
 86 | 
 87 | 
 88 |     // Continuous Integration mode
 89 |     // if true, Karma captures browsers, runs the tests and exits
 90 |     singleRun: false
 91 |   });
 92 | 
 93 |   if (process.env.APPVEYOR) {
 94 |     config.browsers = ['IE'];
 95 |     config.singleRun = true;
 96 |     config.browserNoActivityTimeout = 90000; // Note: default value (10000) is not enough
 97 |   }
 98 | 
 99 |   if (process.env.TRAVIS || process.env.CIRCLECI) {
100 |     config.browsers = ['Chrome_travis_ci'];
101 |     config.singleRun = true;
102 |   }
103 | };
104 | 


--------------------------------------------------------------------------------
/logo.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AlexTatiyants/pev/6d31cdd75f557761d7581da6c46586792e5f2dad/logo.ai


--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "pev",
 3 |   "version": "0.0.0",
 4 |   "description": "Postgres Explain Visualizer",
 5 |   "repository": {
 6 |     "url": "http://www.tatiyants.com"
 7 |   },
 8 |   "scripts": {
 9 |     "build.dev": "gulp build.dev",
10 |     "build.dev.watch": "gulp build.dev.watch",
11 |     "build.prod": "gulp build.prod --env prod",
12 |     "build.test": "gulp build.test",
13 |     "build.test.watch": "gulp build.test.watch",
14 |     "docs": "npm run gulp -- build.docs && npm run gulp -- serve.docs",
15 |     "gulp": "gulp",
16 |     "karma": "karma",
17 |     "karma.start": "karma start",
18 |     "lint": "gulp tslint",
19 |     "postinstall": "tsd reinstall --clean && tsd link && tsd rebundle && gulp check.versions && gulp postinstall",
20 |     "reinstall": "rimraf node_modules && npm cache clean && npm install",
21 |     "start": "gulp serve --env dev",
22 |     "serve.dev": "gulp serve --env dev",
23 |     "tasks.list": "gulp --tasks-simple",
24 |     "test": "gulp test",
25 |     "tsd": "tsd"
26 |   },
27 |   "author": "Alex Tatiyants",
28 |   "license": "MIT",
29 |   "devDependencies": {
30 |     "async": "^1.4.2",
31 |     "chalk": "^1.1.1",
32 |     "connect-livereload": "^0.5.3",
33 |     "del": "^1.1.1",
34 |     "express": "~4.13.1",
35 |     "extend": "^3.0.0",
36 |     "gulp": "^3.9.0",
37 |     "gulp-compass": "^2.1.0",
38 |     "gulp-concat": "^2.5.2",
39 |     "gulp-filter": "^2.0.2",
40 |     "gulp-inject": "^1.3.1",
41 |     "gulp-inline-ng2-template": "^0.0.7",
42 |     "gulp-load-plugins": "^0.10.0",
43 |     "gulp-minify-css": "^1.1.6",
44 |     "gulp-minify-html": "^1.0.3",
45 |     "gulp-plumber": "~1.0.1",
46 |     "gulp-sass": "^2.0.4",
47 |     "gulp-shell": "~0.4.3",
48 |     "gulp-sourcemaps": "~1.5.2",
49 |     "gulp-template": "^3.0.0",
50 |     "gulp-tslint": "^3.3.0",
51 |     "gulp-tslint-stylish": "^1.0.4",
52 |     "gulp-typescript": "~2.8.2",
53 |     "gulp-uglify": "^1.2.0",
54 |     "gulp-util": "^3.0.7",
55 |     "gulp-watch": "^4.2.4",
56 |     "jasmine-core": "~2.3.4",
57 |     "karma": "~0.13.15",
58 |     "karma-chrome-launcher": "~0.2.0",
59 |     "karma-ie-launcher": "^0.2.0",
60 |     "karma-jasmine": "~0.3.6",
61 |     "karma-mocha-reporter": "^1.1.1",
62 |     "karma-phantomjs2-launcher": "^0.3.2",
63 |     "merge-stream": "^1.0.0",
64 |     "open": "0.0.5",
65 |     "rimraf": "^2.4.3",
66 |     "run-sequence": "^1.1.0",
67 |     "semver": "^5.0.3",
68 |     "serve-static": "^1.9.2",
69 |     "slash": "~1.0.0",
70 |     "stream-series": "^0.1.1",
71 |     "systemjs-builder": "^0.14.8",
72 |     "tiny-lr": "^0.2.1",
73 |     "traceur": "^0.0.91",
74 |     "ts-node": "^0.5.4",
75 |     "tsd": "^0.6.4",
76 |     "typedoc": "^0.3.12",
77 |     "typescript": "~1.7.3",
78 |     "typescript-register": "^1.1.0",
79 |     "yargs": "^3.25.0"
80 |   },
81 |   "dependencies": {
82 |     "angular2": "2.0.0-beta.0",
83 |     "bootstrap": "^3.3.5",
84 |     "es6-module-loader": "^0.17.8",
85 |     "es6-shim": "^0.33.3",
86 |     "highlight.js": "^9.1.0",
87 |     "lodash": "^3.10.1",
88 |     "moment": "^2.10.6",
89 |     "reflect-metadata": "0.1.2",
90 |     "rxjs": "5.0.0-beta.0",
91 |     "systemjs": "^0.19.4",
92 |     "zone.js": "0.5.10"
93 |   }
94 | }
95 | 


--------------------------------------------------------------------------------
/test-main.js:
--------------------------------------------------------------------------------
 1 | // Turn on full stack traces in errors to help debugging
 2 | Error.stackTraceLimit=Infinity;
 3 | 
 4 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 100;
 5 | 
 6 | // Cancel Karma's synchronous start,
 7 | // we will call `__karma__.start()` later, once all the specs are loaded.
 8 | __karma__.loaded = function() {};
 9 | 
10 | System.config({
11 |   baseURL: '/base/',
12 |   defaultJSExtensions: true,
13 |   paths: {
14 |     'angular2/*': 'node_modules/angular2/*.js',
15 |     'rxjs/*': 'node_modules/rxjs/*.js'
16 |   }
17 | });
18 | 
19 | System.import('angular2/src/platform/browser/browser_adapter').then(function(browser_adapter) {
20 |   browser_adapter.BrowserDomAdapter.makeCurrent();
21 | }).then(function() {
22 |   return Promise.all(
23 |     Object.keys(window.__karma__.files) // All files served by Karma.
24 |     .filter(onlySpecFiles)
25 |     .map(file2moduleName)
26 |     .map(function(path) {
27 |       return System.import(path).then(function(module) {
28 |         if (module.hasOwnProperty('main')) {
29 |           module.main();
30 |         } else {
31 |           throw new Error('Module ' + path + ' does not implement main() method.');
32 |         }
33 |       });
34 |     }));
35 | })
36 | .then(function() {
37 |   __karma__.start();
38 | }, function(error) {
39 |   console.error(error.stack || error);
40 |   __karma__.start();
41 | });
42 | 
43 | 
44 | function onlySpecFiles(path) {
45 |   return /[\.|_]spec\.js$/.test(path);
46 | }
47 | 
48 | // Normalize paths to module names.
49 | function file2moduleName(filePath) {
50 |   return filePath.replace(/\\/g, '/')
51 |     .replace(/^\/base\//, '')
52 |     .replace(/\.js/, '');
53 | }
54 | 


--------------------------------------------------------------------------------
/tools/config.ts:
--------------------------------------------------------------------------------
  1 | import {readFileSync} from 'fs';
  2 | import {argv} from 'yargs';
  3 | 
  4 | // --------------
  5 | // Configuration.
  6 | export const ENV = argv['env'] || 'dev';
  7 | export const DEBUG = argv['debug'] || false;
  8 | export const PORT = argv['port'] || 5555;
  9 | export const LIVE_RELOAD_PORT = argv['reload-port'] || 4002;
 10 | export const DOCS_PORT = argv['docs-port'] || 4003;
 11 | export const APP_BASE = argv['base'] || '/';
 12 | 
 13 | export const APP_TITLE = 'Postgres EXPLAIN Visualizer (pev)';
 14 | 
 15 | export const APP_SRC = 'app';
 16 | export const ASSETS_SRC = `${APP_SRC}/assets`;
 17 | 
 18 | export const TOOLS_DIR = 'tools';
 19 | export const TMP_DIR = 'tmp';
 20 | export const TEST_DEST = 'test';
 21 | export const DOCS_DEST = 'docs';
 22 | export const APP_DEST = `dist/${ENV}`;
 23 | export const ASSETS_DEST = `${APP_DEST}/assets`;
 24 | export const BUNDLES_DEST = `${APP_DEST}/bundles`;
 25 | export const CSS_DEST = `${APP_DEST}/css`;
 26 | export const FONTS_DEST = `${APP_DEST}/fonts`;
 27 | export const LIB_DEST = `${APP_DEST}/lib`;
 28 | export const APP_ROOT = ENV === 'dev' ? `${APP_BASE}${APP_DEST}/` : `${APP_BASE}`;
 29 | export const VERSION = appVersion();
 30 | 
 31 | export const VERSION_NPM = '2.14.7';
 32 | export const VERSION_NODE = '4.0.0';
 33 | 
 34 | // Declare NPM dependencies (Note that globs should not be injected).
 35 | export const NPM_DEPENDENCIES = [
 36 |     { src: 'systemjs/dist/system-polyfills.js', dest: LIB_DEST },
 37 | 
 38 |     { src: 'es6-shim/es6-shim.min.js', inject: 'shims', dest: LIB_DEST },
 39 |     { src: 'reflect-metadata/Reflect.js', inject: 'shims', dest: LIB_DEST },
 40 |     { src: 'systemjs/dist/system.src.js', inject: 'shims', dest: LIB_DEST },
 41 |     { src: 'angular2/bundles/angular2-polyfills.js', inject: 'shims', dest: LIB_DEST },
 42 | 
 43 |     // Faster dev page load
 44 |     { src: 'rxjs/bundles/Rx.min.js', inject: 'libs', dest: LIB_DEST },
 45 |     { src: 'angular2/bundles/angular2.min.js', inject: 'libs', dest: LIB_DEST },
 46 |     { src: 'angular2/bundles/router.js', inject: 'libs', dest: LIB_DEST }, // use router.min.js with alpha47
 47 |     { src: 'angular2/bundles/http.min.js', inject: 'libs', dest: LIB_DEST },
 48 | 
 49 |     { src: 'lodash/index.js', inject: 'libs', dest: LIB_DEST },
 50 |     { src: 'moment/moment.js', inject: 'libs', dest: LIB_DEST },
 51 |     { src: 'highlight.js/lib/highlight.js', inject: 'libs', dest: LIB_DEST },
 52 | ];
 53 | 
 54 | // Declare local files that needs to be injected
 55 | export const APP_ASSETS = [
 56 |     { src: `${ASSETS_SRC}/css/styles.css`, inject: true, dest: CSS_DEST }
 57 | ];
 58 | 
 59 | NPM_DEPENDENCIES
 60 |     .filter(d => !/\*/.test(d.src)) // Skip globs
 61 |     .forEach(d => d.src = require.resolve(d.src));
 62 | 
 63 | export const DEPENDENCIES = NPM_DEPENDENCIES.concat(APP_ASSETS);
 64 | 
 65 | // ----------------
 66 | // SystemsJS Configuration.
 67 | const SYSTEM_CONFIG_DEV = {
 68 |     defaultJSExtensions: true,
 69 |     paths: {
 70 |         'bootstrap': `${APP_ROOT}bootstrap`,
 71 |         '*': `${APP_BASE}node_modules/*`
 72 |     }
 73 | };
 74 | 
 75 | const SYSTEM_CONFIG_PROD = {
 76 |     defaultJSExtensions: true,
 77 |     bundles: {
 78 |         'bundles/app': ['bootstrap']
 79 |     }
 80 | };
 81 | 
 82 | export const SYSTEM_CONFIG = ENV === 'dev' ? SYSTEM_CONFIG_DEV : SYSTEM_CONFIG_PROD;
 83 | 
 84 | // This is important to keep clean module names as 'module name == module uri'.
 85 | export const SYSTEM_CONFIG_BUILDER = {
 86 |     defaultJSExtensions: true,
 87 |     paths: {
 88 |         '*': `${TMP_DIR}/*`,
 89 |         'angular2/*': 'node_modules/angular2/*',
 90 |         'rxjs/*': 'node_modules/rxjs/*'
 91 |     }
 92 | };
 93 | 
 94 | // --------------
 95 | // Private.
 96 | function appVersion(): number | string {
 97 |     var pkg = JSON.parse(readFileSync('package.json').toString());
 98 |     return pkg.version;
 99 | }
100 | 


--------------------------------------------------------------------------------
/tools/tasks/build.bundles.ts:
--------------------------------------------------------------------------------
 1 | import {parallel} from 'async';
 2 | import {join} from 'path';
 3 | import * as Builder from 'systemjs-builder';
 4 | import {BUNDLES_DEST, SYSTEM_CONFIG_BUILDER} from '../config';
 5 | 
 6 | const BUNDLE_OPTS = {
 7 |   minify: true,
 8 |   sourceMaps: true,
 9 |   format: 'cjs'
10 | };
11 | 
12 | export = function bundles(gulp, plugins) {
13 |   return function (done) {
14 |     let builder = new Builder(SYSTEM_CONFIG_BUILDER);
15 | 
16 |     parallel([
17 |       bundleApp
18 |     ], () => done());
19 | 
20 |     function bundleApp(done) {
21 |       builder.bundle(
22 |         'bootstrap - angular2/*',
23 |         join(BUNDLES_DEST, 'app.js'), BUNDLE_OPTS).then(done);
24 |     }
25 |   };
26 | };
27 | 


--------------------------------------------------------------------------------
/tools/tasks/build.deps.ts:
--------------------------------------------------------------------------------
 1 | import * as merge from 'merge-stream';
 2 | import {DEPENDENCIES} from '../config';
 3 | 
 4 | export = function buildDepsProd(gulp, plugins) {
 5 |   return function () {
 6 |     let stream = merge();
 7 | 
 8 |     DEPENDENCIES.forEach(dep => {
 9 |       stream.add(addStream(dep));
10 |     });
11 | 
12 |     return stream;
13 | 
14 |     function addStream(dep) {
15 |       let stream = gulp.src(dep.src);
16 |       stream.pipe(gulp.dest(dep.dest));
17 |       return stream;
18 |     }
19 |   };
20 | };
21 | 


--------------------------------------------------------------------------------
/tools/tasks/build.docs.ts:
--------------------------------------------------------------------------------
 1 | import {join} from 'path';
 2 | import {APP_SRC, APP_TITLE, DOCS_DEST} from '../config';
 3 | 
 4 | export = function buildDocs(gulp, plugins, option) {
 5 |   return function() {
 6 | 
 7 |     let src = [
 8 |                 join(APP_SRC, '**/*.ts'),
 9 |                 '!' + join(APP_SRC, '**/*_spec.ts')
10 |               ];
11 | 
12 |     return gulp.src(src)
13 |       .pipe(plugins.typedoc({
14 |         // TypeScript options (see typescript docs)
15 |         module: 'commonjs',
16 |         target: 'es5',
17 |         includeDeclarations: true,
18 |         // Output options (see typedoc docs)
19 |         out: DOCS_DEST,
20 |         json: join(DOCS_DEST , 'data/docs.json' ),
21 |         name: APP_TITLE,
22 |         ignoreCompilerErrors: false,
23 |         experimentalDecorators: true,
24 |         version: true
25 |       }));
26 |     };
27 | }
28 | 


--------------------------------------------------------------------------------
/tools/tasks/build.fonts.dev.ts:
--------------------------------------------------------------------------------
 1 | import {join} from 'path';
 2 | import {APP_SRC, APP_DEST} from '../config';
 3 | 
 4 | export = function buildFontsDev(gulp, plugins) {
 5 |   return function () {
 6 |     return gulp.src([
 7 |         join(APP_SRC, '**/*.eot'),
 8 |         join(APP_SRC, '**/*.ttf'),
 9 |         join(APP_SRC, '**/*.woff'),
10 |         join(APP_SRC, '**/*.woff2'),
11 |         join(APP_SRC, '**/*.otf')
12 |       ])
13 |       .pipe(gulp.dest(APP_DEST));
14 |   };
15 | }
16 | 


--------------------------------------------------------------------------------
/tools/tasks/build.html_css.prod.ts:
--------------------------------------------------------------------------------
 1 | import * as merge from 'merge-stream';
 2 | import {join} from 'path';
 3 | import {APP_SRC, TMP_DIR} from '../config';
 4 | 
 5 | // const HTML_MINIFIER_OPTS = { empty: true };
 6 | 
 7 | export = function buildJSDev(gulp, plugins) {
 8 |   return function () {
 9 | 
10 |     return merge(minifyHtml(), minifyCss());
11 | 
12 |     function minifyHtml() {
13 |       return gulp.src(join(APP_SRC, '**/*.html'))
14 |         // .pipe(plugins.minifyHtml(HTML_MINIFIER_OPTS))
15 |         .pipe(gulp.dest(TMP_DIR));
16 |     }
17 | 
18 |     function minifyCss() {
19 |       return gulp.src(join(APP_SRC, '**/*.css'))
20 |         .pipe(plugins.minifyCss())
21 |         .pipe(gulp.dest(TMP_DIR));
22 |     }
23 |   };
24 | };
25 | 


--------------------------------------------------------------------------------
/tools/tasks/build.img.dev.ts:
--------------------------------------------------------------------------------
 1 | import {join} from 'path';
 2 | import {APP_SRC, APP_DEST} from '../config';
 3 | 
 4 | export = function buildImagesDev(gulp, plugins) {
 5 |   return function () {
 6 |     return gulp.src([
 7 |         join(APP_SRC, '**/*.gif'),
 8 |         join(APP_SRC, '**/*.jpg'),
 9 |         join(APP_SRC, '**/*.png'),
10 |         join(APP_SRC, '**/*.svg')
11 |       ])
12 |       .pipe(gulp.dest(APP_DEST));
13 |   };
14 | }
15 | 


--------------------------------------------------------------------------------
/tools/tasks/build.index.ts:
--------------------------------------------------------------------------------
 1 | import {join, sep} from 'path';
 2 | import {APP_SRC, APP_DEST, DEPENDENCIES, ENV} from '../config';
 3 | import {transformPath, templateLocals} from '../utils';
 4 | 
 5 | export = function buildIndexDev(gulp, plugins) {
 6 |   return function () {
 7 |     return gulp.src(join(APP_SRC, 'index.html'))
 8 |       // NOTE: There might be a way to pipe in loop.
 9 |       .pipe(inject('shims'))
10 |       .pipe(inject('libs'))
11 |       .pipe(inject())
12 |       .pipe(plugins.template(templateLocals()))
13 |       .pipe(gulp.dest(APP_DEST));
14 |   };
15 | 
16 |   function inject(name?: string) {
17 |     return plugins.inject(gulp.src(getInjectablesDependenciesRef(name), { read: false }), {
18 |       name,
19 |       transform: transformPath(plugins, 'dev')
20 |     });
21 |   }
22 | 
23 |   function getInjectablesDependenciesRef(name?: string) {
24 |     return DEPENDENCIES
25 |       .filter(dep => dep['inject'] && dep['inject'] === (name || true))
26 |       .map(mapPath);
27 |   }
28 | 
29 |   function mapPath(dep) {
30 |     let prodPath = join(dep.dest, dep.src.split(sep).pop());
31 |     return ('prod' === ENV ? prodPath : dep.src );
32 |   }
33 | };
34 | 


--------------------------------------------------------------------------------
/tools/tasks/build.js.dev.ts:
--------------------------------------------------------------------------------
 1 | import {join} from 'path';
 2 | import {APP_SRC, APP_DEST} from '../config';
 3 | import {templateLocals, tsProjectFn} from '../utils';
 4 | 
 5 | export = function buildJSDev(gulp, plugins) {
 6 |   let tsProject = tsProjectFn(plugins);
 7 |   return function () {
 8 |     let src = [
 9 |                 join(APP_SRC, '**/*.ts'),
10 |                 '!' + join(APP_SRC, '**/*_spec.ts')
11 |               ];
12 | 
13 |     let result = gulp.src(src)
14 |       .pipe(plugins.plumber())
15 |       // Won't be required for non-production build after the change
16 |       .pipe(plugins.inlineNg2Template({ base: APP_SRC }))
17 |       .pipe(plugins.sourcemaps.init())
18 |       .pipe(plugins.typescript(tsProject));
19 | 
20 |     return result.js
21 |       .pipe(plugins.sourcemaps.write())
22 |       .pipe(plugins.template(templateLocals()))
23 |       .pipe(gulp.dest(APP_DEST));
24 |   };
25 | };
26 | 


--------------------------------------------------------------------------------
/tools/tasks/build.js.prod.ts:
--------------------------------------------------------------------------------
 1 | import {join} from 'path';
 2 | import {APP_SRC, TMP_DIR} from '../config';
 3 | import {templateLocals, tsProjectFn} from '../utils';
 4 | 
 5 | export = function buildJSDev(gulp, plugins) {
 6 |   return function () {
 7 |     let tsProject = tsProjectFn(plugins);
 8 |     let src = [
 9 |                 join(APP_SRC, '**/*.ts'),
10 |                 '!' + join(APP_SRC, '**/*_spec.ts')
11 |               ];
12 | 
13 |     let result = gulp.src(src)
14 |       .pipe(plugins.plumber())
15 |       .pipe(plugins.inlineNg2Template({ base: TMP_DIR }))
16 |       .pipe(plugins.typescript(tsProject));
17 | 
18 |     return result.js
19 |       .pipe(plugins.template(templateLocals()))
20 |       .pipe(gulp.dest(TMP_DIR));
21 |   };
22 | };
23 | 


--------------------------------------------------------------------------------
/tools/tasks/build.sass.dev.ts:
--------------------------------------------------------------------------------
 1 | import {join} from 'path';
 2 | import {APP_SRC} from '../config';
 3 | 
 4 | export = function buildSassDev(gulp, plugins, option) {
 5 |     return function() {
 6 |         return gulp.src(join(APP_SRC, '**', '*.scss'))
 7 |             .pipe(plugins.plumber({
 8 |                 // this allows gulp not to crash on sass compilation errors
 9 |                 errorHandler: function(error) {
10 |                     console.log(error.message);
11 |                     this.emit('end');
12 |                 }
13 |             }))
14 |             .pipe(plugins.compass({
15 |                 style: 'compressed',
16 |                 css: 'app/assets/css',
17 |                 sass: join(APP_SRC, 'assets/sass')
18 |             }))
19 |             .pipe(gulp.dest(join(APP_SRC, 'assets')));
20 |     };
21 | }
22 | 


--------------------------------------------------------------------------------
/tools/tasks/build.test.ts:
--------------------------------------------------------------------------------
 1 | import {join} from 'path';
 2 | import {APP_SRC, TEST_DEST} from '../config';
 3 | import {tsProjectFn} from '../utils';
 4 | 
 5 | export = function buildTest(gulp, plugins) {
 6 |   return function () {
 7 |     let tsProject = tsProjectFn(plugins);
 8 |     let src = [
 9 |                 join(APP_SRC, '**/*.ts'),
10 |                 '!' + join(APP_SRC, 'bootstrap.ts')
11 |               ];
12 | 
13 |     let result = gulp.src(src)
14 |       .pipe(plugins.plumber())
15 |       .pipe(plugins.inlineNg2Template({ base: APP_SRC }))
16 |       .pipe(plugins.typescript(tsProject));
17 | 
18 |     return result.js
19 |       .pipe(gulp.dest(TEST_DEST));
20 |   };
21 | };
22 | 


--------------------------------------------------------------------------------
/tools/tasks/check.versions.ts:
--------------------------------------------------------------------------------
 1 | import {VERSION_NPM, VERSION_NODE} from '../config';
 2 | 
 3 | function reportError(message: string) {
 4 |   console.error(require('chalk').white.bgRed.bold(message));
 5 |   process.exit(1);
 6 | }
 7 | 
 8 | module.exports = function check(gulp, plugins) {
 9 |   return function () {
10 |     let exec = require('child_process').exec;
11 |     let semver = require('semver');
12 | 
13 |     exec('npm --version',
14 |       function (error, stdout, stderr) {
15 |         if (error !== null) {
16 |           reportError('npm preinstall error: ' + error + stderr);
17 |         }
18 | 
19 |         if (!semver.gte(stdout, VERSION_NPM)) {
20 |           reportError('NPM is not in required version! Required is ' + VERSION_NPM + ' and you\'re using ' + stdout);
21 |         }
22 |       });
23 | 
24 |     exec('node --version',
25 |       function (error, stdout, stderr) {
26 |         if (error !== null) {
27 |           reportError('npm preinstall error: ' + error + stderr);
28 |         }
29 | 
30 |         if (!semver.gte(stdout, VERSION_NODE)) {
31 |           reportError('NODE is not in required version! Required is ' + VERSION_NODE + ' and you\'re using ' + stdout);
32 |         }
33 |       });
34 |   };
35 | };
36 | 


--------------------------------------------------------------------------------
/tools/tasks/clean.ts:
--------------------------------------------------------------------------------
 1 | import * as async from 'async';
 2 | import * as del from 'del';
 3 | import {APP_DEST, TEST_DEST, TMP_DIR} from '../config';
 4 | 
 5 | export = function clean(gulp, plugins, option) {
 6 |   return function (done) {
 7 | 
 8 |     switch(option) {
 9 |       case 'all'    : cleanAll(done);     break;
10 |       case 'dist'   : cleanDist(done);    break;
11 |       case 'test'   : cleanTest(done);    break;
12 |       case 'tmp'    : cleanTmp(done);     break;
13 |       default: done();
14 |     }
15 | 
16 |   };
17 | };
18 | 
19 | function cleanAll(done) {
20 |   async.parallel([
21 |     cleanDist,
22 |     cleanTest,
23 |     cleanTmp
24 |   ], done);
25 | }
26 | function cleanDist(done) {
27 |   del(APP_DEST, done);
28 | }
29 | function cleanTest(done) {
30 |   del(TEST_DEST, done);
31 | }
32 | function cleanTmp(done) {
33 |   del(TMP_DIR, done);
34 | }
35 | 


--------------------------------------------------------------------------------
/tools/tasks/karma.start.ts:
--------------------------------------------------------------------------------
 1 | import * as karma from 'karma';
 2 | import {join} from 'path';
 3 | 
 4 | export = function karmaStart() {
 5 |   return function (done) {
 6 |     new (<any>karma).Server({
 7 |       configFile: join(process.cwd(), 'karma.conf.js'),
 8 |       singleRun: true
 9 |     }).start(done);
10 |   };
11 | };
12 | 


--------------------------------------------------------------------------------
/tools/tasks/npm.ts:
--------------------------------------------------------------------------------
1 | export = function npm(gulp, plugins) {
2 |   return plugins.shell.task([
3 |     'npm prune'
4 |   ]);
5 | };
6 | 


--------------------------------------------------------------------------------
/tools/tasks/serve.docs.ts:
--------------------------------------------------------------------------------
1 | import {serveDocs} from '../utils';
2 | 
3 | export = function serverStart(gulp, plugins) {
4 |   return function () {
5 |     serveDocs();
6 |   };
7 | };
8 | 


--------------------------------------------------------------------------------
/tools/tasks/server.start.ts:
--------------------------------------------------------------------------------
1 | import {serveSPA} from '../utils';
2 | 
3 | export = function serverStart(gulp, plugins) {
4 |   return function () {
5 |     serveSPA();
6 |   };
7 | };
8 | 


--------------------------------------------------------------------------------
/tools/tasks/tsd.ts:
--------------------------------------------------------------------------------
1 | export = function tsd(gulp, plugins) {
2 |   return plugins.shell.task([
3 |     'tsd reinstall --clean',
4 |     'tsd link',
5 |     'tsd rebundle'
6 |   ]);
7 | };
8 | 


--------------------------------------------------------------------------------
/tools/tasks/tslint.ts:
--------------------------------------------------------------------------------
 1 | import {join} from 'path';
 2 | import {APP_SRC, TOOLS_DIR} from '../config';
 3 | 
 4 | export = function tslint(gulp, plugins) {
 5 |   return function () {
 6 |     let src = [
 7 |                 join(APP_SRC, '**/*.ts'),
 8 |                 '!' + join(APP_SRC, '**/*.d.ts'),
 9 |                 join(TOOLS_DIR, '**/*.ts'),
10 |                 '!' + join(TOOLS_DIR, '**/*.d.ts')
11 |               ];
12 | 
13 |     return gulp.src(src)
14 |       .pipe(plugins.tslint())
15 |       .pipe(plugins.tslint.report(plugins.tslintStylish, {
16 |         emitError: false,
17 |         sort: true,
18 |         bell: true
19 |       }));
20 |   };
21 | };
22 | 


--------------------------------------------------------------------------------
/tools/tasks/watch.dev.ts:
--------------------------------------------------------------------------------
1 | import {join} from 'path';
2 | import {APP_SRC} from '../config';
3 | 
4 | export = function watchDev(gulp, plugins) {
5 |   return function () {
6 |     plugins.watch(join(APP_SRC, '**/*'), () => gulp.start('build.dev'));
7 |   };
8 | };
9 | 


--------------------------------------------------------------------------------
/tools/tasks/watch.serve.ts:
--------------------------------------------------------------------------------
 1 | import * as runSequence from 'run-sequence';
 2 | import {join} from 'path';
 3 | import {APP_SRC} from '../config';
 4 | import {notifyLiveReload} from '../utils';
 5 | 
 6 | export = function watchServe(gulp, plugins) {
 7 |   return function () {
 8 |     plugins.watch(join(APP_SRC, '**'), e =>
 9 |       runSequence('build.dev', () => notifyLiveReload(e))
10 |     );
11 |   };
12 | };
13 | 


--------------------------------------------------------------------------------
/tools/tasks/watch.test.ts:
--------------------------------------------------------------------------------
1 | import {join} from 'path';
2 | import {APP_SRC} from '../config';
3 | 
4 | export = function watchTest(gulp, plugins) {
5 |   return function () {
6 |     plugins.watch(join(APP_SRC, '**/*.ts'), () => gulp.start('build.test'));
7 |   };
8 | };
9 | 


--------------------------------------------------------------------------------
/tools/typings/connect-livereload.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'connect-livereload' {
2 |   function connectLivereload(options?: any): any;
3 |   module connectLivereload {}
4 |   export = connectLivereload;
5 | }
6 | 


--------------------------------------------------------------------------------
/tools/typings/gulp-load-plugins.d.ts:
--------------------------------------------------------------------------------
 1 | // Type definitions for gulp-load-plugins
 2 | // Project: https://github.com/jackfranklin/gulp-load-plugins
 3 | // Definitions by: Joe Skeen <http://github.com/joeskeen>
 4 | // Definitions: https://github.com/borisyankov/DefinitelyTyped
 5 | 
 6 | // Does not support ES2015 import (import * as open from 'open').
 7 | 
 8 | /** Loads in any gulp plugins and attaches them to an object, freeing you up from having to manually require each gulp plugin. */
 9 | declare module 'gulp-load-plugins' {
10 | 
11 | 	interface IOptions {
12 | 		/** the glob(s) to search for, default ['gulp-*', 'gulp.*'] */
13 | 		pattern?: string[];
14 | 		/** where to find the plugins, searched up from process.cwd(), default 'package.json' */
15 | 		config?: string;
16 | 		/** which keys in the config to look within, default ['dependencies', 'devDependencies', 'peerDependencies'] */
17 | 		scope?: string[];
18 | 		/** what to remove from the name of the module when adding it to the context, default /^gulp(-|\.)/ */
19 | 		replaceString?: RegExp;
20 | 		/** if true, transforms hyphenated plugin names to camel case, default true */
21 | 		camelize?: boolean;
22 | 		/** whether the plugins should be lazy loaded on demand, default true */
23 | 		lazy?: boolean;
24 | 		/** a mapping of plugins to rename, the key being the NPM name of the package, and the value being an alias you define */
25 | 		rename?: IPluginNameMappings;
26 | 	}
27 | 
28 | 	interface IPluginNameMappings {
29 | 		[npmPackageName: string]: string;
30 | 	}
31 | 
32 | 	/** Loads in any gulp plugins and attaches them to an object, freeing you up from having to manually require each gulp plugin. */
33 | 	function gulpLoadPlugins<T extends IGulpPlugins>(options?: IOptions): T;
34 | 	module gulpLoadPlugins {}
35 | 	export = gulpLoadPlugins;
36 | }
37 | 
38 | /**
39 |  * Extend this interface to use Gulp plugins in your gulpfile.js
40 |  */
41 | interface IGulpPlugins {
42 | }
43 | 


--------------------------------------------------------------------------------
/tools/typings/karma.d.ts:
--------------------------------------------------------------------------------
 1 | // Use this minimalistic definition file as bluebird dependency
 2 | // generate a lot of errors.
 3 | 
 4 | declare module 'karma' {
 5 |   var karma: IKarma;
 6 |   export = karma;
 7 |   interface IKarma {
 8 |     server: {
 9 |       start(options: any, callback: Function): void
10 |     };
11 |   }
12 | }
13 | 


--------------------------------------------------------------------------------
/tools/typings/merge-stream.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'merge-stream' {
2 |   function mergeStream(...streams: NodeJS.ReadWriteStream[]): MergeStream;
3 |   interface MergeStream extends NodeJS.ReadWriteStream {
4 |     add(stream: NodeJS.ReadWriteStream): MergeStream;
5 |   }
6 |   module mergeStream {}
7 |   export = mergeStream;
8 | }


--------------------------------------------------------------------------------
/tools/typings/open.d.ts:
--------------------------------------------------------------------------------
1 | // https://github.com/borisyankov/DefinitelyTyped/tree/master/open
2 | // Does not support ES2015 import (import * as open from 'open').
3 | 
4 | declare module 'open' {
5 | 	function open(target: string, app?: string): void;
6 |   module open {}
7 | 	export = open;
8 | }
9 | 


--------------------------------------------------------------------------------
/tools/typings/run-sequence.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'run-sequence' {
2 |   function runSequence(...args: any[]): void;
3 |   module runSequence {}
4 |   export = runSequence;
5 | }
6 | 


--------------------------------------------------------------------------------
/tools/typings/slash.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'slash' {
2 |   function slash(path: string): string;
3 |   module slash {}
4 |   export = slash;
5 | }
6 | 


--------------------------------------------------------------------------------
/tools/typings/systemjs-builder.d.ts:
--------------------------------------------------------------------------------
 1 | declare module 'systemjs-builder' {
 2 |   class Builder {
 3 |     constructor(configObject?: any, baseUrl?: string, configPath?: string);
 4 |     bundle(source: string, target: string, options?: any): Promise<any>;
 5 |     buildStatic(source: string, target: string, options?: any): Promise<any>;
 6 |   }
 7 | 
 8 |   module Builder {}
 9 |   export = Builder;
10 | }
11 | 


--------------------------------------------------------------------------------
/tools/typings/tiny-lr.d.ts:
--------------------------------------------------------------------------------
 1 | declare module 'tiny-lr' {
 2 |   function tinylr(): ITinylr;
 3 |   module tinylr {}
 4 |   export = tinylr;
 5 | 
 6 |   interface ITinylr {
 7 |     changed(options: any): void;
 8 |     listen(port: number): void;
 9 |   }
10 | }
11 | 


--------------------------------------------------------------------------------
/tools/typings/yargs.d.ts:
--------------------------------------------------------------------------------
 1 | declare module 'yargs' {
 2 |   var yargs: IYargs;
 3 |   export = yargs;
 4 | 
 5 |   // Minimalistic but serves the usage in the seed.
 6 |   interface IYargs {
 7 |     argv: any;
 8 |   }
 9 | }
10 | 


--------------------------------------------------------------------------------
/tools/utils.ts:
--------------------------------------------------------------------------------
 1 | export * from './utils/template-injectables';
 2 | export * from './utils/template-locals';
 3 | export * from './utils/server';
 4 | export * from './utils/tasks_tools';
 5 | 
 6 | 
 7 | export function tsProjectFn(plugins) {
 8 |   return plugins.typescript.createProject('tsconfig.json', {
 9 |     typescript: require('typescript')
10 |   });
11 | }
12 | 


--------------------------------------------------------------------------------
/tools/utils/server.ts:
--------------------------------------------------------------------------------
 1 | import * as connectLivereload from 'connect-livereload';
 2 | import * as express from 'express';
 3 | import * as tinylrFn from 'tiny-lr';
 4 | import * as openResource from 'open';
 5 | import * as serveStatic from 'serve-static';
 6 | import {resolve} from 'path';
 7 | import {APP_BASE, APP_DEST, DOCS_DEST, LIVE_RELOAD_PORT, DOCS_PORT, PORT} from '../config';
 8 | 
 9 | let tinylr = tinylrFn();
10 | 
11 | 
12 | export function serveSPA() {
13 |   let server = express();
14 |   tinylr.listen(LIVE_RELOAD_PORT);
15 | 
16 |   server.use(
17 |     APP_BASE,
18 |     connectLivereload({ port: LIVE_RELOAD_PORT }),
19 |     express.static(process.cwd())
20 |   );
21 | 
22 |   server.listen(PORT, () =>
23 |     openResource('http://localhost:' + PORT + APP_BASE + APP_DEST)
24 |   );
25 | }
26 | 
27 | export function notifyLiveReload(e) {
28 |   let fileName = e.path;
29 |   tinylr.changed({
30 |     body: { files: [fileName] }
31 |   });
32 | }
33 | 
34 | export function serveDocs() {
35 |   let server = express();
36 | 
37 |    server.use(
38 |     APP_BASE,
39 |     serveStatic(resolve(process.cwd(), DOCS_DEST))
40 |   );
41 | 
42 |    server.listen(DOCS_PORT, () =>
43 |     openResource('http://localhost:' + DOCS_PORT + APP_BASE)
44 |   );
45 | }
46 | 


--------------------------------------------------------------------------------
/tools/utils/tasks_tools.ts:
--------------------------------------------------------------------------------
 1 | import * as gulp from 'gulp';
 2 | import * as util from 'gulp-util';
 3 | import * as chalk from 'chalk';
 4 | import * as gulpLoadPlugins from 'gulp-load-plugins';
 5 | import * as _runSequence from 'run-sequence';
 6 | import {readdirSync, existsSync, lstatSync} from 'fs';
 7 | import {join} from 'path';
 8 | import {TOOLS_DIR} from '../config';
 9 | 
10 | const TASKS_PATH = join(TOOLS_DIR, 'tasks');
11 | 
12 | // NOTE: Remove if no issues with runSequence function below.
13 | // export function loadTasks(): void {
14 | //   scanDir(TASKS_PATH, (taskname) => registerTask(taskname));
15 | // }
16 | 
17 | export function task(taskname: string, option?: string) {
18 |   util.log('Loading task', chalk.yellow(taskname, option || ''));
19 |   return require(join('..', 'tasks', taskname))(gulp, gulpLoadPlugins(), option);
20 | }
21 | 
22 | export function runSequence(...sequence: any[]) {
23 |   let tasks = [];
24 |   let _sequence = sequence.slice(0);
25 |   sequence.pop();
26 | 
27 |   scanDir(TASKS_PATH, taskname => tasks.push(taskname));
28 | 
29 |   sequence.forEach(task => {
30 |     if (tasks.indexOf(task) > -1) { registerTask(task); }
31 |   });
32 | 
33 |   return _runSequence(..._sequence);
34 | }
35 | 
36 | // ----------
37 | // Private.
38 | 
39 | function registerTask(taskname: string, filename?: string, option: string = ''): void {
40 |   gulp.task(taskname, task(filename || taskname, option));
41 | }
42 | 
43 | // TODO: add recursive lookup ? or enforce pattern file + folder (ie ./tools/utils & ./tools/utils.ts )
44 | function scanDir(root: string, cb: (taskname: string) => void) {
45 |   if (!existsSync(root)) return;
46 | 
47 |   walk(root);
48 | 
49 |   function walk(path) {
50 |     let files = readdirSync(path);
51 |     for (let i = 0; i < files.length; i += 1) {
52 |       let file = files[i];
53 |       let curPath = join(path, file);
54 |       // if (lstatSync(curPath).isDirectory()) { // recurse
55 |       //   path = file;
56 |       //   walk(curPath);
57 |       // }
58 |       if (lstatSync(curPath).isFile() && /\.ts$/.test(file)) {
59 |         let taskname = file.replace(/(\.ts)/, '');
60 |         cb(taskname);
61 |       }
62 |     }
63 |   }
64 | }
65 | 


--------------------------------------------------------------------------------
/tools/utils/template-injectables.ts:
--------------------------------------------------------------------------------
 1 | import * as slash from 'slash';
 2 | import {join} from 'path';
 3 | import {APP_BASE, APP_DEST, ENV} from '../config';
 4 | 
 5 | let injectables: string[] = [];
 6 | 
 7 | export function injectableAssetsRef() {
 8 |   return injectables;
 9 | }
10 | 
11 | export function registerInjectableAssetsRef(paths: string[], target: string = '') {
12 |   injectables = injectables.concat(
13 |     paths
14 |       .filter(path => !/(\.map)$/.test(path))
15 |       .map(path => join(target, slash(path).split('/').pop()))
16 |   );
17 | }
18 | 
19 | export function transformPath(plugins, env) {
20 |   return function (filepath) {
21 |     filepath = ENV === 'prod' ? filepath.replace(`/${APP_DEST}`, '') : filepath;
22 |     return slash(plugins.inject.transform.apply(plugins.inject.transform, arguments));
23 |   };
24 | }
25 | 


--------------------------------------------------------------------------------
/tools/utils/template-locals.ts:
--------------------------------------------------------------------------------
 1 | import {APP_BASE, APP_DEST, APP_ROOT, APP_TITLE, SYSTEM_CONFIG, VERSION} from '../config';
 2 | 
 3 | // TODO: Add an interface to register more template locals.
 4 | export function templateLocals() {
 5 |   return {
 6 |     APP_BASE,
 7 |     APP_DEST,
 8 |     APP_ROOT,
 9 |     APP_TITLE,
10 |     SYSTEM_CONFIG,
11 |     VERSION
12 |   };
13 | }
14 | 


--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "compilerOptions": {
 3 |         "target": "es5",
 4 |         "module": "commonjs",
 5 |         "declaration": false,
 6 |         "noImplicitAny": false,
 7 |         "removeComments": true,
 8 |         "noLib": false,
 9 |         "emitDecoratorMetadata": true,
10 |         "experimentalDecorators": true,
11 |         "sourceMap": true
12 |     },
13 |     "exclude": [
14 |         "node_modules"
15 |     ],
16 |     "compileOnSave": false
17 | }
18 | 


--------------------------------------------------------------------------------
/tsd.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "version": "v4",
 3 |   "repo": "DefinitelyTyped/DefinitelyTyped",
 4 |   "ref": "master",
 5 |   "path": "tools/typings/tsd",
 6 |   "bundle": "tools/typings/tsd/tsd.d.ts",
 7 |   "installed": {
 8 |     "systemjs/systemjs.d.ts": {
 9 |       "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f"
10 |     },
11 |     "gulp/gulp.d.ts": {
12 |       "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f"
13 |     },
14 |     "q/Q.d.ts": {
15 |       "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f"
16 |     },
17 |     "orchestrator/orchestrator.d.ts": {
18 |       "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f"
19 |     },
20 |     "gulp-shell/gulp-shell.d.ts": {
21 |       "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f"
22 |     },
23 |     "mime/mime.d.ts": {
24 |       "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f"
25 |     },
26 |     "express/express.d.ts": {
27 |       "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f"
28 |     },
29 |     "serve-static/serve-static.d.ts": {
30 |       "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f"
31 |     },
32 |     "del/del.d.ts": {
33 |       "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f"
34 |     },
35 |     "glob/glob.d.ts": {
36 |       "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f"
37 |     },
38 |     "minimatch/minimatch.d.ts": {
39 |       "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f"
40 |     },
41 |     "async/async.d.ts": {
42 |       "commit": "5c3e47967affa3c4128a3875d1664ba206ae1b0f"
43 |     },
44 |     "es6-promise/es6-promise.d.ts": {
45 |       "commit": "923c5431d9447db9d5cf41adc5914e3c94c1ff10"
46 |     },
47 |     "node/node.d.ts": {
48 |       "commit": "5a8fc5ee71701431e4fdbb80c506e3c13f85a9ff"
49 |     },
50 |     "gulp-util/gulp-util.d.ts": {
51 |       "commit": "5a8fc5ee71701431e4fdbb80c506e3c13f85a9ff"
52 |     },
53 |     "vinyl/vinyl.d.ts": {
54 |       "commit": "5a8fc5ee71701431e4fdbb80c506e3c13f85a9ff"
55 |     },
56 |     "through2/through2.d.ts": {
57 |       "commit": "5a8fc5ee71701431e4fdbb80c506e3c13f85a9ff"
58 |     },
59 |     "chalk/chalk.d.ts": {
60 |       "commit": "5a8fc5ee71701431e4fdbb80c506e3c13f85a9ff"
61 |     }
62 |   }
63 | }
64 | 


--------------------------------------------------------------------------------
/tsdrc:
--------------------------------------------------------------------------------
1 | {
2 |     "strictSSL": false
3 | }
4 | 


--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "rules": {
 3 |     "class-name": true,
 4 |     "curly": false,
 5 |     "eofline": true,
 6 |     "indent": "spaces",
 7 |     "max-line-length": [true, 140],
 8 |     "member-ordering": [true,
 9 |       "public-before-private",
10 |       "static-before-instance",
11 |       "variables-before-functions"
12 |     ],
13 |     "no-arg": true,
14 |     "no-construct": true,
15 |     "no-duplicate-key": true,
16 |     "no-duplicate-variable": true,
17 |     "no-empty": true,
18 |     "no-eval": true,
19 |     "no-trailing-comma": true,
20 |     "no-trailing-whitespace": true,
21 |     "no-unused-expression": true,
22 |     "no-unused-variable": true,
23 |     "no-unreachable": true,
24 |     "no-use-before-declare": true,
25 |     "one-line": [true,
26 |       "check-open-brace",
27 |       "check-catch",
28 |       "check-else",
29 |       "check-whitespace"
30 |     ],
31 |     "quotemark": [true, "single"],
32 |     "semicolon": true,
33 |     "triple-equals": true,
34 |     "variable-name": false
35 |   }
36 | }
37 | 


--------------------------------------------------------------------------------