├── .DS_Store ├── dashboard-interface ├── .angular-cli.json ├── .editorconfig ├── .gitignore ├── README.md ├── e2e │ ├── app.e2e-spec.ts │ ├── app.po.ts │ └── tsconfig.e2e.json ├── karma.conf.js ├── package.json ├── protractor.conf.js ├── src │ ├── app │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── dashboards │ │ │ ├── dashboard │ │ │ │ ├── dashboard.component.html │ │ │ │ ├── dashboard.component.scss │ │ │ │ ├── dashboard.component.ts │ │ │ │ └── gridster │ │ │ │ │ ├── GridListItem.ts │ │ │ │ │ ├── GridsterOptions.ts │ │ │ │ │ ├── IGridsterDraggableOptions.ts │ │ │ │ │ ├── IGridsterOptions.ts │ │ │ │ │ ├── gridList.ts │ │ │ │ │ ├── gridList │ │ │ │ │ ├── GridListItem.ts │ │ │ │ │ └── gridList.ts │ │ │ │ │ ├── gridster-item │ │ │ │ │ ├── gridster-item.component.scss │ │ │ │ │ └── gridster-item.component.ts │ │ │ │ │ ├── gridster-prototype │ │ │ │ │ ├── gridster-item-prototype.directive.ts │ │ │ │ │ └── gridster-prototype.service.ts │ │ │ │ │ ├── gridster.component.scss │ │ │ │ │ ├── gridster.component.ts │ │ │ │ │ ├── gridster.service.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── utils │ │ │ │ │ ├── DraggableEvent.ts │ │ │ │ │ ├── draggable.ts │ │ │ │ │ └── utils.ts │ │ │ ├── dashboards.component.html │ │ │ ├── dashboards.component.scss │ │ │ └── dashboards.component.ts │ │ ├── data.service.ts │ │ ├── elasticsearch │ │ │ ├── elasticsearch.service.ts │ │ │ └── index.ts │ │ ├── example │ │ │ ├── astronaut.component.ts │ │ │ ├── mission.service.ts │ │ │ └── missioncontrol.component.ts │ │ ├── object-classes │ │ │ ├── aggregationData.ts │ │ │ ├── searchSourceJSON.ts │ │ │ ├── visualizationObj.ts │ │ │ └── visualizationState.ts │ │ ├── shared │ │ │ ├── collapse.directive.ts │ │ │ ├── dynamicComponent.component.ts │ │ │ ├── hidden.directive.ts │ │ │ ├── validators.directive.ts │ │ │ └── visualization-tools.ts │ │ └── visualizations │ │ │ ├── bar-chart │ │ │ ├── bar-chart.component.html │ │ │ ├── bar-chart.component.scss │ │ │ ├── bar-chart.component.ts │ │ │ └── bar-chart.service.ts │ │ │ ├── buckets │ │ │ ├── bucket.component.html │ │ │ ├── bucket.component.scss │ │ │ ├── bucket.component.ts │ │ │ ├── buckets.component.html │ │ │ ├── buckets.component.scss │ │ │ └── buckets.component.ts │ │ │ ├── data-table │ │ │ ├── data-table.service.ts │ │ │ ├── dataTable.component.html │ │ │ ├── dataTable.component.scss │ │ │ ├── dataTable.component.ts │ │ │ ├── table.component.html │ │ │ ├── table.component.scss │ │ │ └── table.component.ts │ │ │ ├── metrics │ │ │ ├── aggregations │ │ │ │ ├── percentileRanksMetric.component.html │ │ │ │ ├── percentileRanksMetric.component.scss │ │ │ │ ├── percentileRanksMetric.component.ts │ │ │ │ ├── percentilesMetric.component.html │ │ │ │ ├── percentilesMetric.component.scss │ │ │ │ ├── percentilesMetric.component.ts │ │ │ │ ├── topHitMetric.component.html │ │ │ │ ├── topHitMetric.component.scss │ │ │ │ └── topHitMetric.component.ts │ │ │ ├── metric.component.html │ │ │ ├── metric.component.scss │ │ │ ├── metric.component.ts │ │ │ ├── metrics.component.html │ │ │ ├── metrics.component.scss │ │ │ ├── metrics.component.ts │ │ │ └── metrics.service.ts │ │ │ ├── pie-chart │ │ │ ├── pie-chart.service.ts │ │ │ ├── pieChart.component.html │ │ │ ├── pieChart.component.scss │ │ │ └── pieChart.component.ts │ │ │ ├── visualizations.component.html │ │ │ ├── visualizations.component.scss │ │ │ ├── visualizations.component.ts │ │ │ └── visualizations.service.ts │ ├── assets │ │ ├── .gitkeep │ │ ├── Chart.min.js │ │ ├── bodybuilder.min.js │ │ └── img │ │ │ ├── sakura.png │ │ │ ├── sakura.svg │ │ │ └── sakura2.png │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.css │ ├── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── typings.d.ts ├── tsconfig.json └── tslint.json ├── index.html ├── plantilla-memoria ├── .DS_Store ├── LICENSE ├── Makefile ├── README.md ├── img │ ├── .DS_Store │ ├── Scrum_Framework.png │ ├── angular-architecture.png │ ├── arquitectura.png │ ├── bar-chart-calculation.png │ ├── bar-chart-lengend-filter-example.png │ ├── center-example.png │ ├── chartjs-result-example.png │ ├── dashboard-example.png │ ├── dashboards-diagram.png │ ├── dashboards-initial-view.png │ ├── data-table-histogram-calculation.png │ ├── data-table-initial-view.png │ ├── data-table-multiaggregation-calculation.png │ ├── data-table-range-calculation.png │ ├── final-satge-sprint1.png │ ├── final-user-interface.png │ ├── flex-end-example.png │ ├── foro1.png │ ├── jquery-result-example.png │ ├── kibana-dashboard-example.png │ ├── kibana-nav-bar.png │ ├── kibana-visualization-example.png │ ├── logo_vect.png │ ├── metric-average-calculation.png │ ├── metric-count-calcuation.png │ ├── metric-max-calculation.png │ ├── metric-median-calculation.png │ ├── metric-min-calculation.png │ ├── metric-percentiles-calculation.png │ ├── metric-ranks-calculation.png │ ├── metric-std-calculation.png │ ├── metric-sum-calculation.png │ ├── metric-top-calculation.png │ ├── metric-unique-calculation.png │ ├── metric-visualization-initial-view.png │ ├── no-wrap-example.png │ ├── parent-child-via-service.png │ ├── pie-chart-calculation.png │ ├── pie-chart-example.png │ ├── saved-visualizations.png │ ├── sprint1_architecture.png │ ├── sprint2_architecture_1.png │ ├── sprint2_architecture_2.png │ ├── user_interface_mockup.png │ ├── visualization-options.png │ ├── visualizations-section.png │ └── wrap-example.png ├── memoria.bib ├── memoria.pdf ├── memoria.tex └── template.tex └── web ├── css └── grayscale.css ├── font-awesome ├── css │ ├── font-awesome.css │ └── font-awesome.min.css ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ └── fontawesome-webfont.woff2 ├── less │ ├── animated.less │ ├── bordered-pulled.less │ ├── core.less │ ├── fixed-width.less │ ├── font-awesome.less │ ├── icons.less │ ├── larger.less │ ├── list.less │ ├── mixins.less │ ├── path.less │ ├── rotated-flipped.less │ ├── screen-reader.less │ ├── spinning.less │ ├── stacked.less │ └── variables.less └── scss │ ├── _animated.scss │ ├── _bordered-pulled.scss │ ├── _core.scss │ ├── _fixed-width.scss │ ├── _icons.scss │ ├── _larger.scss │ ├── _list.scss │ ├── _mixins.scss │ ├── _path.scss │ ├── _rotated-flipped.scss │ ├── _screen-reader.scss │ ├── _spinning.scss │ ├── _stacked.scss │ ├── _variables.scss │ └── font-awesome.scss ├── fonts ├── glyphicons-halflings-regular.eot ├── glyphicons-halflings-regular.svg ├── glyphicons-halflings-regular.ttf ├── glyphicons-halflings-regular.woff └── glyphicons-halflings-regular.woff2 ├── img ├── bar-chart-lengend-filter-example.png ├── dashboard-example.png ├── data-table-multiaggregation-calculation.png ├── fondo_principal.jpg ├── ismael.jpeg ├── metric-ranks-calculation.png └── pie-chart-calculation.png └── js ├── grayscale.js └── jquery.easing.min.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/.DS_Store -------------------------------------------------------------------------------- /dashboard-interface/.angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "project": { 4 | "name": "dashboard-interface" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "dist", 10 | "assets": [ 11 | "assets", 12 | "favicon.ico" 13 | ], 14 | "index": "index.html", 15 | "main": "main.ts", 16 | "polyfills": "polyfills.ts", 17 | "test": "test.ts", 18 | "tsconfig": "tsconfig.app.json", 19 | "testTsconfig": "tsconfig.spec.json", 20 | "prefix": "app", 21 | "styles": [ 22 | "styles.css" 23 | ], 24 | "scripts": [ 25 | "../node_modules/chart.js/dist/Chart.min.js", 26 | "../node_modules/jquery/dist/jquery.min.js" 27 | ], 28 | "environmentSource": "environments/environment.ts", 29 | "environments": { 30 | "dev": "environments/environment.ts", 31 | "prod": "environments/environment.prod.ts" 32 | } 33 | } 34 | ], 35 | "e2e": { 36 | "protractor": { 37 | "config": "./protractor.conf.js" 38 | } 39 | }, 40 | "lint": [ 41 | { 42 | "project": "src/tsconfig.app.json" 43 | }, 44 | { 45 | "project": "src/tsconfig.spec.json" 46 | }, 47 | { 48 | "project": "e2e/tsconfig.e2e.json" 49 | } 50 | ], 51 | "test": { 52 | "karma": { 53 | "config": "./karma.conf.js" 54 | } 55 | }, 56 | "defaults": { 57 | "styleExt": "scss", 58 | "component": {} 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /dashboard-interface/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = tab 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /dashboard-interface/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | testem.log 34 | /typings 35 | 36 | # e2e 37 | /e2e/*.js 38 | /e2e/*.map 39 | 40 | # System Files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /dashboard-interface/README.md: -------------------------------------------------------------------------------- 1 | # DashboardInterface 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.0.0. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | Before running the tests make sure you are serving the app via `ng serve`. 25 | 26 | ## Further help 27 | 28 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 29 | -------------------------------------------------------------------------------- /dashboard-interface/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { DashboardInterfacePage } from './app.po'; 2 | 3 | describe('dashboard-interface App', () => { 4 | let page: DashboardInterfacePage; 5 | 6 | beforeEach(() => { 7 | page = new DashboardInterfacePage(); 8 | }); 9 | 10 | it('should display message saying app works', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('app works!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /dashboard-interface/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, element, by } from 'protractor'; 2 | 3 | export class DashboardInterfacePage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /dashboard-interface/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types":[ 8 | "jasmine", 9 | "node" 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /dashboard-interface/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/0.13/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular/cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular/cli/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | files: [ 19 | { pattern: './src/test.ts', watched: false } 20 | ], 21 | preprocessors: { 22 | './src/test.ts': ['@angular/cli'] 23 | }, 24 | mime: { 25 | 'text/x-typescript': ['ts','tsx'] 26 | }, 27 | coverageIstanbulReporter: { 28 | reports: [ 'html', 'lcovonly' ], 29 | fixWebpackSourcePaths: true 30 | }, 31 | angularCli: { 32 | environment: 'dev' 33 | }, 34 | reporters: config.angularCli && config.angularCli.codeCoverage 35 | ? ['progress', 'coverage-istanbul'] 36 | : ['progress', 'kjhtml'], 37 | port: 9876, 38 | colors: true, 39 | logLevel: config.LOG_INFO, 40 | autoWatch: true, 41 | browsers: ['Chrome'], 42 | singleRun: false 43 | }); 44 | }; 45 | -------------------------------------------------------------------------------- /dashboard-interface/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dashboard-interface", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/animations": "^4.1.3", 16 | "@angular/cdk": "github:angular/cdk-builds", 17 | "@angular/common": "^4.0.3", 18 | "@angular/compiler": "^4.0.3", 19 | "@angular/core": "^4.0.3", 20 | "@angular/flex-layout": "^2.0.0-beta.10-4905443", 21 | "@angular/forms": "^4.0.3", 22 | "@angular/http": "^4.0.0", 23 | "@angular/material": "github:angular/material2-builds", 24 | "@angular/platform-browser": "^4.0.3", 25 | "@angular/platform-browser-dynamic": "^4.0.0", 26 | "@angular/router": "^4.0.0", 27 | "@types/elasticsearch": "^5.0.13", 28 | "@types/lodash": "^4.14.69", 29 | "angular2-modal": "^3.0.3", 30 | "bodybuilder": "2.2.0", 31 | "chart.js": "^2.6.0", 32 | "core-js": "^2.4.1", 33 | "elasticsearch": "^13.0.0", 34 | "jquery": "^3.2.1", 35 | "lodash": "^4.17.4", 36 | "mobx": "^3.2.2", 37 | "mobx-angular": "^1.5.0", 38 | "ng2-charts": "^1.6.0", 39 | "ng2-mobx": "^1.2.9", 40 | "ngx-modialog": "^3.0.4", 41 | "rxjs": "^5.1.0", 42 | "text-table": "^0.2.0", 43 | "zone.js": "^0.8.5" 44 | }, 45 | "devDependencies": { 46 | "@angular/cli": "1.2.6", 47 | "@angular/compiler-cli": "^4.0.0", 48 | "@types/jasmine": "2.5.53", 49 | "@types/jquery": "^3.2.10", 50 | "@types/node": "~8.0.17", 51 | "codelyzer": "~3.1.2", 52 | "jasmine-core": "~2.6.4", 53 | "jasmine-spec-reporter": "~4.1.1", 54 | "karma": "~1.7.0", 55 | "karma-chrome-launcher": "~2.2.0", 56 | "karma-cli": "~1.0.1", 57 | "karma-coverage-istanbul-reporter": "^1.3.0", 58 | "karma-jasmine": "~1.1.0", 59 | "karma-jasmine-html-reporter": "^0.2.2", 60 | "node-sass": "^4.5.3", 61 | "protractor": "~5.1.0", 62 | "raw-loader": "^0.5.1", 63 | "sass-loader": "^6.0.6", 64 | "ts-node": "~3.3.0", 65 | "tslint": "~5.5.0", 66 | "typescript": "~2.4.2" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /dashboard-interface/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | beforeLaunch: function() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | }, 27 | onPrepare() { 28 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { RouterModule, Routes } from '@angular/router'; 3 | 4 | import { VisualizationsComponent } from './visualizations/visualizations.component'; 5 | import { DashboardsComponent } from './dashboards/dashboards.component'; 6 | 7 | const routes: Routes = [ 8 | { path: '', redirectTo: '/visualizations', pathMatch: 'full' }, 9 | { path: 'visualizations', component: VisualizationsComponent }, 10 | { path: 'dashboards', component: DashboardsComponent } 11 | ]; 12 | 13 | @NgModule({ 14 | imports: [ RouterModule.forRoot(routes) ], 15 | exports: [ RouterModule ] 16 | }) 17 | 18 | export class AppRoutingModule {} 19 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 14 | 15 |
16 | 17 |
18 |
19 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Open+Sans:400,600'); 2 | @import url('https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css'); 3 | 4 | * { 5 | padding: 0; 6 | margin: 0; 7 | /* pour maîtriser width et flex-basis */ 8 | list-style: none; 9 | } 10 | 11 | div.header{ 12 | display: flex; 13 | position: fixed; 14 | width: 100%; 15 | height: 3rem; 16 | background-color: #57c17b; 17 | box-shadow: 0px 0px 15px 0px; 18 | z-index: 20; 19 | 20 | div { 21 | //height: 9vh; 22 | display: flex; 23 | /* crée un contexte flex pour centrer le contenu de la balise a */ 24 | justify-content: center; 25 | /*alignement horizontal*/ 26 | align-items: center; 27 | /*alignement vertical*/ 28 | flex: 1; 29 | /*on autorise la balise a à prendre toute la place disponible*/ 30 | } 31 | 32 | div.img-container { 33 | flex: 1; 34 | 35 | img{ 36 | max-width: 70%; 37 | } 38 | } 39 | 40 | div.title-container { 41 | box-shadow: 20px -20px 10px 20px black; 42 | flex: 21; 43 | font: 600 16px 'Open Sans', sans-serif; 44 | line-height: 50px; 45 | text-align: center; 46 | text-transform: uppercase; 47 | letter-spacing: 7px; 48 | color: #eee; 49 | //border-bottom: 1px solid #222; 50 | } 51 | } 52 | 53 | .active { 54 | background-color: #607D8B; 55 | } 56 | 57 | div.main { 58 | display: flex; 59 | flex: 1; 60 | //padding: 3rem 0 0 0; 61 | flex-flow: wrap; 62 | height: 100%; 63 | 64 | div.nav-bar { 65 | display: flex; 66 | flex-flow: column wrap; 67 | flex: 1; 68 | position: fixed; 69 | z-index: 10; 70 | min-width: 4.54%; 71 | height: 100%; 72 | //height: calc(100% - 25vh); 73 | /*hauteur des li : 12vh + hauteur du footer 10vh = 25vh*/ 74 | background: #749cf7; 75 | 76 | ul { 77 | flex: 1;/* occupe toute la place restante */ 78 | display: flex; 79 | flex-flow: column wrap; 80 | 81 | li.nav-item { 82 | flex: 1; 83 | display: flex; 84 | justify-content: center; 85 | align-items: center; 86 | /*on centre horiz et vertic grace flexbox*/ 87 | font-size: 1.25rem; 88 | outline: none; 89 | } 90 | 91 | li.nav-item:hover { 92 | background-color: rgba(0, 0, 0, 0.32); 93 | cursor: pointer; 94 | } 95 | 96 | li.nav-void { 97 | flex: 8; 98 | } 99 | } 100 | } 101 | 102 | div.content { 103 | flex: 1; 104 | margin-left: 4.45%; 105 | box-shadow: -10px 0px 10px 1px rgba(0, 0, 0, 0.14); 106 | z-index: 10; 107 | overflow: hidden; 108 | } 109 | } 110 | 111 | @media screen and (max-width: 940px) { 112 | div.nav-bar { 113 | display: none; 114 | } 115 | div.content { 116 | margin: 0 117 | } 118 | } 119 | 120 | /* FOOTER */ 121 | footer { 122 | position: fixed; 123 | bottom: 0; 124 | width: 100%; 125 | z-index: 5; 126 | height: 10vh; 127 | display: flex; 128 | flex-flow: column wrap; 129 | 130 | p { 131 | flex: 1; 132 | width: 20%; 133 | display: flex; 134 | justify-content: center; 135 | align-items: center; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | declarations: [ 9 | AppComponent 10 | ], 11 | }).compileComponents(); 12 | })); 13 | 14 | it('should create the app', async(() => { 15 | const fixture = TestBed.createComponent(AppComponent); 16 | const app = fixture.debugElement.componentInstance; 17 | expect(app).toBeTruthy(); 18 | })); 19 | 20 | it(`should have as title 'app works!'`, async(() => { 21 | const fixture = TestBed.createComponent(AppComponent); 22 | const app = fixture.debugElement.componentInstance; 23 | expect(app.title).toEqual('app works!'); 24 | })); 25 | 26 | it('should render title in a h1 tag', async(() => { 27 | const fixture = TestBed.createComponent(AppComponent); 28 | fixture.detectChanges(); 29 | const compiled = fixture.debugElement.nativeElement; 30 | expect(compiled.querySelector('h1').textContent).toContain('app works!'); 31 | })); 32 | }); 33 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewChild } from '@angular/core'; 2 | 3 | import { VisualizationsComponent } from './visualizations/visualizations.component'; 4 | 5 | import { DataService } from './data.service'; 6 | 7 | 8 | @Component({ 9 | selector: 'app-root', 10 | templateUrl: './app.component.html', 11 | styleUrls: ['./app.component.scss'] 12 | }) 13 | 14 | export class AppComponent { 15 | title: string = ''; 16 | 17 | constructor( 18 | private dataService: DataService 19 | ) { } 20 | 21 | ngOnInit(): void { 22 | this.getTitle(); 23 | } 24 | 25 | getTitle(): void { 26 | this.title = this.dataService.getTitle(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; 4 | import { HttpModule } from '@angular/http'; 5 | import { FlexLayoutModule } from "@angular/flex-layout"; 6 | import { AppRoutingModule } from './app-routing.module'; 7 | //import { GridsterModule } from './dashboards/dashboard/gridster2/gridster.module'; 8 | 9 | import { AppComponent } from './app.component'; 10 | import { VisualizationsComponent } from './visualizations/visualizations.component'; 11 | import { MetricsComponent } from './visualizations/metrics/metrics.component'; 12 | import { MetricComponent } from './visualizations/metrics/metric.component'; 13 | import { PercentilesMetricComponent } from './visualizations/metrics/aggregations/percentilesMetric.component'; 14 | import { PercentileRanksMetricComponent } from './visualizations/metrics/aggregations/percentileRanksMetric.component'; 15 | import { TopHitMetricComponent } from './visualizations/metrics/aggregations/topHitMetric.component'; 16 | import { DataTableComponent } from './visualizations/data-table/dataTable.component'; 17 | import { TableComponent } from './visualizations/data-table/table.component'; 18 | import { BucketsComponent } from './visualizations/buckets/buckets.component'; 19 | import { BucketComponent } from './visualizations/buckets/bucket.component'; 20 | import { PieChartComponent } from './visualizations/pie-chart/pieChart.component'; 21 | import { BarChartComponent } from './visualizations/bar-chart/bar-chart.component'; 22 | import { DynamicComponent } from './shared/dynamicComponent.component'; 23 | import { DashboardsComponent } from './dashboards/dashboards.component'; 24 | import { DashboardComponent } from './dashboards/dashboard/dashboard.component'; 25 | 26 | // Gridster 27 | import { GridsterComponent } from './dashboards/dashboard/gridster/gridster.component'; 28 | import { GridsterItemComponent } from './dashboards/dashboard/gridster/gridster-item/gridster-item.component'; 29 | import { GridsterItemPrototypeDirective } from './dashboards/dashboard/gridster/gridster-prototype/gridster-item-prototype.directive'; 30 | import { GridsterPrototypeService } from './dashboards/dashboard/gridster/gridster-prototype/gridster-prototype.service'; 31 | 32 | import { DataService } from './data.service'; 33 | import { MetricsService } from './visualizations/metrics/metrics.service'; 34 | import { VisualizationsService } from './visualizations/visualizations.service'; 35 | 36 | import { HiddenDirective } from './shared/hidden.directive'; 37 | 38 | import { Elasticsearch } from './elasticsearch'; 39 | import { ChartsModule } from 'ng2-charts/ng2-charts'; 40 | 41 | import { Collapse } from './shared/collapse.directive'; 42 | 43 | @NgModule({ 44 | declarations: [ 45 | AppComponent, 46 | VisualizationsComponent, 47 | MetricsComponent, 48 | MetricComponent, 49 | DataTableComponent, 50 | PercentilesMetricComponent, 51 | PercentileRanksMetricComponent, 52 | TopHitMetricComponent, 53 | TableComponent, 54 | PieChartComponent, 55 | BarChartComponent, 56 | DynamicComponent, 57 | HiddenDirective, 58 | BucketsComponent, 59 | BucketComponent, 60 | Collapse, 61 | DashboardsComponent, 62 | DashboardComponent, 63 | GridsterComponent, 64 | GridsterItemComponent, 65 | GridsterItemPrototypeDirective 66 | ], 67 | imports: [ 68 | BrowserModule, 69 | FormsModule, 70 | ReactiveFormsModule, 71 | HttpModule, 72 | ChartsModule, 73 | FlexLayoutModule, 74 | AppRoutingModule, 75 | //GridsterModule 76 | ], 77 | providers: [ 78 | DataService, 79 | Elasticsearch, 80 | MetricsService, 81 | VisualizationsService, 82 | GridsterPrototypeService 83 | ], 84 | bootstrap: [ 85 | AppComponent 86 | ], 87 | entryComponents: [ 88 | MetricsComponent, 89 | DataTableComponent, 90 | MetricComponent, 91 | BucketComponent, 92 | PieChartComponent, 93 | BarChartComponent, 94 | GridsterItemComponent 95 | ] 96 | }) 97 | export class AppModule { } 98 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/dashboards/dashboard/dashboard.component.scss: -------------------------------------------------------------------------------- 1 | div.container{ 2 | height: 100%; 3 | 4 | div.section-title { 5 | font-family: "Trebuchet MS", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Tahoma, sans-serif; 6 | font-size: 1.2rem; 7 | padding: 1rem; 8 | background-color: #ddd; 9 | border-bottom: 1px solid #fbfbfb; 10 | cursor: pointer; 11 | } 12 | 13 | div.section-title:hover { 14 | background-color: #b7b7b7; 15 | } 16 | 17 | div.section-content { 18 | background-color: #fbfbfb; 19 | 20 | .option-container { 21 | display: flex; 22 | //justify-content: space-between; 23 | //align-items: center; 24 | 25 | .title-container { 26 | flex: 20; 27 | padding: 0.5rem; 28 | padding-left: 1rem; 29 | } 30 | 31 | .delete-icon-container { 32 | padding: 0.5rem; 33 | cursor: pointer; 34 | color: red; 35 | border-radius: 0.25rem; 36 | flex: 1; 37 | display: flex; 38 | justify-content: center; 39 | } 40 | 41 | .delete-icon-container:hover { 42 | background-color: #ffd3d3; 43 | } 44 | } 45 | } 46 | 47 | div.saved-vis { 48 | ul { 49 | margin: 0px; 50 | padding: 0px; 51 | list-style-type: none; 52 | 53 | li { 54 | //padding: 0.25rem; 55 | //padding-right: 1rem; 56 | //padding-left: 1rem; 57 | border-bottom: 1px solid #efefef; 58 | color: #676767; 59 | } 60 | 61 | li:hover { 62 | background-color: #ecf5ff; 63 | cursor: pointer; 64 | } 65 | } 66 | } 67 | 68 | div#configFunctions { 69 | display: flex; 70 | align-items: center; 71 | background-color: #dddddd; 72 | border-bottom: 1px solid #fbfbfb; 73 | 74 | span.function-icon { 75 | cursor: pointer; 76 | background-color: #dddddd; 77 | color: #3c75ca; 78 | //border-radius: 0.25rem; 79 | padding: 0.75rem; 80 | } 81 | 82 | span.function-icon:hover { 83 | background-color: #b3b3b3; 84 | } 85 | 86 | /*div#calculateButton { 87 | flex: 1; 88 | //padding: 0.25rem; 89 | }*/ 90 | 91 | input { 92 | height: 1.25rem; 93 | width: 100%; 94 | //border-radius: 0.25rem; 95 | font-size: 1rem; 96 | border: 0px; 97 | padding-left: 0.5rem; 98 | padding-right: 0.5rem; 99 | margin-left: 0.5rem; 100 | margin-right: 0.5rem; 101 | border-radius: 0.25rem; 102 | } 103 | 104 | /*div#saveForm { 105 | flex: 5; 106 | //padding: 0.25rem; 107 | 108 | input { 109 | height: 1.25rem; 110 | //border-radius: 0.25rem; 111 | font-size: 1rem; 112 | border: 0px; 113 | } 114 | }*/ 115 | } 116 | 117 | .main-content { 118 | //position: absolute; 119 | top: 200px; 120 | left: 0; 121 | width: 100%; 122 | } 123 | 124 | h1 { 125 | font-size: 22px; 126 | padding: 0 20px; 127 | height: 50px; 128 | margin: 0; 129 | } 130 | 131 | .toolbar { 132 | position: relative; 133 | padding: 0 20px; 134 | } 135 | 136 | .widgetbar { 137 | width: 100%; 138 | position: relative; 139 | height: 100px; 140 | padding: 10px 0; 141 | 142 | .gridster-item-prototype { 143 | display: block; 144 | background-color: #afafaf; 145 | position: relative; 146 | float: left; 147 | z-index: 99; 148 | text-align: center; 149 | font-weight: bold; 150 | margin: 5px; 151 | width: 80px; 152 | height: 60px; 153 | } 154 | 155 | .gridster-item-prototype.is-over .gridster-item-inner { 156 | visibility: visible; 157 | } 158 | 159 | .gridster-item-inner { 160 | position: absolute; 161 | top:0; 162 | left: 0; 163 | overflow: auto; 164 | width: 80px; 165 | height: 60px; 166 | visibility: hidden; 167 | background-color: #eeeded; 168 | 169 | -webkit-transition: width 0.2s ease-in-out, height 0.2s ease-in-out; 170 | -moz-transition: width 0.2s ease-in-out, height 0.2s ease-in-out; 171 | -o-transition: width 0.2s ease-in-out, height 0.2s ease-in-out; 172 | transition: width 0.2s ease-in-out, height 0.2s ease-in-out; 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/dashboards/dashboard/gridster/GridsterOptions.ts: -------------------------------------------------------------------------------- 1 | import {IGridsterOptions} from './IGridsterOptions'; 2 | import { Observable } from 'rxjs/Observable'; 3 | 4 | export class GridsterOptions { 5 | direction: string; 6 | lanes: number; 7 | widthHeightRatio: number; 8 | heightToFontSizeRatio: number; 9 | responsiveView: boolean; 10 | dragAndDrop: boolean; 11 | resizable: boolean; 12 | shrink: boolean; 13 | minWidth: number; 14 | useCSSTransforms: boolean; 15 | 16 | defaults: IGridsterOptions = { 17 | lanes: 5, 18 | direction: 'horizontal', 19 | widthHeightRatio: 1, 20 | shrink: false, 21 | responsiveView: true, 22 | dragAndDrop: true, 23 | resizable: false, 24 | useCSSTransforms: false 25 | }; 26 | 27 | change: Observable; 28 | 29 | responsiveOptions: Array = []; 30 | basicOptions: IGridsterOptions; 31 | 32 | breakpointsMap = { 33 | sm: 576, // Small devices 34 | md: 768, // Medium devices 35 | lg: 992, // Large devices 36 | xl: 1200 // Extra large 37 | }; 38 | 39 | constructor(config: IGridsterOptions) { 40 | this.basicOptions = config; 41 | 42 | this.responsiveOptions = this.extendResponsiveOptions(config.responsiveOptions || []); 43 | 44 | this.change = Observable.merge( 45 | Observable.of(this.getOptionsByWidth(document.documentElement.clientWidth)), 46 | Observable.fromEvent(window, 'resize') 47 | .debounceTime(config.responsiveDebounce || 0) 48 | .map((event: Event) => this.getOptionsByWidth(document.documentElement.clientWidth)) 49 | ) 50 | .distinctUntilChanged(null, (options: any) => options.minWidth); 51 | } 52 | 53 | getOptionsByWidth(width: number): IGridsterOptions { 54 | let i = 0; 55 | let options: IGridsterOptions = Object.assign({}, this.defaults, this.basicOptions); 56 | 57 | while (this.responsiveOptions[i]) { 58 | if (this.responsiveOptions[i].minWidth <= width) { 59 | options = this.responsiveOptions[i]; 60 | } 61 | i++; 62 | } 63 | 64 | return options; 65 | } 66 | 67 | private extendResponsiveOptions(responsiveOptions: Array): Array { 68 | return responsiveOptions 69 | // responsive options are valid only with "breakpoint" property 70 | .filter(options => options.breakpoint) 71 | // set default minWidth if not given 72 | .map((options) => { 73 | return Object.assign({ 74 | minWidth: this.breakpointsMap[options.breakpoint] || 0 75 | }, options); 76 | }) 77 | .sort((curr, next) => curr.minWidth - next.minWidth) 78 | .map((options) => Object.assign({}, this.defaults, this.basicOptions, options)); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/dashboards/dashboard/gridster/IGridsterDraggableOptions.ts: -------------------------------------------------------------------------------- 1 | export interface IGridsterDraggableOptions { 2 | handlerClass?: string; 3 | zIndex?: number; 4 | scroll?: boolean; 5 | containment?: string; 6 | } 7 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/dashboards/dashboard/gridster/IGridsterOptions.ts: -------------------------------------------------------------------------------- 1 | export interface IGridsterOptions { 2 | direction?: string; 3 | lanes?: number; 4 | widthHeightRatio?: number; 5 | heightToFontSizeRatio?: number; 6 | dragAndDrop?: boolean; 7 | itemSelector?: string; 8 | resizable?: boolean; 9 | shrink?: boolean; 10 | responsiveView?: boolean; 11 | responsiveDebounce?: number; 12 | breakpoint?: string; 13 | minWidth?: number; 14 | useCSSTransforms?: boolean; 15 | responsiveOptions?: Array; 16 | } 17 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/dashboards/dashboard/gridster/gridster-item/gridster-item.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | position: absolute; 4 | top: 0; 5 | left: 0; 6 | z-index: 1; 7 | transition: all 200ms ease; 8 | transition-property: left, top; 9 | } 10 | 11 | :host-context(.css-transform) { 12 | transition-property: transform; 13 | } 14 | 15 | :host.is-dragging, :host.is-resizing { 16 | -webkit-transition: none; 17 | transition: none; 18 | z-index: 9999; 19 | } 20 | 21 | :host.no-transition { 22 | -webkit-transition: none; 23 | transition: none; 24 | } 25 | .gridster-item-resizable-handler { 26 | position: absolute; 27 | z-index: 2; 28 | display: none; 29 | } 30 | 31 | .gridster-item-resizable-handler.handle-n { 32 | cursor: n-resize; 33 | height: 10px; 34 | right: 0; 35 | top: 0; 36 | left: 0; 37 | } 38 | 39 | .gridster-item-resizable-handler.handle-e { 40 | cursor: e-resize; 41 | width: 10px; 42 | bottom: 0; 43 | right: 0; 44 | top: 0; 45 | } 46 | 47 | .gridster-item-resizable-handler.handle-s { 48 | cursor: s-resize; 49 | height: 10px; 50 | right: 0; 51 | bottom: 0; 52 | left: 0; 53 | } 54 | 55 | .gridster-item-resizable-handler.handle-w { 56 | cursor: w-resize; 57 | width: 10px; 58 | left: 0; 59 | top: 0; 60 | bottom: 0; 61 | } 62 | 63 | .gridster-item-resizable-handler.handle-ne { 64 | cursor: ne-resize; 65 | width: 10px; 66 | height: 10px; 67 | right: 0; 68 | top: 0; 69 | } 70 | 71 | .gridster-item-resizable-handler.handle-nw { 72 | cursor: nw-resize; 73 | width: 10px; 74 | height: 10px; 75 | left: 0; 76 | top: 0; 77 | } 78 | 79 | .gridster-item-resizable-handler.handle-se { 80 | cursor: se-resize; 81 | width: 0; 82 | height: 0; 83 | right: 0; 84 | bottom: 0; 85 | border-style: solid; 86 | border-width: 0 0 10px 10px; 87 | border-color: transparent; 88 | } 89 | 90 | .gridster-item-resizable-handler.handle-sw { 91 | cursor: sw-resize; 92 | width: 10px; 93 | height: 10px; 94 | left: 0; 95 | bottom: 0; 96 | } 97 | 98 | :host(:hover) .gridster-item-resizable-handler.handle-se { 99 | border-color: transparent transparent #ccc 100 | } 101 | 102 | .gridster-item-inner { 103 | position: absolute; 104 | background: #fff; 105 | border: 1px solid transparent; 106 | top: 5px; 107 | bottom: 5px; 108 | left: 5px; 109 | right: 5px; 110 | -webkit-transition: background 3s; 111 | transition: background 3s; 112 | border-radius: 3px; 113 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); 114 | } 115 | 116 | :host.is-dragging .gridster-item-inner { 117 | box-shadow: 0 2px 6px rgba(0, 0, 0, 0.24), 0 2px 4px rgba(0, 0, 0, 0.48); 118 | top: -3px; 119 | bottom: 13px; 120 | } 121 | 122 | :host.is-dragging /deep/ .panel-heading .panel-title { 123 | cursor: -webkit-grabbing; 124 | } 125 | 126 | :host /deep/ .panel-heading { 127 | border-bottom: 1px solid #F0F0F0; 128 | //display: inline-block; 129 | position: relative; 130 | width: 100%; 131 | top: 0; 132 | display: flex; 133 | justify-content: space-between; 134 | align-items: center; 135 | 136 | .panel-title { 137 | padding: 10px; 138 | margin: 0; 139 | cursor: -webkit-grab; 140 | } 141 | } 142 | 143 | :host /deep/ .panel-body { 144 | overflow: auto; 145 | //padding: 0 10px 10px; 146 | position: absolute; 147 | top: 37px; 148 | bottom: 0; 149 | left: 0; 150 | right: 0; 151 | } 152 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/dashboards/dashboard/gridster/gridster.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | position: relative; 3 | display: block; 4 | left: 0; 5 | width: 100%; 6 | } 7 | 8 | :host.gridster--dragging { 9 | -moz-user-select: none; 10 | -khtml-user-select: none; 11 | -webkit-user-select: none; 12 | -ms-user-select: none; 13 | user-select: none; 14 | } 15 | 16 | .gridster-container { 17 | position: relative; 18 | width: 100%; 19 | list-style: none; 20 | -webkit-transition: width 0.2s, height 0.2s; 21 | transition: width 0.2s, height 0.2s; 22 | } 23 | 24 | .position-highlight { 25 | display: block; 26 | position: absolute; 27 | z-index: 1; 28 | } 29 | 30 | .position-highlight .inner { 31 | position: absolute; 32 | top: 5px; 33 | bottom: 5px; 34 | left: 5px; 35 | right: 5px; 36 | 37 | background-color: #768294; 38 | border: 1px solid transparent; 39 | -webkit-transition: background 3s; 40 | transition: background 3s; 41 | border-radius: 3px; 42 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); 43 | } 44 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/dashboards/dashboard/gridster/index.ts: -------------------------------------------------------------------------------- 1 | //export {GridsterModule} from './gridster.module'; 2 | export {GridsterComponent} from './gridster.component'; 3 | export {GridsterItemComponent} from './gridster-item/gridster-item.component'; 4 | export {GridsterItemPrototypeDirective} from './gridster-prototype/gridster-item-prototype.directive'; 5 | export {IGridsterOptions} from './IGridsterOptions'; 6 | export {IGridsterDraggableOptions} from './IGridsterDraggableOptions'; 7 | export {GridsterOptions} from './GridsterOptions'; 8 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/dashboards/dashboard/gridster/utils/DraggableEvent.ts: -------------------------------------------------------------------------------- 1 | export class DraggableEvent { 2 | clientX: number; 3 | 4 | clientY: number; 5 | 6 | pageX: number; 7 | 8 | pageY: number; 9 | 10 | target: EventTarget; 11 | 12 | 13 | private touchEvent: TouchEvent; 14 | 15 | private mouseEvent: MouseEvent; 16 | 17 | constructor(event: any) { 18 | if (event.touches) { 19 | this.touchEvent = (event); 20 | this.setDataFromTouchEvent(this.touchEvent); 21 | } else { 22 | this.mouseEvent = (event); 23 | this.setDataFromMouseEvent(this.mouseEvent); 24 | } 25 | } 26 | 27 | isTouchEvent(): boolean { 28 | return !!this.touchEvent; 29 | } 30 | 31 | pauseEvent() { 32 | const event: Event = this.touchEvent || this.mouseEvent; 33 | 34 | if (event.stopPropagation) { 35 | event.stopPropagation(); 36 | } 37 | if (event.preventDefault) { 38 | event.preventDefault(); 39 | } 40 | event.cancelBubble = true; 41 | event.returnValue = false; 42 | return false; 43 | } 44 | 45 | getRelativeCoordinates(container: HTMLElement): {x: number, y: number} { 46 | const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop; 47 | const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft; 48 | 49 | const rect = container.getBoundingClientRect(); 50 | 51 | return { 52 | x: this.pageX - rect.left - scrollLeft, 53 | y: this.pageY - rect.top - scrollTop, 54 | }; 55 | } 56 | 57 | private setDataFromMouseEvent(event: MouseEvent): void { 58 | this.target = event.target; 59 | this.clientX = event.clientX; 60 | this.clientY = event.clientY; 61 | this.pageX = event.pageX; 62 | this.pageY = event.pageY; 63 | } 64 | 65 | private setDataFromTouchEvent(event: TouchEvent): void { 66 | this.target = event.target; 67 | this.clientX = event.touches[0].clientX; 68 | this.clientY = event.touches[0].clientY; 69 | this.pageX = event.touches[0].pageX; 70 | this.pageY = event.touches[0].pageY; 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/dashboards/dashboard/gridster/utils/draggable.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs/Observable'; 2 | import 'rxjs/add/observable/fromEvent'; 3 | import 'rxjs/add/operator/takeUntil'; 4 | import 'rxjs/add/operator/switchMap'; 5 | import 'rxjs/add/operator/filter'; 6 | import 'rxjs/add/operator/merge'; 7 | import 'rxjs/add/operator/map'; 8 | 9 | import {DraggableEvent} from './DraggableEvent'; 10 | import {utils} from './utils'; 11 | 12 | export class Draggable { 13 | element: Element; 14 | 15 | dragStart: Observable; 16 | dragMove: Observable; 17 | dragStop: Observable; 18 | 19 | private mousemove: Observable<{} | Event> = Observable.merge( 20 | Observable.fromEvent(document, 'mousemove'), 21 | Observable.fromEvent(document, 'touchmove', {passive: true}) 22 | ).share(); 23 | 24 | private mouseup: Observable<{} | Event> = Observable.merge( 25 | Observable.fromEvent(document, 'mouseup'), 26 | Observable.fromEvent(document, 'touchend'), 27 | Observable.fromEvent(document, 'touchcancel') 28 | ).share(); 29 | 30 | private mousedown: Observable<{} | Event>; 31 | 32 | private config = { 33 | handlerClass: null 34 | }; 35 | 36 | constructor(element: Element, config: {handlerClass?: string} = {}) { 37 | this.element = element; 38 | this.mousedown = Observable.merge( 39 | Observable.fromEvent(element, 'mousedown'), 40 | Observable.fromEvent(element, 'touchstart') 41 | ).share(); 42 | 43 | this.config.handlerClass = config.handlerClass; 44 | 45 | this.dragStart = this.createDragStartObservable().share(); 46 | this.dragMove = this.createDragMoveObservable(this.dragStart); 47 | this.dragStop = this.createDragStopObservable(this.dragStart); 48 | 49 | this.fixProblemWithDnDForIE(element); 50 | } 51 | 52 | private createDragStartObservable(): Observable { 53 | return this.mousedown 54 | .map(md => new DraggableEvent(md)) 55 | .filter((event: DraggableEvent) => this.isDragingByHandler(event)) 56 | .do(e => { 57 | e.pauseEvent(); 58 | (document.activeElement).blur(); 59 | // prevents rendering performance issues while dragging item with selection inside 60 | utils.clearSelection(); 61 | }) 62 | .switchMap((startEvent: DraggableEvent) => { 63 | 64 | return this.mousemove 65 | .map(mm => new DraggableEvent(mm)) 66 | .filter((moveEvent: DraggableEvent) => this.inRange(startEvent, moveEvent, 5)) 67 | .map(() => startEvent) 68 | .takeUntil(this.mouseup) 69 | .take(1); 70 | }); 71 | } 72 | 73 | private createDragMoveObservable(dragStart: Observable): Observable { 74 | return dragStart 75 | .switchMap(() => { 76 | return this.mousemove 77 | .skip(1) 78 | .map(mm => new DraggableEvent(mm)) 79 | .takeUntil(this.mouseup); 80 | }) 81 | .filter(val => !!val); 82 | } 83 | 84 | private createDragStopObservable(dragStart: Observable): Observable { 85 | return dragStart 86 | .switchMap(() => { 87 | return this.mouseup.take(1); 88 | }); 89 | } 90 | 91 | private isDragingByHandler(event: DraggableEvent): boolean { 92 | if (!this.isValidDragHandler(event.target)) { 93 | return false; 94 | } 95 | 96 | return !this.config.handlerClass || 97 | (this.config.handlerClass && this.hasElementWithClass(this.config.handlerClass, event.target)); 98 | } 99 | 100 | private isValidDragHandler(targetEl: any): boolean { 101 | return ['input', 'textarea'].indexOf(targetEl.tagName.toLowerCase()) === -1; 102 | } 103 | 104 | private inRange(startEvent: DraggableEvent, moveEvent: DraggableEvent, range: number): boolean { 105 | return Math.abs(moveEvent.clientX - startEvent.clientX) > range || 106 | Math.abs(moveEvent.clientY - startEvent.clientY) > range; 107 | } 108 | 109 | private hasElementWithClass(className: string, target: any): boolean { 110 | while (target !== this.element) { 111 | if (target.classList.contains(className)) { 112 | return true; 113 | } 114 | target = target.parentElement; 115 | } 116 | return false; 117 | } 118 | 119 | private pauseEvent(e: Event): void { 120 | if (e.stopPropagation) { 121 | e.stopPropagation(); 122 | } 123 | if (e.preventDefault) { 124 | e.preventDefault(); 125 | } 126 | e.cancelBubble = true; 127 | e.returnValue = false; 128 | } 129 | 130 | private fixProblemWithDnDForIE(element: Element) { 131 | (element).style['touch-action'] = 'none'; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/dashboards/dashboard/gridster/utils/utils.ts: -------------------------------------------------------------------------------- 1 | 2 | export const utils = { 3 | setCssElementPosition: ($element: HTMLElement, position: {x: number, y: number}) => { 4 | $element.style.left = position.x + 'px'; 5 | $element.style.top = position.y + 'px'; 6 | }, 7 | resetCSSElementPosition: ($element: HTMLElement) => { 8 | $element.style.left = ''; 9 | $element.style.top = ''; 10 | }, 11 | setTransform: ($element: HTMLElement, position: {x: number, y: number}) => { 12 | const left = position.x; 13 | const top = position.y; 14 | 15 | // Replace unitless items with px 16 | const translate = `translate(${left}px,${top}px)`; 17 | 18 | $element.style['transform'] = translate; 19 | $element.style['WebkitTransform'] = translate; 20 | $element.style['MozTransform'] = translate; 21 | $element.style['msTransform'] = translate; 22 | $element.style['OTransform'] = translate; 23 | }, 24 | resetTransform: ($element: HTMLElement) => { 25 | $element.style['transform'] = ''; 26 | $element.style['WebkitTransform'] = ''; 27 | $element.style['MozTransform'] = ''; 28 | $element.style['msTransform'] = ''; 29 | $element.style['OTransform'] = ''; 30 | }, 31 | clearSelection: function clearSelection() { 32 | if (document['selection']) { 33 | document['selection'].empty(); 34 | } else if (window.getSelection) { 35 | window.getSelection().removeAllRanges(); 36 | } 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/dashboards/dashboards.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | Saved Dashboards 5 |
6 |
7 |
    8 |
  • 9 |
    10 |
    11 | {{dashboard._source.title}} 12 |
    13 |
    14 | 15 |
    16 |
    17 |
  • 18 |
19 |
20 |
21 | 22 |
23 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/dashboards/dashboards.component.scss: -------------------------------------------------------------------------------- 1 | div.container { 2 | height: 100%; 3 | overflow-y: scroll; 4 | background-color: #eeeded; 5 | 6 | div.section-title { 7 | font-family: "Trebuchet MS", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Tahoma, sans-serif; 8 | font-size: 1.2rem; 9 | padding: 1rem; 10 | background-color: #ddd; 11 | border-bottom: 1px solid #fbfbfb; 12 | cursor: pointer; 13 | } 14 | 15 | div.section-title:hover { 16 | background-color: #b7b7b7; 17 | } 18 | 19 | div.section-content { 20 | background-color: #fbfbfb; 21 | 22 | .option-container { 23 | display: flex; 24 | //justify-content: space-between; 25 | //align-items: center; 26 | 27 | .title-container { 28 | flex: 20; 29 | padding: 0.5rem; 30 | padding-left: 1rem; 31 | } 32 | 33 | .delete-icon-container { 34 | padding: 0.5rem; 35 | cursor: pointer; 36 | color: red; 37 | border-radius: 0.25rem; 38 | flex: 1; 39 | display: flex; 40 | justify-content: center; 41 | } 42 | 43 | .delete-icon-container:hover { 44 | background-color: #ffd3d3; 45 | } 46 | } 47 | } 48 | 49 | div.saved-dash { 50 | ul { 51 | margin: 0px; 52 | padding: 0px; 53 | list-style-type: none; 54 | 55 | li { 56 | //padding: 0.25rem; 57 | //padding-right: 1rem; 58 | //padding-left: 1rem; 59 | border-bottom: 1px solid #efefef; 60 | color: #676767; 61 | } 62 | 63 | li:hover { 64 | background-color: #ecf5ff; 65 | cursor: pointer; 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/dashboards/dashboards.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, ViewChild } from '@angular/core'; 2 | 3 | import { DashboardComponent } from './dashboard/dashboard.component'; 4 | 5 | import { Elasticsearch } from '../elasticsearch'; 6 | 7 | @Component({ 8 | selector: 'dashboards', 9 | templateUrl: './dashboards.component.html', 10 | styleUrls: ['./dashboards.component.scss'] 11 | }) 12 | 13 | export class DashboardsComponent { 14 | @ViewChild(DashboardComponent) 15 | private dashboardComponent: DashboardComponent; 16 | 17 | private _displaySavedDash: boolean = false; 18 | private _savedDashboards: any[] = []; 19 | 20 | constructor( 21 | public _elasticsearch: Elasticsearch 22 | ) { } 23 | 24 | ngOnInit(): void { 25 | console.log('DASHBOARDS COMPONENT - ngOnInit()'); 26 | this._setSavedDashboards(); 27 | } 28 | 29 | private _setSavedDashboards(): void { 30 | console.log('DASHBOARDS COMPONENT - _setSavedVisualizations()'); 31 | this._savedDashboards = []; 32 | this._elasticsearch.getSavedDashboards().then(hits => { 33 | for(let i=0; i this._setSavedDashboards()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/data.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | //import { Headers, Http } from '@angular/http'; 3 | 4 | @Injectable() 5 | export class DataService { 6 | 7 | getTitle(): string { 8 | return 'Sakura - Elasticsearch Dashboard Interface'; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/elasticsearch/index.ts: -------------------------------------------------------------------------------- 1 | export * from './elasticsearch.service'; 2 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/example/astronaut.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, OnDestroy } from '@angular/core'; 2 | 3 | import { MissionService } from './mission.service'; 4 | import { Subscription } from 'rxjs/Subscription'; 5 | 6 | @Component({ 7 | selector: 'my-astronaut', 8 | template: ` 9 |

10 | {{astronaut}}: {{mission}} 11 | 16 |

17 | ` 18 | }) 19 | 20 | export class AstronautComponent implements OnDestroy { 21 | @Input() astronaut: string; 22 | mission = ''; 23 | confirmed = false; 24 | announced = false; 25 | subscription: Subscription; 26 | 27 | constructor(private missionService: MissionService) { 28 | this.subscription = missionService.missionAnnounced$.subscribe( 29 | mission => { 30 | console.log('mission:', mission); 31 | this.mission = mission; 32 | this.announced = true; 33 | this.confirmed = false; 34 | } 35 | ); 36 | } 37 | 38 | confirm() { 39 | this.confirmed = true; 40 | this.missionService.confirmMission(this.astronaut); 41 | } 42 | 43 | ngOnDestroy() { 44 | // prevent memory leak when component destroyed 45 | this.subscription.unsubscribe(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/example/mission.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs/Subject'; 3 | 4 | @Injectable() 5 | export class MissionService { 6 | 7 | // Observable string sources 8 | private missionAnnouncedSource = new Subject(); 9 | private missionConfirmedSource = new Subject(); 10 | 11 | // Observable string streams 12 | missionAnnounced$ = this.missionAnnouncedSource.asObservable(); 13 | missionConfirmed$ = this.missionConfirmedSource.asObservable(); 14 | 15 | // Service message commands 16 | announceMission(mission: string) { 17 | console.log('announceMission:', mission); 18 | this.missionAnnouncedSource.next(mission); 19 | } 20 | 21 | confirmMission(astronaut: string) { 22 | console.log('confirmMission:', astronaut); 23 | this.missionConfirmedSource.next(astronaut); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/example/missioncontrol.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | import { MissionService } from './mission.service'; 4 | 5 | @Component({ 6 | selector: 'mission-control', 7 | template: ` 8 |

Mission Control

9 | 10 |

History

11 |
    12 |
  • {{event}}
  • 13 |
14 | ` 15 | }) 16 | 17 | export class MissionControlComponent { 18 | history: string[] = []; 19 | missions = [ 20 | 'Fly to the moon!', 21 | 'Fly to mars!', 22 | 'Fly to Vegas!']; 23 | nextMission = 0; 24 | 25 | constructor(private _missionService: MissionService) { 26 | _missionService.missionConfirmed$.subscribe( 27 | astronaut => { 28 | this.history.push(`${astronaut} confirmed the mission`); 29 | }); 30 | } 31 | 32 | announce() { 33 | let mission = this.missions[this.nextMission++]; 34 | this._missionService.announceMission(mission); 35 | this.history.push(`Mission "${mission}" announced`); 36 | if (this.nextMission >= this.missions.length) { this.nextMission = 0; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/object-classes/aggregationData.ts: -------------------------------------------------------------------------------- 1 | export class AggregationData { 2 | id: string; 3 | enabled: true; 4 | type: string; 5 | schema: string; 6 | params: any 7 | 8 | constructor (aggData?: AggregationData){ 9 | this.id = aggData && aggData.id || ''; 10 | this.enabled = aggData && aggData.enabled || true; 11 | this.type = aggData && aggData.type || ''; 12 | this.schema = aggData && aggData.schema || ''; 13 | this.params = aggData && aggData.params || ''; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/object-classes/searchSourceJSON.ts: -------------------------------------------------------------------------------- 1 | export class SearchSourceJSON { 2 | index: string; 3 | query: any; 4 | 5 | constructor(index: string, query: any){ 6 | this.index = index; 7 | this.query = query; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/object-classes/visualizationObj.ts: -------------------------------------------------------------------------------- 1 | import { VisualizationState } from './visualizationState'; 2 | import { SearchSourceJSON } from './searchSourceJSON'; 3 | 4 | export class VisualizationObj { 5 | title: string; 6 | visState: string; 7 | kibanaSavedObjectMeta: { 8 | searchSourceJSON: string 9 | }; 10 | 11 | constructor(title: string, visState: string, searchSourceJSON: string){ 12 | this.title = title; 13 | this.visState = visState; 14 | this.kibanaSavedObjectMeta = { searchSourceJSON: searchSourceJSON }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/object-classes/visualizationState.ts: -------------------------------------------------------------------------------- 1 | import { AggregationData } from './aggregationData'; 2 | 3 | export class VisualizationState { 4 | title: string; 5 | type: string; 6 | params: any; 7 | aggs: AggregationData[]; 8 | listeners: {} 9 | } 10 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/shared/collapse.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, Input, HostBinding, ElementRef, Renderer } from '@angular/core' 2 | 3 | @Directive( 4 | {selector: '[collapse]'} 5 | ) 6 | 7 | export class Collapse { 8 | 9 | @HostBinding('class.collapsing') 10 | private isCollapsing:boolean 11 | 12 | // style 13 | @HostBinding('style.height') 14 | private height: string; 15 | private h: any = null; 16 | 17 | @Input() 18 | 19 | private set collapse(value:boolean) { 20 | if(value!==undefined){ 21 | if(value){ 22 | this.hide(); 23 | }else { 24 | this.show(); 25 | } 26 | } 27 | } 28 | 29 | constructor(public el: ElementRef) { 30 | console.log('COLLAPSE DIRECTIVE - el:' + el.nativeElement.scrollHeight); 31 | this.measureHeight(); 32 | } 33 | 34 | measureHeight() { 35 | let elem = this.el.nativeElement; 36 | console.log('COLLAPSE DIRECTIVE - elem:', elem); 37 | //lets be sure the element has display:block style 38 | elem.className = elem.className.replace('collapse', ''); 39 | //this.h = elem.scrollHeight; 40 | 41 | console.log('COLLAPSE DIRECTIVE - offsetHeight:' + elem.offsetHeight); 42 | console.log('COLLAPSE DIRECTIVE - h:', this.h); 43 | } 44 | 45 | hide(){ 46 | if(this.h===null) this.h = this.el.nativeElement.scrollHeight; 47 | this.height = this.h +'px' 48 | console.log('COLLAPSE DIRECTIVE - h:', this.h); 49 | setTimeout(() => { 50 | this.height = '0px'; 51 | this.isCollapsing = true;//apply 'collapsing' class 52 | },1); 53 | } 54 | 55 | show() { 56 | this.height = '0px' 57 | setTimeout(() => { 58 | this.height = this.h + 'px'; 59 | console.log('COLLAPSE DIRECTIVE - h:', this.h); 60 | this.isCollapsing = true;//apply 'collapsing' class 61 | },1); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/shared/dynamicComponent.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, ViewContainerRef, ViewChild, ComponentFactoryResolver, EventEmitter } from '@angular/core'; 2 | 3 | 4 | @Component({ 5 | selector: 'dynamic-component', 6 | template: ` 7 |
8 | ` 9 | }) 10 | 11 | 12 | export class DynamicComponent { 13 | // We need to get a reference to our div element 14 | @ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) dynamicComponentContainer: ViewContainerRef; 15 | 16 | // An object with key/value pairs mapped to input name/input value 17 | @Input() componentType: any; 18 | // This event will return a object, with the event name 19 | // as a string key 20 | @Output() event: EventEmitter = new EventEmitter(); 21 | @Output() init: EventEmitter = new EventEmitter(); 22 | 23 | private _componentsMap: Map = new Map(); 24 | 25 | constructor(private resolver: ComponentFactoryResolver) {} 26 | 27 | ngOnInit(): void { 28 | console.log('DYNAMIC COMPONENT - ngOnInit()'); 29 | this.init.emit(); 30 | } 31 | 32 | // Append a new component instance on the container 33 | addComponent(uniqueId: string, inputs: any, events: Array, componentType: any): any { 34 | console.log('DYNAMIC COMPONENT - addComponent():', uniqueId); 35 | const factory = this.resolver.resolveComponentFactory(componentType); 36 | let componentRef: any = this.dynamicComponentContainer.createComponent(factory); 37 | this._componentsMap.set(uniqueId, componentRef); 38 | this.setInputs(uniqueId, inputs); 39 | this.subscribeEvents(uniqueId, events); 40 | componentRef.changeDetectorRef.detectChanges(); 41 | return componentRef.instance; 42 | } 43 | 44 | setInputs(uniqueId:string, inputs: any): void{ 45 | let componentRef = this._componentsMap.get(uniqueId); 46 | if(componentRef){ 47 | for(var inputField in inputs){ 48 | console.log('BEFORE - this.componentRef.instance[' + inputField + ']:', componentRef.instance[inputField]); 49 | componentRef.instance[inputField] = inputs[inputField]; 50 | console.log('AFTER - this.componentRef.instance[' + inputField + ']:', componentRef.instance[inputField]); 51 | } 52 | }else{ 53 | console.error('Error: unique ID not found.'); 54 | } 55 | } 56 | 57 | subscribeEvents(uniqueId:string, events: Array): void{ 58 | //console.log('DYNAMIC COMPONENT - subscribeEvents()'); 59 | let componentRef = this._componentsMap.get(uniqueId); 60 | if(componentRef){ 61 | for(let i=0; i this.event.emit({ 64 | uniqueId: uniqueId, 65 | name: events[i], 66 | data: event 67 | })); 68 | //console.log('AFTER - this.componentRef.instance[' + events[i] + ']'); 69 | } 70 | }else{ 71 | console.error('Error: unique ID not found.'); 72 | } 73 | } 74 | 75 | getCmp(uniqueId: string): Function { 76 | return this._componentsMap.get(uniqueId).instance; 77 | } 78 | 79 | destroyCmp(uniqueId: string): void { 80 | console.log('DYNAMIC COMPONENT - destroyCmp():', uniqueId); 81 | this._componentsMap.get(uniqueId).destroy(); 82 | this._componentsMap.delete(uniqueId); 83 | } 84 | 85 | debug(): void { 86 | console.log('%c DEBUG', 'background: #222; color: #bada55'); 87 | console.log('%c componentType', 'background: #222; color: #bada55', this.componentType); 88 | console.log('%c _componentsMap', 'background: #222; color: #bada55', this._componentsMap); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/shared/hidden.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, ElementRef, Input, Renderer } from '@angular/core'; 2 | 3 | @Directive({ selector: '[hidden]' }) 4 | export class HiddenDirective { 5 | 6 | constructor(public el: ElementRef, public renderer: Renderer) {} 7 | 8 | @Input() hidden: boolean; 9 | 10 | ngOnInit(){ 11 | // Use renderer to render the emelemt with styles 12 | console.log(this.hidden) 13 | if(this.hidden) { 14 | this.renderer.setElementStyle(this.el.nativeElement, 'display', 'none'); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/shared/validators.directive.ts: -------------------------------------------------------------------------------- 1 | import { AbstractControl, ValidatorFn } from '@angular/forms'; 2 | 3 | export function maxValidator(maxValue: number): ValidatorFn { 4 | return (control: AbstractControl): {[key: string]: any} => { 5 | const value = control.value; 6 | console.log('value', value); 7 | return (value!==null && (value>maxValue)) ? {'maxValue': {name}} : null; 8 | }; 9 | } 10 | 11 | export function minValidator(minValue: number): ValidatorFn { 12 | return (control: AbstractControl): {[key: string]: any} => { 13 | const value = control.value; 14 | console.log('value', value); 15 | return (value!==null && (value 2 |
3 | 4 |
5 |
6 | 7 |
8 | 9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 25 |
26 | 27 |
28 | 29 | 32 | 33 | 34 | 35 | 37 | 38 |
39 |
40 | 41 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/bar-chart/bar-chart.component.scss: -------------------------------------------------------------------------------- 1 | div.vis-container { 2 | display: flex; 3 | height: 100%; 4 | 5 | div#chart-config { 6 | flex: 1; 7 | min-width: 115px; 8 | /*background-color: #f1f6ff; 9 | box-shadow: -5px 0px 10px 0px rgba(0, 0, 0, 0.14);*/ 10 | 11 | div#metricsAndBuckets { 12 | overflow-y: scroll; 13 | } 14 | 15 | div#configFunctions { 16 | display: flex; 17 | align-items: center; 18 | background-color: #dddddd; 19 | 20 | span.function-icon { 21 | cursor: pointer; 22 | background-color: #dddddd; 23 | color: #3c75ca; 24 | //border-radius: 0.25rem; 25 | padding: 0.75rem; 26 | } 27 | 28 | span.function-icon:hover { 29 | background-color: #b3b3b3; 30 | } 31 | 32 | /*div#calculateButton { 33 | flex: 1; 34 | //padding: 0.25rem; 35 | }*/ 36 | 37 | input { 38 | height: 1.25rem; 39 | width: 100%; 40 | //border-radius: 0.25rem; 41 | font-size: 1rem; 42 | border: 0px; 43 | padding-left: 0.5rem; 44 | padding-right: 0.5rem; 45 | margin-left: 0.5rem; 46 | margin-right: 0.5rem; 47 | border-radius: 0.25rem; 48 | } 49 | 50 | /*div#saveForm { 51 | flex: 5; 52 | //padding: 0.25rem; 53 | 54 | input { 55 | height: 1.25rem; 56 | //border-radius: 0.25rem; 57 | font-size: 1rem; 58 | border: 0px; 59 | } 60 | }*/ 61 | } 62 | } 63 | 64 | div#chart-container { 65 | flex: 3; 66 | position: relative; 67 | min-width: 0px; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/bar-chart/bar-chart.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { Elasticsearch } from '../../elasticsearch'; 4 | import { MetricsService } from '../metrics/metrics.service'; 5 | 6 | import { AggregationData } from '../../object-classes/aggregationData'; 7 | import { VisualizationObj } from '../../object-classes/visualizationObj'; 8 | 9 | import { VisualizationTools } from "../../shared/visualization-tools"; 10 | 11 | import * as _ from "lodash"; 12 | declare let bodybuilder: any; 13 | 14 | @Injectable() 15 | export class BarChartService { 16 | 17 | constructor( 18 | private _elasticCli: Elasticsearch, 19 | private _metricsService: MetricsService 20 | ) { } 21 | 22 | saveBarChart(visualizationObj: VisualizationObj): void { 23 | this._elasticCli.saveVisualization(visualizationObj); 24 | } 25 | 26 | getResults(index: string, metrics: AggregationData[], buckets: AggregationData[]): PromiseLike { 27 | let body = this._getRequestBody(buckets, metrics); 28 | return this._elasticCli.request(index, body).then(response => { 29 | console.log('BAR CHART SERVICE - response:', response); 30 | return this._getFormattedResults(response, this._getAggByIdMap(_.concat(metrics, buckets)), buckets[0], metrics); 31 | }); 32 | } 33 | 34 | private _getFormattedResults(response: any, aggsById: Map, bucket: AggregationData, metrics: AggregationData[]): any { 35 | let bucketResponse = response.aggregations[bucket.id]; 36 | if(!bucketResponse) return null; 37 | let buckets = bucketResponse.buckets; 38 | 39 | console.log('BAR CHART SERVICE - aggsById:', aggsById); 40 | 41 | let results = []; 42 | for(let i=0; i(); 66 | for(let i=0; i { 73 | console.log('BAR CHART SERVICE - _getResultsMap()'); 74 | let resultsMap = new Map(); 75 | for(let i=0; i, 88 | parentResultId: string 89 | ): any{ 90 | let currentBucket = aggsById.get(aggBucketId); 91 | let results = []; 92 | for(let i in responseBucket.buckets){ 93 | let bucketObj = responseBucket.buckets[i]; 94 | for(let field in bucketObj){ 95 | //console.log('BAR CHART SERVICE - field:', field); 96 | //console.log('BAR CHART SERVICE - obj:', bucketObj[field]); 97 | if(this._isMetric(field)){ 98 | let metricId = field; 99 | let metricResult = this._getMetricResult(bucketObj, aggsById.get(metricId)); 100 | //console.log('BAR CHART SERVICE - metricResult:', metricResult); 101 | metricResult = _.map(metricResult, (r) => { 102 | return { 103 | id: VisualizationTools.guidGenerator(), 104 | responseBucket: currentBucket, 105 | metricResult: r, 106 | bucketValue: this._getBucketValue(currentBucket.type, bucketObj), 107 | percent: null, 108 | parentResultId: parentResultId 109 | } 110 | }); 111 | results = _.concat(results, metricResult); 112 | } 113 | } 114 | } 115 | return results; 116 | } 117 | 118 | private _getMetricResult(bucket: any, metric: AggregationData): any { 119 | return this._metricsService.getAggResult(bucket, metric); 120 | } 121 | 122 | private _isMetric(aggId: string): boolean { 123 | return aggId.split('_')[0]==='metric'; 124 | } 125 | 126 | private _getBucketValue(bucketType: string, bucket: any){ 127 | switch(bucketType){ 128 | case 'range': 129 | return bucket.from + ' to ' + bucket.to; 130 | case 'histogram': 131 | return bucket.key; 132 | default: 133 | return null; 134 | } 135 | } 136 | 137 | private _getAggByIdMap(aggs: AggregationData[]): Map { 138 | let aggByIdMap = new Map(); 139 | for(let i=0; i=0; i--){ 150 | console.log('BAR CHART SERVICE - bucket:', buckets[i]); 151 | console.log('BAR CHART SERVICE - aggType:', this._elasticCli.getAggType(buckets[i])); 152 | console.log('BAR CHART SERVICE - aggParams:', this._elasticCli.getAggParams(buckets[i])); 153 | console.log('BAR CHART SERVICE - body:', body.build()); 154 | body = bodybuilder().aggregation( 155 | this._elasticCli.getAggType(buckets[i]), 156 | null, 157 | buckets[i].id, 158 | this._elasticCli.getAggParams(buckets[i]), 159 | (a) => body 160 | ); 161 | } 162 | console.log('BAR CHART SERVICE - body:', body.build()); 163 | return body; 164 | } 165 | 166 | private _getMetricsBody(metrics: AggregationData[]): any { 167 | var body = bodybuilder(); 168 | 169 | for(let i=0; i 2 |
3 |
4 | {{_selectedAgg}} - {{_selectedNumField}} 5 |
6 |
7 | 8 |
9 |
10 | 11 |
12 |
13 | Aggregation: 14 | 23 |
24 | 25 |
26 | Field: 27 | 36 |
37 | 38 | 39 |
40 |
41 | Interval: 42 |
43 | 49 | 51 | {{ formErrors.naturalNumber }} 52 | 53 |
54 |
55 |
56 | 57 | 58 |
59 |
60 |
61 |
62 | From: 63 | 64 | 70 |
71 |
72 | to: 73 | 79 |
80 |
81 | 82 |
83 |
84 |
85 | 86 |
87 |
88 |
89 |
90 | 91 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/buckets/bucket.component.scss: -------------------------------------------------------------------------------- 1 | div.bucket { 2 | 3 | div.bucket-header { 4 | display: flex; 5 | align-items: center; 6 | 7 | div { 8 | flex: 1; 9 | padding: 1rem; 10 | 11 | span.fa-trash-o { 12 | float: right; 13 | color: red; 14 | } 15 | } 16 | 17 | div.display-info { 18 | flex: 5; 19 | } 20 | 21 | div.display-info:hover { 22 | cursor: pointer; 23 | background-color: #e8e8e8; 24 | } 25 | 26 | div.remove { 27 | flex: 1; 28 | display: flex; 29 | align-items: center; 30 | flex-direction: column; 31 | } 32 | 33 | div.remove:hover { 34 | cursor: pointer; 35 | background-color: #ffe6e6; 36 | } 37 | } 38 | 39 | div.bucket-body { 40 | padding: 0rem 1rem 1rem 1rem; 41 | 42 | div.bucket-opt { 43 | display: flex; 44 | flex-direction: column; 45 | margin: 0.5rem 0 0.5rem 0; 46 | 47 | span { 48 | font-weight: bold; 49 | font-size: 0.75rem; 50 | padding-bottom: 0.25rem; 51 | } 52 | 53 | select { 54 | font-size: 1rem; 55 | border-radius: .25rem; 56 | height: 1.75rem; 57 | color: rgb(0, 112, 210); 58 | white-space: normal; 59 | -webkit-user-select: none; 60 | background-color: white; 61 | padding-left: 0.5rem; 62 | cursor: pointer; 63 | border-color: #d4d4d4; 64 | } 65 | 66 | input { 67 | height: 1.75rem; 68 | font-size: 1rem; 69 | border-radius: .25rem; 70 | border: 1px solid #d4d4d4; 71 | color: #0070d2; 72 | padding-left: 0.5rem; 73 | cursor: pointer; 74 | box-sizing: border-box; 75 | width: 100%; 76 | } 77 | 78 | div.from-to { 79 | display: flex; 80 | margin-bottom: 0.5rem; 81 | 82 | div.range-field { 83 | flex: 2; 84 | padding-right: 0.5rem; 85 | } 86 | 87 | div.icon { 88 | flex: 1; 89 | padding-top: 1rem; 90 | display: flex; 91 | align-items: center; 92 | justify-content: center; 93 | color: red; 94 | cursor: pointer; 95 | 96 | span { 97 | padding: 0.5rem; 98 | } 99 | 100 | span:hover { 101 | background-color: #ffe6e6; 102 | border-radius: 0.25rem; 103 | } 104 | } 105 | } 106 | 107 | div.add-range-button { 108 | display: flex; 109 | align-items: center; 110 | justify-content: center; 111 | padding: 0.5rem; 112 | border: 1px solid #d4d4d4; 113 | border-radius: 0.25rem; 114 | cursor: pointer; 115 | 116 | span { 117 | padding: 0px; 118 | } 119 | } 120 | 121 | div.add-range-button:hover { 122 | background-color: #ccdffb; 123 | } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/buckets/bucket.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, SimpleChange, EventEmitter } from '@angular/core'; 2 | import { FormGroup, FormBuilder} from '@angular/forms'; 3 | 4 | import { AggregationData } from '../../object-classes/aggregationData'; 5 | 6 | import { minValidator } from '../../shared/validators.directive'; 7 | 8 | @Component({ 9 | selector: 'bucket', 10 | templateUrl: './bucket.component.html', 11 | styleUrls: ['./bucket.component.scss'] 12 | }) 13 | 14 | export class BucketComponent { 15 | @Output() dataChange = new EventEmitter(); 16 | @Output() remove = new EventEmitter(); 17 | 18 | private _numFields: string[] = []; 19 | @Input() set numFields(numFields: string[]) { 20 | console.log('BUCKET - numFields:', numFields); 21 | this._numFields = numFields; 22 | if(numFields && numFields.length>0) 23 | this._selectedNumField = numFields[0]; 24 | }; 25 | private _selectedNumField: string; 26 | 27 | private _savedData: any = null; 28 | @Input() set savedData(savedData: any) { 29 | console.log('METRIC - SET - savedData'); 30 | this._savedData = savedData; 31 | if(savedData) this._loadSavedBucket(savedData); 32 | }; 33 | 34 | private _aggregations: any[] = [ 35 | //{ label: 'Date Histogram', value: 'date_histogram'}, 36 | { label: 'Histogram', value: 'histogram'}, 37 | { label: 'Range', value: 'range'}, 38 | //{ label: 'Date Range', value: 'date_range'}, 39 | //{ label: 'IPv4 Range', value: 'ipv4_range'}, 40 | //{ label: 'Terms', value: 'terms'}, 41 | //{ label: 'Filters', value: 'filters'}, 42 | //{ label: 'Significant Terms', value: 'sig_terms'}, 43 | //{ label: 'Geohash', value: 'geo'} 44 | ]; 45 | private _selectedAgg: string = this._aggregations[0].value; 46 | 47 | interval: number = null; 48 | ranges: any[] = []; 49 | 50 | form: FormGroup; 51 | formErrors = { 52 | 'naturalNumber': '' 53 | }; 54 | validationMessages = { 55 | 'naturalNumber': { 56 | 'minValue': 'Percentile value can\'t be lower than 0.' 57 | } 58 | }; 59 | 60 | 61 | constructor( private fb: FormBuilder ) {} 62 | 63 | ngOnInit(): void{ 64 | console.log('BUCKET - ngOnInit()'); 65 | this.buildForm(); 66 | } 67 | 68 | triggerRemoveEvent(): void { 69 | this.remove.emit(); 70 | } 71 | 72 | dataChangeEvent(): void { 73 | console.log('BUCKET - dataChangeEvent()'); 74 | let aggregationData = this.getAggregationData(); 75 | console.log('BUCKET - aggregationData:', aggregationData); 76 | if(aggregationData.params){ 77 | this.dataChange.emit(aggregationData); 78 | } 79 | } 80 | 81 | private _loadSavedBucket(bucket: AggregationData): void { 82 | console.log('BUCKET - _loadSavedBucket():', bucket); 83 | this._selectedAgg = bucket.type; 84 | this._selectedNumField = bucket.params.field; 85 | this.interval = bucket.params.interval || null; 86 | this.ranges = bucket.params.ranges || []; 87 | } 88 | 89 | getAggregationData(): AggregationData { 90 | var aggregationData = new AggregationData(); 91 | aggregationData.enabled = true; 92 | aggregationData.type = this._selectedAgg; 93 | aggregationData.schema = 'metric'; 94 | aggregationData.params = this.getAggParams(); 95 | 96 | return aggregationData; 97 | } 98 | 99 | getAggParams(): any { 100 | switch (this._selectedAgg){ 101 | case 'histogram': 102 | return { 103 | field: this._selectedNumField, 104 | interval: this.interval 105 | }; 106 | case 'range': 107 | return { 108 | field: this._selectedNumField, 109 | ranges: this.ranges 110 | }; 111 | default: 112 | return null; 113 | } 114 | } 115 | 116 | // AGGREGATIONS CODE 117 | addRange(): void { 118 | this.ranges.push({ 119 | from: 0, 120 | to: 0 121 | }); 122 | } 123 | 124 | removeRange(index: number): void { 125 | this.ranges.splice(index, 1); 126 | } 127 | 128 | // FORM CODE 129 | 130 | buildForm(): void { 131 | this.form = this.fb.group({ 132 | 'naturalNumber': ['', [ 133 | minValidator(0) 134 | ] 135 | ] 136 | }); 137 | 138 | this.form.valueChanges 139 | .subscribe(data => this.onValueChanged(data)); 140 | 141 | this.onValueChanged(); // (re)set validation messages now 142 | } 143 | 144 | onValueChanged(data?: any) { 145 | if (!this.form) { return; } 146 | const form = this.form; 147 | 148 | for (const field in this.formErrors) { 149 | // clear previous error message (if any) 150 | this.formErrors[field] = ''; 151 | const control = form.get(field); 152 | 153 | if (control && control.dirty && !control.valid) { 154 | const messages = this.validationMessages[field]; 155 | for (const key in control.errors) { 156 | this.formErrors[field] += messages[key] + ' '; 157 | } 158 | } 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/buckets/buckets.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | Buckets 5 |
6 |
7 |
8 | 9 |
10 |
11 | 12 |
13 |
14 | 15 |
16 |
17 |
18 | 19 |
20 | 22 | 23 |
24 | 25 |
26 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/buckets/buckets.component.scss: -------------------------------------------------------------------------------- 1 | div.buckets-container{ 2 | border-bottom: 1px solid #d8d8d8; 3 | 4 | div.buckets-header { 5 | display: flex; 6 | 7 | div.title { 8 | flex: 5; 9 | font-family: "Trebuchet MS", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Tahoma, sans-serif; 10 | font-size: 1.2rem; 11 | padding: 1rem; 12 | } 13 | 14 | div.buckets-actions { 15 | display: flex; 16 | flex-direction: row-reverse; 17 | flex: 1; 18 | 19 | div { 20 | padding: 1rem; 21 | border-radius: 0.25rem; 22 | cursor: pointer; 23 | } 24 | 25 | div.add-bucket:hover { 26 | background-color: #ccdffb; 27 | } 28 | 29 | div.remove-all { 30 | color: red; 31 | } 32 | 33 | div.remove-all:hover { 34 | background-color: #ffe6e6; 35 | } 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/buckets/buckets.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, ViewChild } from '@angular/core'; 2 | import { BucketComponent } from './bucket.component'; 3 | import { DynamicComponent } from '../../shared/dynamicComponent.component'; 4 | 5 | import { VisualizationState } from '../../object-classes/visualizationState'; 6 | import { AggregationData } from '../../object-classes/aggregationData'; 7 | import { SearchSourceJSON } from '../../object-classes/searchSourceJSON'; 8 | import { VisualizationObj } from '../../object-classes/visualizationObj'; 9 | 10 | import { VisualizationsService } from '../visualizations.service'; 11 | 12 | import { Subscription } from 'rxjs/Subscription'; 13 | 14 | 15 | @Component({ 16 | selector: 'buckets', 17 | templateUrl: './buckets.component.html', 18 | styleUrls: ['./buckets.component.scss'] 19 | }) 20 | 21 | 22 | export class BucketsComponent { 23 | @ViewChild(DynamicComponent) dynamicComponents; 24 | 25 | @Input() index: string; 26 | 27 | // This fields come from _visualizationsService 28 | numFields: string[] = []; 29 | textFields: string[] = []; 30 | 31 | subscriptions: Subscription[] = []; 32 | 33 | bucketEvents: Array = [ 'remove', 'dataChange' ]; 34 | bucketsMap: Map = new Map(); 35 | 36 | constructor( 37 | private _visualizationsService: VisualizationsService 38 | ) { 39 | let sub1 = _visualizationsService.numFieldsSent$.subscribe(numFields => { 40 | console.log('BUCKETS - RECIEVED - numFields:', numFields); 41 | this.numFields = numFields; 42 | if(numFields) this.updateBucketsInputs(); 43 | }) 44 | 45 | let sub2 = _visualizationsService.textFieldsSent$.subscribe(textFields => { 46 | console.log('BUCKETS - RECIEVED - textFields:', textFields); 47 | this.textFields = textFields; 48 | if(textFields) this.updateBucketsInputs(); 49 | }) 50 | 51 | this.subscriptions.push(sub1, sub2); 52 | } 53 | 54 | onEvent(event): void { 55 | console.log('event:', event); 56 | switch(event.name){ 57 | case 'dataChange': 58 | this.onDataChange(event.uniqueId, event.data); 59 | break; 60 | case 'remove': 61 | this._removeBucket(event.uniqueId); 62 | break; 63 | default: 64 | console.error('ERROR: event name [' + event.name + '] not found.'); 65 | } 66 | } 67 | 68 | onDataChange(uniqueId: string, data: any): void { 69 | console.log('DATA CHANGE FOR BUCKET:', uniqueId); 70 | console.log('DATA:', data); 71 | 72 | this.bucketsMap.set(uniqueId, data); 73 | } 74 | 75 | ngOnDestroy() { 76 | for(let i=0; i { 90 | let inputs = { 91 | numFields: this.numFields 92 | } 93 | 94 | this.dynamicComponents.setInputs(key, inputs); 95 | }); 96 | } 97 | 98 | loadBuckets(aggs: AggregationData[]): void { 99 | console.log('BUCKETS - loadSavedBuckets():', aggs); 100 | this._removeAll(); 101 | for(let i=0; i { 134 | this.dynamicComponents.destroyCmp(key); 135 | this.bucketsMap.delete(key); 136 | }); 137 | } 138 | 139 | private _setBucketIds(): void { 140 | let i = 0; 141 | this.bucketsMap.forEach((value, key, map) => { 142 | i++; 143 | value.id = 'bucket_' + i; 144 | }); 145 | } 146 | 147 | private _guidGenerator(): string { 148 | var S4 = function() { 149 | return (((1+Math.random())*0x10000)|0).toString(16).substring(1); 150 | }; 151 | return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4()); 152 | } 153 | 154 | debug(): void { 155 | console.log('%c DEBUG', 'background: #222; color: #bada55'); 156 | console.log('%c bucketsMap', 'background: #222; color: #bada55', this.bucketsMap); 157 | console.log('%c buckets', 'background: #222; color: #bada55', Array.from(this.bucketsMap.values())); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/data-table/dataTable.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 6 | 7 |
8 | 9 |
10 |
11 | 12 | 13 | 14 | 19 | 22 |
23 | 24 |
25 | 26 | 29 | 30 | 31 | 32 | 34 | 35 |
36 |
37 |
38 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/data-table/dataTable.component.scss: -------------------------------------------------------------------------------- 1 | div.vis-container { 2 | display: flex; 3 | 4 | div#table-config { 5 | flex: 1; 6 | min-width: 115px; 7 | 8 | div#metricsAndBuckets { 9 | overflow-y: scroll; 10 | } 11 | 12 | div#configFunctions { 13 | display: flex; 14 | align-items: center; 15 | background-color: #dddddd; 16 | 17 | span.function-icon { 18 | cursor: pointer; 19 | background-color: #dddddd; 20 | color: #3c75ca; 21 | padding: 0.75rem; 22 | } 23 | 24 | span.function-icon:hover { 25 | background-color: #b3b3b3; 26 | } 27 | 28 | input { 29 | height: 1.25rem; 30 | width: 100%; 31 | //border-radius: 0.25rem; 32 | font-size: 1rem; 33 | border: 0px; 34 | padding-left: 0.5rem; 35 | padding-right: 0.5rem; 36 | margin-left: 0.5rem; 37 | margin-right: 0.5rem; 38 | border-radius: 0.25rem; 39 | } 40 | } 41 | } 42 | 43 | div#tableContainer { 44 | flex: 3; 45 | position: relative; 46 | min-width: 0px; 47 | overflow-y: scroll; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/data-table/dataTable.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, ViewChild, EventEmitter } from '@angular/core'; 2 | 3 | import { MetricsComponent } from '../metrics/metrics.component'; 4 | import { BucketsComponent } from '../buckets/buckets.component'; 5 | 6 | import { DataTableService } from './data-table.service'; 7 | 8 | import { VisualizationState } from '../../object-classes/visualizationState'; 9 | import { AggregationData } from '../../object-classes/aggregationData'; 10 | import { SearchSourceJSON } from '../../object-classes/searchSourceJSON'; 11 | import { VisualizationObj } from '../../object-classes/visualizationObj'; 12 | 13 | import * as _ from "lodash"; 14 | declare var $: any; 15 | 16 | @Component({ 17 | selector: 'data-table', 18 | templateUrl: './dataTable.component.html', 19 | styleUrls: ['./dataTable.component.scss'], 20 | providers: [ DataTableService ] 21 | }) 22 | 23 | export class DataTableComponent { 24 | @ViewChild(MetricsComponent) private _metricsComponent: MetricsComponent; 25 | @ViewChild(BucketsComponent) private _bucketsComponent: BucketsComponent; 26 | 27 | @Output() init: EventEmitter = new EventEmitter(); 28 | 29 | @Input() dashMode: boolean = false; 30 | @Input() index: string; 31 | private _numFields: string[]; 32 | private _textFields: string[]; 33 | 34 | results: any[] = []; 35 | columns: string[] = [] 36 | rows: string[][] = []; 37 | 38 | private _columnsHeaders: string[] = [] 39 | private _rows: string[][] = []; 40 | 41 | constructor( private _dataTableService: DataTableService ) {} 42 | 43 | ngOnInit(): void { 44 | console.log('DATA TABLE - ngOnInit()'); 45 | console.log('this._numFields:', this._numFields); 46 | this.init.emit(); 47 | } 48 | 49 | calculate(): void{ 50 | console.log('DATA TABLE - calculate()'); 51 | let metricAggs = this._metricsComponent.getAggs(); 52 | let bucketAggs = this._bucketsComponent.getAggs(); 53 | let resultsObj: any = this._dataTableService.getResults(this.index, metricAggs, bucketAggs).then( 54 | resultsObj => { 55 | this._columnsHeaders = resultsObj.columnsHeaders; 56 | this._rows = this._getFormattedRows(resultsObj.rows); 57 | console.log('DATA TABLE - resultsObj:', resultsObj); 58 | } 59 | ); 60 | } 61 | 62 | private _getHeight(elemId: string): Number { 63 | //console.log('PIE CHART - elemId:', elemId); 64 | let configHeight = ($(window).height() - $('#' + elemId).position().top); 65 | //console.log('PIE CHART - config height:', configHeight); 66 | return configHeight; 67 | } 68 | 69 | private _getDisplayStyle(): string { 70 | if(this.dashMode) return 'none'; 71 | return ''; 72 | } 73 | 74 | private _getFormattedRows(rows: any[]): any[]{ 75 | let formattedRows = []; 76 | for(let i=0; i { 78 | return (valueObj.value) ? valueObj.value : valueObj.result; 79 | }); 80 | console.log('DATA TABLE - formattedRow:', formattedRow); 81 | formattedRows.push(formattedRow); 82 | } 83 | return formattedRows; 84 | } 85 | 86 | private _save(visTitle: string): void { 87 | let allAggs = this._getAllAggs(); 88 | if(visTitle !== ''){ 89 | var visualizationState = new VisualizationState(); 90 | visualizationState.title = visTitle; 91 | visualizationState.type = 'table'; 92 | visualizationState.aggs = allAggs; 93 | console.log(visualizationState); 94 | 95 | var searchSourceJSON = new SearchSourceJSON( 96 | (this.index) ? this.index : '', {} 97 | ); 98 | 99 | var visualizationObject = new VisualizationObj( 100 | visTitle, 101 | JSON.stringify(visualizationState), 102 | JSON.stringify(searchSourceJSON) 103 | ); 104 | 105 | this._dataTableService.saveDataTable(visualizationObject); 106 | } 107 | } 108 | 109 | loadVis(aggs: AggregationData[]): void { 110 | console.log('DATA TABLE - loadVis():', aggs); 111 | let metrics = _.filter(aggs, (agg) => agg.id.split('_')[0]==='metric'); 112 | let buckets = _.filter(aggs, (agg) => agg.id.split('_')[0]==='bucket'); 113 | console.log('DATA TABLE - metrics:', metrics); 114 | console.log('DATA TABLE - buckets:', buckets); 115 | this._metricsComponent.loadMetrics(metrics); 116 | this._bucketsComponent.loadBuckets(buckets); 117 | this.calculate(); 118 | } 119 | 120 | private _getAllAggs(): AggregationData[] { 121 | let metricAggs = this._metricsComponent.getAggs(); 122 | let bucketAggs = this._bucketsComponent.getAggs(); 123 | return _.concat(metricAggs, bucketAggs); 124 | } 125 | 126 | resetTable(): void{ 127 | this.columns = []; 128 | this.rows = []; 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/data-table/table.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
5 | {{ column }} 6 |
{{ field }}
14 |
15 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/data-table/table.component.scss: -------------------------------------------------------------------------------- 1 | div.table-container { 2 | display: flex; 3 | padding: 1rem; 4 | align-items: center; 5 | justify-content: center; 6 | 7 | table { 8 | border-collapse: collapse; 9 | width: 100%; 10 | 11 | th, td { 12 | border: 1px solid #bfbfbf; 13 | padding: 0.5rem; 14 | border-top: 0; 15 | border-left: 0; 16 | border-right: 0; 17 | text-align: -webkit-left; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/data-table/table.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'dataTable-table', 5 | templateUrl: './table.component.html', 6 | styleUrls: [ './table.component.scss' ] 7 | }) 8 | 9 | export class TableComponent { 10 | 11 | @Input() columns: string[]; 12 | @Input() rows: string[][]; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/metrics/aggregations/percentileRanksMetric.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 19 | Percentile: 20 |
21 |
22 | 26 |
27 |
28 | 29 |
30 |
31 |
32 |
33 | 34 | {{'[' + value + ']'}} 35 | 36 |
37 |
38 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/metrics/aggregations/percentileRanksMetric.component.scss: -------------------------------------------------------------------------------- 1 | div.metric-opt { 2 | display: flex; 3 | flex-direction: column; 4 | margin: 0.75rem 0 0.75rem 0; 5 | 6 | span.opt-title { 7 | font-weight: bold; 8 | font-size: 0.75rem; 9 | padding-bottom: 0.25rem; 10 | } 11 | 12 | div.add-percentile-rank { 13 | display: flex; 14 | 15 | div.input { 16 | flex: 9; 17 | 18 | input { 19 | height: 1.75rem; 20 | font-size: 1rem; 21 | border-radius: .25rem; 22 | border: 1px solid #d4d4d4; 23 | color: #0070d2; 24 | padding-left: 0.5rem; 25 | cursor: pointer; 26 | box-sizing: border-box; 27 | width: 100%; 28 | } 29 | } 30 | 31 | div.icon { 32 | flex: 1; 33 | display: flex; 34 | justify-content: center; 35 | align-items: center; 36 | margin-left: 0.25rem; 37 | } 38 | 39 | div.icon:hover { 40 | background-color: #ccdffb; 41 | cursor: pointer; 42 | border-radius: 0.25rem; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/metrics/aggregations/percentileRanksMetric.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, EventEmitter } from '@angular/core'; 2 | import { FormGroup, FormBuilder} from '@angular/forms'; 3 | 4 | import { maxValidator } from '../../../shared/validators.directive'; 5 | 6 | @Component({ 7 | selector: 'percentileRanks-metric', 8 | templateUrl: './percentileRanksMetric.component.html', 9 | styleUrls: ['./percentileRanksMetric.component.scss'] 10 | }) 11 | 12 | export class PercentileRanksMetricComponent { 13 | @Output() dataChange = new EventEmitter(); 14 | 15 | private _savedData: any = null; 16 | @Input() set savedData(savedData: any) { 17 | console.log('PERCENTILERANKS - SET savedData:', savedData); 18 | this._savedData = savedData; 19 | if(this._savedData && savedData.type==='percentile_ranks'){ 20 | this.percentileRankValues = this._savedData.params.values; 21 | } 22 | } 23 | 24 | form: FormGroup; 25 | formErrors = { 26 | 'percentileRanks': '' 27 | }; 28 | validationMessages = { 29 | 'percentileRanks': {} 30 | }; 31 | 32 | percentileRankValues: number[] = []; 33 | 34 | constructor( private fb: FormBuilder ) { } 35 | 36 | ngOnInit(): void { 37 | console.log('PERCENTILE RANKS - ngOnInit()'); 38 | this.buildForm(); 39 | if(!this._savedData) 40 | this.dataChange.emit(); 41 | } 42 | 43 | dataChangeEvent(): void { 44 | this.dataChange.emit(); 45 | } 46 | 47 | buildForm(): void { 48 | this.form = this.fb.group({ 49 | 'percentileRanks': ['', []] 50 | }); 51 | 52 | this.form.valueChanges 53 | .subscribe(data => this.onValueChanged(data)); 54 | 55 | this.onValueChanged(); // (re)set validation messages now 56 | } 57 | 58 | onValueChanged(data?: any) { 59 | if (!this.form) { return; } 60 | const form = this.form; 61 | 62 | for (const field in this.formErrors) { 63 | // clear previous error message (if any) 64 | this.formErrors[field] = ''; 65 | const control = form.get(field); 66 | 67 | if (control && control.dirty && !control.valid) { 68 | const messages = this.validationMessages[field]; 69 | for (const key in control.errors) { 70 | this.formErrors[field] += messages[key] + ' '; 71 | } 72 | } 73 | } 74 | } 75 | 76 | addPercentileRank(value: string): void{ 77 | console.log(value); 78 | var percentile = parseInt(value); 79 | if(!isNaN(percentile)){ 80 | this.percentileRankValues = Array.from( 81 | new Set(this.percentileRankValues).add(percentile) 82 | ).sort((a,b) => a - b); 83 | } 84 | this.dataChangeEvent(); 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/metrics/aggregations/percentilesMetric.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 17 | Percentile: 18 |
19 |
20 | 24 |
25 |
26 | 27 |
28 |
29 |
30 |
31 | 32 | {{'[' + value + ']'}} 33 | 34 |
35 |
36 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/metrics/aggregations/percentilesMetric.component.scss: -------------------------------------------------------------------------------- 1 | div.metric-opt { 2 | display: flex; 3 | flex-direction: column; 4 | margin: 0.75rem 0 0.75rem 0; 5 | 6 | span.opt-title { 7 | font-weight: bold; 8 | font-size: 0.75rem; 9 | padding-bottom: 0.25rem; 10 | } 11 | 12 | div.add-percentile { 13 | display: flex; 14 | 15 | div.input { 16 | flex: 9; 17 | 18 | input { 19 | height: 1.75rem; 20 | font-size: 1rem; 21 | border-radius: .25rem; 22 | border: 1px solid #d4d4d4; 23 | color: #0070d2; 24 | padding-left: 0.5rem; 25 | cursor: pointer; 26 | box-sizing: border-box; 27 | width: 100%; 28 | } 29 | } 30 | 31 | div.icon { 32 | flex: 1; 33 | display: flex; 34 | justify-content: center; 35 | align-items: center; 36 | margin-left: 0.25rem; 37 | } 38 | 39 | div.icon:hover { 40 | background-color: #ccdffb; 41 | cursor: pointer; 42 | border-radius: 0.25rem; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/metrics/aggregations/percentilesMetric.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, EventEmitter } from '@angular/core'; 2 | import { FormGroup, FormBuilder} from '@angular/forms'; 3 | 4 | import { maxValidator } from '../../../shared/validators.directive'; 5 | 6 | @Component({ 7 | selector: 'percentiles-metric', 8 | templateUrl: './percentilesMetric.component.html', 9 | styleUrls: ['./percentilesMetric.component.scss'] 10 | }) 11 | 12 | export class PercentilesMetricComponent { 13 | @Output() dataChange = new EventEmitter(); 14 | 15 | private _savedData: any = null; 16 | @Input() set savedData(savedData: any) { 17 | console.log('PERCENTILES - SET savedData:', savedData); 18 | this._savedData = savedData; 19 | if(savedData && savedData.type==='percentiles'){ 20 | this.percentileValues = savedData.params.percents; 21 | } 22 | }; 23 | 24 | form: FormGroup; 25 | formErrors = { 26 | 'percentileBox': '' 27 | }; 28 | validationMessages = { 29 | 'percentileBox': { 30 | 'maxValue': 'Percentile value can\'t be greater tha 100.' 31 | } 32 | }; 33 | 34 | percentileValues: number[] = []; 35 | 36 | constructor( private fb: FormBuilder ) {} 37 | 38 | ngOnInit(): void { 39 | console.log('PERCENTILES - ngOnInit()'); 40 | this.buildForm(); 41 | if(!this._savedData) 42 | this.dataChange.emit(); 43 | } 44 | 45 | dataChangeEvent(): void { 46 | this.dataChange.emit(); 47 | } 48 | 49 | buildForm(): void { 50 | this.form = this.fb.group({ 51 | 'percentileBox': ['', [ maxValidator(100) ] ] 52 | }); 53 | 54 | this.form.valueChanges 55 | .subscribe(data => this.onValueChanged(data)); 56 | 57 | this.onValueChanged(); // (re)set validation messages now 58 | } 59 | 60 | onValueChanged(data?: any) { 61 | if (!this.form) { return; } 62 | const form = this.form; 63 | 64 | for (const field in this.formErrors) { 65 | // clear previous error message (if any) 66 | this.formErrors[field] = ''; 67 | const control = form.get(field); 68 | 69 | if (control && control.dirty && !control.valid) { 70 | const messages = this.validationMessages[field]; 71 | for (const key in control.errors) { 72 | this.formErrors[field] += messages[key] + ' '; 73 | } 74 | } 75 | } 76 | } 77 | 78 | addPercentile(value: string): void{ 79 | console.log(value); 80 | var percentile = parseInt(value); 81 | if(!isNaN(percentile)){ 82 | this.percentileValues = Array.from( 83 | new Set(this.percentileValues).add(percentile) 84 | ).sort((a,b) => a - b); 85 | } 86 | this.dataChangeEvent(); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/metrics/aggregations/topHitMetric.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | Aggregate With: 4 | 13 |
14 | 15 |
16 | 30 | Size: 31 | 37 |
38 | 39 |
40 | Sort On: 41 | 50 |
51 | 52 |
53 | Order: 54 | 63 |
64 |
65 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/metrics/aggregations/topHitMetric.component.scss: -------------------------------------------------------------------------------- 1 | div.metric-opt { 2 | display: flex; 3 | flex-direction: column; 4 | margin: 0.75rem 0 0.75rem 0; 5 | 6 | span { 7 | font-weight: bold; 8 | font-size: 0.75rem; 9 | padding-bottom: 0.25rem; 10 | } 11 | 12 | input { 13 | font-size: 1rem; 14 | border-radius: .25rem; 15 | border: 1px solid #d4d4d4; 16 | color: #0070d2; 17 | padding-left: 0.5rem; 18 | cursor: pointer; 19 | } 20 | 21 | select { 22 | font-size: 1rem; 23 | border-radius: .25rem; 24 | height: 1.75rem; 25 | color: rgb(0, 112, 210); 26 | white-space: normal; 27 | -webkit-user-select: none; 28 | background-color: white; 29 | padding-left: 0.5rem; 30 | cursor: pointer; 31 | border-color: #d4d4d4; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/metrics/aggregations/topHitMetric.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, EventEmitter, SimpleChange } from '@angular/core'; 2 | import {Observable} from 'rxjs/Observable'; 3 | import { FormGroup, FormBuilder} from '@angular/forms'; 4 | 5 | import { maxValidator } from '../../../shared/validators.directive'; 6 | 7 | import * as _ from "lodash"; 8 | 9 | @Component({ 10 | selector: 'topHit-metric', 11 | templateUrl: './topHitMetric.component.html', 12 | styleUrls: ['./topHitMetric.component.scss'] 13 | }) 14 | 15 | export class TopHitMetricComponent { 16 | @Output() dataChange = new EventEmitter(); 17 | 18 | private _selectedFieldData: any = { 19 | field: '', 20 | isTextField: false 21 | }; 22 | @Input() set selectedFieldData(selectedFieldData: any) { 23 | this._selectedFieldData = selectedFieldData; 24 | this._aggsToDisplay = this.topHitAggregations.filter((agg) => this.isValidAgg(agg)); 25 | if(this._aggsToDisplay.length===1) 26 | this.selectedTopHitAgg = this._aggsToDisplay[0]; 27 | }; 28 | 29 | private _fields: string[] = []; 30 | @Input() set fields(fields: string[]) { 31 | console.log('TOP HIT - SET - fields:', fields); 32 | if(!_.isEqual(fields,this._fields)){ 33 | this.selectedSortField = fields[0]; 34 | if(this._lodaded) this.dataChangeEvent(); 35 | } 36 | this._fields = fields; 37 | }; 38 | 39 | private _savedData: any = null; 40 | @Input() set savedData(savedData: any) { 41 | console.log('TOP HIT - SET savedData:', savedData); 42 | this._savedData = savedData; 43 | if(savedData && savedData.type==='top_hits'){ 44 | this.selectedTopHitAgg = savedData.params.aggregate; 45 | this.hitsSize = savedData.params.size; 46 | this.selectedSortField = savedData.params.sortField; 47 | this.selectedOrder = savedData.params.sortOrder; 48 | } 49 | }; 50 | 51 | private _lodaded: boolean = false; 52 | 53 | form: FormGroup; 54 | formErrors = { 55 | 'topHits': '' 56 | }; 57 | validationMessages = { 58 | 'topHits': {} 59 | }; 60 | 61 | topHitAggregations: string[] = [ 62 | 'Concatenate', 63 | 'Average', 64 | 'Max', 65 | 'Min', 66 | 'Sum' 67 | ]; 68 | private _aggsToDisplay: string[] = this.topHitAggregations; 69 | selectedTopHitAgg: string = this._aggsToDisplay[0]; 70 | 71 | hitsSize: number = 1; 72 | 73 | orders: string[] = ['desc', 'asc']; 74 | selectedOrder: string = this.orders[0]; 75 | 76 | selectedSortField: string = (this._fields) ? this._fields[0] : ''; 77 | 78 | 79 | constructor( private fb: FormBuilder ) {} 80 | 81 | ngOnInit(): void{ 82 | console.log('TOP HIT METRIC - ngOnInit()'); 83 | this._lodaded = true; 84 | this.buildForm(); 85 | console.log('_savedData:', this._savedData); 86 | if(!this._savedData) 87 | this.dataChangeEvent(); 88 | } 89 | 90 | dataChangeEvent(): void { 91 | console.log('TOP HIT - EMIT CHANGE'); 92 | this.dataChange.emit(); 93 | } 94 | 95 | buildForm(): void { 96 | this.form = this.fb.group({ 97 | 'topHits': ['', []] 98 | }); 99 | 100 | this.form.valueChanges 101 | .subscribe(data => this.onValueChanged(data)); 102 | 103 | this.onValueChanged(); // (re)set validation messages now 104 | } 105 | 106 | onValueChanged(data?: any) { 107 | if (!this.form) { return; } 108 | const form = this.form; 109 | 110 | for (const field in this.formErrors) { 111 | // clear previous error message (if any) 112 | this.formErrors[field] = ''; 113 | const control = form.get(field); 114 | 115 | if (control && control.dirty && !control.valid) { 116 | const messages = this.validationMessages[field]; 117 | for (const key in control.errors) { 118 | this.formErrors[field] += messages[key] + ' '; 119 | } 120 | } 121 | } 122 | } 123 | 124 | isValidAgg(agg: string){ 125 | if(!this._selectedFieldData.isTextField || agg==='Concatenate') 126 | return true; 127 | 128 | return false; 129 | } 130 | 131 | debug(): void{ 132 | console.log('selectedFieldData:', this._selectedFieldData); 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/metrics/metric.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | Metrics 6 |
7 |
8 | 9 | {{selectedAggregation}} 10 | - {{index}} 11 | - {{selectedField}} 12 |
13 |
14 | 15 |
16 |
17 | 18 |
19 |
20 | Aggregation: 21 | 30 |
31 | 32 |
33 | Field: 34 | 43 |
44 | 45 |
46 | 49 | 50 |
51 | 52 |
53 | 56 | 57 |
58 | 59 |
60 | 65 | 66 |
67 | 68 | 71 |
72 |
73 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/metrics/metric.component.scss: -------------------------------------------------------------------------------- 1 | div.metric { 2 | 3 | div.metric-header { 4 | display: flex; 5 | align-items: center; 6 | 7 | div { 8 | flex: 1; 9 | padding: 1rem; 10 | 11 | span.fa-trash-o { 12 | float: right; 13 | color: red; 14 | } 15 | } 16 | 17 | div.display-info { 18 | flex: 5; 19 | } 20 | 21 | div.display-info:hover { 22 | cursor: pointer; 23 | background-color: #e8e8e8; 24 | } 25 | 26 | div.remove { 27 | flex: 1; 28 | display: flex; 29 | align-items: center; 30 | flex-direction: column; 31 | } 32 | 33 | div.remove:hover { 34 | cursor: pointer; 35 | background-color: #ffe6e6; 36 | } 37 | } 38 | 39 | div.metric-body { 40 | padding: 0rem 1rem 1rem 1rem; 41 | 42 | div.metric-opt { 43 | display: flex; 44 | flex-direction: column; 45 | margin: 0.5rem 0 0.5rem 0; 46 | 47 | span { 48 | font-weight: bold; 49 | font-size: 0.75rem; 50 | padding-bottom: 0.25rem; 51 | } 52 | } 53 | 54 | select { 55 | font-size: 1rem; 56 | border-radius: .25rem; 57 | height: 1.75rem; 58 | color: rgb(0, 112, 210); 59 | white-space: normal; 60 | -webkit-user-select: none; 61 | background-color: white; 62 | padding-left: 0.5rem; 63 | cursor: pointer; 64 | border-color: #d4d4d4; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/metrics/metric.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, Input, Output, OnChanges, SimpleChange, EventEmitter, ViewChild } from '@angular/core'; 2 | 3 | import { MetricsService } from './metrics.service'; 4 | import { PercentilesMetricComponent } from './aggregations/percentilesMetric.component'; 5 | import { PercentileRanksMetricComponent } from './aggregations/percentileRanksMetric.component'; 6 | import { TopHitMetricComponent } from './aggregations/topHitMetric.component'; 7 | 8 | import { AggregationData } from '../../object-classes/aggregationData'; 9 | 10 | import * as _ from "lodash"; 11 | 12 | @Component({ 13 | selector: 'metric', 14 | templateUrl: './metric.component.html', 15 | styleUrls: ['./metric.component.scss'], 16 | providers: [ MetricsService ] 17 | }) 18 | 19 | export class MetricComponent { 20 | @ViewChild(PercentilesMetricComponent) 21 | private percentilesMetricComponent: PercentilesMetricComponent; 22 | @ViewChild(PercentileRanksMetricComponent) 23 | private percentileRanksMetricComponent: PercentileRanksMetricComponent; 24 | @ViewChild(TopHitMetricComponent) 25 | private topHitMetricComponent: TopHitMetricComponent; 26 | 27 | @Output() dataChange = new EventEmitter(); 28 | @Output() remove = new EventEmitter(); 29 | 30 | @Input() index: string; 31 | @Input() widgetMode: boolean = false; 32 | 33 | private _numFields: string[] = []; 34 | @Input() set numFields(numFields: string[]) { 35 | console.log('numFields:', numFields); 36 | this._numFields = numFields; 37 | if(numFields && numFields.length>0) 38 | this.selectedField = numFields[0]; 39 | this.dataChangeEvent(); 40 | }; 41 | get numFields(): string[] { 42 | return this._numFields; 43 | }; 44 | 45 | private _textFields: string[] = []; 46 | @Input() set textFields(textFields: string[]) { 47 | console.log('textFields:', textFields); 48 | this._textFields = textFields; 49 | this.dataChangeEvent(); 50 | }; 51 | get textFields(): string[] { 52 | return this._textFields; 53 | }; 54 | 55 | private _savedData: any = null; 56 | @Input() set savedData(savedData: any) { 57 | console.log('METRIC - SET - savedData'); 58 | this._savedData = savedData; 59 | if(savedData) 60 | this._loadSavedMetric(savedData); 61 | }; 62 | get savedData(): any { 63 | return this._savedData; 64 | }; 65 | 66 | results: number[] = []; 67 | 68 | aggregationsArr: any[] = [ 69 | { label: 'Count', value: 'count'}, 70 | { label: 'Average', value: 'avg'}, 71 | { label: 'Sum', value: 'sum'}, 72 | { label: 'Min', value: 'min'}, 73 | { label: 'Max', value: 'max'}, 74 | { label: 'Median', value: 'median'}, 75 | { label: 'Standard Deviation', value: 'extended_stats'}, 76 | { label: 'Unique Count', value: 'cardinality'}, 77 | { label: 'Percentiles', value: 'percentiles'}, 78 | { label: 'Percentile Ranks', value: 'percentile_ranks'}, 79 | { label: 'Top Hit', value: 'top_hits'} 80 | ]; 81 | selectedAggregation: string = this.aggregationsArr[0].value; 82 | numFieldAgg: string[] = [ 83 | 'avg', 'sum', 'min', 'max', 'median', 'extended_stats', 84 | 'cardinality', 'percentiles', 'percentile_ranks', 'top_hits' 85 | ]; 86 | selectedField: string = ''; 87 | 88 | private _displayed = false; 89 | 90 | constructor( public metricsService: MetricsService ) { } 91 | 92 | ngOnInit(): void { 93 | console.log('METRIC - ngOnInit()'); 94 | } 95 | 96 | triggerRemoveEvent(): void { 97 | this.remove.emit(); 98 | } 99 | 100 | dataChangeEvent(): void { 101 | console.log('METRIC - dataChangeEvent()'); 102 | let aggregationData = this.getAggregationData(); 103 | console.log('aggregationData:', aggregationData); 104 | if(aggregationData.params){ 105 | this.dataChange.emit(aggregationData); 106 | } 107 | } 108 | 109 | getAggregationData(): AggregationData { 110 | var aggregationData = new AggregationData(); 111 | aggregationData.enabled = true; 112 | aggregationData.type = this.selectedAggregation; 113 | aggregationData.schema = 'metric'; 114 | aggregationData.params = this.getAggParams(); 115 | 116 | return aggregationData; 117 | } 118 | 119 | private _loadSavedMetric(agg: AggregationData): void { 120 | console.log('METRIC - load():', agg); 121 | this.selectedAggregation = agg.type; 122 | this.selectedField = (agg.params.field) ? agg.params.field : this._numFields[0]; 123 | } 124 | 125 | isNumFieldAgg(): Boolean{ 126 | //console.log(this.selectedAggregation); 127 | return (this.numFieldAgg.indexOf(this.selectedAggregation)>=0); 128 | } 129 | 130 | getAggParams(): any { 131 | switch (this.selectedAggregation){ 132 | case 'count': 133 | return {}; 134 | case 'avg': 135 | case 'sum': 136 | case 'min': 137 | case 'max': 138 | case 'median': 139 | case 'extended_stats': 140 | case 'cardinality': 141 | return { 142 | field: this.selectedField 143 | }; 144 | case 'percentiles': { 145 | let cmp = this.percentilesMetricComponent; 146 | return { 147 | field: this.selectedField, 148 | percents: (cmp) ? cmp.percentileValues : [] 149 | }; 150 | }case 'percentile_ranks': { 151 | let cmp = this.percentileRanksMetricComponent; 152 | return { 153 | field: this.selectedField, 154 | values: (cmp) ? cmp.percentileRankValues : [] 155 | }; 156 | }case 'top_hits': { 157 | let cmp = this.topHitMetricComponent; 158 | return { 159 | field: this.selectedField, 160 | aggregate: (cmp) ? cmp.selectedTopHitAgg: 'Concatenate', 161 | size: (cmp) ? cmp.hitsSize: '1', 162 | sortField: (cmp) ? cmp.selectedSortField: this._numFields[0], 163 | sortOrder: (cmp) ? cmp.selectedOrder: 'desc' 164 | }; 165 | }default: 166 | return null; 167 | } 168 | } 169 | 170 | getTopHitsFieldData(): any { 171 | return { 172 | field: this.selectedField, 173 | isTextField: (this._textFields.indexOf(this.selectedField)>-1) 174 | } 175 | } 176 | 177 | getFields(): any { 178 | if(this.selectedAggregation==='top_hits') 179 | return this.getAllFields(); 180 | 181 | return this._numFields; 182 | } 183 | 184 | getAllFields(): any { 185 | return _.concat(this._numFields, this._textFields); 186 | } 187 | 188 | debug(): void { 189 | console.log('METRIC - selectedField:', this.selectedField); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/metrics/metrics.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 9 |
10 | {{_formatResult(metricResult.result)}} 11 | {{metricResult.label}} 12 |
13 |
14 | 15 |
16 |
17 | {{_formatResult(metricResult.result)}} 18 | {{metricResult.label}} 19 |
20 |
21 | 22 | 24 | 25 | 33 | 34 |
35 |
36 | 37 | 38 | 39 | 44 | 47 |
48 | 49 |
54 |
55 |
56 | Metrics 57 |
58 | 59 |
60 |
61 | 62 |
63 |
64 | 65 |
66 |
67 | 68 |
69 |
70 |
71 | 72 | 74 | 75 |
76 |
77 |
78 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/metrics/metrics.component.scss: -------------------------------------------------------------------------------- 1 | div.metrics-vis.widget { 2 | height: 100%; 3 | } 4 | 5 | div.metrics-vis{ 6 | display: flex; 7 | border-bottom: 1px solid #d8d8d8; 8 | //height: 100%; 9 | 10 | div.scrollable { 11 | overflow-y: scroll; 12 | } 13 | 14 | div#metricsConfig { 15 | flex: 1; 16 | min-width: 115px; 17 | 18 | div#configFunctions { 19 | display: flex; 20 | align-items: center; 21 | background-color: #dddddd; 22 | 23 | span.function-icon { 24 | cursor: pointer; 25 | background-color: #dddddd; 26 | color: #3c75ca; 27 | padding: 0.75rem; 28 | } 29 | 30 | span.function-icon:hover { 31 | background-color: #b3b3b3; 32 | } 33 | 34 | input { 35 | height: 1.25rem; 36 | width: 100%; 37 | //border-radius: 0.25rem; 38 | font-size: 1rem; 39 | border: 0px; 40 | padding-left: 0.5rem; 41 | padding-right: 0.5rem; 42 | margin-left: 0.5rem; 43 | margin-right: 0.5rem; 44 | border-radius: 0.25rem; 45 | } 46 | } 47 | 48 | div#metricsContainer { 49 | 50 | div.metrics-header { 51 | display: flex; 52 | 53 | div.title { 54 | flex: 5; 55 | font-family: "Trebuchet MS", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Tahoma, sans-serif; 56 | font-size: 1.2rem; 57 | padding: 1rem; 58 | } 59 | 60 | div.metrics-actions { 61 | display: flex; 62 | flex-direction: row-reverse; 63 | flex: 1; 64 | 65 | div { 66 | padding: 1rem; 67 | border-radius: 0.25rem; 68 | cursor: pointer; 69 | } 70 | 71 | div.add-metric:hover { 72 | background-color: #ccdffb; 73 | } 74 | 75 | div.remove-all { 76 | color: red; 77 | } 78 | 79 | div.remove-all:hover { 80 | background-color: #ffe6e6; 81 | } 82 | } 83 | } 84 | } 85 | } 86 | 87 | div.results-container { 88 | flex: 3; 89 | overflow-y: scroll; 90 | display: flex; 91 | flex-wrap: wrap; 92 | 93 | div.result-container { 94 | flex: 1; 95 | display: flex; 96 | flex-direction: column; 97 | align-items: center; 98 | justify-content: center; 99 | padding: 2rem; 100 | white-space: nowrap; 101 | 102 | span.result { 103 | font-size: 5rem; 104 | font-weight: bold; 105 | } 106 | 107 | span.result-label {} 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/pie-chart/pie-chart.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | 3 | import { Elasticsearch } from '../../elasticsearch'; 4 | import { MetricsService } from '../metrics/metrics.service'; 5 | 6 | import { AggregationData } from '../../object-classes/aggregationData'; 7 | import { VisualizationObj } from '../../object-classes/visualizationObj'; 8 | 9 | import { VisualizationTools } from "../../shared/visualization-tools"; 10 | 11 | import * as _ from "lodash"; 12 | declare let bodybuilder: any; 13 | 14 | @Injectable() 15 | export class PieChartService { 16 | 17 | constructor( 18 | private _elasticCli: Elasticsearch, 19 | private _metricsService: MetricsService 20 | ) { } 21 | 22 | savePieChart(visualizationObj: VisualizationObj): void { 23 | this._elasticCli.saveVisualization(visualizationObj); 24 | } 25 | 26 | getResults(index: string, metric: AggregationData, buckets: AggregationData[]): PromiseLike { 27 | let body = this._getRequestBody(buckets, metric); 28 | return this._elasticCli.request(index, body).then(response => { 29 | console.log('PIE CHART SERVICE - response:', response); 30 | return this._getFormattedResults(response, this._getAggByIdMap(_.concat([metric], buckets)), metric) 31 | }); 32 | } 33 | 34 | private _getFormattedResults(response: any, aggsById: Map, metric: AggregationData): any { 35 | let aggregations = response.aggregations; 36 | if(!aggregations) return null; 37 | let results = []; 38 | for(let bucketId in aggregations){ 39 | results = this._getBucketsResults( 40 | bucketId, 41 | aggregations[bucketId], 42 | metric, 43 | aggsById, 44 | 'root' 45 | ); 46 | } 47 | console.log('PIE CHART SERVICE - results:', results); 48 | let resultsMap = this._getResultsMap(results); 49 | console.log('PIE CHART SERVICE - resultsMap:', resultsMap); 50 | return { 51 | rMap: resultsMap, 52 | rArray: results 53 | }; 54 | } 55 | 56 | private _getResultsMap(results: any[]): Map { 57 | let resultsMap = new Map(); 58 | for(let i=0; i, 72 | parentResultId: string 73 | ): any{ 74 | let currentBucket = aggsById.get(bucketId); 75 | let results = []; 76 | for(let i in bucket.buckets){ 77 | let bucketObj = bucket.buckets[i]; 78 | let metricResult = this._getMetricResult(bucketObj, metric); 79 | metricResult = (metricResult.length>0) ? metricResult[0] : null; 80 | console.log('PIE CHART SERVICE - metricResult:', metricResult); 81 | let bucketValue = this._getBucketValue(currentBucket.type, bucketObj); 82 | console.log('PIE CHART SERVICE - bucketValue:', bucketValue); 83 | let id = VisualizationTools.guidGenerator(); 84 | results.push({ 85 | id: id, 86 | bucket: aggsById.get(bucketId), 87 | metricResult: metricResult, 88 | bucketValue: bucketValue, 89 | percent: null, 90 | parentResultId: parentResultId 91 | }); 92 | let nestedResults = this._getNestedBucketResults( 93 | bucketId, 94 | bucketObj, 95 | metric, 96 | aggsById, 97 | id 98 | ); 99 | results = (nestedResults) ? _.concat(results, nestedResults) : results; 100 | } 101 | return results; 102 | } 103 | 104 | private _getNestedBucketResults( 105 | bucketId: string, 106 | bucketObj: any, 107 | metric: AggregationData, 108 | aggsById: Map, 109 | parentResultId: string 110 | ): any{ 111 | for(let key in bucketObj){ 112 | if(this._isBucket(key)) 113 | return this._getBucketsResults(key, bucketObj[key], metric, aggsById, parentResultId); 114 | } 115 | return null; 116 | } 117 | 118 | private _getMetricResult(bucket: any, metric: AggregationData): any { 119 | return this._metricsService.getAggResult(bucket, metric); 120 | } 121 | 122 | private _isBucket(aggId: string): boolean { 123 | return aggId.split('_')[0]==='bucket'; 124 | } 125 | 126 | private _isMetric(aggId: string): boolean { 127 | return aggId.split('_')[0]==='metric'; 128 | } 129 | 130 | private _getBucketValue(bucketType: string, bucket: any){ 131 | switch(bucketType){ 132 | case 'range': 133 | return bucket.from + ' to ' + bucket.to; 134 | case 'histogram': 135 | return bucket.key; 136 | default: 137 | return null; 138 | } 139 | } 140 | 141 | private _getAggByIdMap(aggs: AggregationData[]): Map { 142 | let aggByIdMap = new Map(); 143 | for(let i=0; i=0; i--){ 154 | console.log('PIE CHART SERVICE - bucket:', buckets[i]); 155 | console.log('PIE CHART SERVICE - aggType:', this._elasticCli.getAggType(buckets[i])); 156 | console.log('PIE CHART SERVICE - aggParams:', this._elasticCli.getAggParams(buckets[i])); 157 | let nestedBody = body.aggregation( 158 | this._elasticCli.getAggType(metric), 159 | null, 160 | metric.id, 161 | this._elasticCli.getAggParams(metric) 162 | ); 163 | console.log('PIE CHART SERVICE - nestedBody:', body.build()); 164 | body = bodybuilder().aggregation( 165 | this._elasticCli.getAggType(buckets[i]), 166 | null, 167 | buckets[i].id, 168 | this._elasticCli.getAggParams(buckets[i]), 169 | (a) => nestedBody 170 | ); 171 | } 172 | console.log('PIE CHART SERVICE - body:', body.build()); 173 | return body; 174 | } 175 | 176 | private _getSortedAggs(buckets: AggregationData[], metrics: AggregationData[]): AggregationData[] { 177 | return null; 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/pie-chart/pieChart.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 |
6 | 7 |
8 | 9 |
10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 25 |
26 | 27 |
28 | 29 | 32 | 33 | 34 | 35 | 37 | 38 |
39 |
40 | 41 | 42 |
43 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/pie-chart/pieChart.component.scss: -------------------------------------------------------------------------------- 1 | div.vis-container { 2 | display: flex; 3 | height: 100%; 4 | 5 | div#chart-config { 6 | flex: 1; 7 | min-width: 115px; 8 | /*background-color: #f1f6ff; 9 | box-shadow: -5px 0px 10px 0px rgba(0, 0, 0, 0.14);*/ 10 | 11 | div#metricsAndBuckets { 12 | overflow-y: scroll; 13 | } 14 | 15 | div#configFunctions { 16 | display: flex; 17 | align-items: center; 18 | background-color: #dddddd; 19 | 20 | span.function-icon { 21 | cursor: pointer; 22 | background-color: #dddddd; 23 | color: #3c75ca; 24 | //border-radius: 0.25rem; 25 | padding: 0.75rem; 26 | } 27 | 28 | span.function-icon:hover { 29 | background-color: #b3b3b3; 30 | } 31 | 32 | /*div#calculateButton { 33 | flex: 1; 34 | //padding: 0.25rem; 35 | }*/ 36 | 37 | input { 38 | height: 1.25rem; 39 | width: 100%; 40 | //border-radius: 0.25rem; 41 | font-size: 1rem; 42 | border: 0px; 43 | padding-left: 0.5rem; 44 | padding-right: 0.5rem; 45 | margin-left: 0.5rem; 46 | margin-right: 0.5rem; 47 | border-radius: 0.25rem; 48 | } 49 | 50 | /*div#saveForm { 51 | flex: 5; 52 | //padding: 0.25rem; 53 | 54 | input { 55 | height: 1.25rem; 56 | //border-radius: 0.25rem; 57 | font-size: 1rem; 58 | border: 0px; 59 | } 60 | }*/ 61 | } 62 | } 63 | 64 | div#chart-container { 65 | flex: 3; 66 | position: relative; 67 | min-width: 0px; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/visualizations.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | Saved Visualizations 5 |
6 |
7 |
    8 |
  • 9 |
    10 |
    11 | {{visualization._source.title}} 12 |
    13 |
    14 | 15 |
    16 |
    17 |
  • 18 |
19 |
20 |
21 | 22 |
23 |
24 | Visualization Options - {{_selectedIndex}} - {{_selectedVisualization}} 25 |
26 | 27 |
28 |
29 | Index: 30 | 40 |
41 | 42 |
43 | Visualization: 44 | 53 |
54 |
55 | 56 | 60 | 61 |
62 |
63 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/visualizations.component.scss: -------------------------------------------------------------------------------- 1 | div.main { 2 | background-color: white; 3 | height: 100%; 4 | 5 | div.saved-vis { 6 | ul { 7 | margin: 0px; 8 | padding: 0px; 9 | list-style-type: none; 10 | 11 | li { 12 | //padding: 0.25rem; 13 | //padding-right: 1rem; 14 | //padding-left: 1rem; 15 | border-bottom: 1px solid #efefef; 16 | color: #676767; 17 | } 18 | 19 | li:hover { 20 | background-color: #ecf5ff; 21 | cursor: pointer; 22 | } 23 | } 24 | } 25 | 26 | div#visOptions { 27 | display: flex; 28 | padding: 1rem; 29 | 30 | div#indexOpt { 31 | flex: 1; 32 | display: flex; 33 | align-items: center; 34 | 35 | span { 36 | padding-right: 0.5rem; 37 | } 38 | } 39 | 40 | div#visOpt { 41 | flex: 1; 42 | display: flex; 43 | align-items: center; 44 | 45 | span { 46 | padding-right: 0.5rem; 47 | } 48 | } 49 | 50 | select { 51 | font-size: 1rem; 52 | border-radius: .25rem; 53 | height: 1.75rem; 54 | color: rgb(0, 112, 210); 55 | white-space: normal; 56 | -webkit-user-select: none; 57 | background-color: white; 58 | padding-left: 0.5rem; 59 | cursor: pointer; 60 | border-color: #d4d4d4; 61 | } 62 | } 63 | 64 | div.section-title { 65 | font-family: "Trebuchet MS", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Tahoma, sans-serif; 66 | font-size: 1.2rem; 67 | padding: 1rem; 68 | background-color: #ddd; 69 | border-bottom: 1px solid #fbfbfb; 70 | cursor: pointer; 71 | } 72 | 73 | div.section-title:hover { 74 | background-color: #b7b7b7; 75 | } 76 | 77 | div.section-content { 78 | background-color: #fbfbfb; 79 | 80 | .option-container { 81 | display: flex; 82 | //justify-content: space-between; 83 | //align-items: center; 84 | 85 | .title-container { 86 | flex: 20; 87 | padding: 0.5rem; 88 | padding-left: 1rem; 89 | } 90 | 91 | .delete-icon-container { 92 | padding: 0.5rem; 93 | cursor: pointer; 94 | color: red; 95 | border-radius: 0.25rem; 96 | flex: 1; 97 | display: flex; 98 | justify-content: center; 99 | } 100 | 101 | .delete-icon-container:hover { 102 | background-color: #ffd3d3; 103 | } 104 | } 105 | } 106 | 107 | div.panel { 108 | display: block; 109 | } 110 | 111 | div.panel:host { 112 | display: block; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /dashboard-interface/src/app/visualizations/visualizations.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Subject } from 'rxjs/Subject'; 3 | 4 | import { AggregationData } from '../object-classes/aggregationData'; 5 | 6 | 7 | /* 8 | The reason why numFieldsSent and textFieldsSent observables are necessary 9 | is becasuse the angular 2 livecycle hook order is problematic with fields 10 | update syncrhonization on visualizations children. 11 | */ 12 | 13 | @Injectable() 14 | export class VisualizationsService { 15 | 16 | // Observable sources 17 | private numFieldsSource = new Subject(); 18 | private textFieldsSource = new Subject(); 19 | 20 | // Observable streams 21 | numFieldsSent$ = this.numFieldsSource.asObservable(); 22 | textFieldsSent$ = this.textFieldsSource.asObservable(); 23 | 24 | // Service fields commands 25 | sendNumFields(numFields: string[]) { 26 | console.log('VISUALIZATIONS SERVICE - SEND - sendNumFields:', numFields); 27 | this.numFieldsSource.next(numFields); 28 | } 29 | 30 | sendTextFields(textFields: string[]) { 31 | console.log('VISUALIZATIONS SERVICE - SEND - sendTextFiedls:', textFields); 32 | this.textFieldsSource.next(textFields); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /dashboard-interface/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/dashboard-interface/src/assets/.gitkeep -------------------------------------------------------------------------------- /dashboard-interface/src/assets/img/sakura.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/dashboard-interface/src/assets/img/sakura.png -------------------------------------------------------------------------------- /dashboard-interface/src/assets/img/sakura.svg: -------------------------------------------------------------------------------- 1 | 2 | sakura blossom - alice 3 | 4 | Layer 1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /dashboard-interface/src/assets/img/sakura2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/dashboard-interface/src/assets/img/sakura2.png -------------------------------------------------------------------------------- /dashboard-interface/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /dashboard-interface/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false 8 | }; 9 | -------------------------------------------------------------------------------- /dashboard-interface/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/dashboard-interface/src/favicon.ico -------------------------------------------------------------------------------- /dashboard-interface/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DashboardInterface 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Loading... 16 | 17 | 18 | -------------------------------------------------------------------------------- /dashboard-interface/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule); 12 | -------------------------------------------------------------------------------- /dashboard-interface/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/set'; 35 | 36 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 37 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 38 | 39 | /** IE10 and IE11 requires the following to support `@angular/animation`. */ 40 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 41 | 42 | 43 | /** Evergreen browsers require these. **/ 44 | import 'core-js/es6/reflect'; 45 | import 'core-js/es7/reflect'; 46 | 47 | 48 | /** ALL Firefox browsers require the following to support `@angular/animation`. **/ 49 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 50 | 51 | 52 | 53 | /*************************************************************************************************** 54 | * Zone JS is required by Angular itself. 55 | */ 56 | import 'zone.js/dist/zone'; // Included with Angular CLI. 57 | 58 | 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | 64 | /** 65 | * Date, currency, decimal and percent pipes. 66 | * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 67 | */ 68 | // import 'intl'; // Run `npm install --save intl`. 69 | -------------------------------------------------------------------------------- /dashboard-interface/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import "~@angular/material/prebuilt-themes/indigo-pink.css"; 3 | 4 | html, 5 | body { 6 | height: 100%; 7 | padding: 0; 8 | margin: 0; 9 | display: flex; /*enables flex content for its children*/ 10 | box-sizing: border-box; 11 | flex-direction: column; 12 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 13 | } 14 | 15 | app-root { 16 | height: 100%; 17 | overflow: hidden; 18 | } 19 | -------------------------------------------------------------------------------- /dashboard-interface/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | import { getTestBed } from '@angular/core/testing'; 10 | import { 11 | BrowserDynamicTestingModule, 12 | platformBrowserDynamicTesting 13 | } from '@angular/platform-browser-dynamic/testing'; 14 | 15 | // Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. 16 | declare var __karma__: any; 17 | declare var require: any; 18 | 19 | // Prevent Karma from running prematurely. 20 | __karma__.loaded = function () {}; 21 | 22 | // First, initialize the Angular testing environment. 23 | getTestBed().initTestEnvironment( 24 | BrowserDynamicTestingModule, 25 | platformBrowserDynamicTesting() 26 | ); 27 | // Then we find all the tests. 28 | const context = require.context('./', true, /\.spec\.ts$/); 29 | // And load the modules. 30 | context.keys().map(context); 31 | // Finally, start Karma to run the tests. 32 | __karma__.start(); 33 | -------------------------------------------------------------------------------- /dashboard-interface/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "es2015", 6 | "baseUrl": "", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /dashboard-interface/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "baseUrl": "", 8 | "types": [ 9 | "jasmine", 10 | "node" 11 | ] 12 | }, 13 | "files": [ 14 | "test.ts" 15 | ], 16 | "include": [ 17 | "**/*.spec.ts", 18 | "**/*.d.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /dashboard-interface/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /dashboard-interface/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "baseUrl": "src", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "moduleResolution": "node", 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "allowSyntheticDefaultImports": true, 12 | "target": "es5", 13 | "typeRoots": [ 14 | "node_modules/@types" 15 | ], 16 | "lib": [ 17 | "es2016", 18 | "dom" 19 | ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /dashboard-interface/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "callable-types": true, 7 | "class-name": true, 8 | "comment-format": [ 9 | true, 10 | "check-space" 11 | ], 12 | "curly": true, 13 | "eofline": true, 14 | "forin": true, 15 | "import-blacklist": [true, "rxjs"], 16 | "import-spacing": true, 17 | "indent": [ 18 | true, 19 | "spaces" 20 | ], 21 | "interface-over-type-literal": true, 22 | "label-position": true, 23 | "max-line-length": [ 24 | true, 25 | 140 26 | ], 27 | "member-access": false, 28 | "member-ordering": [ 29 | true, 30 | "static-before-instance", 31 | "variables-before-functions" 32 | ], 33 | "no-arg": true, 34 | "no-bitwise": true, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-construct": true, 44 | "no-debugger": true, 45 | "no-duplicate-variable": true, 46 | "no-empty": false, 47 | "no-empty-interface": true, 48 | "no-eval": true, 49 | "no-inferrable-types": [true, "ignore-params"], 50 | "no-shadowed-variable": true, 51 | "no-string-literal": false, 52 | "no-string-throw": true, 53 | "no-switch-case-fall-through": true, 54 | "no-trailing-whitespace": true, 55 | "no-unused-expression": true, 56 | "no-use-before-declare": true, 57 | "no-var-keyword": true, 58 | "object-literal-sort-keys": false, 59 | "one-line": [ 60 | true, 61 | "check-open-brace", 62 | "check-catch", 63 | "check-else", 64 | "check-whitespace" 65 | ], 66 | "prefer-const": true, 67 | "quotemark": [ 68 | true, 69 | "single" 70 | ], 71 | "radix": true, 72 | "semicolon": [ 73 | "always" 74 | ], 75 | "triple-equals": [ 76 | true, 77 | "allow-null-check" 78 | ], 79 | "typedef-whitespace": [ 80 | true, 81 | { 82 | "call-signature": "nospace", 83 | "index-signature": "nospace", 84 | "parameter": "nospace", 85 | "property-declaration": "nospace", 86 | "variable-declaration": "nospace" 87 | } 88 | ], 89 | "typeof-compare": true, 90 | "unified-signatures": true, 91 | "variable-name": false, 92 | "whitespace": [ 93 | true, 94 | "check-branch", 95 | "check-decl", 96 | "check-operator", 97 | "check-separator", 98 | "check-type" 99 | ], 100 | 101 | "directive-selector": [true, "attribute", "app", "camelCase"], 102 | "component-selector": [true, "element", "app", "kebab-case"], 103 | "use-input-property-decorator": true, 104 | "use-output-property-decorator": true, 105 | "use-host-property-decorator": true, 106 | "no-input-rename": true, 107 | "no-output-rename": true, 108 | "use-life-cycle-interface": true, 109 | "use-pipe-transform-interface": true, 110 | "component-class-suffix": true, 111 | "directive-class-suffix": true, 112 | "no-access-missing-member": true, 113 | "templates-use-public": true, 114 | "invoke-injectable": true 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /plantilla-memoria/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/.DS_Store -------------------------------------------------------------------------------- /plantilla-memoria/Makefile: -------------------------------------------------------------------------------- 1 | all: memoria.pdf 2 | 3 | memoria.pdf: memoria.tex 4 | pdflatex memoria.tex; bibtex memoria; pdflatex memoria.tex; pdflatex memoria.tex 5 | 6 | clean: 7 | rm -f memoria.dvi memoria.ps *.snm *.out *.nav *.log *.aux *.toc *.vrb *.pdf *~ *.lof *.blg *.bbl 8 | -------------------------------------------------------------------------------- /plantilla-memoria/README.md: -------------------------------------------------------------------------------- 1 | # plantilla-memoria 2 | Plantilla en LaTeX para la elaboración de una memoria de Trabajo Fin de Grado/Máster (TFG/TFM) 3 | 4 | ## Instrucciones de inicio 5 | 6 | Para hacer uso de esta plantilla, lo mejor es que te crees un fork de este repositorio, de manera que obtengas una copia del repositorio propia, con permisos para poder subir tus modificaciones a GitHub. 7 | 8 | Una buena práctica es, una vez realizado el fork, cambiar el nombre de tu repositorio a MemoriaTFG-TuNombreYApellidos, de mamera que sea fácilmente identificable para el resto. 9 | 10 | ## Comentarios y realimentación 11 | 12 | Esta plantilla no está terminada, por lo que se puede considerar trabajo en curso. Si tienes algun comentario o sugerencia, estaré encantado de recibirlo. Agradezco especialmente pull-requests. 13 | 14 | Para otras cuestiones, mándame un correo a grex (arroba) gsyc.urjc.es. 15 | -------------------------------------------------------------------------------- /plantilla-memoria/img/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/.DS_Store -------------------------------------------------------------------------------- /plantilla-memoria/img/Scrum_Framework.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/Scrum_Framework.png -------------------------------------------------------------------------------- /plantilla-memoria/img/angular-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/angular-architecture.png -------------------------------------------------------------------------------- /plantilla-memoria/img/arquitectura.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/arquitectura.png -------------------------------------------------------------------------------- /plantilla-memoria/img/bar-chart-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/bar-chart-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/bar-chart-lengend-filter-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/bar-chart-lengend-filter-example.png -------------------------------------------------------------------------------- /plantilla-memoria/img/center-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/center-example.png -------------------------------------------------------------------------------- /plantilla-memoria/img/chartjs-result-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/chartjs-result-example.png -------------------------------------------------------------------------------- /plantilla-memoria/img/dashboard-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/dashboard-example.png -------------------------------------------------------------------------------- /plantilla-memoria/img/dashboards-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/dashboards-diagram.png -------------------------------------------------------------------------------- /plantilla-memoria/img/dashboards-initial-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/dashboards-initial-view.png -------------------------------------------------------------------------------- /plantilla-memoria/img/data-table-histogram-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/data-table-histogram-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/data-table-initial-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/data-table-initial-view.png -------------------------------------------------------------------------------- /plantilla-memoria/img/data-table-multiaggregation-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/data-table-multiaggregation-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/data-table-range-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/data-table-range-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/final-satge-sprint1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/final-satge-sprint1.png -------------------------------------------------------------------------------- /plantilla-memoria/img/final-user-interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/final-user-interface.png -------------------------------------------------------------------------------- /plantilla-memoria/img/flex-end-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/flex-end-example.png -------------------------------------------------------------------------------- /plantilla-memoria/img/foro1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/foro1.png -------------------------------------------------------------------------------- /plantilla-memoria/img/jquery-result-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/jquery-result-example.png -------------------------------------------------------------------------------- /plantilla-memoria/img/kibana-dashboard-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/kibana-dashboard-example.png -------------------------------------------------------------------------------- /plantilla-memoria/img/kibana-nav-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/kibana-nav-bar.png -------------------------------------------------------------------------------- /plantilla-memoria/img/kibana-visualization-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/kibana-visualization-example.png -------------------------------------------------------------------------------- /plantilla-memoria/img/logo_vect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/logo_vect.png -------------------------------------------------------------------------------- /plantilla-memoria/img/metric-average-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/metric-average-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/metric-count-calcuation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/metric-count-calcuation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/metric-max-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/metric-max-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/metric-median-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/metric-median-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/metric-min-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/metric-min-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/metric-percentiles-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/metric-percentiles-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/metric-ranks-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/metric-ranks-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/metric-std-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/metric-std-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/metric-sum-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/metric-sum-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/metric-top-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/metric-top-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/metric-unique-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/metric-unique-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/metric-visualization-initial-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/metric-visualization-initial-view.png -------------------------------------------------------------------------------- /plantilla-memoria/img/no-wrap-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/no-wrap-example.png -------------------------------------------------------------------------------- /plantilla-memoria/img/parent-child-via-service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/parent-child-via-service.png -------------------------------------------------------------------------------- /plantilla-memoria/img/pie-chart-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/pie-chart-calculation.png -------------------------------------------------------------------------------- /plantilla-memoria/img/pie-chart-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/pie-chart-example.png -------------------------------------------------------------------------------- /plantilla-memoria/img/saved-visualizations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/saved-visualizations.png -------------------------------------------------------------------------------- /plantilla-memoria/img/sprint1_architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/sprint1_architecture.png -------------------------------------------------------------------------------- /plantilla-memoria/img/sprint2_architecture_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/sprint2_architecture_1.png -------------------------------------------------------------------------------- /plantilla-memoria/img/sprint2_architecture_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/sprint2_architecture_2.png -------------------------------------------------------------------------------- /plantilla-memoria/img/user_interface_mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/user_interface_mockup.png -------------------------------------------------------------------------------- /plantilla-memoria/img/visualization-options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/visualization-options.png -------------------------------------------------------------------------------- /plantilla-memoria/img/visualizations-section.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/visualizations-section.png -------------------------------------------------------------------------------- /plantilla-memoria/img/wrap-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/img/wrap-example.png -------------------------------------------------------------------------------- /plantilla-memoria/memoria.bib: -------------------------------------------------------------------------------- 1 | % Bibliografía 2 | 3 | % Este fichero contiene la bibliografía de la memoria 4 | % Está en un formato que se conoce como BibTex. 5 | % Hay muchos sitios web que exportan referencias en formato BibTeX. 6 | % Prueba a buscar en http://scholar.google.com por referencias 7 | % y verás que lo puedes hacer de manera sencilla. 8 | % Más información: 9 | % http://texblog.org/2014/04/22/using-google-scholar-to-download-bibtex-citations/ 10 | 11 | % Existen varias posibilidades para referenciar diferentes tipos de documentos, 12 | % ya que los datos necesarios varían. Así, existen formatos de entrada para 13 | % * libros (book) 14 | % * artículos en revistas de investigación (article) 15 | % * capítulos de libro (incollection) 16 | % * informes técnicos (techreport) 17 | % * otros (misc) 18 | % 19 | % Las páginas web deberían ir como misc. 20 | 21 | % Una última cosa: no referencias nunca la WikiPedia 22 | % La WikiPedia, como buena enciclopedia, no es fuente primaria. 23 | % Si acaso, busca por citas en la WikiPedia (que sí ha de referenciar fuentes 24 | % primarias) y haz referencia a las mismas. 25 | 26 | @Book{elasticsearch_book, 27 | author = {Clinton Gormley & Zachary Tong}, 28 | title = {Elasticsearch - The Definitive Guide}, 29 | publisher = {O'Reilly}, 30 | year = 2015 31 | } 32 | 33 | @Misc{elasticsearch_doc, 34 | author = {Elasticsearch}, 35 | title = {Elastic Stack and Product Documentation}, 36 | note = {\\ https://www.elastic.co/guide/index.html} 37 | } 38 | 39 | @Book{kibana_book, 40 | author = {Bahaaldine Azarmi}, 41 | title = {Learning Kibana 5.0}, 42 | publisher = {Packt}, 43 | year = 2017 44 | } 45 | 46 | @Misc{kibana_doc, 47 | author = {Kibana}, 48 | title = {Kibana User Guide}, 49 | note = {\\ https://www.elastic.co/guide/en/kibana/5.5/index.html} 50 | } 51 | 52 | @Book{javascript_book, 53 | author = {David Flanagan}, 54 | title = {Javascript - The Definitive Guide}, 55 | publisher = {O'Reilly}, 56 | year = 2006 57 | } 58 | 59 | @Misc{javascript_doc, 60 | author = {Javascript}, 61 | title = {MDN Web Docs}, 62 | note = {\\ https://developer.mozilla.org/bm/docs/Web/JavaScript} 63 | } 64 | 65 | @Book{typescript_book, 66 | author = {Steve Fenton}, 67 | title = {Pro Typescript}, 68 | publisher = {Apress}, 69 | year = 2017 70 | } 71 | 72 | @Misc{typescript_doc, 73 | author = {Typescript}, 74 | title = {Typescript Documentation}, 75 | note = {\\ https://www.typescriptlang.org/docs/home.html} 76 | } 77 | 78 | @Misc{chartjs_doc, 79 | author = {ChartJS}, 80 | title = {ChartJS Documentation}, 81 | note = {\\ http://www.chartjs.org/docs/latest/} 82 | } 83 | 84 | @Misc{bodybuilder_doc, 85 | author = {Bodybuilder}, 86 | title = {Bodybuilder Documentation}, 87 | note = {\\ http://bodybuilder.js.org/docs/} 88 | } 89 | 90 | @Misc{flexbox_doc, 91 | author = {CSS Reference}, 92 | title = {Flexbox in CSS}, 93 | note = {\\ http://cssreference.io/flexbox/} 94 | } 95 | -------------------------------------------------------------------------------- /plantilla-memoria/memoria.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/memoria.pdf -------------------------------------------------------------------------------- /plantilla-memoria/template.tex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/plantilla-memoria/template.tex -------------------------------------------------------------------------------- /web/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /web/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /web/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /web/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /web/font-awesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/font-awesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /web/font-awesome/less/animated.less: -------------------------------------------------------------------------------- 1 | // Animated 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 | -------------------------------------------------------------------------------- /web/font-awesome/less/bordered-pulled.less: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /web/font-awesome/less/core.less: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /web/font-awesome/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /web/font-awesome/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.6.3 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.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "animated.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | @import "screen-reader.less"; 19 | -------------------------------------------------------------------------------- /web/font-awesome/less/larger.less: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /web/font-awesome/less/list.less: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /web/font-awesome/less/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .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 | .fa-icon-rotate(@degrees, @rotation) { 15 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})"; 16 | -webkit-transform: rotate(@degrees); 17 | -ms-transform: rotate(@degrees); 18 | transform: rotate(@degrees); 19 | } 20 | 21 | .fa-icon-flip(@horiz, @vert, @rotation) { 22 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)"; 23 | -webkit-transform: scale(@horiz, @vert); 24 | -ms-transform: scale(@horiz, @vert); 25 | transform: scale(@horiz, @vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | .sr-only() { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | .sr-only-focusable() { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /web/font-awesome/less/path.less: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /web/font-awesome/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .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 | -------------------------------------------------------------------------------- /web/font-awesome/less/screen-reader.less: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { .sr-only(); } 5 | .sr-only-focusable { .sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /web/font-awesome/less/spinning.less: -------------------------------------------------------------------------------- 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 | @-webkit-keyframes fa-spin { 10 | 0% { 11 | -webkit-transform: rotate(0deg); 12 | transform: rotate(0deg); 13 | } 14 | 100% { 15 | -webkit-transform: rotate(359deg); 16 | transform: rotate(359deg); 17 | } 18 | } 19 | 20 | @keyframes fa-spin { 21 | 0% { 22 | -webkit-transform: rotate(0deg); 23 | transform: rotate(0deg); 24 | } 25 | 100% { 26 | -webkit-transform: rotate(359deg); 27 | transform: rotate(359deg); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /web/font-awesome/less/stacked.less: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /web/font-awesome/scss/_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 | -------------------------------------------------------------------------------- /web/font-awesome/scss/_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 | -------------------------------------------------------------------------------- /web/font-awesome/scss/_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 | -------------------------------------------------------------------------------- /web/font-awesome/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /web/font-awesome/scss/_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 | -------------------------------------------------------------------------------- /web/font-awesome/scss/_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 | -------------------------------------------------------------------------------- /web/font-awesome/scss/_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 | -ms-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 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; 23 | -webkit-transform: scale($horiz, $vert); 24 | -ms-transform: scale($horiz, $vert); 25 | transform: scale($horiz, $vert); 26 | } 27 | 28 | 29 | // Only display content to screen readers. A la Bootstrap 4. 30 | // 31 | // See: http://a11yproject.com/posts/how-to-hide-content/ 32 | 33 | @mixin sr-only { 34 | position: absolute; 35 | width: 1px; 36 | height: 1px; 37 | padding: 0; 38 | margin: -1px; 39 | overflow: hidden; 40 | clip: rect(0,0,0,0); 41 | border: 0; 42 | } 43 | 44 | // Use in conjunction with .sr-only to only display content when it's focused. 45 | // 46 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 47 | // 48 | // Credit: HTML5 Boilerplate 49 | 50 | @mixin sr-only-focusable { 51 | &:active, 52 | &:focus { 53 | position: static; 54 | width: auto; 55 | height: auto; 56 | margin: 0; 57 | overflow: visible; 58 | clip: auto; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /web/font-awesome/scss/_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 | -------------------------------------------------------------------------------- /web/font-awesome/scss/_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 | -------------------------------------------------------------------------------- /web/font-awesome/scss/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { @include sr-only(); } 5 | .sr-only-focusable { @include sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /web/font-awesome/scss/_spinning.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 | @-webkit-keyframes fa-spin { 10 | 0% { 11 | -webkit-transform: rotate(0deg); 12 | transform: rotate(0deg); 13 | } 14 | 100% { 15 | -webkit-transform: rotate(359deg); 16 | transform: rotate(359deg); 17 | } 18 | } 19 | 20 | @keyframes fa-spin { 21 | 0% { 22 | -webkit-transform: rotate(0deg); 23 | transform: rotate(0deg); 24 | } 25 | 100% { 26 | -webkit-transform: rotate(359deg); 27 | transform: rotate(359deg); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /web/font-awesome/scss/_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 | -------------------------------------------------------------------------------- /web/font-awesome/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.6.3 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 | @import "screen-reader"; 19 | -------------------------------------------------------------------------------- /web/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /web/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /web/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /web/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /web/img/bar-chart-lengend-filter-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/img/bar-chart-lengend-filter-example.png -------------------------------------------------------------------------------- /web/img/dashboard-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/img/dashboard-example.png -------------------------------------------------------------------------------- /web/img/data-table-multiaggregation-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/img/data-table-multiaggregation-calculation.png -------------------------------------------------------------------------------- /web/img/fondo_principal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/img/fondo_principal.jpg -------------------------------------------------------------------------------- /web/img/ismael.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/img/ismael.jpeg -------------------------------------------------------------------------------- /web/img/metric-ranks-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/img/metric-ranks-calculation.png -------------------------------------------------------------------------------- /web/img/pie-chart-calculation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/islimane/Angular-ElasticSearch-Dashboard_Interface/ae6fc4a88915bbb3695a5400def2b347c19d281f/web/img/pie-chart-calculation.png -------------------------------------------------------------------------------- /web/js/grayscale.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Start Bootstrap - Grayscale Bootstrap Theme (http://startbootstrap.com) 3 | * Code licensed under the Apache License v2.0. 4 | * For details, see http://www.apache.org/licenses/LICENSE-2.0. 5 | */ 6 | 7 | // jQuery to collapse the navbar on scroll 8 | $(window).scroll(function() { 9 | if ($(".navbar").offset().top > 50) { 10 | $(".navbar-fixed-top").addClass("top-nav-collapse"); 11 | } else { 12 | $(".navbar-fixed-top").removeClass("top-nav-collapse"); 13 | } 14 | }); 15 | 16 | // jQuery for page scrolling feature - requires jQuery Easing plugin 17 | $(function() { 18 | $('a.page-scroll').bind('click', function(event) { 19 | var $anchor = $(this); 20 | $('html, body').stop().animate({ 21 | scrollTop: $($anchor.attr('href')).offset().top 22 | }, 1500, 'easeInOutExpo'); 23 | event.preventDefault(); 24 | }); 25 | }); 26 | 27 | // Closes the Responsive Menu on Menu Item Click 28 | $('.navbar-collapse ul li a').click(function() { 29 | $('.navbar-toggle:visible').click(); 30 | }); 31 | 32 | // Google Maps Scripts 33 | // When the window has finished loading create our google map below 34 | google.maps.event.addDomListener(window, 'load', init); 35 | 36 | function init() { 37 | // Basic options for a simple Google Map 38 | // For more options see: https://developers.google.com/maps/documentation/javascript/reference#MapOptions 39 | var mapOptions = { 40 | // How zoomed in you want the map to start at (always required) 41 | zoom: 15, 42 | 43 | // The latitude and longitude to center the map (always required) 44 | center: new google.maps.LatLng(40.6700, -73.9400), // New York 45 | 46 | // Disables the default Google Maps UI components 47 | disableDefaultUI: true, 48 | scrollwheel: false, 49 | draggable: false, 50 | 51 | // How you would like to style the map. 52 | // This is where you would paste any style found on Snazzy Maps. 53 | styles: [{ 54 | "featureType": "water", 55 | "elementType": "geometry", 56 | "stylers": [{ 57 | "color": "#000000" 58 | }, { 59 | "lightness": 17 60 | }] 61 | }, { 62 | "featureType": "landscape", 63 | "elementType": "geometry", 64 | "stylers": [{ 65 | "color": "#000000" 66 | }, { 67 | "lightness": 20 68 | }] 69 | }, { 70 | "featureType": "road.highway", 71 | "elementType": "geometry.fill", 72 | "stylers": [{ 73 | "color": "#000000" 74 | }, { 75 | "lightness": 17 76 | }] 77 | }, { 78 | "featureType": "road.highway", 79 | "elementType": "geometry.stroke", 80 | "stylers": [{ 81 | "color": "#000000" 82 | }, { 83 | "lightness": 29 84 | }, { 85 | "weight": 0.2 86 | }] 87 | }, { 88 | "featureType": "road.arterial", 89 | "elementType": "geometry", 90 | "stylers": [{ 91 | "color": "#000000" 92 | }, { 93 | "lightness": 18 94 | }] 95 | }, { 96 | "featureType": "road.local", 97 | "elementType": "geometry", 98 | "stylers": [{ 99 | "color": "#000000" 100 | }, { 101 | "lightness": 16 102 | }] 103 | }, { 104 | "featureType": "poi", 105 | "elementType": "geometry", 106 | "stylers": [{ 107 | "color": "#000000" 108 | }, { 109 | "lightness": 21 110 | }] 111 | }, { 112 | "elementType": "labels.text.stroke", 113 | "stylers": [{ 114 | "visibility": "on" 115 | }, { 116 | "color": "#000000" 117 | }, { 118 | "lightness": 16 119 | }] 120 | }, { 121 | "elementType": "labels.text.fill", 122 | "stylers": [{ 123 | "saturation": 36 124 | }, { 125 | "color": "#000000" 126 | }, { 127 | "lightness": 40 128 | }] 129 | }, { 130 | "elementType": "labels.icon", 131 | "stylers": [{ 132 | "visibility": "off" 133 | }] 134 | }, { 135 | "featureType": "transit", 136 | "elementType": "geometry", 137 | "stylers": [{ 138 | "color": "#000000" 139 | }, { 140 | "lightness": 19 141 | }] 142 | }, { 143 | "featureType": "administrative", 144 | "elementType": "geometry.fill", 145 | "stylers": [{ 146 | "color": "#000000" 147 | }, { 148 | "lightness": 20 149 | }] 150 | }, { 151 | "featureType": "administrative", 152 | "elementType": "geometry.stroke", 153 | "stylers": [{ 154 | "color": "#000000" 155 | }, { 156 | "lightness": 17 157 | }, { 158 | "weight": 1.2 159 | }] 160 | }] 161 | }; 162 | 163 | // Get the HTML DOM element that will contain your map 164 | // We are using a div with id="map" seen below in the 165 | var mapElement = document.getElementById('map'); 166 | 167 | // Create the Google Map using out element and options defined above 168 | var map = new google.maps.Map(mapElement, mapOptions); 169 | 170 | // Custom Map Marker Icon - Customize the map-marker.png file to customize your icon 171 | var image = 'img/map-marker.png'; 172 | var myLatLng = new google.maps.LatLng(40.6700, -73.9400); 173 | var beachMarker = new google.maps.Marker({ 174 | position: myLatLng, 175 | map: map, 176 | icon: image 177 | }); 178 | } 179 | -------------------------------------------------------------------------------- /web/js/jquery.easing.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ 3 | * 4 | * Uses the built in easing capabilities added In jQuery 1.1 5 | * to offer multiple easing options 6 | * 7 | * TERMS OF USE - EASING EQUATIONS 8 | * 9 | * Open source under the BSD License. 10 | * 11 | * Copyright © 2001 Robert Penner 12 | * All rights reserved. 13 | * 14 | * TERMS OF USE - jQuery Easing 15 | * 16 | * Open source under the BSD License. 17 | * 18 | * Copyright © 2008 George McGinley Smith 19 | * All rights reserved. 20 | * 21 | * Redistribution and use in source and binary forms, with or without modification, 22 | * are permitted provided that the following conditions are met: 23 | * 24 | * Redistributions of source code must retain the above copyright notice, this list of 25 | * conditions and the following disclaimer. 26 | * Redistributions in binary form must reproduce the above copyright notice, this list 27 | * of conditions and the following disclaimer in the documentation and/or other materials 28 | * provided with the distribution. 29 | * 30 | * Neither the name of the author nor the names of contributors may be used to endorse 31 | * or promote products derived from this software without specific prior written permission. 32 | * 33 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 34 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 35 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 36 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 37 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 38 | * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 39 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 40 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 41 | * OF THE POSSIBILITY OF SUCH DAMAGE. 42 | * 43 | */ 44 | jQuery.easing.jswing=jQuery.easing.swing;jQuery.extend(jQuery.easing,{def:"easeOutQuad",swing:function(e,f,a,h,g){return jQuery.easing[jQuery.easing.def](e,f,a,h,g)},easeInQuad:function(e,f,a,h,g){return h*(f/=g)*f+a},easeOutQuad:function(e,f,a,h,g){return -h*(f/=g)*(f-2)+a},easeInOutQuad:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f+a}return -h/2*((--f)*(f-2)-1)+a},easeInCubic:function(e,f,a,h,g){return h*(f/=g)*f*f+a},easeOutCubic:function(e,f,a,h,g){return h*((f=f/g-1)*f*f+1)+a},easeInOutCubic:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f+a}return h/2*((f-=2)*f*f+2)+a},easeInQuart:function(e,f,a,h,g){return h*(f/=g)*f*f*f+a},easeOutQuart:function(e,f,a,h,g){return -h*((f=f/g-1)*f*f*f-1)+a},easeInOutQuart:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f+a}return -h/2*((f-=2)*f*f*f-2)+a},easeInQuint:function(e,f,a,h,g){return h*(f/=g)*f*f*f*f+a},easeOutQuint:function(e,f,a,h,g){return h*((f=f/g-1)*f*f*f*f+1)+a},easeInOutQuint:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f*f+a}return h/2*((f-=2)*f*f*f*f+2)+a},easeInSine:function(e,f,a,h,g){return -h*Math.cos(f/g*(Math.PI/2))+h+a},easeOutSine:function(e,f,a,h,g){return h*Math.sin(f/g*(Math.PI/2))+a},easeInOutSine:function(e,f,a,h,g){return -h/2*(Math.cos(Math.PI*f/g)-1)+a},easeInExpo:function(e,f,a,h,g){return(f==0)?a:h*Math.pow(2,10*(f/g-1))+a},easeOutExpo:function(e,f,a,h,g){return(f==g)?a+h:h*(-Math.pow(2,-10*f/g)+1)+a},easeInOutExpo:function(e,f,a,h,g){if(f==0){return a}if(f==g){return a+h}if((f/=g/2)<1){return h/2*Math.pow(2,10*(f-1))+a}return h/2*(-Math.pow(2,-10*--f)+2)+a},easeInCirc:function(e,f,a,h,g){return -h*(Math.sqrt(1-(f/=g)*f)-1)+a},easeOutCirc:function(e,f,a,h,g){return h*Math.sqrt(1-(f=f/g-1)*f)+a},easeInOutCirc:function(e,f,a,h,g){if((f/=g/2)<1){return -h/2*(Math.sqrt(1-f*f)-1)+a}return h/2*(Math.sqrt(1-(f-=2)*f)+1)+a},easeInElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g