mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
Merge branch 'vislib/refactor'
This commit is contained in:
commit
305efe8647
93 changed files with 15727 additions and 4198 deletions
18
.travis.yml
18
.travis.yml
|
@ -1,20 +1,22 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- '0.10'
|
||||
- '0.10'
|
||||
install:
|
||||
- npm install -g grunt-cli bower
|
||||
- npm install
|
||||
- bower install
|
||||
- npm install -g grunt-cli bower
|
||||
- npm install
|
||||
- bower install
|
||||
script:
|
||||
- npm test
|
||||
- npm test
|
||||
notifications:
|
||||
email:
|
||||
- rashid.khan@elasticsearch.com
|
||||
- rashid.khan@elasticsearch.com
|
||||
hipchat:
|
||||
rooms:
|
||||
secure: a2FERvICecrUAR62vP4vrUCTG3haRzf6kSzDDzGu6SICEXWLRrK0xeNQDpdwDAfzFmaIJ6txpkmInvEFeNPYNngTgEDyfhqdIa/lW0Ermdg+1hL0dK6QJiVmT1V6LDB2mgtaTTmfontxJqq7P2tmr0zz8ny4Eqq3lUnwPxYFNNo=
|
||||
template:
|
||||
- '%{repository}/%{branch} #%{build_number} by %{author}: %{message} (<a href="%{build_url}">open</a>)'
|
||||
- ! '%{repository}/%{branch} #%{build_number} by %{author}: %{message} (<a href="%{build_url}">open</a>)'
|
||||
format: html
|
||||
on_success: change
|
||||
on_failure: always
|
||||
env:
|
||||
global:
|
||||
secure: AX9xidE0quyS07ZfOcecxEGjlNDT9YlM+fvtQHqOaODBII2jg5rgz0SyyxmTPSG68aqUNk8ML9slbRE4h0iPqNkB6fbDE2Dc6oTrRE7XFGDBjw66OHV2ZbsobdORf4UtWO06JBgLUEU2pzRYphe/B14dyA+ZO6p+bAgBmcdLd8k=
|
||||
|
|
4
TODOS.md
4
TODOS.md
|
@ -443,6 +443,10 @@
|
|||
- Now that all calls to _data and _removeData have been replaced
|
||||
- **[src/kibana/components/index_patterns/_mapper.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/index_patterns/_mapper.js)**
|
||||
- Change index to be the resolved in some way, last three months, last hour, last year, whatever
|
||||
- **[src/kibana/components/vislib/vis.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/vislib/vis.js)**
|
||||
- need to come up with a solution for resizing when no data is available
|
||||
- **[src/kibana/components/vislib/visualizations/column_chart.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/vislib/visualizations/column_chart.js)**
|
||||
- refactor so that this is called from the data module
|
||||
- **[src/kibana/components/visualize/visualize.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/components/visualize/visualize.js)**
|
||||
- we need to have some way to clean up result requests
|
||||
- **[src/kibana/directives/rows.js](https://github.com/elasticsearch/kibana4/blob/master/src/kibana/directives/rows.js)**
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"bluebird": "~2.0.7",
|
||||
"connect": "~2.19.5",
|
||||
"event-stream": "~3.1.5",
|
||||
"expect.js": "~0.2.0",
|
||||
"expect.js": "~0.3.1",
|
||||
"grunt": "~0.4.5",
|
||||
"grunt-contrib-clean": "~0.5.0",
|
||||
"grunt-contrib-compress": "~0.9.1",
|
||||
|
@ -22,6 +22,7 @@
|
|||
"grunt-mocha": "~0.4.10",
|
||||
"grunt-replace": "^0.7.9",
|
||||
"grunt-run": "^0.2.3",
|
||||
"grunt-saucelabs": "~8.3.2",
|
||||
"http-proxy": "~1.1.4",
|
||||
"husky": "~0.6.0",
|
||||
"istanbul": "~0.2.4",
|
||||
|
@ -29,6 +30,7 @@
|
|||
"lodash": "~2.4.1",
|
||||
"mkdirp": "^0.5.0",
|
||||
"mocha": "~1.20.1",
|
||||
"mocha-screencast-reporter": "~0.1.4",
|
||||
"path-browserify": "0.0.0",
|
||||
"progress": "^1.1.8",
|
||||
"request": "^2.40.0",
|
||||
|
|
219
src/kibana/apps/dashboard/styles/main.css
Normal file
219
src/kibana/apps/dashboard/styles/main.css
Normal file
|
@ -0,0 +1,219 @@
|
|||
.thumbnail > img,
|
||||
.thumbnail a > img,
|
||||
.carousel-inner > .item > img,
|
||||
.carousel-inner > .item > a > img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.btn-group-lg > .btn {
|
||||
padding: 18px 27px;
|
||||
font-size: 17px;
|
||||
line-height: 1.33;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.btn-group-sm > .btn {
|
||||
padding: 6px 9px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.btn-group-xs > .btn {
|
||||
padding: 1px 5px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.container:before,
|
||||
.container:after,
|
||||
.container-fluid:before,
|
||||
.container-fluid:after,
|
||||
.row:before,
|
||||
.row:after,
|
||||
.form-horizontal .form-group:before,
|
||||
.form-horizontal .form-group:after,
|
||||
.btn-toolbar:before,
|
||||
.btn-toolbar:after,
|
||||
.btn-group-vertical > .btn-group:before,
|
||||
.btn-group-vertical > .btn-group:after,
|
||||
.nav:before,
|
||||
.nav:after,
|
||||
.navbar:before,
|
||||
.navbar:after,
|
||||
.navbar-header:before,
|
||||
.navbar-header:after,
|
||||
.navbar-collapse:before,
|
||||
.navbar-collapse:after,
|
||||
.pager:before,
|
||||
.pager:after,
|
||||
.panel-body:before,
|
||||
.panel-body:after,
|
||||
.modal-footer:before,
|
||||
.modal-footer:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.container:after,
|
||||
.container-fluid:after,
|
||||
.row:after,
|
||||
.form-horizontal .form-group:after,
|
||||
.btn-toolbar:after,
|
||||
.btn-group-vertical > .btn-group:after,
|
||||
.nav:after,
|
||||
.navbar:after,
|
||||
.navbar-header:after,
|
||||
.navbar-collapse:after,
|
||||
.pager:after,
|
||||
.panel-body:after,
|
||||
.modal-footer:after {
|
||||
clear: both;
|
||||
}
|
||||
body.application-dashboard {
|
||||
background-color: #b4bcc2;
|
||||
}
|
||||
dashboard-grid {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
}
|
||||
.start-screen {
|
||||
margin: 20px;
|
||||
background-color: #ecf0f1;
|
||||
padding: 20px;
|
||||
}
|
||||
.start-screen i {
|
||||
padding: 4px 8px;
|
||||
background-color: #3d636b;
|
||||
color: white;
|
||||
}
|
||||
.gridster {
|
||||
list-style-type: none;
|
||||
display: block;
|
||||
background-color: #b4bcc2;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.gridster .gs-resize-handle {
|
||||
background-position: 50% 50% !important;
|
||||
bottom: 0 !important;
|
||||
right: 0 !important;
|
||||
padding: 4px;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
}
|
||||
.gridster .preview-holder {
|
||||
border: none!important;
|
||||
background: #b4bcc2 !important;
|
||||
}
|
||||
.gridster i.remove {
|
||||
cursor: pointer;
|
||||
}
|
||||
.gridster dashboard-panel {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
color: #444444;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.gridster dashboard-panel .panel {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-direction: normal;
|
||||
-moz-box-direction: normal;
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-webkit-box-pack: start;
|
||||
-moz-box-pack: start;
|
||||
-ms-flex-pack: start;
|
||||
-webkit-justify-content: flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.gridster dashboard-panel .panel .panel-heading {
|
||||
-webkit-box-flex: 0;
|
||||
-moz-box-flex: 0;
|
||||
-webkit-flex: 0 1 auto;
|
||||
-ms-flex: 0 1 auto;
|
||||
flex: 0 1 auto;
|
||||
min-height: 25px;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
}
|
||||
.gridster dashboard-panel .panel .panel-heading div.btn-group {
|
||||
white-space: nowrap;
|
||||
-webkit-box-flex: 0;
|
||||
-moz-box-flex: 0;
|
||||
-webkit-flex: 0 0 auto;
|
||||
-ms-flex: 0 0 auto;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.gridster dashboard-panel .panel .panel-heading .panel-title {
|
||||
font-size: inherit;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 1 0;
|
||||
-ms-flex: 1 1 0;
|
||||
flex: 1 1 0;
|
||||
}
|
||||
.gridster dashboard-panel .panel .panel-heading a {
|
||||
color: #444444;
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0px 3px;
|
||||
}
|
||||
.gridster dashboard-panel .panel .panel-heading a:hover {
|
||||
color: #298fa3;
|
||||
}
|
||||
.gridster dashboard-panel .panel .load-error {
|
||||
text-align: center;
|
||||
font-size: 1em;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 0 auto;
|
||||
-ms-flex: 1 0 auto;
|
||||
flex: 1 0 auto;
|
||||
-webkit-box-pack: center;
|
||||
-moz-box-pack: center;
|
||||
-ms-flex-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
justify-content: center;
|
||||
-webkit-box-direction: normal;
|
||||
-moz-box-direction: normal;
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
.gridster dashboard-panel .panel .load-error .fa-exclamation-triangle {
|
||||
font-size: 2em;
|
||||
color: #e74c3c;
|
||||
}
|
||||
.gridster dashboard-panel .panel visualize {
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 1 100%;
|
||||
-ms-flex: 1 1 100%;
|
||||
flex: 1 1 100%;
|
||||
height: auto;
|
||||
}
|
||||
.dashboard-load {
|
||||
margin: 10px;
|
||||
}
|
|
@ -605,7 +605,6 @@ define(function (require) {
|
|||
timefilter.time.from = moment(e.point.x);
|
||||
timefilter.time.to = moment(e.point.x + e.data.ordered.interval);
|
||||
timefilter.time.mode = 'absolute';
|
||||
$scope.$apply();
|
||||
},
|
||||
brush: function (e) {
|
||||
var from = moment(e.range[0]);
|
||||
|
@ -616,7 +615,6 @@ define(function (require) {
|
|||
timefilter.time.from = from;
|
||||
timefilter.time.to = to;
|
||||
timefilter.time.mode = 'absolute';
|
||||
$scope.$apply();
|
||||
}
|
||||
},
|
||||
aggs: [
|
||||
|
|
238
src/kibana/apps/discover/styles/main.css
Normal file
238
src/kibana/apps/discover/styles/main.css
Normal file
|
@ -0,0 +1,238 @@
|
|||
.thumbnail > img,
|
||||
.thumbnail a > img,
|
||||
.carousel-inner > .item > img,
|
||||
.carousel-inner > .item > a > img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.btn-group-lg > .btn {
|
||||
padding: 18px 27px;
|
||||
font-size: 17px;
|
||||
line-height: 1.33;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.btn-group-sm > .btn {
|
||||
padding: 6px 9px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.btn-group-xs > .btn {
|
||||
padding: 1px 5px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.container:before,
|
||||
.container:after,
|
||||
.container-fluid:before,
|
||||
.container-fluid:after,
|
||||
.row:before,
|
||||
.row:after,
|
||||
.form-horizontal .form-group:before,
|
||||
.form-horizontal .form-group:after,
|
||||
.btn-toolbar:before,
|
||||
.btn-toolbar:after,
|
||||
.btn-group-vertical > .btn-group:before,
|
||||
.btn-group-vertical > .btn-group:after,
|
||||
.nav:before,
|
||||
.nav:after,
|
||||
.navbar:before,
|
||||
.navbar:after,
|
||||
.navbar-header:before,
|
||||
.navbar-header:after,
|
||||
.navbar-collapse:before,
|
||||
.navbar-collapse:after,
|
||||
.pager:before,
|
||||
.pager:after,
|
||||
.panel-body:before,
|
||||
.panel-body:after,
|
||||
.modal-footer:before,
|
||||
.modal-footer:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.container:after,
|
||||
.container-fluid:after,
|
||||
.row:after,
|
||||
.form-horizontal .form-group:after,
|
||||
.btn-toolbar:after,
|
||||
.btn-group-vertical > .btn-group:after,
|
||||
.nav:after,
|
||||
.navbar:after,
|
||||
.navbar-header:after,
|
||||
.navbar-collapse:after,
|
||||
.pager:after,
|
||||
.panel-body:after,
|
||||
.modal-footer:after {
|
||||
clear: both;
|
||||
}
|
||||
.application-discover {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.discover-timechart {
|
||||
margin-bottom: 20px;
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
.discover-timechart visualize {
|
||||
height: 200px;
|
||||
max-height: 600px;
|
||||
}
|
||||
.discover-timechart visualize.only-spy {
|
||||
height: auto;
|
||||
}
|
||||
.discover-timechart visualize.only-spy .visualize-table {
|
||||
height: auto;
|
||||
}
|
||||
.discover-timechart .chartwrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.discover-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 20;
|
||||
opacity: 0.75;
|
||||
text-align: center;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.discover-overlay > div {
|
||||
text-align: center;
|
||||
}
|
||||
.discover-hits {
|
||||
background-color: #ecf0f1;
|
||||
float: right;
|
||||
padding: 5px 10px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
.discover-table {
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !important;
|
||||
}
|
||||
.discover-table .discover-table-timefield {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.discover-table-footer {
|
||||
background-color: #ecf0f1;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.discover-table-details-toggle {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.discover-table-details-buttons,
|
||||
.discover-table-details-field {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.discover-table-details-value,
|
||||
.discover-table-details pre {
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.discover-field-filter {
|
||||
background-color: #ecf0f1;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.discover-field-filter .form-group {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.discover-field-filter-toggle {
|
||||
color: #ffffff;
|
||||
font-size: 9px;
|
||||
}
|
||||
.sidebar-item button.discover-field-toggle {
|
||||
display: none;
|
||||
font-size: 9px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
margin: 5px 10px;
|
||||
}
|
||||
.sidebar-item:hover:hover button.discover-field-toggle {
|
||||
display: block;
|
||||
}
|
||||
.discover-field-details-close {
|
||||
text-align: center;
|
||||
border-top: 1px solid transparent;
|
||||
background-color: #ecf0f1;
|
||||
}
|
||||
.discover-field-details-close:hover {
|
||||
background-color: #cfd9db;
|
||||
color: #444444;
|
||||
}
|
||||
.discover-field-details {
|
||||
border-top: 1px solid transparent;
|
||||
padding: 5px 10px;
|
||||
background-color: #ffffff;
|
||||
color: #444444;
|
||||
}
|
||||
.discover-field-details .discover-field-details-count {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.discover-field-details a {
|
||||
color: #1f6b7a !important;
|
||||
}
|
||||
.discover-field-details a.btn {
|
||||
color: #444444 !important;
|
||||
}
|
||||
.discover-field-details .discover-field-details-error {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.discover-field-details .discover-field-details-item {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.discover-field-details .discover-field-details-filter {
|
||||
cursor: pointer;
|
||||
}
|
||||
.discover-field-details .progress {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.discover-field-details .progress .progress-bar {
|
||||
padding-left: 10px;
|
||||
text-align: right;
|
||||
line-height: 18px;
|
||||
max-width: 100%;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.discover-field-details .progress .progress-bar span {
|
||||
background-color: #95a5a6;
|
||||
padding: 3px;
|
||||
border-radius: 4px;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
field-name > i,
|
||||
[field-name] > i {
|
||||
margin-right: 5px;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
color: #b4bcc2;
|
||||
}
|
||||
field-name.no-results,
|
||||
[field-name].no-results {
|
||||
color: #b4bcc2;
|
||||
}
|
||||
disc-field-chooser ul.discover-selected-fields {
|
||||
/* If only _source is presend in the selected fields list, hide its toggle button */
|
||||
}
|
||||
disc-field-chooser ul.discover-selected-fields li:first-child:nth-last-child(1)[field=_source] button.discover-field-toggle {
|
||||
display: none;
|
||||
}
|
||||
disc-field-chooser .sidebar-item-title {
|
||||
position: relative;
|
||||
}
|
||||
.results h3 {
|
||||
margin: -20px 0 10px 0;
|
||||
text-align: center;
|
||||
}
|
131
src/kibana/apps/settings/styles/main.css
Normal file
131
src/kibana/apps/settings/styles/main.css
Normal file
|
@ -0,0 +1,131 @@
|
|||
.thumbnail > img,
|
||||
.thumbnail a > img,
|
||||
.carousel-inner > .item > img,
|
||||
.carousel-inner > .item > a > img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.btn-group-lg > .btn {
|
||||
padding: 18px 27px;
|
||||
font-size: 17px;
|
||||
line-height: 1.33;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.btn-group-sm > .btn {
|
||||
padding: 6px 9px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.btn-group-xs > .btn {
|
||||
padding: 1px 5px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.container:before,
|
||||
.container:after,
|
||||
.container-fluid:before,
|
||||
.container-fluid:after,
|
||||
.row:before,
|
||||
.row:after,
|
||||
.form-horizontal .form-group:before,
|
||||
.form-horizontal .form-group:after,
|
||||
.btn-toolbar:before,
|
||||
.btn-toolbar:after,
|
||||
.btn-group-vertical > .btn-group:before,
|
||||
.btn-group-vertical > .btn-group:after,
|
||||
.nav:before,
|
||||
.nav:after,
|
||||
.navbar:before,
|
||||
.navbar:after,
|
||||
.navbar-header:before,
|
||||
.navbar-header:after,
|
||||
.navbar-collapse:before,
|
||||
.navbar-collapse:after,
|
||||
.pager:before,
|
||||
.pager:after,
|
||||
.panel-body:before,
|
||||
.panel-body:after,
|
||||
.modal-footer:before,
|
||||
.modal-footer:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.container:after,
|
||||
.container-fluid:after,
|
||||
.row:after,
|
||||
.form-horizontal .form-group:after,
|
||||
.btn-toolbar:after,
|
||||
.btn-group-vertical > .btn-group:after,
|
||||
.nav:after,
|
||||
.navbar:after,
|
||||
.navbar-header:after,
|
||||
.navbar-collapse:after,
|
||||
.pager:after,
|
||||
.panel-body:after,
|
||||
.modal-footer:after {
|
||||
clear: both;
|
||||
}
|
||||
kibana-settings-app,
|
||||
kbn-settings-indices,
|
||||
kbn-settings-indices-edit,
|
||||
kbn-settings-indices-create,
|
||||
kbn-settings-advanced,
|
||||
kbn-settings-objects,
|
||||
kbn-settings-objects-view {
|
||||
display: block;
|
||||
}
|
||||
.settings-nav {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
li.kbn-settings-tab {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
kbn-settings-objects form {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
kbn-settings-objects .list-unstyled li {
|
||||
border-bottom: 1px solid #ecf0f1;
|
||||
padding: 8px;
|
||||
}
|
||||
kbn-settings-objects .empty {
|
||||
color: #b4bcc2;
|
||||
}
|
||||
kbn-settings-objects .action-bar {
|
||||
margin-top: 10px;
|
||||
background-color: #ecf0f1;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
kbn-settings-objects .action-bar .btn-xs {
|
||||
font-size: 10px;
|
||||
}
|
||||
kbn-settings-objects-view label {
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
}
|
||||
kbn-settings-objects-view .ace_editor {
|
||||
height: 300px;
|
||||
}
|
||||
.advanced-settings table {
|
||||
width: 100%;
|
||||
}
|
||||
.advanced-settings table tr.default td.value {
|
||||
color: #acb6c0;
|
||||
}
|
||||
.advanced-settings table td {
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
}
|
||||
.advanced-settings table td.actions {
|
||||
width: 150px;
|
||||
}
|
||||
.indices-settings i.active {
|
||||
color: #31c471;
|
||||
}
|
||||
.indices-settings tr.field-settings .field-popularize {
|
||||
margin-left: 5px;
|
||||
display: none;
|
||||
}
|
||||
.indices-settings tr.field-settings:hover .field-popularize {
|
||||
display: inline;
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
define(function (require) {
|
||||
require('modules')
|
||||
.get('kibana')
|
||||
.directive('nestingIndicator', function ($rootScope, $parse) {
|
||||
.directive('nestingIndicator', function ($rootScope, $parse, Private) {
|
||||
var _ = require('lodash');
|
||||
var angular = require('angular');
|
||||
var ruleBase = 'border-left-';
|
||||
|
||||
var getColor = (function () {
|
||||
var i = 0;
|
||||
var colorPool = require('components/vislib/utils/colorspace')(100);
|
||||
var colorPool = Private(require('components/vislib/components/_functions/color/color_palette'))(100);
|
||||
var assigned = {};
|
||||
return function (item) {
|
||||
var key = item.$$hashKey;
|
||||
|
|
451
src/kibana/apps/visualize/styles/main.css
Normal file
451
src/kibana/apps/visualize/styles/main.css
Normal file
|
@ -0,0 +1,451 @@
|
|||
.thumbnail > img,
|
||||
.thumbnail a > img,
|
||||
.carousel-inner > .item > img,
|
||||
.carousel-inner > .item > a > img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.btn-group-lg > .btn {
|
||||
padding: 18px 27px;
|
||||
font-size: 17px;
|
||||
line-height: 1.33;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.btn-group-sm > .btn {
|
||||
padding: 6px 9px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.btn-group-xs > .btn {
|
||||
padding: 1px 5px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.container:before,
|
||||
.container:after,
|
||||
.container-fluid:before,
|
||||
.container-fluid:after,
|
||||
.row:before,
|
||||
.row:after,
|
||||
.form-horizontal .form-group:before,
|
||||
.form-horizontal .form-group:after,
|
||||
.btn-toolbar:before,
|
||||
.btn-toolbar:after,
|
||||
.btn-group-vertical > .btn-group:before,
|
||||
.btn-group-vertical > .btn-group:after,
|
||||
.nav:before,
|
||||
.nav:after,
|
||||
.navbar:before,
|
||||
.navbar:after,
|
||||
.navbar-header:before,
|
||||
.navbar-header:after,
|
||||
.navbar-collapse:before,
|
||||
.navbar-collapse:after,
|
||||
.pager:before,
|
||||
.pager:after,
|
||||
.panel-body:before,
|
||||
.panel-body:after,
|
||||
.modal-footer:before,
|
||||
.modal-footer:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.container:after,
|
||||
.container-fluid:after,
|
||||
.row:after,
|
||||
.form-horizontal .form-group:after,
|
||||
.btn-toolbar:after,
|
||||
.btn-group-vertical > .btn-group:after,
|
||||
.nav:after,
|
||||
.navbar:after,
|
||||
.navbar-header:after,
|
||||
.navbar-collapse:after,
|
||||
.pager:after,
|
||||
.panel-body:after,
|
||||
.modal-footer:after {
|
||||
clear: both;
|
||||
}
|
||||
.vis-wizard h1 {
|
||||
margin-top: 45px;
|
||||
}
|
||||
.vis-editor {
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 1 auto;
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-direction: normal;
|
||||
-moz-box-direction: normal;
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
.vis-editor > * {
|
||||
-webkit-flex-shrink: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.vis-editor navbar .bitty-modal-container {
|
||||
position: relative;
|
||||
}
|
||||
.vis-editor navbar .bitty-modal-container .bitty-modal {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
z-index: 10;
|
||||
background: rgba(70, 82, 93, 0.9);
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding-top: 6px;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.vis-editor-content {
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 1 auto;
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-direction: normal;
|
||||
-moz-box-direction: normal;
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
.vis-editor-content > * {
|
||||
-webkit-flex-shrink: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.vis-editor-content {
|
||||
-webkit-box-direction: normal;
|
||||
-moz-box-direction: normal;
|
||||
-webkit-box-orient: horizontal;
|
||||
-moz-box-orient: horizontal;
|
||||
-webkit-flex-direction: row;
|
||||
-ms-flex-direction: row;
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
.vis-editor-sidebar {
|
||||
-webkit-box-flex: 0;
|
||||
-moz-box-flex: 0;
|
||||
-webkit-flex: 0 0 auto;
|
||||
-ms-flex: 0 0 auto;
|
||||
flex: 0 0 auto;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-direction: normal;
|
||||
-moz-box-direction: normal;
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
.vis-editor-sidebar > * {
|
||||
-webkit-flex-shrink: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.vis-editor-sidebar {
|
||||
-webkit-flex-basis: 16.666666666666668%;
|
||||
flex-basis: 16.666666666666668%;
|
||||
min-width: 300px;
|
||||
}
|
||||
}
|
||||
.vis-editor-sidebar .sidebar-container {
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 0 auto;
|
||||
-ms-flex: 1 0 auto;
|
||||
flex: 1 0 auto;
|
||||
background-color: #ffffff;
|
||||
border-right-color: #ecf0f1;
|
||||
}
|
||||
.vis-editor-sidebar .sidebar-item-title {
|
||||
background: #ecf0f1;
|
||||
}
|
||||
.vis-editor-sidebar .sidebar-item-title:hover {
|
||||
color: #444444 !important;
|
||||
background-color: #ecf0f1 !important;
|
||||
}
|
||||
.vis-editor nesting-indicator {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-flex: 0;
|
||||
-moz-box-flex: 0;
|
||||
-webkit-flex: 0 0 auto;
|
||||
-ms-flex: 0 0 auto;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.vis-editor nesting-indicator > span {
|
||||
width: 3px;
|
||||
background-color: #31c471;
|
||||
-webkit-transition: width 0.3s ease-out;
|
||||
-moz-transition: width 0.3s ease-out;
|
||||
-o-transition: width 0.3s ease-out;
|
||||
transition: width 0.3s ease-out;
|
||||
}
|
||||
.vis-editor nesting-indicator > span.expand {
|
||||
width: 10px;
|
||||
}
|
||||
.vis-editor-agg {
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 1 auto;
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-direction: normal;
|
||||
-moz-box-direction: normal;
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
padding: 5px;
|
||||
border-bottom: 1px solid #ecf0f1;
|
||||
}
|
||||
.vis-editor-agg > * {
|
||||
-webkit-flex-shrink: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.vis-editor-agg-wrapper {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
.vis-editor-agg-group {
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 1 auto;
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-direction: normal;
|
||||
-moz-box-direction: normal;
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
color: #444444;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.vis-editor-agg-group > * {
|
||||
-webkit-flex-shrink: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.vis-editor-agg-header {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-moz-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
.vis-editor-agg-header-toggle {
|
||||
-webkit-box-flex: 0;
|
||||
-moz-box-flex: 0;
|
||||
-webkit-flex: 0 0 auto;
|
||||
-ms-flex: 0 0 auto;
|
||||
flex: 0 0 auto;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.vis-editor-agg-header-title {
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 1 auto;
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
font-weight: bold;
|
||||
}
|
||||
.vis-editor-agg-header-description {
|
||||
font-weight: normal;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.vis-editor-agg-header-description.danger {
|
||||
color: #ffffff;
|
||||
color: #e74c3c;
|
||||
font-weight: bold;
|
||||
}
|
||||
a.vis-editor-agg-header-description.danger:hover {
|
||||
color: #e6e6e6;
|
||||
}
|
||||
.vis-editor-agg-header-controls {
|
||||
-webkit-box-flex: 0;
|
||||
-moz-box-flex: 0;
|
||||
-webkit-flex: 0 0 auto;
|
||||
-ms-flex: 0 0 auto;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.vis-editor-agg-editor {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.vis-editor-agg-editor-ranges td {
|
||||
padding: 0 5px 5px 0;
|
||||
}
|
||||
.vis-editor-agg-editor-ranges td:last-child {
|
||||
padding-right: 0;
|
||||
}
|
||||
.vis-editor-agg-form-row {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
.vis-editor-agg-form-row > * {
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 1 auto;
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.vis-editor-agg-form-row > *:last-child {
|
||||
margin-right: 0px;
|
||||
}
|
||||
.vis-editor-agg-form-row > .btn {
|
||||
-webkit-align-self: center;
|
||||
-ms-flex-item-align: center;
|
||||
align-self: center;
|
||||
}
|
||||
.vis-editor-agg-wide-btn {
|
||||
-webkit-border-radius: 0;
|
||||
-webkit-background-clip: padding-box;
|
||||
-moz-border-radius: 0;
|
||||
-moz-background-clip: padding;
|
||||
border-radius: 0;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
.vis-editor-agg-add-form {
|
||||
margin: 15px;
|
||||
padding: 5px;
|
||||
}
|
||||
.vis-editor-agg-add-form > button {
|
||||
display: block;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
.vis-editor-canvas {
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 0 975.3333333333334px;
|
||||
-ms-flex: 1 0 975.3333333333334px;
|
||||
flex: 1 0 975.3333333333334px;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-direction: normal;
|
||||
-moz-box-direction: normal;
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
@media (min-width: 992px) {
|
||||
.vis-editor-canvas {
|
||||
-webkit-flex-shrink: 1;
|
||||
flex-shrink: 1;
|
||||
-webkit-flex-basis: 100%;
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
.vis-editor-canvas-title {
|
||||
text-align: center;
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
.vis-editor-canvas visualize {
|
||||
-webkit-flex: 1 1 auto;
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-direction: normal;
|
||||
-moz-box-direction: normal;
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 0 auto;
|
||||
-ms-flex: 1 0 auto;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
.vis-editor-canvas visualize > * {
|
||||
-webkit-flex-shrink: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.vis-editor-canvas .visualize-chart {
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 0 100%;
|
||||
-ms-flex: 1 0 100%;
|
||||
flex: 1 0 100%;
|
||||
position: relative;
|
||||
}
|
||||
form.vis-share div.form-control {
|
||||
height: inherit;
|
||||
}
|
221
src/kibana/apps/visualize/styles/visualization.css
Normal file
221
src/kibana/apps/visualize/styles/visualization.css
Normal file
|
@ -0,0 +1,221 @@
|
|||
.thumbnail > img,
|
||||
.thumbnail a > img,
|
||||
.carousel-inner > .item > img,
|
||||
.carousel-inner > .item > a > img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.btn-group-lg > .btn {
|
||||
padding: 18px 27px;
|
||||
font-size: 17px;
|
||||
line-height: 1.33;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.btn-group-sm > .btn {
|
||||
padding: 6px 9px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.btn-group-xs > .btn {
|
||||
padding: 1px 5px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.container:before,
|
||||
.container:after,
|
||||
.container-fluid:before,
|
||||
.container-fluid:after,
|
||||
.row:before,
|
||||
.row:after,
|
||||
.form-horizontal .form-group:before,
|
||||
.form-horizontal .form-group:after,
|
||||
.btn-toolbar:before,
|
||||
.btn-toolbar:after,
|
||||
.btn-group-vertical > .btn-group:before,
|
||||
.btn-group-vertical > .btn-group:after,
|
||||
.nav:before,
|
||||
.nav:after,
|
||||
.navbar:before,
|
||||
.navbar:after,
|
||||
.navbar-header:before,
|
||||
.navbar-header:after,
|
||||
.navbar-collapse:before,
|
||||
.navbar-collapse:after,
|
||||
.pager:before,
|
||||
.pager:after,
|
||||
.panel-body:before,
|
||||
.panel-body:after,
|
||||
.modal-footer:before,
|
||||
.modal-footer:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.container:after,
|
||||
.container-fluid:after,
|
||||
.row:after,
|
||||
.form-horizontal .form-group:after,
|
||||
.btn-toolbar:after,
|
||||
.btn-group-vertical > .btn-group:after,
|
||||
.nav:after,
|
||||
.navbar:after,
|
||||
.navbar-header:after,
|
||||
.navbar-collapse:after,
|
||||
.pager:after,
|
||||
.panel-body:after,
|
||||
.modal-footer:after {
|
||||
clear: both;
|
||||
}
|
||||
visualize {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-direction: normal;
|
||||
-moz-box-direction: normal;
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
}
|
||||
visualize .visualize-error {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
font-size: 1em;
|
||||
}
|
||||
visualize .visualize-error .fa-exclamation-triangle {
|
||||
font-size: 2em;
|
||||
color: #e74c3c;
|
||||
}
|
||||
visualize .k4tip {
|
||||
white-space: pre-line;
|
||||
}
|
||||
visualize .visualize-chart {
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 1;
|
||||
-ms-flex: 1 1;
|
||||
flex: 1 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
visualize .visualize-chart.spy-visible {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
visualize .visualize-chart.spy-only {
|
||||
display: none;
|
||||
}
|
||||
visualize-spy {
|
||||
-webkit-box-flex: 0;
|
||||
-moz-box-flex: 0;
|
||||
-webkit-flex: 0 0 auto;
|
||||
-ms-flex: 0 0 auto;
|
||||
flex: 0 0 auto;
|
||||
-webkit-box-direction: normal;
|
||||
-moz-box-direction: normal;
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
padding-top: 10px;
|
||||
}
|
||||
visualize-spy.visible {
|
||||
display: block;
|
||||
}
|
||||
visualize-spy.only {
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 1 auto;
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
padding-top: 0px;
|
||||
}
|
||||
visualize-spy .visualize-show-spy {
|
||||
-webkit-box-flex: 0;
|
||||
-moz-box-flex: 0;
|
||||
-webkit-flex: 0 0 auto;
|
||||
-ms-flex: 0 0 auto;
|
||||
flex: 0 0 auto;
|
||||
background-color: #ecf0f1;
|
||||
text-align: center;
|
||||
}
|
||||
visualize-spy .visualize-show-spy i {
|
||||
padding: 0 10px;
|
||||
}
|
||||
visualize-spy .visualize-spy-container {
|
||||
-webkit-box-flex: 1;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-flex: 1 1 auto;
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-direction: normal;
|
||||
-moz-box-direction: normal;
|
||||
-webkit-box-orient: vertical;
|
||||
-moz-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
-ms-flex-direction: column;
|
||||
flex-direction: column;
|
||||
padding: 10px 10px 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
visualize-spy .visualize-spy-container tr > td {
|
||||
font-size: 0.85em;
|
||||
}
|
||||
visualize-spy .visualize-table th i.fa-sort {
|
||||
color: #b4bcc2;
|
||||
}
|
||||
visualize-spy .visualize-table-right {
|
||||
text-align: right;
|
||||
}
|
||||
visualize-spy .visualize-table-controls {
|
||||
padding: 0 5px;
|
||||
text-align: right;
|
||||
}
|
||||
visualize-spy .visualize-csv-options .form-control {
|
||||
padding: 0px 6px;
|
||||
height: auto;
|
||||
}
|
||||
visualize-spy .visualize-csv-options label {
|
||||
margin: 0 10px;
|
||||
padding: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
visualize-spy .visualize-csv-options .visualize-csv-separator {
|
||||
width: 1.5em;
|
||||
}
|
||||
visualize-spy .visualize-csv-options button[type=submit] {
|
||||
margin-left: 5px;
|
||||
}
|
||||
visualize-spy .visualize-spy-tabs {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
-webkit-box-flex: 0;
|
||||
-moz-box-flex: 0;
|
||||
-webkit-flex: 0 0 auto;
|
||||
-ms-flex: 0 0 auto;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
visualize-spy pre {
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
77
src/kibana/components/query_input/query_input.css
Normal file
77
src/kibana/components/query_input/query_input.css
Normal file
|
@ -0,0 +1,77 @@
|
|||
.thumbnail > img,
|
||||
.thumbnail a > img,
|
||||
.carousel-inner > .item > img,
|
||||
.carousel-inner > .item > a > img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.btn-group-lg > .btn {
|
||||
padding: 18px 27px;
|
||||
font-size: 17px;
|
||||
line-height: 1.33;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.btn-group-sm > .btn {
|
||||
padding: 6px 9px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.btn-group-xs > .btn {
|
||||
padding: 1px 5px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.container:before,
|
||||
.container:after,
|
||||
.container-fluid:before,
|
||||
.container-fluid:after,
|
||||
.row:before,
|
||||
.row:after,
|
||||
.form-horizontal .form-group:before,
|
||||
.form-horizontal .form-group:after,
|
||||
.btn-toolbar:before,
|
||||
.btn-toolbar:after,
|
||||
.btn-group-vertical > .btn-group:before,
|
||||
.btn-group-vertical > .btn-group:after,
|
||||
.nav:before,
|
||||
.nav:after,
|
||||
.navbar:before,
|
||||
.navbar:after,
|
||||
.navbar-header:before,
|
||||
.navbar-header:after,
|
||||
.navbar-collapse:before,
|
||||
.navbar-collapse:after,
|
||||
.pager:before,
|
||||
.pager:after,
|
||||
.panel-body:before,
|
||||
.panel-body:after,
|
||||
.modal-footer:before,
|
||||
.modal-footer:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.container:after,
|
||||
.container-fluid:after,
|
||||
.row:after,
|
||||
.form-horizontal .form-group:after,
|
||||
.btn-toolbar:after,
|
||||
.btn-group-vertical > .btn-group:after,
|
||||
.nav:after,
|
||||
.navbar:after,
|
||||
.navbar-header:after,
|
||||
.navbar-collapse:after,
|
||||
.pager:after,
|
||||
.panel-body:after,
|
||||
.modal-footer:after {
|
||||
clear: both;
|
||||
}
|
||||
i.query-input-error {
|
||||
position: absolute;
|
||||
margin-left: -25px;
|
||||
color: #e74c3c;
|
||||
margin-top: 10px;
|
||||
z-index: 5;
|
||||
}
|
68
src/kibana/components/reflow_watcher.js
Normal file
68
src/kibana/components/reflow_watcher.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
define(function (require) {
|
||||
return function ReflowWatcherService(Private, $rootScope, $http) {
|
||||
var angular = require('angular');
|
||||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
|
||||
var EventEmitter = Private(require('factories/events'));
|
||||
var $body = $(document.body);
|
||||
var $window = $(window);
|
||||
|
||||
var MOUSE_EVENTS = 'mouseup';
|
||||
var WINDOW_EVENTS = 'resize';
|
||||
|
||||
_(ReflowWatcher).inherits(EventEmitter);
|
||||
/**
|
||||
* Watches global activity which might hint at a change in the content, which
|
||||
* in turn provides a hint to resizers that they should check their size
|
||||
*/
|
||||
function ReflowWatcher() {
|
||||
ReflowWatcher.Super.call(this);
|
||||
|
||||
// bound version of trigger that can be used as a handler
|
||||
this.trigger = _.bind(this.trigger, this);
|
||||
this._emitReflow = _.bind(this._emitReflow, this);
|
||||
|
||||
// list of functions to call that will unbind our watchers
|
||||
this._unwatchers = [
|
||||
$rootScope.$watchCollection(function () {
|
||||
return $http.pendingRequests;
|
||||
}, this.trigger)
|
||||
];
|
||||
|
||||
$body.on(MOUSE_EVENTS, this.trigger);
|
||||
$window.on(WINDOW_EVENTS, this.trigger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply emit reflow, but in a way that can be bound and passed to
|
||||
* other functions. Using _.bind caused extra arguments to be added, and
|
||||
* then emitted to other places. No Bueno
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
ReflowWatcher.prototype._emitReflow = function () {
|
||||
this.emit('reflow');
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit the "reflow" event in the next tick of the digest cycle
|
||||
* @return {void}
|
||||
*/
|
||||
ReflowWatcher.prototype.trigger = function () {
|
||||
$rootScope.$evalAsync(this._emitReflow);
|
||||
};
|
||||
|
||||
/**
|
||||
* Signal to the ReflowWatcher that it should clean up it's listeners
|
||||
* @return {void}
|
||||
*/
|
||||
ReflowWatcher.prototype.destroy = function () {
|
||||
$body.off(MOUSE_EVENTS, this.trigger);
|
||||
$window.off(WINDOW_EVENTS, this.trigger);
|
||||
_.callEach(this._unwatchers);
|
||||
};
|
||||
|
||||
return new ReflowWatcher();
|
||||
};
|
||||
});
|
|
@ -9,7 +9,8 @@ define(function (require) {
|
|||
vislibParams: {
|
||||
shareYAxis: true,
|
||||
addTooltip: true,
|
||||
addLegend: true
|
||||
addLegend: true,
|
||||
addBrushing: true
|
||||
},
|
||||
schemas: new Schemas([
|
||||
{
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
define(function (require) {
|
||||
return function ColorUtilService(Private) {
|
||||
var _ = require('lodash');
|
||||
|
||||
var createColorPalette = Private(require('components/vislib/components/_functions/color/color_palette'));
|
||||
var createColorObj = Private(require('components/vislib/components/_functions/color/color_obj'));
|
||||
|
||||
// Takes an array of strings or numbers
|
||||
return function (arr) {
|
||||
if (!_.isArray(arr)) {
|
||||
throw new Error(typeof arr + ' should be an array of strings or numbers');
|
||||
}
|
||||
|
||||
var colorObj = createColorObj(arr, createColorPalette(arr.length));
|
||||
|
||||
// Returns a function that accepts a value (i.e. a string or number)
|
||||
// and returns a hex color from the colorObj
|
||||
return function (value) {
|
||||
return colorObj[value];
|
||||
};
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
define(function (require) {
|
||||
return function ColorObjUtilService() {
|
||||
var _ = require('lodash');
|
||||
|
||||
// Accepts 2 arrays of strings or numbers
|
||||
return function (arr1, arr2) {
|
||||
// Returns an object with arr1 values as keys
|
||||
// and arr2 values as values
|
||||
return _.zipObject(arr1, arr2);
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
define(function (require) {
|
||||
return function ColorPaletteUtilService(d3, Private) {
|
||||
var _ = require('lodash');
|
||||
|
||||
var seedColors = Private(require('components/vislib/components/_functions/color/seed_colors'));
|
||||
|
||||
// Accepts a number that represents a length of an array
|
||||
return function (num) {
|
||||
var usedColors = [];
|
||||
|
||||
// checks if more colors are needed
|
||||
var diff = num - seedColors.length;
|
||||
|
||||
if (diff > 0) {
|
||||
// generate more colors
|
||||
usedColors = _.clone(seedColors);
|
||||
|
||||
for (var newColor, i = 0; i < diff; i++) {
|
||||
newColor = d3.rgb(usedColors[i])
|
||||
.darker(1.3)
|
||||
.toString();
|
||||
|
||||
usedColors.push(newColor);
|
||||
}
|
||||
} else {
|
||||
// trim to length of array labels
|
||||
usedColors = _.first(seedColors, num);
|
||||
}
|
||||
|
||||
// Returns an array of hex colors
|
||||
// Returned array should be same length as input (num).
|
||||
return usedColors;
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,79 @@
|
|||
define(function () {
|
||||
return function SeedColorUtilService() {
|
||||
// returns an array of 72 seed colors
|
||||
return [
|
||||
'#57c17b',
|
||||
'#006e8a',
|
||||
'#6f87d8',
|
||||
'#663db8',
|
||||
'#bc52bc',
|
||||
'#9e3533',
|
||||
'#daa05d',
|
||||
'#967b17',
|
||||
'#a0caae',
|
||||
'#73a4b0',
|
||||
'#acb5d8',
|
||||
'#9b8bbb',
|
||||
'#c19fc1',
|
||||
'#b88484',
|
||||
'#e0cbb2',
|
||||
'#bfb282',
|
||||
'#336c46',
|
||||
'#00455c',
|
||||
'#394e93',
|
||||
'#422c6d',
|
||||
'#783678',
|
||||
'#6a2424',
|
||||
'#936734',
|
||||
'#60521f',
|
||||
'#b3d5bf',
|
||||
'#85adb7',
|
||||
'#bdc5e0',
|
||||
'#aa9dc3',
|
||||
'#c9acc9',
|
||||
'#c39898',
|
||||
'#e8d7c5',
|
||||
'#cbc09a',
|
||||
'#2c593b',
|
||||
'#003252',
|
||||
'#34457f',
|
||||
'#352456',
|
||||
'#673267',
|
||||
'#591d1d',
|
||||
'#7f592f',
|
||||
'#443b17',
|
||||
'#c7e0cf',
|
||||
'#a8c5cc',
|
||||
'#d2d7ea',
|
||||
'#c2b9d4',
|
||||
'#dbc7db',
|
||||
'#d5b9b9',
|
||||
'#f2e9de',
|
||||
'#d9d1b5',
|
||||
'#254b32',
|
||||
'#002c47',
|
||||
'#2b3969',
|
||||
'#30214f',
|
||||
'#562956',
|
||||
'#491818',
|
||||
'#654625',
|
||||
'#393114',
|
||||
'#dbebe0',
|
||||
'#bcd2d7',
|
||||
'#d0d6eb',
|
||||
'#cdc4de',
|
||||
'#e8d9e8',
|
||||
'#e0cccc',
|
||||
'#f5eee5',
|
||||
'#e4dec9',
|
||||
'#20412b',
|
||||
'#001f33',
|
||||
'#232f57',
|
||||
'#281b41',
|
||||
'#482348',
|
||||
'#3e1414',
|
||||
'#563c20',
|
||||
'#2e2810'
|
||||
];
|
||||
};
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
define(function (require) {
|
||||
return function GetArrayUtilService(Private) {
|
||||
var _ = require('lodash');
|
||||
|
||||
var flattenSeries = Private(require('components/vislib/components/_functions/labels/get_series'));
|
||||
|
||||
/* Takes a kibana obj object
|
||||
* for example:
|
||||
* {
|
||||
* labels: '',
|
||||
* rows: [...],
|
||||
* raw: [...],
|
||||
* ...,
|
||||
* };
|
||||
* Data object can have a key that has rows, columns, or series.
|
||||
*/
|
||||
return function (obj) {
|
||||
|
||||
if (!obj.series) {
|
||||
return flattenSeries(obj);
|
||||
}
|
||||
|
||||
// Returns a kibana obj.series array of objects with values array
|
||||
return obj.series;
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
define(function (require) {
|
||||
return function GetSeriesUtilService() {
|
||||
var _ = require('lodash');
|
||||
|
||||
// Accepts a kibana data object
|
||||
return function (obj) {
|
||||
obj = obj.rows ? obj.rows : obj.columns;
|
||||
|
||||
/*
|
||||
* Flattens the obj.rows or obj.columns arrays
|
||||
* to an array of d.series objects
|
||||
* for example:
|
||||
* [
|
||||
* { label: .., values: [...] },
|
||||
* { label: .., values: [...] },
|
||||
* { label: .., values: [...] }
|
||||
* ]
|
||||
*/
|
||||
|
||||
return _.chain(obj)
|
||||
.pluck()
|
||||
.pluck('series')
|
||||
.flatten()
|
||||
.value();
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,25 @@
|
|||
define(function (require) {
|
||||
return function LabelUtilService(Private) {
|
||||
var getArr = Private(require('components/vislib/components/_functions/labels/data_array'));
|
||||
var getArrOfUniqLabels = Private(require('components/vislib/components/_functions/labels/uniq_labels'));
|
||||
|
||||
/* Takes a kibana data object
|
||||
* for example:
|
||||
* {
|
||||
* labels: '',
|
||||
* rows: [...],
|
||||
* raw: [...],
|
||||
* ...,
|
||||
* };
|
||||
* Data object can have a key that has rows, columns, or series.
|
||||
*/
|
||||
return function (obj) {
|
||||
if (!obj instanceof Object) {
|
||||
throw new Error(obj + ' should be an object');
|
||||
}
|
||||
|
||||
// Returns an array of unique chart labels
|
||||
return getArrOfUniqLabels(getArr(obj));
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
define(function (require) {
|
||||
return function UniqLabelUtilService() {
|
||||
var _ = require('lodash');
|
||||
|
||||
// Takes an array of objects
|
||||
return function (arr) {
|
||||
if (!arr instanceof Array) {
|
||||
throw TypeError(arr + ' should be an array of objects');
|
||||
}
|
||||
|
||||
// Returns a array of unique chart labels
|
||||
return _.uniq(_.pluck(arr, 'label'));
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
define(function (require) {
|
||||
return function FlattenDataObjectUtilService() {
|
||||
var _ = require('lodash');
|
||||
|
||||
// Takes a kibana data.series array of objects
|
||||
return function (obj) {
|
||||
if (!obj.series) {
|
||||
obj = obj.rows ? obj.rows : obj.columns;
|
||||
|
||||
return _.chain(obj)
|
||||
.pluck('series')
|
||||
.flatten()
|
||||
.pluck('values')
|
||||
.flatten()
|
||||
.value();
|
||||
}
|
||||
|
||||
// Returns an array of objects
|
||||
/*
|
||||
* [
|
||||
* { x: ..., y: ...},
|
||||
* { x: ..., y: ...},
|
||||
* { x: ..., y: ...}
|
||||
* ]
|
||||
*/
|
||||
return _.flatten(obj.series, 'values');
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
define(function (require) {
|
||||
return function ZeroInjectionUtilService(Private) {
|
||||
var _ = require('lodash');
|
||||
|
||||
var orderXValues = Private(require('components/vislib/components/_functions/zero_injection/ordered_x_keys'));
|
||||
var createZeroFilledArray = Private(require('components/vislib/components/_functions/zero_injection/zero_filled_array'));
|
||||
var zeroFillDataArray = Private(require('components/vislib/components/_functions/zero_injection/zero_fill_data_array'));
|
||||
|
||||
// Takes the kibana data objects
|
||||
return function (obj) {
|
||||
var keys = orderXValues(obj);
|
||||
var max;
|
||||
var zeroArray;
|
||||
var dataArray;
|
||||
var i;
|
||||
var j;
|
||||
|
||||
if (!obj.series) {
|
||||
var arr = obj.rows ? obj.rows : obj.columns;
|
||||
max = arr.length;
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
var jMax = arr[i].series.length;
|
||||
|
||||
for (j = 0; j < jMax; j++) {
|
||||
zeroArray = createZeroFilledArray(keys);
|
||||
dataArray = arr[i].series[j].values;
|
||||
arr[i].series[j].values = zeroFillDataArray(zeroArray, dataArray);
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Looping thru each arr.values object and replacing
|
||||
// the y value of the zero-filled array
|
||||
max = obj.series.length;
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
zeroArray = createZeroFilledArray(keys);
|
||||
dataArray = obj.series[i].values;
|
||||
|
||||
obj.series[i].values = zeroFillDataArray(zeroArray, dataArray);
|
||||
}
|
||||
|
||||
// Returns a zero-filled array of objects
|
||||
return obj;
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
define(function (require) {
|
||||
return function OrderedXKeysUtilService(Private) {
|
||||
var _ = require('lodash');
|
||||
var getUniqKeys = Private(require('components/vislib/components/_functions/zero_injection/uniq_keys'));
|
||||
|
||||
// Takes a kibana data objects
|
||||
return function (obj) {
|
||||
var objKeys = getUniqKeys(obj);
|
||||
|
||||
// Returns an array x axis values
|
||||
return _.chain(objKeys)
|
||||
.pairs()
|
||||
.map(function (d) {
|
||||
return d[1].isNumber ? +d[0] : d[0];
|
||||
})
|
||||
.value();
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
define(function () {
|
||||
return function ReplaceIndexUtilService() {
|
||||
/*
|
||||
* Replaces an object in an array at a specific index
|
||||
*
|
||||
* Accepts an array of objects
|
||||
* an index (num)
|
||||
* and an obj
|
||||
*/
|
||||
return function (arr, index, obj) {
|
||||
arr.splice(index, 1);
|
||||
arr.splice(index, 0, obj);
|
||||
|
||||
// Returns an array with a replaced object
|
||||
return arr;
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
define(function (require) {
|
||||
return function UniqueXValuesUtilService(Private) {
|
||||
var _ = require('lodash');
|
||||
|
||||
var flattenDataArray = Private(require('components/vislib/components/_functions/zero_injection/flatten_data'));
|
||||
|
||||
// accepts a kibana data.series array of objects
|
||||
return function (obj) {
|
||||
var flattenedData = flattenDataArray(obj);
|
||||
var uniqueXValues = {};
|
||||
|
||||
// Appends unique x values in the order they appear to an empty object
|
||||
flattenedData.forEach(function (d, i) {
|
||||
var key = d.x;
|
||||
uniqueXValues[key] = uniqueXValues[key] === void 0 ?
|
||||
{ index: i, isNumber: _.isNumber(key) } : { index: Math.max(i, uniqueXValues[key]), isNumber: _.isNumber(key) };
|
||||
});
|
||||
|
||||
// returns an object with unique x values in the correct order
|
||||
return uniqueXValues;
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
define(function (require) {
|
||||
return function ZeroFillDataArrayUtilService(Private) {
|
||||
var _ = require('lodash');
|
||||
|
||||
var replaceIndex = Private(require('components/vislib/components/_functions/zero_injection/replace_index'));
|
||||
|
||||
// Accepts an array of zero-filled y value objects
|
||||
// and a kibana data.series[i].values array of objects
|
||||
return function (arr1, arr2) {
|
||||
var getX = function (d) {
|
||||
return d.x === val.x;
|
||||
};
|
||||
var max = arr2.length;
|
||||
var i;
|
||||
var val;
|
||||
var index;
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
val = arr2[i];
|
||||
index = _.findIndex(arr1, getX);
|
||||
replaceIndex(arr1, index, val);
|
||||
}
|
||||
|
||||
// Return a zero-filled array of objects
|
||||
return arr1;
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
define(function () {
|
||||
return function ZeroFilledArrayUtilService() {
|
||||
// Accepts an array of strings or numbers
|
||||
// and a kibana data.ordered object
|
||||
return function (arr) {
|
||||
var max = arr.length;
|
||||
var i;
|
||||
var val;
|
||||
var zeroFilledArray = [];
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
val = arr[i];
|
||||
|
||||
zeroFilledArray.push({
|
||||
x: val,
|
||||
y: 0
|
||||
});
|
||||
}
|
||||
|
||||
// Returns an array of objects with y value of 0
|
||||
return zeroFilledArray;
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
define(function () {
|
||||
return function ChartSplitFactory(d3) {
|
||||
/*
|
||||
* Adds div DOM elements to the `.chart-wrapper` element based on the data layout.
|
||||
* For example, if the data has rows, it returns the same number of
|
||||
* `.chart` elements as row objects.
|
||||
*/
|
||||
return function split(selection) {
|
||||
selection.each(function (data) {
|
||||
var div = d3.select(this)
|
||||
.attr('class', function () {
|
||||
// Determine the parent class
|
||||
return data.rows ? 'chart-wrapper-row' : data.columns ? 'chart-wrapper-column' : 'chart-wrapper';
|
||||
});
|
||||
var divClass;
|
||||
|
||||
var charts = div.selectAll('charts')
|
||||
.append('div')
|
||||
.data(function (d) {
|
||||
// Determine the child class
|
||||
divClass = d.rows ? 'chart-row' : d.columns ? 'chart-column' : 'chart';
|
||||
return d.rows ? d.rows : d.columns ? d.columns : [d];
|
||||
})
|
||||
.enter().append('div')
|
||||
.attr('class', function () {
|
||||
return divClass;
|
||||
});
|
||||
|
||||
if (!data.series) {
|
||||
charts.call(split);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
define(function () {
|
||||
return function ChartTitleSplitFactory(d3) {
|
||||
/*
|
||||
* Adds div DOM elements to either the `.y-axis-chart-title` element or the
|
||||
* `.x-axis-chart-title` element based on the data layout.
|
||||
* For example, if the data has rows, it returns the same number of
|
||||
* `.chart-title` elements as row objects.
|
||||
*/
|
||||
return function (selection) {
|
||||
selection.each(function (data) {
|
||||
var div = d3.select(this);
|
||||
|
||||
if (!data.series) {
|
||||
div.selectAll('.chart-title').append('div')
|
||||
.data(function (d) {
|
||||
return d.rows ? d.rows : d.columns;
|
||||
})
|
||||
.enter().append('div')
|
||||
.attr('class', 'chart-title');
|
||||
|
||||
if (data.rows) {
|
||||
// if rows remove the x axis chart title element
|
||||
d3.select('.x-axis-chart-title').remove();
|
||||
} else {
|
||||
// if columns, remove the y axis chart title element
|
||||
d3.select('.y-axis-chart-title').remove();
|
||||
}
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
// if not data.rows or data.columns, return no chart titles
|
||||
return d3.select(this).remove();
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
define(function () {
|
||||
return function XAxisSplitFactory(d3) {
|
||||
/*
|
||||
* Adds div DOM elements to the `.x-axis-div-wrapper` element based on the data layout.
|
||||
* For example, if the data has rows, it returns the same number of
|
||||
* `.x-axis-div` elements as row objects.
|
||||
*/
|
||||
return function (selection) {
|
||||
selection.each(function () {
|
||||
var div = d3.select(this);
|
||||
|
||||
div.selectAll('.x-axis-div')
|
||||
.append('div')
|
||||
.data(function (d) {
|
||||
return d.columns ? d.columns : [d];
|
||||
})
|
||||
.enter()
|
||||
.append('div')
|
||||
.attr('class', 'x-axis-div');
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
define(function () {
|
||||
return function YAxisSplitFactory(d3) {
|
||||
/*
|
||||
* Adds div DOM elements to the `.y-axis-div-wrapper` element based on the data layout.
|
||||
* For example, if the data has rows, it returns the same number of
|
||||
* `.y-axis-div` elements as row objects.
|
||||
*/
|
||||
return function (selection) {
|
||||
selection.each(function () {
|
||||
var div = d3.select(this);
|
||||
|
||||
div.selectAll('.y-axis-div')
|
||||
.append('div')
|
||||
.data(function (d) {
|
||||
return d.rows ? d.rows : [d];
|
||||
})
|
||||
.enter()
|
||||
.append('div')
|
||||
.attr('class', 'y-axis-div');
|
||||
});
|
||||
};
|
||||
};
|
||||
});
|
|
@ -0,0 +1,112 @@
|
|||
define(function (require) {
|
||||
return function ColumnLayoutFactory(d3, Private) {
|
||||
|
||||
var chartSplit = Private(require('components/vislib/components/layouts/splits/column_chart/chart_split'));
|
||||
var yAxisSplit = Private(require('components/vislib/components/layouts/splits/column_chart/y_axis_split'));
|
||||
var xAxisSplit = Private(require('components/vislib/components/layouts/splits/column_chart/x_axis_split'));
|
||||
var chartTitleSplit = Private(require('components/vislib/components/layouts/splits/column_chart/chart_title_split'));
|
||||
|
||||
/*
|
||||
* Specifies the visualization layout for column charts.
|
||||
*
|
||||
* This is done using an array of objects. The first object has
|
||||
* a `parent` DOM element, a DOM `type` (e.g. div, svg, etc),
|
||||
* and a `class` (required). Each child can omit the parent object,
|
||||
* but must include a type and class.
|
||||
*
|
||||
* Optionally, you can specify `datum` to be bound to the DOM
|
||||
* element, a `splits` function that divides the selected element
|
||||
* into more DOM elements based on a callback function provided, or
|
||||
* a children array which nests other layout objects.
|
||||
*
|
||||
* Objects in children arrays are children of the current object and return
|
||||
* DOM elements which are children of their respective parent element.
|
||||
*/
|
||||
|
||||
return function (el, data) {
|
||||
if (!el || !data) {
|
||||
throw new Error('Both an el and data need to be specified');
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
parent: el,
|
||||
type: 'div',
|
||||
class: 'vis-wrapper',
|
||||
datum: data,
|
||||
children: [
|
||||
{
|
||||
type: 'div',
|
||||
class: 'y-axis-col-wrapper',
|
||||
children: [
|
||||
{
|
||||
type: 'div',
|
||||
class: 'y-axis-col',
|
||||
children: [
|
||||
{
|
||||
type: 'div',
|
||||
class: 'y-axis-title'
|
||||
},
|
||||
{
|
||||
type: 'div',
|
||||
class: 'y-axis-chart-title',
|
||||
splits: chartTitleSplit
|
||||
},
|
||||
{
|
||||
type: 'div',
|
||||
class: 'y-axis-div-wrapper',
|
||||
splits: yAxisSplit
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'div',
|
||||
class: 'y-axis-spacer-block'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'div',
|
||||
class: 'vis-col-wrapper',
|
||||
children: [
|
||||
{
|
||||
type: 'div',
|
||||
class: 'chart-wrapper',
|
||||
splits: chartSplit
|
||||
},
|
||||
{
|
||||
type: 'div',
|
||||
class: 'x-axis-wrapper',
|
||||
children: [
|
||||
{
|
||||
type: 'div',
|
||||
class: 'x-axis-div-wrapper',
|
||||
splits: xAxisSplit
|
||||
},
|
||||
{
|
||||
type: 'div',
|
||||
class: 'x-axis-chart-title',
|
||||
splits: chartTitleSplit
|
||||
},
|
||||
{
|
||||
type: 'div',
|
||||
class: 'x-axis-title'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'div',
|
||||
class: 'legend-col-wrapper'
|
||||
},
|
||||
{
|
||||
type: 'div',
|
||||
class: 'k4tip'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
});
|
387
src/kibana/components/vislib/components/styles/main.less
Normal file
387
src/kibana/components/vislib/components/styles/main.less
Normal file
|
@ -0,0 +1,387 @@
|
|||
@import (reference) "lesshat.less";
|
||||
|
||||
/* vislib styles */
|
||||
.arc path {
|
||||
stroke: #fff;
|
||||
/* stroke-width: 3px; */
|
||||
}
|
||||
|
||||
div.columns {
|
||||
.display(flex);
|
||||
.flex-direction(row);
|
||||
.flex(1 1 100%);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.rows {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
.flex(1 1 100%);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.row-labels, .column-labels {
|
||||
margin: 0;
|
||||
padding: 10;
|
||||
}
|
||||
|
||||
visualize {
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
.visualize-chart {
|
||||
.display(flex);
|
||||
.flex(1 1 100%);
|
||||
}
|
||||
|
||||
.visualize-wrapper {
|
||||
.display(flex);
|
||||
.flex-direction(row);
|
||||
}
|
||||
|
||||
.y-axis-wrapper {
|
||||
.display(flex);
|
||||
.flex(1 1);
|
||||
.flex-direction(column);
|
||||
}
|
||||
|
||||
.y-axis-filler-div {
|
||||
.flex(1 1);
|
||||
}
|
||||
|
||||
div.x-axis-label {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
bottom: 15px;
|
||||
font-size: 7pt;
|
||||
color: #848e96;
|
||||
}
|
||||
|
||||
div.y-axis-label {
|
||||
position: absolute;
|
||||
left: -25px;
|
||||
top: 42.5%;
|
||||
font-size: 7pt;
|
||||
color: #848e96;
|
||||
-webkit-transform: rotate(270deg);
|
||||
-moz-transform: rotate(270deg);
|
||||
-o-transform: rotate(270deg);
|
||||
-ms-transform: rotate(270deg);
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
|
||||
/* legend */
|
||||
.legend-col-wrapper {
|
||||
.flex(0 1 auto);
|
||||
z-index: 10;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
height: 26px;
|
||||
margin: 0 0 14px 0;
|
||||
}
|
||||
|
||||
.legend {
|
||||
width: 100%;
|
||||
height: 26px;
|
||||
margin: 0 0 14px 0;
|
||||
}
|
||||
|
||||
.legend-ul {
|
||||
list-style-type: none;
|
||||
margin: 0 0 0 14px;
|
||||
padding: 0;
|
||||
visibility: visible;
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
}
|
||||
|
||||
.legend-ul.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
li.color {
|
||||
min-height: 22px;
|
||||
margin: 0 18px 0 18px;
|
||||
padding: 3px 0 3px 0;
|
||||
text-align: left;
|
||||
font-size: 12px;
|
||||
line-height: 13px;
|
||||
color: #666;
|
||||
cursor: default;
|
||||
text-align: left;
|
||||
.flex-grow(2);
|
||||
word-wrap: break-word;
|
||||
max-width: 150px;
|
||||
}
|
||||
.wordwrap {
|
||||
word-wrap: break-word;
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
.dots {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
float: left;
|
||||
margin: 1px 0 0 -14px;
|
||||
}
|
||||
|
||||
.legend-toggle {
|
||||
position: relative;
|
||||
float: right;
|
||||
right: 9px;
|
||||
top: 9px;
|
||||
}
|
||||
|
||||
.column-labels {
|
||||
color: #777;
|
||||
font-size: 10pt;
|
||||
font-weight: normal;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-left: 1.0em;
|
||||
line-height: 2.0em;
|
||||
}
|
||||
|
||||
/* histogram axis and label styles */
|
||||
.vis-canvas {
|
||||
/* background-color: #fff; */
|
||||
}
|
||||
|
||||
.chart-bkgd {
|
||||
fill: #ffffff;
|
||||
/*fill: #eff3f4;*/
|
||||
/*stroke: #ddd;*/
|
||||
/*shape-rendering: crispEdges;*/
|
||||
}
|
||||
|
||||
p.rows-label, p.columns-label {
|
||||
display: block;
|
||||
background: #eff3f4;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 9pt;
|
||||
fill: #46525d;
|
||||
text-align: center;
|
||||
line-height: 1.9em;
|
||||
}
|
||||
|
||||
.charts-label {
|
||||
font-size: 8pt;
|
||||
fill: #848e96;
|
||||
}
|
||||
|
||||
.x-axis-label, .y-axis-label {
|
||||
font-size: 7pt;
|
||||
fill: #848e96;
|
||||
}
|
||||
|
||||
.tick text {
|
||||
font-size: 7pt;
|
||||
fill: #848e96;
|
||||
/*cursor: pointer;*/
|
||||
}
|
||||
|
||||
.axis {
|
||||
shape-rendering: crispEdges;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
path, line, .axis line, .axis path {
|
||||
stroke: #ddd;
|
||||
fill: none;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.legend-box {
|
||||
fill: #ffffff;
|
||||
}
|
||||
|
||||
.brush .extent {
|
||||
stroke: #fff;
|
||||
fill-opacity: .125;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.layer .rect:hover {
|
||||
stroke: 3px;
|
||||
}
|
||||
|
||||
.k4tip {
|
||||
line-height: 1.1;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
padding: 8px;
|
||||
background: rgba(70, 82, 93, 0.95);
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
position: fixed;
|
||||
z-index: 120;
|
||||
visibility: hidden;
|
||||
word-wrap: break-word;
|
||||
max-width: 140px;
|
||||
}
|
||||
|
||||
.rect {
|
||||
/*shape-rendering: crispEdges;*/
|
||||
stroke: transparent;
|
||||
stroke-width: 2;
|
||||
}
|
||||
.rect.hover {
|
||||
/*shape-rendering: crispEdges;*/
|
||||
stroke: #333;
|
||||
}
|
||||
|
||||
/* Flex Box */
|
||||
.vis-wrapper {
|
||||
.display(flex);
|
||||
.flex(1 1 100%);
|
||||
.flex-direction(row);
|
||||
margin: 10px 0 0 6px;
|
||||
}
|
||||
|
||||
.error {
|
||||
.flex(1 1 100%);
|
||||
text-align: center;
|
||||
|
||||
p {
|
||||
margin-top: 15%;
|
||||
font-size: 18px;
|
||||
text-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
/* YAxis logic */
|
||||
.y-axis-col-wrapper {
|
||||
.display(flex);
|
||||
.flex(0 0 auto);
|
||||
.flex-direction(column);
|
||||
min-width: 35px;
|
||||
}
|
||||
|
||||
.y-axis-col {
|
||||
.display(flex);
|
||||
.flex-direction(row);
|
||||
.flex(1 0 50px);
|
||||
}
|
||||
|
||||
.y-axis-spacer-block {
|
||||
.flex(0 1 50px);
|
||||
}
|
||||
|
||||
.y-axis-div-wrapper {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
.flex(0 0 33px);
|
||||
}
|
||||
|
||||
.y-axis-div {
|
||||
.flex(1 1 100%);
|
||||
}
|
||||
|
||||
.y-axis-title {
|
||||
.flex(0 0 15px);
|
||||
}
|
||||
|
||||
.y-axis-chart-title {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
.flex(0 0 15px);
|
||||
}
|
||||
|
||||
.y-axis-title text {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
.flex(1 1 100%);
|
||||
}
|
||||
|
||||
.chart-title text {
|
||||
font-size: 11px;
|
||||
fill: #848e96;
|
||||
}
|
||||
|
||||
.vis-col-wrapper {
|
||||
.display(flex);
|
||||
.flex(1 0 20px);
|
||||
.flex-direction(column);
|
||||
}
|
||||
|
||||
.chart-wrapper {
|
||||
.display(flex);
|
||||
.flex(1 0 20px);
|
||||
overflow: visible;
|
||||
margin: 0 4px 0 0;
|
||||
}
|
||||
|
||||
.chart-wrapper-column {
|
||||
.display(flex);
|
||||
.flex(1 0 20px);
|
||||
.flex-direction(row);
|
||||
}
|
||||
|
||||
.chart-wrapper-row {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
.flex(1 0 50px);
|
||||
}
|
||||
|
||||
.chart {
|
||||
.flex(1 1);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.chart-row {
|
||||
.flex(1 1);
|
||||
}
|
||||
|
||||
.chart-column {
|
||||
.flex(1 1);
|
||||
}
|
||||
|
||||
.x-axis-wrapper {
|
||||
.display(flex);
|
||||
.flex-direction(column);
|
||||
.flex(0 1 50px);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.x-axis-div-wrapper {
|
||||
.display(flex);
|
||||
.flex-direction(row);
|
||||
.flex(0 1 15px);
|
||||
}
|
||||
|
||||
.x-axis-chart-title {
|
||||
.display(flex);
|
||||
.flex-direction(row);
|
||||
.flex(0 0 15px);
|
||||
}
|
||||
|
||||
.x-axis-title {
|
||||
.flex(0 0 15px);
|
||||
}
|
||||
|
||||
.x-axis-title text {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.x-axis-div {
|
||||
.flex(1 1 100%);
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.x.axis path {
|
||||
display: none;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Main module
|
||||
* Accepts an html element and a config object.
|
||||
* Calls all other K4 modules.
|
||||
* Returns the charting function.
|
||||
*/
|
||||
define(function (require) {
|
||||
|
||||
return function (elem, args) {
|
||||
var type = args.type,
|
||||
charts = {
|
||||
'histogram': require('components/vislib/modules/histogram'),
|
||||
'line': require('components/vislib/modules/lineChart'),
|
||||
'area': require('components/vislib/modules/areaChart'),
|
||||
'pie': require('components/vislib/modules/pieChart'),
|
||||
};
|
||||
|
||||
if (typeof (charts[type]) !== 'function') {
|
||||
throw type + ' is not a supported k4 function.';
|
||||
}
|
||||
|
||||
var chartFunc = charts[type](elem, args);
|
||||
|
||||
return chartFunc;
|
||||
};
|
||||
});
|
|
@ -1,14 +1,17 @@
|
|||
define(function (require) {
|
||||
var module = require('modules').get('kibana/vislib', ['kibana']);
|
||||
|
||||
require('services/private');
|
||||
|
||||
var k4 = {
|
||||
version: '0.0.0',
|
||||
legend: require('components/vislib/modules/legend'),
|
||||
Chart: require('components/vislib/core'),
|
||||
histogram: require('components/vislib/modules/histogram'),
|
||||
line: require('components/vislib/modules/lineChart'),
|
||||
area: require('components/vislib/modules/areaChart'),
|
||||
pie: require('components/vislib/modules/pieChart'),
|
||||
};
|
||||
module.service('d3', function () {
|
||||
return require('d3');
|
||||
});
|
||||
|
||||
return k4;
|
||||
// Kibana visualization library
|
||||
module.service('visLib', function (Private) {
|
||||
return {
|
||||
version: '0.0.0',
|
||||
Vis: Private(require('components/vislib/vis'))
|
||||
};
|
||||
});
|
||||
});
|
||||
|
|
8
src/kibana/components/vislib/layout_types.js
Normal file
8
src/kibana/components/vislib/layout_types.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
define(function (require) {
|
||||
return function LayoutTypeFactory(Private) {
|
||||
// visLib layout types
|
||||
return {
|
||||
histogram: Private(require('components/vislib/components/layouts/types/column_layout'))
|
||||
};
|
||||
};
|
||||
});
|
19
src/kibana/components/vislib/lib/_error_handler.js
Normal file
19
src/kibana/components/vislib/lib/_error_handler.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
define(function (require) {
|
||||
return function ErrorHandlerFactory(Private) {
|
||||
var _ = require('lodash');
|
||||
|
||||
// Common errors shared between constructors
|
||||
function ErrorHandler() {}
|
||||
|
||||
// Validate that the height and width are not 0 or NaN
|
||||
ErrorHandler.prototype.validateWidthandHeight = function (width, height) {
|
||||
if (_.isNaN(height) || height <= 0 || _.isNaN(width) || width <= 0) {
|
||||
throw new Error('The height and/or width of this container is too ' +
|
||||
'small for this chart. w:' + width + ', h:' + height);
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
return ErrorHandler;
|
||||
};
|
||||
});
|
59
src/kibana/components/vislib/lib/axis_title.js
Normal file
59
src/kibana/components/vislib/lib/axis_title.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
define(function (require) {
|
||||
return function AxisTitleFactory(d3, Private) {
|
||||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
|
||||
var ErrorHandler = Private(require('components/vislib/lib/_error_handler'));
|
||||
|
||||
/*
|
||||
* Appends axis title(s) to the visualization
|
||||
*/
|
||||
function AxisTitle(el, xTitle, yTitle) {
|
||||
if (!(this instanceof AxisTitle)) {
|
||||
return new AxisTitle(el, xTitle, yTitle);
|
||||
}
|
||||
|
||||
this.el = el;
|
||||
this.xTitle = xTitle;
|
||||
this.yTitle = yTitle;
|
||||
}
|
||||
|
||||
_(AxisTitle.prototype).extend(ErrorHandler.prototype);
|
||||
|
||||
// Render both x and y axis titles
|
||||
AxisTitle.prototype.render = function () {
|
||||
d3.select(this.el).select('.x-axis-title').call(this.draw(this.xTitle));
|
||||
d3.select(this.el).select('.y-axis-title').call(this.draw(this.yTitle));
|
||||
};
|
||||
|
||||
// Return a callback function that appends an svg with title text
|
||||
AxisTitle.prototype.draw = function (title) {
|
||||
var self = this;
|
||||
|
||||
return function (selection) {
|
||||
selection.each(function () {
|
||||
var div = d3.select(this);
|
||||
var width = $(this).width();
|
||||
var height = $(this).height();
|
||||
|
||||
self.validateWidthandHeight(width, height);
|
||||
|
||||
div.append('svg')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.append('text')
|
||||
.attr('transform', function () {
|
||||
if (div.attr('class') === 'x-axis-title') {
|
||||
return 'translate(' + width / 2 + ',11)';
|
||||
}
|
||||
return 'translate(11,' + height / 2 + ')rotate(270)';
|
||||
})
|
||||
.attr('text-anchor', 'middle')
|
||||
.text(title);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
return AxisTitle;
|
||||
};
|
||||
});
|
115
src/kibana/components/vislib/lib/chart_title.js
Normal file
115
src/kibana/components/vislib/lib/chart_title.js
Normal file
|
@ -0,0 +1,115 @@
|
|||
define(function (require) {
|
||||
return function ChartTitleFactory(d3, Private) {
|
||||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
|
||||
var ErrorHandler = Private(require('components/vislib/lib/_error_handler'));
|
||||
var Tooltip = Private(require('components/vislib/lib/tooltip'));
|
||||
|
||||
/*
|
||||
* Append chart titles to the visualization
|
||||
* arguments:
|
||||
* el => reference to a DOM element
|
||||
*/
|
||||
function ChartTitle(el) {
|
||||
if (!(this instanceof ChartTitle)) {
|
||||
return new ChartTitle(el);
|
||||
}
|
||||
|
||||
this.el = el;
|
||||
this.tooltip = new Tooltip(el, function (d) {
|
||||
return d.label;
|
||||
});
|
||||
}
|
||||
|
||||
_(ChartTitle.prototype).extend(ErrorHandler.prototype);
|
||||
|
||||
// Render chart titles
|
||||
ChartTitle.prototype.render = function () {
|
||||
return d3.select(this.el).selectAll('.chart-title').call(this.draw());
|
||||
};
|
||||
|
||||
// Return a function that truncates chart title text
|
||||
ChartTitle.prototype.truncate = function (size) {
|
||||
var self = this;
|
||||
|
||||
return function (selection) {
|
||||
selection.each(function () {
|
||||
var text = d3.select(this);
|
||||
var n = text[0].length;
|
||||
var maxWidth = size / n * 0.9;
|
||||
var length = this.getComputedTextLength();
|
||||
var str;
|
||||
var avg;
|
||||
var end;
|
||||
|
||||
if (length > maxWidth) {
|
||||
str = text.text();
|
||||
avg = length / str.length;
|
||||
end = Math.floor(maxWidth / avg) - 5;
|
||||
|
||||
str = str.substr(0, end) + '...';
|
||||
|
||||
// mouseover and mouseout
|
||||
self.addMouseEvents(text);
|
||||
|
||||
return text.text(str);
|
||||
}
|
||||
|
||||
return text.text();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// Add mouseover and mouseout events on truncated chart titles
|
||||
ChartTitle.prototype.addMouseEvents = function (target) {
|
||||
if (this.tooltip) {
|
||||
return target.call(this.tooltip.render());
|
||||
}
|
||||
};
|
||||
|
||||
// Return a callback function that appends chart titles to the visualization
|
||||
ChartTitle.prototype.draw = function () {
|
||||
var self = this;
|
||||
|
||||
return function (selection) {
|
||||
selection.each(function () {
|
||||
var div = d3.select(this);
|
||||
var dataType = this.parentNode.__data__.rows ? 'rows' : 'columns';
|
||||
var width = $(this).width();
|
||||
var height = $(this).height();
|
||||
var size = dataType === 'rows' ? height : width;
|
||||
|
||||
// Check if width or height are 0 or NaN
|
||||
self.validateWidthandHeight(width, height);
|
||||
|
||||
div.append('svg')
|
||||
.attr('width', function () {
|
||||
if (dataType === 'rows') {
|
||||
return 15;
|
||||
}
|
||||
return width;
|
||||
})
|
||||
.attr('height', height)
|
||||
.append('text')
|
||||
.attr('transform', function () {
|
||||
if (dataType === 'rows') {
|
||||
// if `rows`, rotate the chart titles
|
||||
return 'translate(11,' + height / 2 + ')rotate(270)';
|
||||
}
|
||||
return 'translate(' + width / 2 + ',8)';
|
||||
})
|
||||
.attr('text-anchor', 'middle')
|
||||
.text(function (d) {
|
||||
return d.label;
|
||||
});
|
||||
|
||||
// truncate long chart titles
|
||||
div.selectAll('text').call(self.truncate(size));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
return ChartTitle;
|
||||
};
|
||||
});
|
132
src/kibana/components/vislib/lib/data.js
Normal file
132
src/kibana/components/vislib/lib/data.js
Normal file
|
@ -0,0 +1,132 @@
|
|||
define(function (require) {
|
||||
return function DataFactory(d3, Private) {
|
||||
var _ = require('lodash');
|
||||
|
||||
var injectZeros = Private(require('components/vislib/components/_functions/zero_injection/inject_zeros'));
|
||||
var orderKeys = Private(require('components/vislib/components/_functions/zero_injection/ordered_x_keys'));
|
||||
var getLabels = Private(require('components/vislib/components/_functions/labels/labels'));
|
||||
var color = Private(require('components/vislib/components/_functions/color/color'));
|
||||
|
||||
/*
|
||||
* Provides an API for pulling values off the data
|
||||
* arguments:
|
||||
* data => Provided data object
|
||||
*/
|
||||
function Data(data, attr) {
|
||||
if (!(this instanceof Data)) {
|
||||
return new Data(data, attr);
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
this._attr = attr;
|
||||
// d3 stack function
|
||||
this._attr = _.defaults(attr || {}, {
|
||||
offset: 'zero',
|
||||
stack: d3.layout.stack()
|
||||
.x(function (d) { return d.x; })
|
||||
.y(function (d) { return d.y; })
|
||||
.offset(this._attr.offset)
|
||||
});
|
||||
}
|
||||
|
||||
// Return the actual x and y data values
|
||||
Data.prototype.chartData = function () {
|
||||
if (!this.data.series) {
|
||||
var arr = this.data.rows ? this.data.rows : this.data.columns;
|
||||
return _.pluck(arr);
|
||||
}
|
||||
return [this.data];
|
||||
};
|
||||
|
||||
// Get attributes off the data, e.g. `tooltipFormatter` or `xAxisFormatter`
|
||||
Data.prototype.get = function (thing) {
|
||||
var data = this.chartData();
|
||||
// pulls the value off the first item in the array
|
||||
// these values are typically the same between data objects of the same chart
|
||||
// May need to verify this or refactor
|
||||
return _.pluck(data, thing)[0];
|
||||
};
|
||||
|
||||
// Return an array of all value objects
|
||||
Data.prototype.flatten = function () {
|
||||
var data = this.chartData();
|
||||
// Pluck the data.series array from each data object
|
||||
var series = _.chain(data).pluck('series').pluck().value();
|
||||
var values = [];
|
||||
|
||||
// Create an array of all the value objects from the series array
|
||||
_(series).forEach(function (d) {
|
||||
values.push(_.chain(d).flatten().pluck('values').value());
|
||||
});
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
Data.prototype.shouldBeStacked = function (series) {
|
||||
// Series should be an array
|
||||
if (series.length > 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Calculate the max y value from this.dataArray
|
||||
Data.prototype.getYMaxValue = function () {
|
||||
var self = this;
|
||||
var arr = [];
|
||||
|
||||
// for each object in the dataArray,
|
||||
// push the calculated y value to the initialized array (arr)
|
||||
_.forEach(this.flatten(), function (series) {
|
||||
arr.push(self.getYStackMax(series));
|
||||
});
|
||||
|
||||
// return the largest value from the array
|
||||
return _.max(arr);
|
||||
};
|
||||
|
||||
Data.prototype.stackData = function (series) {
|
||||
// Determine if the data should be stacked
|
||||
if (this.shouldBeStacked(series)) {
|
||||
// if true, stack data
|
||||
return this._attr.stack(series);
|
||||
}
|
||||
return series;
|
||||
};
|
||||
|
||||
Data.prototype.getYStackMax = function (series) {
|
||||
// Return the calculated y value
|
||||
return d3.max(this.stackData(series), function (data) {
|
||||
return d3.max(data, function (d) {
|
||||
// if stacked, need to add d.y0 + d.y for the y value
|
||||
if (d.y0) {
|
||||
return d.y0 + d.y;
|
||||
}
|
||||
return d.y;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Inject zeros into the data
|
||||
Data.prototype.injectZeros = function () {
|
||||
return injectZeros(this.data);
|
||||
};
|
||||
|
||||
// Return an array of all x item values from the data
|
||||
Data.prototype.xValues = function () {
|
||||
return orderKeys(this.data);
|
||||
};
|
||||
|
||||
// Return an array of unique labels
|
||||
Data.prototype.getLabels = function () {
|
||||
return getLabels(this.data);
|
||||
};
|
||||
|
||||
// Return a function that does color lookup on labels
|
||||
Data.prototype.getColorFunc = function () {
|
||||
return color(this.getLabels(this.data));
|
||||
};
|
||||
|
||||
return Data;
|
||||
};
|
||||
});
|
147
src/kibana/components/vislib/lib/handler.js
Normal file
147
src/kibana/components/vislib/lib/handler.js
Normal file
|
@ -0,0 +1,147 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
|
||||
return function HandlerBaseClass(d3, Private) {
|
||||
var Data = Private(require('components/vislib/lib/data'));
|
||||
var Layout = Private(require('components/vislib/lib/layout'));
|
||||
var Legend = Private(require('components/vislib/lib/legend'));
|
||||
var Tooltip = Private(require('components/vislib/lib/tooltip'));
|
||||
var XAxis = Private(require('components/vislib/lib/x_axis'));
|
||||
var YAxis = Private(require('components/vislib/lib/y_axis'));
|
||||
var AxisTitle = Private(require('components/vislib/lib/axis_title'));
|
||||
var ChartTitle = Private(require('components/vislib/lib/chart_title'));
|
||||
|
||||
/*
|
||||
* Handles building all the components of the visualization
|
||||
* arguments:
|
||||
* vis => this object from vis.js
|
||||
*
|
||||
* returns an object with reference to the vis.prototype,
|
||||
* and news up all the constructors needed to build a visualization
|
||||
*/
|
||||
function Handler(vis) {
|
||||
if (!(this instanceof Handler)) {
|
||||
return new Handler(vis);
|
||||
}
|
||||
|
||||
this.data = new Data(vis.data, vis._attr);
|
||||
this.vis = vis;
|
||||
this.el = vis.el;
|
||||
this.ChartClass = vis.ChartClass;
|
||||
this._attr = _.defaults(vis._attr || {}, {
|
||||
'margin' : { top: 10, right: 3, bottom: 5, left: 3 }
|
||||
});
|
||||
|
||||
// Visualization constructors
|
||||
// Add the visualization layout
|
||||
this.layout = new Layout(this.el, this.data.injectZeros(), this._attr.type);
|
||||
|
||||
// Only add legend if addLegend attribute set
|
||||
if (this._attr.addLegend) {
|
||||
this.legend = new Legend(this.vis, this.el, this.data.getLabels(), this.data.getColorFunc(), this._attr);
|
||||
}
|
||||
|
||||
// only add tooltip if addTooltip attribute set
|
||||
if (this._attr.addTooltip) {
|
||||
this.tooltip = new Tooltip(this.el, this.data.get('tooltipFormatter'));
|
||||
}
|
||||
|
||||
// add a x axis
|
||||
this.xAxis = new XAxis({
|
||||
el: this.el,
|
||||
xValues: this.data.xValues(),
|
||||
ordered: this.data.get('ordered'),
|
||||
xAxisFormatter: this.data.get('xAxisFormatter'),
|
||||
_attr: this._attr
|
||||
});
|
||||
|
||||
// add a y axis
|
||||
this.yAxis = new YAxis({
|
||||
el: this.el,
|
||||
yMax: this.data.getYMaxValue(),
|
||||
_attr: this._attr
|
||||
});
|
||||
|
||||
// add axis titles
|
||||
this.axisTitle = new AxisTitle(this.el, this.data.get('xAxisLabel'), this.data.get('yAxisLabel'));
|
||||
|
||||
// add chart titles
|
||||
this.chartTitle = new ChartTitle(this.el);
|
||||
|
||||
// Array of objects to render to the visualization
|
||||
this.renderArray = _.filter([
|
||||
this.layout,
|
||||
this.legend,
|
||||
this.tooltip,
|
||||
this.axisTitle,
|
||||
this.chartTitle,
|
||||
this.yAxis,
|
||||
this.xAxis
|
||||
], Boolean);
|
||||
}
|
||||
|
||||
// Render the visualization
|
||||
Handler.prototype.render = function () {
|
||||
var self = this;
|
||||
// Save a reference to the charts
|
||||
var charts = this.charts = [];
|
||||
|
||||
// Render objects in the render array
|
||||
_.forEach(this.renderArray, function (property) {
|
||||
if (typeof property.render === 'function') {
|
||||
property.render();
|
||||
}
|
||||
});
|
||||
|
||||
// Add charts to the visualization
|
||||
d3.select(this.el)
|
||||
.selectAll('.chart')
|
||||
.each(function (chartData) {
|
||||
// new up the visualization type
|
||||
var chart = new self.ChartClass(self, this, chartData);
|
||||
|
||||
// Bind events to the chart
|
||||
d3.rebind(chart, chart._attr.dispatch, 'on');
|
||||
|
||||
// Bubble events up to the Vis Class and Events Class
|
||||
chart.on('click', function (e) {
|
||||
self.vis.emit('click', e);
|
||||
});
|
||||
|
||||
chart.on('hover', function (e) {
|
||||
self.vis.emit('hover', e);
|
||||
});
|
||||
|
||||
chart.on('brush', function (e) {
|
||||
self.vis.emit('brush', e);
|
||||
});
|
||||
|
||||
// Save reference to charts
|
||||
charts.push(chart);
|
||||
|
||||
// Render charts to screen
|
||||
chart.render();
|
||||
});
|
||||
};
|
||||
|
||||
// Remove all DOM elements from the `el` provided
|
||||
Handler.prototype.removeAll = function (el) {
|
||||
return d3.select(el).selectAll('*').remove();
|
||||
};
|
||||
|
||||
// Display an error message on the screen
|
||||
Handler.prototype.error = function (message) {
|
||||
this.removeAll(this.el);
|
||||
|
||||
// Return an error wrapper DOM element
|
||||
return d3.select(this.el).append('div')
|
||||
// class name needs `chart` in it for the polling checkSize function
|
||||
// to continuously call render on resize
|
||||
.attr('class', 'chart error')
|
||||
.append('p')
|
||||
.text(message);
|
||||
};
|
||||
|
||||
return Handler;
|
||||
};
|
||||
});
|
118
src/kibana/components/vislib/lib/layout.js
Normal file
118
src/kibana/components/vislib/lib/layout.js
Normal file
|
@ -0,0 +1,118 @@
|
|||
define(function (require) {
|
||||
return function LayoutFactory(d3, Private) {
|
||||
var _ = require('lodash');
|
||||
|
||||
var layoutType = Private(require('components/vislib/layout_types'));
|
||||
|
||||
/*
|
||||
* The Layout Constructor is responsible for rendering the visualization
|
||||
* layout, which includes all the DOM div elements.
|
||||
* Input:
|
||||
* 1. DOM div - parent element for which the layout is attached
|
||||
* 2. data - data is bound to the div element
|
||||
* 3. chartType (e.g. 'histogram') - specifies the layout type to grab
|
||||
*/
|
||||
function Layout(el, data, chartType) {
|
||||
if (!(this instanceof Layout)) {
|
||||
return new Layout(el, data, chartType);
|
||||
}
|
||||
|
||||
this.el = el;
|
||||
this.data = data;
|
||||
this.layoutType = layoutType[chartType](el, data);
|
||||
}
|
||||
|
||||
// Render the layout
|
||||
Layout.prototype.render = function () {
|
||||
// Remove all elements from the current visualization
|
||||
this.removeAll(this.el);
|
||||
|
||||
// Create the layout
|
||||
this.createLayout(this.layoutType);
|
||||
};
|
||||
|
||||
// Create the layout based on the json array provided
|
||||
Layout.prototype.createLayout = function (arr) {
|
||||
var self = this;
|
||||
|
||||
// for each object in the layout array, call the layout function
|
||||
return _(arr).forEach(function (obj) {
|
||||
self.layout(obj);
|
||||
});
|
||||
};
|
||||
|
||||
// Appends a DOM element based on the object keys
|
||||
Layout.prototype.layout = function (obj) {
|
||||
if (!obj.parent) {
|
||||
throw new Error('No parent element provided');
|
||||
}
|
||||
|
||||
if (!obj.type) {
|
||||
throw new Error('No element type provided');
|
||||
}
|
||||
|
||||
if (typeof obj.type !== 'string') {
|
||||
throw new Error(obj.type + ' must be a string');
|
||||
}
|
||||
|
||||
// check to see if reference to DOM element is string but not class selector
|
||||
if (typeof obj.parent === 'string' && obj.parent.charAt(0) !== '.') {
|
||||
// Create a class selector
|
||||
obj.parent = '.' + obj.parent;
|
||||
}
|
||||
|
||||
// append child
|
||||
var childEl = this.appendElem(obj.parent, obj.type, obj.class);
|
||||
|
||||
if (obj.datum) {
|
||||
// Bind datum to the element
|
||||
childEl.datum(obj.datum);
|
||||
}
|
||||
|
||||
if (obj.splits) {
|
||||
// Call the split on its obj.class
|
||||
d3.select(this.el).select('.' + obj.class).call(obj.splits);
|
||||
}
|
||||
|
||||
if (obj.children) {
|
||||
// Creating the parent elem for the child nodes
|
||||
var newParent = d3.select(this.el).select('.' + obj.class)[0][0];
|
||||
|
||||
_.forEach(obj.children, function (obj) {
|
||||
if (!obj.parent) {
|
||||
obj.parent = newParent;
|
||||
}
|
||||
});
|
||||
|
||||
// Recursively pass children to createLayout
|
||||
this.createLayout(obj.children);
|
||||
}
|
||||
|
||||
return childEl;
|
||||
};
|
||||
|
||||
// Appends a `type` of DOM element to `el` and gives it a class name attribute `className`
|
||||
Layout.prototype.appendElem = function (el, type, className) {
|
||||
if (!el || !type || !className) {
|
||||
throw new Error('Function requires that an el, type, and class be provided');
|
||||
}
|
||||
|
||||
if (typeof el === 'string') {
|
||||
// Create a DOM reference with a d3 selection
|
||||
// Need to make sure that the `el` is bound to this object
|
||||
// to prevent it from being appended to another Layout
|
||||
el = d3.select(this.el).select(el)[0][0];
|
||||
}
|
||||
|
||||
return d3.select(el).append(type)
|
||||
.attr('class', className);
|
||||
};
|
||||
|
||||
// Removes all DOM elements from `el`
|
||||
Layout.prototype.removeAll = function (el) {
|
||||
return d3.select(el).selectAll('*').remove();
|
||||
};
|
||||
|
||||
return Layout;
|
||||
};
|
||||
});
|
128
src/kibana/components/vislib/lib/legend.js
Normal file
128
src/kibana/components/vislib/lib/legend.js
Normal file
|
@ -0,0 +1,128 @@
|
|||
define(function (require) {
|
||||
return function LegendFactory(d3, Private) {
|
||||
var _ = require('lodash');
|
||||
|
||||
// Dynamically adds css file
|
||||
require('css!components/vislib/components/styles/main');
|
||||
|
||||
/*
|
||||
* Append legend to the visualization
|
||||
* arguments:
|
||||
* el => reference to DOM element
|
||||
* labels => array of labels from the chart data
|
||||
* color => color function to assign colors to labels
|
||||
* _attr => visualization attributes
|
||||
*/
|
||||
function Legend(vis, el, labels, color, _attr) {
|
||||
if (!(this instanceof Legend)) {
|
||||
return new Legend(vis, el, labels, color, _attr);
|
||||
}
|
||||
|
||||
this.vis = vis;
|
||||
this.el = el;
|
||||
this.labels = labels;
|
||||
this.color = color;
|
||||
this._attr = _.defaults(_attr || {}, {
|
||||
// Legend specific attributes
|
||||
'legendClass' : 'legend-col-wrapper',
|
||||
'blurredOpacity' : 0.3,
|
||||
'focusOpacity' : 1,
|
||||
'defaultOpacity' : 1,
|
||||
'isOpen' : false
|
||||
});
|
||||
}
|
||||
|
||||
// Add legend header
|
||||
// Need to change the header icon
|
||||
Legend.prototype.header = function (el, args) {
|
||||
return el.append('div')
|
||||
.attr('class', 'header')
|
||||
.append('div')
|
||||
.attr('class', 'column-labels')
|
||||
.html(function (d) {
|
||||
if (args._attr.isOpen) {
|
||||
return '<span class="btn btn-xs btn-default legend-toggle">' +
|
||||
'<i class="fa fa-chevron-right"></i></span>';
|
||||
}
|
||||
return '<span class="btn btn-xs btn-default legend-toggle">' +
|
||||
'<i class="fa fa-chevron-left"></i></span>';
|
||||
});
|
||||
};
|
||||
|
||||
// Add legend list
|
||||
Legend.prototype.list = function (el, arrOfLabels, args) {
|
||||
var self = this;
|
||||
|
||||
return el.append('ul')
|
||||
.attr('class', function () {
|
||||
if (args._attr.isOpen) {
|
||||
return 'legend-ul';
|
||||
}
|
||||
return 'legend-ul hidden';
|
||||
})
|
||||
.selectAll('li')
|
||||
.data(arrOfLabels)
|
||||
.enter()
|
||||
.append('li')
|
||||
.attr('class', function (d) {
|
||||
// class names reflect the color assigned to the labels
|
||||
return 'color ' + self.colorToClass(args.color(d));
|
||||
})
|
||||
.html(function (d) {
|
||||
// return the appropriate color for each dot
|
||||
return '<span class="dots" style="background:' + args.color(d) + '"></span>' + d;
|
||||
});
|
||||
};
|
||||
|
||||
// Create a class name based on the colors assigned to each label
|
||||
Legend.prototype.colorToClass = function (name) {
|
||||
return 'c' + name.replace(/[#]/g, '');
|
||||
};
|
||||
|
||||
// Render the legend
|
||||
Legend.prototype.render = function () {
|
||||
var visEl = d3.select(this.el);
|
||||
var legendDiv = visEl.select('.' + this._attr.legendClass);
|
||||
var items = this.labels;
|
||||
var header = this.header(legendDiv, this);
|
||||
var headerIcon = visEl.select('.legend-toggle');
|
||||
var list = this.list(legendDiv, items, this);
|
||||
|
||||
var self = this;
|
||||
|
||||
// toggle
|
||||
headerIcon.on('click', function () {
|
||||
if (self._attr.isOpen) {
|
||||
// close legend
|
||||
visEl.select('ul.legend-ul')
|
||||
.classed('hidden', true);
|
||||
self._attr.isOpen = false;
|
||||
// need to add reference to resize function on toggle
|
||||
self.vis.resize();
|
||||
} else {
|
||||
// open legend
|
||||
visEl.select('ul.legend-ul')
|
||||
.classed('hidden', false);
|
||||
self._attr.isOpen = true;
|
||||
|
||||
// need to add reference to resize function on toggle
|
||||
self.vis.resize();
|
||||
}
|
||||
});
|
||||
|
||||
visEl.selectAll('.color')
|
||||
.on('mouseover', function (d) {
|
||||
var liClass = '.' + self.colorToClass(self.color(d));
|
||||
visEl.selectAll('.color').style('opacity', self._attr.blurredOpacity);
|
||||
|
||||
// select series on chart
|
||||
visEl.selectAll(liClass).style('opacity', self._attr.focusOpacity);
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
visEl.selectAll('.color').style('opacity', self._attr.defaultOpacity);
|
||||
});
|
||||
};
|
||||
|
||||
return Legend;
|
||||
};
|
||||
});
|
209
src/kibana/components/vislib/lib/resize_checker.js
Normal file
209
src/kibana/components/vislib/lib/resize_checker.js
Normal file
|
@ -0,0 +1,209 @@
|
|||
define(function (require) {
|
||||
return function ResizeCheckerFactory(Private, Notifier) {
|
||||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
|
||||
var EventEmitter = Private(require('factories/events'));
|
||||
var reflowWatcher = Private(require('components/reflow_watcher'));
|
||||
var sequencer = require('utils/sequencer');
|
||||
|
||||
var SCHEDULE_LONG = ResizeChecker.SCHEDULE_LONG = sequencer.createEaseOut(
|
||||
250, // shortest delay
|
||||
10000, // longest delay
|
||||
150 // tick count
|
||||
);
|
||||
|
||||
var SCHEDULE_SHORT = ResizeChecker.SCHEDULE_SHORT = sequencer.createEaseIn(
|
||||
5, // shortest delay
|
||||
500, // longest delay
|
||||
100 // tick count
|
||||
);
|
||||
|
||||
// maximum ms that we can delay emitting 'resize'. This is only used
|
||||
// to debounce resizes when the size of the element is constantly changing
|
||||
var MS_MAX_RESIZE_DELAY = ResizeChecker.MS_MAX_RESIZE_DELAY = 500;
|
||||
|
||||
/**
|
||||
* Checks the size of an element on a regular basis. Provides
|
||||
* an event that is emited when the element has changed size.
|
||||
*
|
||||
* @class ResizeChecker
|
||||
* @param {HtmlElement} el - the element to track the size of
|
||||
*/
|
||||
_(ResizeChecker).inherits(EventEmitter);
|
||||
function ResizeChecker(el) {
|
||||
ResizeChecker.Super.call(this);
|
||||
|
||||
this.$el = $(el);
|
||||
this.notify = new Notifier({ location: 'Vislib ResizeChecker ' + _.uniqueId() });
|
||||
|
||||
this.saveSize();
|
||||
|
||||
this.check = _.bind(this.check, this);
|
||||
this.check();
|
||||
|
||||
this.onReflow = _.bind(this.onReflow, this);
|
||||
reflowWatcher.on('reflow', this.onReflow);
|
||||
}
|
||||
|
||||
ResizeChecker.prototype.onReflow = function () {
|
||||
this.startSchedule(SCHEDULE_LONG);
|
||||
};
|
||||
|
||||
/**
|
||||
* Read the size of the element
|
||||
*
|
||||
* @method read
|
||||
* @return {object} - an object with keys `w` (width) and `h` (height)
|
||||
*/
|
||||
ResizeChecker.prototype.read = function () {
|
||||
return {
|
||||
w: this.$el.width(),
|
||||
h: this.$el.height()
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Save the element size, preventing it from being considered as an
|
||||
* update.
|
||||
*
|
||||
* @method save
|
||||
* @param {object} [size] - optional size to save, otherwise #read() is called
|
||||
* @return {boolean} - true if their was a change in the new
|
||||
*/
|
||||
ResizeChecker.prototype.saveSize = function (size) {
|
||||
if (!size) size = this.read();
|
||||
|
||||
if (this._equalsSavedSize(size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._savedSize = size;
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Determine if a given size matches the currently saved size.
|
||||
*
|
||||
* @private
|
||||
* @method _equalsSavedSize
|
||||
* @param {object} a - an object that matches the return value of #read()
|
||||
* @return {boolean} - true if the passed in value matches the saved size
|
||||
*/
|
||||
ResizeChecker.prototype._equalsSavedSize = function (a) {
|
||||
var b = this._savedSize || {};
|
||||
return a.w === b.w && a.h === b.h;
|
||||
};
|
||||
|
||||
/**
|
||||
* Read the time that the dirty state last changed.
|
||||
*
|
||||
* @method lastDirtyChange
|
||||
* @return {timestamp} - the unix timestamp (in ms) of the last update
|
||||
* to the dirty state
|
||||
*/
|
||||
ResizeChecker.prototype.lastDirtyChange = function () {
|
||||
return this._dirtyChangeStamp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Record the dirty state
|
||||
*
|
||||
* @method saveDirty
|
||||
* @param {boolean} val
|
||||
* @return {boolean} - true if the dirty state changed by this save
|
||||
*/
|
||||
ResizeChecker.prototype.saveDirty = function (val) {
|
||||
val = !!val;
|
||||
|
||||
if (val === this._isDirty) return false;
|
||||
|
||||
this._isDirty = val;
|
||||
this._dirtyChangeStamp = Date.now();
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* The check routine that executes regularly and will reschedule itself
|
||||
* to run again in the future. It determines the state of the elements
|
||||
* size and decides when to emit the "update" event.
|
||||
*
|
||||
* @method check
|
||||
* @return {void}
|
||||
*/
|
||||
ResizeChecker.prototype.check = function () {
|
||||
var newSize = this.read();
|
||||
var dirty = this.saveSize(newSize);
|
||||
var dirtyChanged = this.saveDirty(dirty);
|
||||
|
||||
var doneDirty = !dirty && dirtyChanged;
|
||||
var muchDirty = dirty && (this.lastDirtyChange() - Date.now() > MS_MAX_RESIZE_DELAY);
|
||||
if (doneDirty || muchDirty) {
|
||||
this.emit('resize', newSize);
|
||||
}
|
||||
|
||||
// if the dirty state is unchanged, continue using the previous schedule
|
||||
if (!dirtyChanged) {
|
||||
return this.continueSchedule();
|
||||
}
|
||||
|
||||
// when the state changes start a new schedule. Use a schedule that quickly
|
||||
// slows down if it is unknown wether there are will be additional changes
|
||||
return this.startSchedule(dirty ? SCHEDULE_SHORT : SCHEDULE_LONG);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start running a new schedule, using one of the SCHEDULE_* constants.
|
||||
*
|
||||
* @method startSchedule
|
||||
* @param {integer[]} schedule - an array of millisecond times that should
|
||||
* be used to schedule calls to #check();
|
||||
* @return {integer} - the id of the next timer
|
||||
*/
|
||||
ResizeChecker.prototype.startSchedule = function (schedule) {
|
||||
this._tick = -1;
|
||||
this._currentSchedule = schedule;
|
||||
return this.continueSchedule();
|
||||
};
|
||||
|
||||
/**
|
||||
* Continue running the current schedule. MUST BE CALLED AFTER #startSchedule()
|
||||
*
|
||||
* @method continueSchedule
|
||||
* @return {integer} - the id of the next timer
|
||||
*/
|
||||
ResizeChecker.prototype.continueSchedule = function () {
|
||||
clearTimeout(this._timerId);
|
||||
|
||||
if (this._tick < this._currentSchedule.length - 1) {
|
||||
// at the end of the schedule, don't progress any further but repeat the last value
|
||||
this._tick += 1;
|
||||
}
|
||||
|
||||
var check = this.check; // already bound
|
||||
var tick = this._tick;
|
||||
var notify = this.notify;
|
||||
var ms = this._currentSchedule[this._tick];
|
||||
return (this._timerId = setTimeout(function () {
|
||||
check();
|
||||
}, ms));
|
||||
};
|
||||
|
||||
/**
|
||||
* Signal that the ResizeChecker should shutdown.
|
||||
*
|
||||
* Cleans up it's listeners and timers.
|
||||
*
|
||||
* @method destroy
|
||||
* @return {void}
|
||||
*/
|
||||
ResizeChecker.prototype.destroy = function () {
|
||||
reflowWatcher.off('reflow', this.check);
|
||||
clearTimeout(this._timerId);
|
||||
};
|
||||
|
||||
return ResizeChecker;
|
||||
};
|
||||
});
|
95
src/kibana/components/vislib/lib/tooltip.js
Normal file
95
src/kibana/components/vislib/lib/tooltip.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
define(function (require) {
|
||||
return function TooltipFactory(d3, Private) {
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
|
||||
// Dynamically adds css file
|
||||
require('css!components/vislib/components/styles/main');
|
||||
|
||||
/*
|
||||
* Append a tooltip div element to the visualization
|
||||
* arguments:
|
||||
* el => reference to DOM element
|
||||
* formatter => tooltip formatter
|
||||
*/
|
||||
function Tooltip(el, formatter) {
|
||||
if (!(this instanceof Tooltip)) {
|
||||
return new Tooltip(el, formatter);
|
||||
}
|
||||
|
||||
this.el = el;
|
||||
this.tooltipFormatter = formatter;
|
||||
// hard coded class name for the tooltip `div`
|
||||
this.tooltipClass = 'k4tip';
|
||||
// reference to the width and height of the chart DOM elements
|
||||
// establishes the bounds for the tooltip per chart
|
||||
this.chartWidth = $('.chart').width();
|
||||
this.chartHeight = $('.chart').height();
|
||||
}
|
||||
|
||||
Tooltip.prototype.render = function () {
|
||||
var self = this;
|
||||
|
||||
return function (selection) {
|
||||
|
||||
selection.each(function () {
|
||||
var tooltipDiv = d3.select(self.el).select('.' + self.tooltipClass);
|
||||
// DOM element on which the tooltip is called
|
||||
var element = d3.select(this);
|
||||
|
||||
element
|
||||
.on('mousemove.tip', function (d) {
|
||||
// Calculate the x and y coordinates of the mouse on the page
|
||||
var mouseMove = {
|
||||
left: d3.event.clientX,
|
||||
top: d3.event.clientY
|
||||
};
|
||||
|
||||
// hack to keep active tooltip in front of gridster/dashboard list
|
||||
if ($('.gridster').length) {
|
||||
var gridsterUl = $('.gridster');
|
||||
var gridsterLis = $('.gridster').find('li').removeClass('player-revert');
|
||||
var tipLi = $(tooltipDiv.node()).closest('li').addClass('player-revert');
|
||||
}
|
||||
|
||||
var chartWidth = $(tooltipDiv.node()).closest('.vis-wrapper').width();
|
||||
var yaxisWidth = $('.y-axis-col-wrapper').width();
|
||||
var offsetX = d3.event.offsetX === undefined ? d3.event.layerX : d3.event.offsetX;
|
||||
var tipWidth = tooltipDiv[0][0].clientWidth;
|
||||
var xOffset = 10;
|
||||
|
||||
// check position of tooltip relative to chart width
|
||||
// to apply offset if tooltip should flip 'west'
|
||||
// if tip width + offset puts it off chart, flip direction
|
||||
// unless flip puts it off the left edge of vis wrapper
|
||||
if ((chartWidth - offsetX) < (tipWidth + yaxisWidth + 10) && (offsetX + yaxisWidth + 10) > (tipWidth + 10)) {
|
||||
xOffset = -10 - tipWidth;
|
||||
}
|
||||
|
||||
var chartHeight = self.chartHeight;
|
||||
var offsetY = d3.event.offsetY === undefined ? d3.event.layerY : d3.event.offsetY;
|
||||
var tipHeight = tooltipDiv[0][0].clientHeight;
|
||||
var yOffset = 5;
|
||||
// apply y offset to keep tooltip within bottom of chart
|
||||
if ((chartHeight - offsetY + 5) < (tipHeight)) {
|
||||
yOffset = tipHeight - (chartHeight - offsetY + 0);
|
||||
}
|
||||
|
||||
// return text and position for tooltip
|
||||
return tooltipDiv.datum(d)
|
||||
.text(self.tooltipFormatter)
|
||||
.style('visibility', 'visible')
|
||||
.style('left', mouseMove.left + xOffset + 'px')
|
||||
.style('top', mouseMove.top - yOffset + 'px');
|
||||
})
|
||||
.on('mouseout.tip', function () {
|
||||
// Hide tooltip
|
||||
return tooltipDiv.style('visibility', 'hidden');
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
return Tooltip;
|
||||
};
|
||||
});
|
303
src/kibana/components/vislib/lib/x_axis.js
Normal file
303
src/kibana/components/vislib/lib/x_axis.js
Normal file
|
@ -0,0 +1,303 @@
|
|||
define(function (require) {
|
||||
return function XAxisFactory(d3, Private) {
|
||||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
|
||||
var ErrorHandler = Private(require('components/vislib/lib/_error_handler'));
|
||||
var ChartTitle = Private(require('components/vislib/lib/chart_title'));
|
||||
|
||||
/*
|
||||
* Add an x axis to the visualization
|
||||
* aruments =>
|
||||
* el => reference to DOM element
|
||||
* xValues => array of x values from the dataset
|
||||
* ordered => data object that is defined when the data is ordered
|
||||
* xAxisFormatter => function to formatx axis tick values
|
||||
* _attr => visualization attributes
|
||||
*/
|
||||
function XAxis(args) {
|
||||
if (!(this instanceof XAxis)) {
|
||||
return new XAxis(args);
|
||||
}
|
||||
|
||||
this.el = args.el;
|
||||
this.xValues = args.xValues;
|
||||
this.ordered = args.ordered;
|
||||
this.xAxisFormatter = args.xAxisFormatter;
|
||||
this._attr = _.defaults(args._attr || {}, {
|
||||
// isDiscover: false,
|
||||
//isRotated: true
|
||||
});
|
||||
}
|
||||
|
||||
_(XAxis.prototype).extend(ErrorHandler.prototype);
|
||||
|
||||
// Render the x axis
|
||||
XAxis.prototype.render = function () {
|
||||
d3.select(this.el).selectAll('.x-axis-div').call(this.draw());
|
||||
};
|
||||
|
||||
// Get the d3 scale
|
||||
XAxis.prototype.getScale = function (ordered) {
|
||||
// if time, return time scale
|
||||
if (ordered && ordered.date) {
|
||||
return d3.time.scale();
|
||||
}
|
||||
// return d3 ordinal scale for nominal data
|
||||
return d3.scale.ordinal();
|
||||
};
|
||||
|
||||
// Add domain to the scale
|
||||
XAxis.prototype.getDomain = function (scale, ordered) {
|
||||
// if time, return a time domain
|
||||
if (ordered && ordered.date) {
|
||||
// Calculate the min date, max date, and time interval;
|
||||
return this.getTimeDomain(scale, ordered);
|
||||
}
|
||||
// return a nominal domain, i.e. array of x values
|
||||
return this.getOrdinalDomain(scale, this.xValues);
|
||||
};
|
||||
|
||||
// Returns a time domain
|
||||
XAxis.prototype.getTimeDomain = function (scale, ordered) {
|
||||
return scale.domain([ordered.min, ordered.max]);
|
||||
};
|
||||
|
||||
// Return a nominal(d3 ordinal) domain
|
||||
XAxis.prototype.getOrdinalDomain = function (scale, xValues) {
|
||||
|
||||
return scale.domain(xValues);
|
||||
};
|
||||
|
||||
// Return the range for the x axis scale
|
||||
XAxis.prototype.getRange = function (scale, ordered, width) {
|
||||
// if time, return a normal range
|
||||
if (ordered && ordered.date) {
|
||||
return scale.range([0, width]);
|
||||
}
|
||||
// if nominal, return rangeBands with a default (0.1) spacer specified
|
||||
return scale.rangeBands([0, width], 0.1);
|
||||
};
|
||||
|
||||
// Return the x axis scale
|
||||
XAxis.prototype.getXScale = function (ordered, width) {
|
||||
var scale = this.getScale(ordered);
|
||||
var domain = this.getDomain(scale, ordered);
|
||||
var xScale = this.getRange(domain, ordered, width);
|
||||
|
||||
return xScale;
|
||||
};
|
||||
|
||||
// Create the d3 xAxis function
|
||||
XAxis.prototype.getXAxis = function (width) {
|
||||
this.xAxisFormatter = this.xAxisFormatter;
|
||||
// save a reference to the xScale
|
||||
this.xScale = this.getXScale(this.ordered, width);
|
||||
|
||||
// Scale should never === `NaN`
|
||||
if (!this.xScale || _.isNaN(this.xScale)) {
|
||||
throw new Error('xScale is ' + this.xScale);
|
||||
}
|
||||
|
||||
// save a reference to the xAxis
|
||||
this.xAxis = d3.svg.axis()
|
||||
.scale(this.xScale)
|
||||
.ticks(10)
|
||||
.tickFormat(this.xAxisFormatter)
|
||||
.orient('bottom');
|
||||
};
|
||||
|
||||
// Returns a function that renders the x axis
|
||||
XAxis.prototype.draw = function () {
|
||||
var self = this;
|
||||
var margin = this._attr.margin;
|
||||
var div;
|
||||
var width;
|
||||
var height;
|
||||
var svg;
|
||||
this._attr.isRotated = false;
|
||||
|
||||
return function (selection) {
|
||||
|
||||
selection.each(function () {
|
||||
div = d3.select(this);
|
||||
width = $(this).width();
|
||||
height = $(this).height();
|
||||
|
||||
// Validate that the width and height are not 0 or `NaN`
|
||||
self.validateWidthandHeight(width, height);
|
||||
|
||||
// Return access to xAxis variable on the object
|
||||
self.getXAxis(width);
|
||||
|
||||
// Append svg and x axis
|
||||
svg = div.append('svg')
|
||||
.attr('width', width)
|
||||
.attr('height', height);
|
||||
|
||||
svg.append('g')
|
||||
.attr('class', 'x axis')
|
||||
.attr('transform', 'translate(0,0)')
|
||||
.call(self.xAxis);
|
||||
});
|
||||
|
||||
selection.call(self.filterOrRotate());
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that evaluates scale type and applies
|
||||
// filters tick labels on time scales
|
||||
// rotates and truncates labels on nominal/ordinal scales
|
||||
XAxis.prototype.filterOrRotate = function () {
|
||||
var self = this;
|
||||
var ordered = self.ordered;
|
||||
var axis;
|
||||
var labels;
|
||||
|
||||
return function (selection) {
|
||||
selection.each(function () {
|
||||
axis = d3.select(this);
|
||||
labels = axis.selectAll('.tick text');
|
||||
|
||||
if (!self.ordered) {
|
||||
// nominal/ordinal scale
|
||||
axis.call(self.rotateAxisLabels());
|
||||
axis.call(self.truncateLabels(100));
|
||||
} else {
|
||||
// time scale
|
||||
axis.call(self.filterAxisLabels());
|
||||
}
|
||||
});
|
||||
|
||||
selection.call(self.fitTitles());
|
||||
};
|
||||
};
|
||||
|
||||
// Rotate the axis tick labels within selection
|
||||
XAxis.prototype.rotateAxisLabels = function () {
|
||||
this._attr.isRotated = true;
|
||||
return function (selection) {
|
||||
selection.selectAll('.tick text')
|
||||
.style('text-anchor', 'end')
|
||||
.attr('dx', '-.8em')
|
||||
.attr('dy', '-.60em')
|
||||
.attr('transform', function () {
|
||||
return 'rotate(-90)';
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// Returns a function that truncates tick labels
|
||||
XAxis.prototype.truncateLabels = function (size) {
|
||||
var self = this;
|
||||
var labels;
|
||||
var node;
|
||||
var str;
|
||||
var n;
|
||||
var maxWidth;
|
||||
var maxLength;
|
||||
var pixPerChar;
|
||||
var endChar;
|
||||
|
||||
return function (selection) {
|
||||
|
||||
// get label maxWidth
|
||||
labels = selection.selectAll('.tick text');
|
||||
maxWidth = 0;
|
||||
maxLength = 0;
|
||||
labels.each(function () {
|
||||
node = d3.select(this).node();
|
||||
n = node.innerHTML.length;
|
||||
maxWidth = _.max([maxWidth, node.getComputedTextLength() * 0.9]);
|
||||
maxLength = _.max([maxLength, n]);
|
||||
});
|
||||
pixPerChar = maxWidth / maxLength;
|
||||
|
||||
// truncate str
|
||||
selection.selectAll('.tick text')
|
||||
.text(function (d) {
|
||||
str = self.xAxisFormatter(d);
|
||||
if (maxWidth > size) {
|
||||
endChar = 0;
|
||||
if (Math.floor((size / pixPerChar) - 4) >= 4) {
|
||||
endChar = Math.floor((size / pixPerChar) - 4);
|
||||
while (str[endChar - 1] === ' ' || str[endChar - 1] === '-' || str[endChar - 1] === ',') {
|
||||
endChar = endChar - 1;
|
||||
}
|
||||
}
|
||||
str = str.substr(0, endChar) + '...';
|
||||
return str;
|
||||
}
|
||||
return str;
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// Filter out text labels by width and position on axis
|
||||
XAxis.prototype.filterAxisLabels = function () {
|
||||
var self = this;
|
||||
var startX = 0;
|
||||
var maxW;
|
||||
var par;
|
||||
var myX;
|
||||
var myWidth;
|
||||
var halfWidth;
|
||||
|
||||
return function (selection) {
|
||||
selection.selectAll('.tick text')
|
||||
.text(function (d, i) {
|
||||
par = d3.select(this.parentNode).node();
|
||||
myX = self.xScale(d);
|
||||
myWidth = par.getBBox().width;
|
||||
halfWidth = par.getBBox().width / 2;
|
||||
maxW = $('.x-axis-div').width();
|
||||
// trims labels that would overlap each other
|
||||
// or extend past left or right edges
|
||||
// if prev label pos (or 0) + half of label width is < label pos
|
||||
// and label pos + half width is not > width of axis
|
||||
if ((startX + halfWidth) < myX && maxW > (myX + halfWidth)) {
|
||||
startX = myX + halfWidth;
|
||||
return self.xAxisFormatter(d);
|
||||
} else {
|
||||
d3.select(this.parentNode).remove();
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// Returns a function that adjusts axis title and
|
||||
// all chart title transforms to fit axis labels
|
||||
XAxis.prototype.fitTitles = function () {
|
||||
var self = this;
|
||||
var visEl = $(self.el);
|
||||
var xAxisTitle = visEl.find('.x-axis-title');
|
||||
var xAxisChartTitle = visEl.find('.x-axis-chart-title');
|
||||
var text;
|
||||
var titles;
|
||||
var titleWidth;
|
||||
|
||||
return function () {
|
||||
// set transform of x-axis-title text to fit .x-axis-title div width
|
||||
titleWidth = xAxisTitle.width();
|
||||
text = d3.select('.x-axis-title')
|
||||
.select('svg')
|
||||
.select('text')
|
||||
.attr('transform', 'translate(' + (titleWidth / 2) + ',11)');
|
||||
|
||||
// set transform of x-axis-chart-titles text to fit .chart-title div width
|
||||
titleWidth = xAxisChartTitle.find('.chart-title').width();
|
||||
titles = d3.select('.x-axis-chart-title')
|
||||
.selectAll('.chart-title');
|
||||
titles.each(function () {
|
||||
text = d3.select(this)
|
||||
.select('svg')
|
||||
.select('text')
|
||||
.attr('transform', 'translate(' + (titleWidth / 2) + ',11)');
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
return XAxis;
|
||||
};
|
||||
});
|
111
src/kibana/components/vislib/lib/y_axis.js
Normal file
111
src/kibana/components/vislib/lib/y_axis.js
Normal file
|
@ -0,0 +1,111 @@
|
|||
define(function (require) {
|
||||
return function YAxisFactory(d3, Private) {
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
|
||||
var ErrorHandler = Private(require('components/vislib/lib/_error_handler'));
|
||||
|
||||
/*
|
||||
* Append a y axis to the visualization
|
||||
* arguments:
|
||||
* el => reference to DOM element
|
||||
* _attr => visualization attributes
|
||||
*/
|
||||
function YAxis(args) {
|
||||
this.el = args.el;
|
||||
this.yMax = args.yMax;
|
||||
this._attr = _.defaults(args._attr || {}, {});
|
||||
}
|
||||
|
||||
_(YAxis.prototype).extend(ErrorHandler.prototype);
|
||||
|
||||
// Render the y axis
|
||||
YAxis.prototype.render = function () {
|
||||
d3.select(this.el).selectAll('.y-axis-div').call(this.draw());
|
||||
};
|
||||
|
||||
// Return the d3 y scale
|
||||
YAxis.prototype.getYScale = function (height) {
|
||||
// save reference to y scale
|
||||
this.yScale = d3.scale.linear()
|
||||
.domain([0, this.yMax])
|
||||
.range([height, 0])
|
||||
.nice(this.tickScale(height));
|
||||
|
||||
return this.yScale;
|
||||
};
|
||||
|
||||
// Return the d3 y axis
|
||||
YAxis.prototype.getYAxis = function (height) {
|
||||
var self = this;
|
||||
var yScale = this.getYScale(height);
|
||||
|
||||
// y scale should never be `NaN`
|
||||
if (!yScale || _.isNaN(yScale)) {
|
||||
throw new Error('yScale is ' + yScale);
|
||||
}
|
||||
|
||||
// Create the d3 yAxis function
|
||||
this.yAxis = d3.svg.axis()
|
||||
.scale(yScale)
|
||||
.tickFormat(d3.format('s'))
|
||||
.ticks(this.tickScale(height))
|
||||
.orient('left');
|
||||
|
||||
if (self.yScale.domain()[1] <= 10) {
|
||||
this.yAxis.tickFormat(d3.format('n'));
|
||||
}
|
||||
|
||||
return this.yAxis;
|
||||
};
|
||||
|
||||
// Create a tick scale for the y axis that modifies the number of ticks
|
||||
// based on the height of the wrapping DOM element
|
||||
YAxis.prototype.tickScale = function (height) {
|
||||
// Avoid using even numbers in the yTickScale.range
|
||||
// Causes the top most tickValue in the chart to be missing
|
||||
var yTickScale = d3.scale.linear()
|
||||
.clamp(true)
|
||||
.domain([20, 40, 1000])
|
||||
.range([0, 3, 11]);
|
||||
|
||||
return Math.ceil(yTickScale(height));
|
||||
};
|
||||
|
||||
// Return a function that renders the y axis
|
||||
YAxis.prototype.draw = function () {
|
||||
var self = this;
|
||||
var margin = this._attr.margin;
|
||||
var div;
|
||||
var width;
|
||||
var height;
|
||||
var svg;
|
||||
|
||||
return function (selection) {
|
||||
|
||||
selection.each(function () {
|
||||
div = d3.select(this);
|
||||
width = $(this).width();
|
||||
height = $(this).height() - margin.top - margin.bottom;
|
||||
|
||||
// Validate whether width and height are not 0 or `NaN`
|
||||
self.validateWidthandHeight(width, height);
|
||||
|
||||
var yAxis = self.getYAxis(height);
|
||||
|
||||
// Append svg and y axis
|
||||
svg = div.append('svg')
|
||||
.attr('width', width)
|
||||
.attr('height', height + margin.top + margin.bottom);
|
||||
|
||||
svg.append('g')
|
||||
.attr('class', 'y axis')
|
||||
.attr('transform', 'translate(' + (width - 2) + ',' + margin.top + ')')
|
||||
.call(yAxis);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
return YAxis;
|
||||
};
|
||||
});
|
|
@ -1,889 +0,0 @@
|
|||
define(function (require) {
|
||||
var d3 = require('d3');
|
||||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
|
||||
var getSelection = require('components/vislib/utils/selection');
|
||||
var injectZeros = require('components/vislib/utils/zeroInjection');
|
||||
var getLegend = require('components/vislib/modules/legend');
|
||||
var getColor = require('components/vislib/utils/colorspace');
|
||||
|
||||
return function area(elem, config) {
|
||||
if (typeof config === 'undefined') {
|
||||
config = {};
|
||||
}
|
||||
|
||||
var chart = {};
|
||||
|
||||
var addLegend = config.addLegend || true;
|
||||
var addTooltip = config.addTooltip || true;
|
||||
var shareYAxis = config.shareYAxis || false;
|
||||
var isStacked = config.isStacked || false;
|
||||
|
||||
var destroyFlag = false;
|
||||
|
||||
var dispatch = d3.dispatch('hover', 'click', 'mouseenter', 'mouseleave',
|
||||
'mouseout', 'mouseover', 'brush');
|
||||
var $elem = $(elem); // cached jquery version of elemen
|
||||
var latestData;
|
||||
var prevSize;
|
||||
var xValue = function (d, i) {
|
||||
return d.x;
|
||||
};
|
||||
var yValue = function (d, i) {
|
||||
if (isStacked) {
|
||||
return d.y0 + d.y;
|
||||
}
|
||||
return d.y;
|
||||
};
|
||||
|
||||
/*
|
||||
Renders the chart to the HTML element
|
||||
*/
|
||||
chart.render = function (data) {
|
||||
try {
|
||||
if (!data) {
|
||||
throw new Error('No valid data');
|
||||
}
|
||||
if (!elem) {
|
||||
throw new Error('No elem provided');
|
||||
}
|
||||
|
||||
// store a copy of the data sent to render, so that it can be resent with .resize()
|
||||
latestData = data;
|
||||
|
||||
// removes elements to redraw the chart on subsequent calls
|
||||
d3.select(elem)
|
||||
.selectAll('*')
|
||||
.remove();
|
||||
|
||||
var chartWrapper = chart.getChartWrapper(elem)[0][0];
|
||||
var selection = chart.getSelection(chartWrapper, latestData);
|
||||
|
||||
return chart.getVisualization(selection);
|
||||
} catch (error) {
|
||||
console.error('chart.render: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Creates the d3 visualization
|
||||
*/
|
||||
chart.getVisualization = function (selection) {
|
||||
try {
|
||||
if (!selection) {
|
||||
throw new Error('No valid selection');
|
||||
}
|
||||
|
||||
if (destroyFlag) {
|
||||
throw new Error('You destroyed the chart and tried to use it again');
|
||||
}
|
||||
|
||||
var colors = chart.getColors(selection);
|
||||
|
||||
// Calculates the max Y axis value for all Charts
|
||||
if (shareYAxis) {
|
||||
var yAxisMax = chart.getYAxisMax(selection);
|
||||
}
|
||||
|
||||
// Adds the legend
|
||||
if (addLegend) {
|
||||
var legend = getLegend(elem, colors, chart);
|
||||
}
|
||||
|
||||
// Adds tooltips
|
||||
if (addTooltip) {
|
||||
var tip = chart.getTooltip(elem);
|
||||
}
|
||||
|
||||
return selection.each(function (d, i) {
|
||||
var that = this;
|
||||
|
||||
chart.createAreaChart({
|
||||
'data': d,
|
||||
'index': i,
|
||||
'this': that,
|
||||
'colors': colors,
|
||||
'tip': tip,
|
||||
'yAxisMax': yAxisMax,
|
||||
'isStacked': isStacked
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('chart.getVisualization: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getTooltip = function (elem) {
|
||||
try {
|
||||
if (!elem) {
|
||||
throw new Error('No valid elem');
|
||||
}
|
||||
|
||||
var tooltipDiv;
|
||||
|
||||
tooltipDiv = d3.select(elem)
|
||||
.append('div')
|
||||
.attr('class', 'k4tip');
|
||||
|
||||
return tooltipDiv;
|
||||
} catch (error) {
|
||||
console.error('chart.getTooltip: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getChartWrapper = function (elem) {
|
||||
try {
|
||||
if (!elem) {
|
||||
throw new Error('No valid elem');
|
||||
}
|
||||
|
||||
var chartWrapper = d3.select(elem)
|
||||
.append('div');
|
||||
|
||||
chartWrapper
|
||||
.attr('class', 'chartwrapper')
|
||||
.style('height', $(elem)
|
||||
.height() + 'px');
|
||||
|
||||
return chartWrapper;
|
||||
} catch (error) {
|
||||
console.error('chart.getChartWrapper: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getSelection = function (elem, data) {
|
||||
try {
|
||||
if (!elem) {
|
||||
throw new Error('No valid elem');
|
||||
}
|
||||
if (!data) {
|
||||
throw new Error('No valid data');
|
||||
}
|
||||
|
||||
var selection = d3.selectAll(getSelection(elem, data));
|
||||
|
||||
return selection;
|
||||
} catch (error) {
|
||||
console.error('chart.getSelection: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getColors = function (selection) {
|
||||
try {
|
||||
if (!selection) {
|
||||
throw new Error('No valid selection');
|
||||
}
|
||||
|
||||
var colorDomain = chart.getColorDomain(selection);
|
||||
var lengthOfColorDomain = colorDomain.length;
|
||||
var colorArray = getColor(lengthOfColorDomain);
|
||||
var colorDict;
|
||||
|
||||
colorDict = chart.getColorDict(colorDomain, colorArray);
|
||||
|
||||
return colorDict;
|
||||
} catch (error) {
|
||||
console.error('chart.getColors: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getColorDict = function (colorDomain, colorArray) {
|
||||
try {
|
||||
if (!colorDomain) {
|
||||
throw new Error('No valid colorDomain');
|
||||
}
|
||||
if (!colorArray) {
|
||||
throw new Error('No valid colorArray');
|
||||
}
|
||||
|
||||
var colorDict;
|
||||
|
||||
colorDict = _.zipObject(colorDomain, colorArray);
|
||||
|
||||
return colorDict;
|
||||
} catch (error) {
|
||||
console.error('chart.getColorDict' + error);
|
||||
}
|
||||
};
|
||||
|
||||
/* Color domain */
|
||||
chart.getColorDomain = function (selection) {
|
||||
try {
|
||||
if (!selection) {
|
||||
throw new Error('No valid selection');
|
||||
}
|
||||
|
||||
var items = [];
|
||||
|
||||
selection.each(function (d) {
|
||||
d.series.forEach(function (label) {
|
||||
if (label.label) {
|
||||
items.push(label.label);
|
||||
} else {
|
||||
items.push(d.yAxisLabel);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
items = _.uniq(items);
|
||||
return items;
|
||||
} catch (error) {
|
||||
console.error('chart.getColorDomain: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
/* Function for global yAxis */
|
||||
chart.getYAxisMax = function (selection) {
|
||||
try {
|
||||
if (!selection) {
|
||||
throw new Error('No valid selection');
|
||||
}
|
||||
|
||||
var yArray = [];
|
||||
var stack = d3.layout.stack()
|
||||
.values(function (d) { return d.values; });
|
||||
|
||||
selection.each(function (d) {
|
||||
d = injectZeros(d);
|
||||
return d3.max(stack(d.series), function (layer) {
|
||||
return d3.max(layer.values, function (e) {
|
||||
if (isStacked) {
|
||||
return yArray.push(e.y0 + e.y);
|
||||
}
|
||||
return yArray.push(e.y);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return d3.max(yArray);
|
||||
} catch (error) {
|
||||
console.error('chart.getYAxisMax: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getBounds = function (data) {
|
||||
try {
|
||||
if (!data) {
|
||||
throw new Error('No valid data');
|
||||
}
|
||||
|
||||
var bounds = [];
|
||||
|
||||
data.series.map(function (series) {
|
||||
series.values.map(function (d, i) {
|
||||
bounds.push({
|
||||
x: xValue.call(series, d, i),
|
||||
y: yValue.call(series, d, i)
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return bounds;
|
||||
} catch (error) {
|
||||
console.error('chart.getBounds: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.createAreaChart = function (args) {
|
||||
if (typeof args === 'undefined') {
|
||||
args = {};
|
||||
}
|
||||
|
||||
var data = injectZeros(args.data);
|
||||
var that = args.this;
|
||||
var colors = args.colors;
|
||||
var tip = args.tip;
|
||||
var yAxisMax = args.yAxisMax;
|
||||
var isStacked = args.isStacked;
|
||||
|
||||
var xAxisLabel = data.xAxisLabel;
|
||||
var yAxisLabel = data.yAxisLabel;
|
||||
var label = data.label;
|
||||
var xAxisFormatter = data.xAxisFormatter;
|
||||
var yAxisFormatter = data.yAxisFormatter;
|
||||
var tooltipFormatter = data.tooltipFormatter;
|
||||
|
||||
var vis = d3.select(elem);
|
||||
var allLayers = vis.selectAll('path');
|
||||
var allItms = d3.select('.legendwrapper')
|
||||
.selectAll('li.legends');
|
||||
var scrolltop = document.body.scrollTop;
|
||||
var mousemove;
|
||||
|
||||
var elemWidth = parseInt(d3.select(that)
|
||||
.style('width'), 10);
|
||||
var elemHeight = parseInt(d3.select(that)
|
||||
.style('height'), 10);
|
||||
var margin = { top: 35, right: 15, bottom: 35, left: 50 };
|
||||
var width = elemWidth - margin.left - margin.right;
|
||||
var height = elemHeight - margin.top - margin.bottom;
|
||||
var seriesData = [];
|
||||
var brush;
|
||||
|
||||
// adds the label value to each data point
|
||||
// within the values array for displaying in the tooltip
|
||||
data.series.forEach(function (d) {
|
||||
d.values.forEach(function (e) {
|
||||
e.label = d.label;
|
||||
});
|
||||
});
|
||||
|
||||
data.series.map(function (series) {
|
||||
seriesData.push(series);
|
||||
});
|
||||
|
||||
var stack = d3.layout.stack()
|
||||
.values(function (d) { return d.values; });
|
||||
|
||||
var stackedSeriesData = stack(seriesData);
|
||||
|
||||
var xTickScale = d3.scale.linear()
|
||||
.clamp(true)
|
||||
.domain([80, 300, 800])
|
||||
.range([0, 2, 4]);
|
||||
|
||||
var xTickN = Math.floor(xTickScale(width));
|
||||
|
||||
var yTickScale = d3.scale.linear()
|
||||
.clamp(true)
|
||||
.domain([20, 40, 1000])
|
||||
.range([0, 1, 10]);
|
||||
|
||||
var yTickN = Math.floor(yTickScale(height));
|
||||
|
||||
var xScale = d3.time.scale()
|
||||
.domain(d3.extent(
|
||||
chart.getBounds(data),
|
||||
function (d) {
|
||||
return d.x;
|
||||
}
|
||||
))
|
||||
.range([0, width]);
|
||||
|
||||
var yScale = d3.scale.linear()
|
||||
.range([height, 0]);
|
||||
|
||||
var xAxis = d3.svg.axis()
|
||||
.scale(xScale)
|
||||
.ticks(xTickN)
|
||||
.tickPadding(5)
|
||||
.tickFormat(xAxisFormatter)
|
||||
.orient('bottom');
|
||||
|
||||
var yAxis = d3.svg.axis()
|
||||
.scale(yScale)
|
||||
.ticks(yTickN)
|
||||
.tickPadding(4)
|
||||
.tickFormat(yAxisFormatter)
|
||||
.orient('left');
|
||||
|
||||
var area = d3.svg.area()
|
||||
.x(X)
|
||||
.y0(Y0)
|
||||
.y1(Y);
|
||||
|
||||
var line = d3.svg.line()
|
||||
.interpolate('linear')
|
||||
.x(X)
|
||||
.y(Y);
|
||||
|
||||
// setting the y scale domain
|
||||
if (shareYAxis) {
|
||||
yScale.domain([0, yAxisMax])
|
||||
.nice(yTickN);
|
||||
} else {
|
||||
yScale
|
||||
.domain([0, d3.max(chart.getBounds(data), function (d) {
|
||||
return d.y;
|
||||
})])
|
||||
.nice(yTickN);
|
||||
}
|
||||
|
||||
var svg = d3.select(that)
|
||||
.append('svg')
|
||||
.attr('class', 'canvas')
|
||||
.attr('width', '100%')
|
||||
.attr('height', '100%');
|
||||
|
||||
var g = svg.append('g')
|
||||
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
||||
|
||||
// background rect
|
||||
g.append('rect')
|
||||
.attr('class', 'chart-bkgd')
|
||||
.attr('width', width)
|
||||
.attr('height', height);
|
||||
|
||||
g.append('g')
|
||||
.attr('class', 'x axis')
|
||||
.attr('transform', 'translate(0,' + height + ')')
|
||||
.call(xAxis)
|
||||
.selectAll('text')
|
||||
.call(chart.tickText, width)
|
||||
.on('mouseover', function (d) {
|
||||
var hh = tip[0][0].scrollHeight;
|
||||
|
||||
mousemove = {
|
||||
left: d3.event.pageX,
|
||||
top: d3.event.pageY
|
||||
};
|
||||
scrolltop = document.body.scrollTop;
|
||||
|
||||
d3.select(that)
|
||||
.style('cursor', 'default');
|
||||
return tip.datum(d)
|
||||
.text(d)
|
||||
.style('top', mousemove.top - scrolltop - hh / 2 + 'px')
|
||||
.style('left', mousemove.left + 20 + 'px')
|
||||
.style('visibility', 'visible');
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
d3.select(that)
|
||||
.classed('hover', false)
|
||||
.style('stroke', null);
|
||||
tip.style('visibility', 'hidden');
|
||||
});
|
||||
|
||||
g.append('g')
|
||||
.attr('class', 'y axis')
|
||||
.call(yAxis);
|
||||
|
||||
// Axis labels
|
||||
g.append('text')
|
||||
.attr('class', 'x-axis-label')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('x', width / 2)
|
||||
.attr('y', height + 30)
|
||||
.text(xAxisLabel);
|
||||
|
||||
g.append('text')
|
||||
.attr('class', 'y-axis-label')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('x', -height / 2)
|
||||
.attr('y', -40)
|
||||
.attr('dy', '.75em')
|
||||
.attr('transform', 'rotate(-90)')
|
||||
.text(yAxisLabel);
|
||||
|
||||
// Chart title
|
||||
g.append('text')
|
||||
.attr('class', 'charts-label')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('x', width / 2)
|
||||
.attr('y', -10)
|
||||
.text(label)
|
||||
.call(chart.tickText, width)
|
||||
.on('mouseover', function (d) {
|
||||
var hh = tip[0][0].scrollHeight;
|
||||
|
||||
mousemove = {
|
||||
left: d3.event.pageX,
|
||||
top: d3.event.pageY
|
||||
};
|
||||
scrolltop = document.body.scrollTop;
|
||||
|
||||
d3.select(that)
|
||||
.style('cursor', 'default');
|
||||
return tip.text(d.label)
|
||||
.style('top', mousemove.top - scrolltop - hh / 2 + 'px')
|
||||
.style('left', mousemove.left + 20 + 'px')
|
||||
.style('visibility', 'visible');
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
d3.select(that)
|
||||
.classed('hover', false)
|
||||
.style('stroke', null);
|
||||
tip.style('visibility', 'hidden');
|
||||
});
|
||||
|
||||
var lines = g.selectAll('.lines')
|
||||
.data(stackedSeriesData)
|
||||
.enter()
|
||||
.append('g');
|
||||
|
||||
// lines.append('path')
|
||||
// .attr('class', function (d) {
|
||||
// return 'rl rl-' + chart.getClassName(d.label, yAxisLabel);
|
||||
// })
|
||||
// .attr('d', function (d) {
|
||||
// return line(d.values);
|
||||
// })
|
||||
// .attr('fill', 'none')
|
||||
// .attr('stroke', function (d) {
|
||||
// return d.label ? colors[d.label] : colors[yAxisLabel];
|
||||
// })
|
||||
// .style('stroke-width', '3px');
|
||||
|
||||
lines.append('path')
|
||||
.attr('class', function (d) {
|
||||
return 'rl rl-' + chart.getClassName(d.label, yAxisLabel);
|
||||
})
|
||||
.attr('d', function (d) {
|
||||
return area(d.values);
|
||||
})
|
||||
.style('fill', function (d) {
|
||||
return d.label ? colors[d.label] : colors[yAxisLabel];
|
||||
})
|
||||
.style('stroke', 'none')
|
||||
// .style('stroke', function (d) {
|
||||
// return d.label ? colors[d.label] : colors[yAxisLabel];
|
||||
// })
|
||||
// .style('stroke-width', '3px')
|
||||
// .style('opacity', 1);
|
||||
.style('opacity', 1);
|
||||
|
||||
var layer = g.selectAll('.layer')
|
||||
.data(seriesData)
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('class', function (d) {
|
||||
return 'r' + d.label;
|
||||
})
|
||||
.attr('stroke', function (d) {
|
||||
return d.label ? colors[d.label] : colors[yAxisLabel];
|
||||
});
|
||||
|
||||
var circle = layer.selectAll('.points')
|
||||
.data(function (d) {
|
||||
return d.values;
|
||||
})
|
||||
.enter()
|
||||
.append('circle')
|
||||
.attr('class', 'point')
|
||||
.attr('cx', function (d) {
|
||||
return xScale(d.x);
|
||||
})
|
||||
.attr('cy', function (d) {
|
||||
if (isStacked) {
|
||||
return yScale(d.y0 + d.y);
|
||||
}
|
||||
return yScale(d.y);
|
||||
})
|
||||
/* css styling */
|
||||
.attr('r', 8)
|
||||
.attr('fill', '#ffffff')
|
||||
.attr('stroke', function (d) {
|
||||
return d.label ? colors[d.label] : colors[yAxisLabel];
|
||||
})
|
||||
.attr('stroke-width', 3.5)
|
||||
.attr('opacity', 0)
|
||||
.on('mouseover', function (d, i) {
|
||||
var point = d3.select(this);
|
||||
var layerClass = '.rl-' + chart.getClassName(d.label, yAxisLabel);
|
||||
|
||||
point.attr('opacity', 1)
|
||||
.classed('hover', true)
|
||||
.style('cursor', 'pointer');
|
||||
|
||||
// highlight chart layer
|
||||
allLayers = vis.selectAll('.rl');
|
||||
allLayers.style('opacity', 0.3);
|
||||
|
||||
vis.selectAll(layerClass)
|
||||
.style('opacity', 1);
|
||||
|
||||
// highlight legend item
|
||||
if (allItms) {
|
||||
allItms.style('opacity', 0.3);
|
||||
|
||||
var itm = d3.select('.legendwrapper')
|
||||
.select(layerClass)
|
||||
.style('opacity', 1);
|
||||
}
|
||||
|
||||
dispatch.hover({
|
||||
value: yValue(d, i),
|
||||
point: d,
|
||||
pointIndex: i,
|
||||
series: data.series,
|
||||
config: config,
|
||||
data: latestData,
|
||||
e: d3.event
|
||||
});
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('mousemove', function (d) {
|
||||
var datum, hh = tip[0][0].scrollHeight;
|
||||
|
||||
mousemove = {
|
||||
left: d3.event.pageX,
|
||||
top: d3.event.pageY
|
||||
};
|
||||
scrolltop = document.body.scrollTop;
|
||||
|
||||
if (typeof d.label !== 'undefined') {
|
||||
datum = {
|
||||
label: d.label,
|
||||
x: d.x,
|
||||
y: d.y
|
||||
};
|
||||
} else {
|
||||
datum = {
|
||||
x: d.x,
|
||||
y: d.y
|
||||
};
|
||||
}
|
||||
|
||||
tip.datum(datum)
|
||||
.text(tooltipFormatter)
|
||||
.style('top', mousemove.top - scrolltop - hh / 2 + 'px')
|
||||
.style('left', mousemove.left + 20 + 'px')
|
||||
.style('visibility', 'visible');
|
||||
})
|
||||
.on('click', function (d, i) {
|
||||
dispatch.click({
|
||||
value: yValue(d, i),
|
||||
point: d,
|
||||
pointIndex: i,
|
||||
series: data.series,
|
||||
config: config,
|
||||
data: latestData,
|
||||
e: d3.event
|
||||
});
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
var point = d3.select(this);
|
||||
point.attr('opacity', 0);
|
||||
if (addTooltip) {
|
||||
tip.style('visibility', 'hidden');
|
||||
}
|
||||
allLayers.style('opacity', 1);
|
||||
allItms.style('opacity', 1);
|
||||
});
|
||||
|
||||
if (addTooltip) {
|
||||
// **** hilite series on hover
|
||||
// allLayers = vis.selectAll('path');
|
||||
lines.on('mouseover', function (d) {
|
||||
// highlight chart layer
|
||||
allLayers.style('opacity', 0.3);
|
||||
var layerClass = '.rl-' + chart.getClassName(d.label, yAxisLabel);
|
||||
var myLayer = vis.selectAll(layerClass)
|
||||
.style('opacity', 1);
|
||||
|
||||
// stroke this rect
|
||||
d3.select(this)
|
||||
.classed('hover', true)
|
||||
.style('stroke', '#333')
|
||||
.style('cursor', 'pointer');
|
||||
|
||||
// hilite legend item
|
||||
if (allItms) {
|
||||
allItms.style('opacity', 0.3);
|
||||
var itm = d3.select('.legendwrapper')
|
||||
.select(layerClass)
|
||||
.style('opacity', 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Event Selection: BRUSH */
|
||||
brush = d3.svg.brush()
|
||||
.x(xScale)
|
||||
.on('brushend', function brushend() {
|
||||
var selected, start, lastVal, end, selectedRange;
|
||||
|
||||
// selected is used to determine the range for ordinal scales
|
||||
selected = xScale.domain()
|
||||
.filter(function (d) {
|
||||
return (brush.extent()[0] <= xScale(d)) && (xScale(d) <= brush.extent()[1]);
|
||||
});
|
||||
|
||||
start = selected[0];
|
||||
lastVal = selected.length - 1;
|
||||
end = selected[lastVal];
|
||||
selectedRange = [start, end];
|
||||
|
||||
return brush.extent()[0] instanceof Date ?
|
||||
dispatch.brush({
|
||||
range: brush.extent(),
|
||||
config: config,
|
||||
e: d3.event,
|
||||
data: latestData
|
||||
}) :
|
||||
dispatch.brush({
|
||||
range: selectedRange,
|
||||
config: config,
|
||||
e: d3.event,
|
||||
data: latestData
|
||||
});
|
||||
});
|
||||
|
||||
if (dispatch.on('brush')) {
|
||||
g.append('g')
|
||||
.attr('class', 'brush')
|
||||
.call(brush)
|
||||
.selectAll('rect')
|
||||
.attr('height', height);
|
||||
}
|
||||
/* ************************** */
|
||||
|
||||
lines.on('mouseout', function () {
|
||||
allLayers.style('opacity', 1);
|
||||
allItms.style('opacity', 1);
|
||||
});
|
||||
|
||||
function X(d) {
|
||||
return xScale(d.x);
|
||||
}
|
||||
|
||||
function Y(d) {
|
||||
if (isStacked) {
|
||||
return yScale(d.y0 + d.y);
|
||||
}
|
||||
return yScale(d.y);
|
||||
}
|
||||
|
||||
function Y0(d) {
|
||||
if (isStacked) {
|
||||
return yScale(d.y0);
|
||||
}
|
||||
return height;
|
||||
}
|
||||
|
||||
return svg;
|
||||
};
|
||||
|
||||
// getters / setters
|
||||
chart.resize = _.debounce(function () {
|
||||
if (latestData) {
|
||||
chart.render(latestData);
|
||||
}
|
||||
}, 200);
|
||||
|
||||
// enable auto-resize
|
||||
(function checkSize() {
|
||||
var size = $elem.width() + ':' + $elem.height();
|
||||
if (prevSize !== size) {
|
||||
chart.resize();
|
||||
}
|
||||
prevSize = size;
|
||||
setTimeout(checkSize, 250);
|
||||
}());
|
||||
|
||||
/* Function for truncating x axis tick labels */
|
||||
chart.tickText = function (text, width) {
|
||||
var n = text[0].length,
|
||||
maxw = width / n * 0.9,
|
||||
tickn = Math.floor(maxw);
|
||||
text.each(function () {
|
||||
var text = d3.select(this),
|
||||
length = this.getComputedTextLength(),
|
||||
tspan = text.text();
|
||||
if (length > maxw) {
|
||||
var str = text.text(),
|
||||
avg = length / str.length,
|
||||
end = Math.floor(maxw / avg);
|
||||
str = str.substr(0, end) + '...';
|
||||
tspan = text.text(str);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
chart.getClassName = function (label, yAxisLabel) {
|
||||
try {
|
||||
return label ? chart.classifyString(label) : chart.classifyString(yAxisLabel);
|
||||
} catch (error) {
|
||||
console.error('chart.getClassName: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.classifyString = function (string) {
|
||||
try {
|
||||
if (!chart.isString(string)) {
|
||||
string = chart.stringify(string);
|
||||
}
|
||||
return string.replace(/[.]+|[/]+|[\s]+|[*]+|[;]+|[(]+|[)]+|[:]+|[,]+/g, '');
|
||||
} catch (error) {
|
||||
console.error('chart.classifyString: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.isString = function (value) {
|
||||
try {
|
||||
if (typeof value === 'string') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('chart.isString: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.stringify = function (value) {
|
||||
try {
|
||||
var string = value + '';
|
||||
return string;
|
||||
} catch (error) {
|
||||
console.error('chart.stringify: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.error = function () {
|
||||
// Removes the legend container
|
||||
d3.select(elem)
|
||||
.selectAll('*')
|
||||
.remove();
|
||||
|
||||
var errorWrapper = d3.select(elem)
|
||||
.append('div')
|
||||
.attr('class', 'errorWrapper')
|
||||
.style('height', function () {
|
||||
return $(elem)
|
||||
.height() + 'px';
|
||||
})
|
||||
.style('text-align', 'center');
|
||||
|
||||
errorWrapper.append('p')
|
||||
.style('font-size', '18px')
|
||||
.style('margin-top', function () {
|
||||
return $(elem)
|
||||
.height() / 3 + 'px';
|
||||
})
|
||||
.style('line-height', '18px')
|
||||
.text('The container is too small for this chart.');
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.off = function (event) {
|
||||
dispatch.on(event, null);
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.destroy = function (_) {
|
||||
/*
|
||||
Destroys all charts associated with the parent element
|
||||
if the argument passed is true. By default the argument
|
||||
is true.
|
||||
*/
|
||||
if (!arguments.length || _) {
|
||||
destroyFlag = _ || true;
|
||||
|
||||
// Removing chart and all elements associated with it
|
||||
d3.select(elem)
|
||||
.selectAll('*')
|
||||
.remove();
|
||||
|
||||
// Cleaning up event listeners
|
||||
chart.off('click');
|
||||
chart.off('hover');
|
||||
chart.off('brush');
|
||||
d3.select(window)
|
||||
.on('resize', null);
|
||||
}
|
||||
destroyFlag = _;
|
||||
return chart;
|
||||
};
|
||||
|
||||
chart.dispatch = dispatch;
|
||||
|
||||
d3.rebind(chart, dispatch, 'on');
|
||||
d3.select(window)
|
||||
.on('resize', chart.resize);
|
||||
|
||||
return chart;
|
||||
};
|
||||
});
|
File diff suppressed because it is too large
Load diff
|
@ -1,143 +0,0 @@
|
|||
define(function (require) {
|
||||
var d3 = require('d3');
|
||||
|
||||
function legend(elem, colors, chart) {
|
||||
var vis = d3.select(elem);
|
||||
var chartWrap = vis.select('.chartwrapper');
|
||||
var legendWrap = vis.append('div').attr('class', 'legendwrapper');
|
||||
var allLayers = chartWrap.selectAll('rect');
|
||||
|
||||
var list;
|
||||
var itm;
|
||||
var allItms;
|
||||
|
||||
var header = legendWrap
|
||||
.append('div')
|
||||
.attr('class', 'header')
|
||||
.append('div')
|
||||
.attr('class', 'column-labels')
|
||||
.html('<span class="btn btn-xs btn-default legend-toggle"><i class="fa fa-list-ul"></i></span>');
|
||||
|
||||
list = legendWrap.append('ul')
|
||||
.attr('class', 'legend-ul')
|
||||
.selectAll('li')
|
||||
.data(d3.keys(colors))
|
||||
.enter()
|
||||
.append('li')
|
||||
.attr('class', function (d) {
|
||||
var label = d !== undefined ?
|
||||
d.replace(/[.]+|[/]+|[\s]+|[#]+|[*]+|[;]+|[(]+|[)]+|[:]+|[,]+/g, '') : undefined;
|
||||
|
||||
return 'legends rl rl-' + label;
|
||||
})
|
||||
.html(function (d) {
|
||||
var str = '<span class="dots" style="background:' + colors[d] + '"></span>' + d + '';
|
||||
|
||||
return str;
|
||||
});
|
||||
|
||||
allItms = vis.selectAll('li.legends')
|
||||
.style('cursor', 'pointer');
|
||||
|
||||
list
|
||||
.on('mouseover', function (d) {
|
||||
// chart layer
|
||||
// Regex to remove ., /, white space, *, ;, (, ), :, , from labels.
|
||||
var label = d !== undefined ?
|
||||
d.replace(/[.]+|[/]+|[\s]+|[#]+|[*]+|[;]+|[(]+|[)]+|[:]+|[,]+/g, '') : undefined,
|
||||
layerClass = '.rl-' + label;
|
||||
|
||||
allLayers = vis.selectAll('.rl')
|
||||
.style('opacity', 0.3);
|
||||
|
||||
vis.selectAll(layerClass)
|
||||
.style('opacity', 1);
|
||||
|
||||
// legend list
|
||||
allItms.style('opacity', 0.3);
|
||||
itm = d3.select(this)
|
||||
.style('opacity', 1);
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
allLayers = vis.selectAll('.rl')
|
||||
.style('opacity', 1);
|
||||
allItms.style('opacity', 1);
|
||||
});
|
||||
|
||||
// toggle header
|
||||
function closeheader(e) {
|
||||
var vwidth = vis.style('width'),
|
||||
legwidth = +vwidth.substr(0, vwidth.length - 2);
|
||||
|
||||
chartWrap.style('width', function () {
|
||||
return legwidth - 30 + 'px';
|
||||
});
|
||||
|
||||
legendWrap
|
||||
.classed('legend-open', false)
|
||||
.style('width', '30px')
|
||||
.style('height', '30px');
|
||||
|
||||
header
|
||||
.select('.column-labels')
|
||||
.html('<span class="btn btn-xs btn-default legend-toggle"><i class="fa fa-list-ul"></i></span> ');
|
||||
|
||||
header
|
||||
.select('.legend-toggle')
|
||||
.on('click', function (e) {
|
||||
var sel = d3.select(this)[0].parentNode;
|
||||
toggleheader(e);
|
||||
});
|
||||
}
|
||||
|
||||
function openheader(e) {
|
||||
var vheight = vis.style('height'),
|
||||
vwidth = vis.style('width'),
|
||||
legheight = +vheight.substr(0, vheight.length - 2) - 68,
|
||||
legwidth = +vwidth.substr(0, vwidth.length - 2);
|
||||
|
||||
chartWrap.style('width', function () {
|
||||
return legwidth - 180 + 'px';
|
||||
});
|
||||
|
||||
legendWrap
|
||||
.classed('legend-open', true)
|
||||
.style('width', '170px')
|
||||
.style('height', legheight + 'px');
|
||||
|
||||
header
|
||||
.select('.column-labels')
|
||||
.html('<span class="btn btn-xs legend-toggle btn-primary"><i class="fa fa-list-ul"></i></span>');
|
||||
|
||||
header
|
||||
.select('.legend-toggle')
|
||||
.on('click', function (e) {
|
||||
var sel = d3.select(this)[0].parentNode;
|
||||
toggleheader(e);
|
||||
});
|
||||
}
|
||||
|
||||
function toggleheader(e) {
|
||||
if (chart.headerOpen === undefined || chart.headerOpen === false) {
|
||||
chart.headerOpen = true;
|
||||
openheader(e);
|
||||
} else {
|
||||
chart.headerOpen = false;
|
||||
closeheader(e);
|
||||
}
|
||||
|
||||
chart.resize();
|
||||
}
|
||||
|
||||
// check for last state
|
||||
if (chart.headerOpen === true) {
|
||||
openheader();
|
||||
} else {
|
||||
closeheader();
|
||||
}
|
||||
}
|
||||
|
||||
return function (elem, colors, chart) {
|
||||
return legend(elem, colors, chart);
|
||||
};
|
||||
});
|
|
@ -1,943 +0,0 @@
|
|||
define(function (require) {
|
||||
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
var d3 = require('d3');
|
||||
|
||||
var getSelection = require('components/vislib/utils/selection');
|
||||
var getLegend = require('components/vislib/modules/legend');
|
||||
var getColor = require('components/vislib/utils/colorspace');
|
||||
|
||||
return function getLineChart(elem, config) {
|
||||
if (typeof config === 'undefined') {
|
||||
config = {};
|
||||
}
|
||||
|
||||
var chart = {};
|
||||
|
||||
/* ***** Chart Options ***** */
|
||||
var addLegend = config.addLegend || false;
|
||||
var addTooltip = config.addTooltip || false;
|
||||
var shareYAxis = config.shareYAxis || false;
|
||||
/* ************************* */
|
||||
|
||||
/* ***** Chart Flags ******* */
|
||||
var destroyFlag = false;
|
||||
/* ************************* */
|
||||
|
||||
/* ***** Chart Globals ******* */
|
||||
var dispatch = d3.dispatch('hover', 'click', 'mouseenter', 'mouseleave', 'mouseout', 'mouseover', 'brush');
|
||||
var $elem = $(elem); // cached jquery version of element
|
||||
var latestData;
|
||||
var prevSize;
|
||||
var xValue = function (d, i) {
|
||||
return d.x;
|
||||
};
|
||||
var yValue = function (d, i) {
|
||||
return d.y;
|
||||
};
|
||||
/* ************************* */
|
||||
|
||||
/*
|
||||
Renders the chart to the HTML element
|
||||
*/
|
||||
chart.render = function (data) {
|
||||
try {
|
||||
if (!data) {
|
||||
throw new Error('No valid data');
|
||||
}
|
||||
if (!elem) {
|
||||
throw new Error('No elem provided');
|
||||
}
|
||||
|
||||
// store a copy of the data sent to render, so that it can be resent with .resize()
|
||||
latestData = data;
|
||||
|
||||
// removes elements to redraw the chart on subsequent calls
|
||||
d3.select(elem)
|
||||
.selectAll('*')
|
||||
.remove();
|
||||
|
||||
var chartWrapper = chart.getChartWrapper(elem)[0][0],
|
||||
selection = chart.getSelection(chartWrapper, latestData);
|
||||
|
||||
return chart.getVisualization(selection);
|
||||
} catch (error) {
|
||||
console.error('chart.render: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Creates the d3 visualization
|
||||
*/
|
||||
chart.getVisualization = function (selection) {
|
||||
try {
|
||||
if (!selection) {
|
||||
throw new Error('No valid selection');
|
||||
}
|
||||
|
||||
if (destroyFlag) {
|
||||
throw new Error('You destroyed the chart and tried to use it again');
|
||||
}
|
||||
|
||||
var colors = chart.getColors(selection);
|
||||
|
||||
// Calculates the max Y axis value for all Charts
|
||||
if (shareYAxis) {
|
||||
var yAxisMax = chart.getYAxisMax(selection);
|
||||
}
|
||||
|
||||
// Adds the legend
|
||||
if (addLegend) {
|
||||
var legend = getLegend(elem, colors, chart);
|
||||
}
|
||||
|
||||
// Adds tooltips
|
||||
if (addTooltip) {
|
||||
var tip = chart.getTooltip(elem);
|
||||
}
|
||||
|
||||
return selection.each(function (d, i) {
|
||||
var that = this;
|
||||
|
||||
chart.createLineChart({
|
||||
'data': d,
|
||||
'index': i,
|
||||
'this': that,
|
||||
'colors': colors,
|
||||
'tip': tip,
|
||||
'yAxisMax': yAxisMax
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('chart.getVisualization: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getTooltip = function (elem) {
|
||||
try {
|
||||
if (!elem) {
|
||||
throw new Error('No valid elem');
|
||||
}
|
||||
|
||||
var tooltipDiv;
|
||||
|
||||
tooltipDiv = d3.select(elem)
|
||||
.append('div')
|
||||
.attr('class', 'k4tip');
|
||||
|
||||
return tooltipDiv;
|
||||
} catch (error) {
|
||||
console.error('chart.getTooltip: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getChartWrapper = function (elem) {
|
||||
try {
|
||||
if (!elem) {
|
||||
throw new Error('No valid elem');
|
||||
}
|
||||
|
||||
var chartWrapper = d3.select(elem)
|
||||
.append('div');
|
||||
|
||||
chartWrapper
|
||||
.attr('class', 'chartwrapper')
|
||||
.style('height', $(elem)
|
||||
.height() + 'px');
|
||||
|
||||
return chartWrapper;
|
||||
} catch (error) {
|
||||
console.error('chart.getChartWrapper: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getSelection = function (elem, data) {
|
||||
try {
|
||||
if (!elem) {
|
||||
throw new Error('No valid elem');
|
||||
}
|
||||
if (!data) {
|
||||
throw new Error('No valid data');
|
||||
}
|
||||
|
||||
var selection = d3.selectAll(getSelection(elem, data));
|
||||
|
||||
return selection;
|
||||
} catch (error) {
|
||||
console.error('chart.getSelection: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getColors = function (selection) {
|
||||
try {
|
||||
if (!selection) {
|
||||
throw new Error('No valid selection');
|
||||
}
|
||||
|
||||
var colorDomain = chart.getColorDomain(selection),
|
||||
lengthOfColorDomain = colorDomain.length,
|
||||
colorArray = getColor(lengthOfColorDomain),
|
||||
colorDict;
|
||||
|
||||
colorDict = chart.getColorDict(colorDomain, colorArray);
|
||||
|
||||
return colorDict;
|
||||
} catch (error) {
|
||||
console.error('chart.getColors: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getColorDict = function (colorDomain, colorArray) {
|
||||
try {
|
||||
if (!colorDomain) {
|
||||
throw new Error('No valid colorDomain');
|
||||
}
|
||||
if (!colorArray) {
|
||||
throw new Error('No valid colorArray');
|
||||
}
|
||||
|
||||
var colorDict;
|
||||
|
||||
colorDict = _.zipObject(colorDomain, colorArray);
|
||||
|
||||
return colorDict;
|
||||
} catch (error) {
|
||||
console.error('chart.getColorDict' + error);
|
||||
}
|
||||
};
|
||||
|
||||
/* Color domain */
|
||||
chart.getColorDomain = function (selection) {
|
||||
try {
|
||||
if (!selection) {
|
||||
throw new Error('No valid selection');
|
||||
}
|
||||
|
||||
var items = [];
|
||||
|
||||
selection.each(function (d) {
|
||||
d.series.forEach(function (label) {
|
||||
items.push(label.label);
|
||||
});
|
||||
});
|
||||
|
||||
items = _.uniq(items);
|
||||
return items;
|
||||
} catch (error) {
|
||||
console.error('chart.getColorDomain: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
/* Function for global yAxis */
|
||||
chart.getYAxisMax = function (selection) {
|
||||
try {
|
||||
if (!selection) {
|
||||
throw new Error('No valid selection');
|
||||
}
|
||||
|
||||
var yArray = [];
|
||||
|
||||
selection.each(function (d) {
|
||||
return d3.max(d.series, function (layer) {
|
||||
return d3.max(layer.values, function (d) {
|
||||
yArray.push(d.y);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return d3.max(yArray);
|
||||
} catch (error) {
|
||||
console.error('chart.getYAxisMax: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getBounds = function (data) {
|
||||
try {
|
||||
if (!data) {
|
||||
throw new Error('No valid data');
|
||||
}
|
||||
|
||||
var bounds = [];
|
||||
|
||||
data.series.map(function (series) {
|
||||
series.values.map(function (d, i) {
|
||||
bounds.push({
|
||||
x: xValue.call(series, d, i),
|
||||
y: yValue.call(series, d, i)
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return bounds;
|
||||
} catch (error) {
|
||||
console.error('chart.getBounds: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.createLineChart = function (args) {
|
||||
try {
|
||||
if (typeof args === 'undefined') {
|
||||
args = {};
|
||||
}
|
||||
|
||||
var data = args.data,
|
||||
that = args.this,
|
||||
colors = args.colors,
|
||||
tip = args.tip,
|
||||
yAxisMax = args.yAxisMax,
|
||||
xAxisLabel = data.xAxisLabel,
|
||||
yAxisLabel = data.yAxisLabel,
|
||||
chartLabel = data.label,
|
||||
xAxisFormatter = data.xAxisFormatter,
|
||||
yAxisFormatter = data.yAxisFormatter,
|
||||
tooltipFormatter = data.tooltipFormatter;
|
||||
|
||||
var elemWidth = parseInt(d3.select(that)
|
||||
.style('width'), 10),
|
||||
elemHeight = parseInt(d3.select(that)
|
||||
.style('height'), 10);
|
||||
|
||||
if (!elemWidth) {
|
||||
throw new Error('The visualization element has no width');
|
||||
}
|
||||
if (!elemHeight) {
|
||||
throw new Error('The visualization element has no height');
|
||||
}
|
||||
|
||||
var margin = {
|
||||
top: 35,
|
||||
right: 15,
|
||||
bottom: 35,
|
||||
left: 50
|
||||
},
|
||||
width = elemWidth - margin.left - margin.right,
|
||||
height = elemHeight - margin.top - margin.bottom;
|
||||
|
||||
var xTickScale = d3.scale.linear()
|
||||
.clamp(true)
|
||||
.domain([80, 300, 800])
|
||||
.range([0, 2, 4]);
|
||||
|
||||
var yTickScale = d3.scale.linear()
|
||||
.clamp(true)
|
||||
.domain([20, 40, 1000])
|
||||
.range([0, 1, 10]);
|
||||
|
||||
var xTickN = Math.floor(xTickScale(width)),
|
||||
yTickN = Math.floor(yTickScale(height));
|
||||
|
||||
var xScale = d3.time.scale()
|
||||
.range([0, width]);
|
||||
|
||||
var yScale = d3.scale.linear()
|
||||
.range([height, 0]);
|
||||
|
||||
var xAxis = d3.svg.axis()
|
||||
.scale(xScale)
|
||||
.ticks(xTickN)
|
||||
.tickPadding(5)
|
||||
.tickFormat(xAxisFormatter)
|
||||
.orient('bottom');
|
||||
|
||||
var yAxis = d3.svg.axis()
|
||||
.scale(yScale)
|
||||
.ticks(yTickN)
|
||||
.tickPadding(4)
|
||||
.tickFormat(yAxisFormatter)
|
||||
.orient('left');
|
||||
|
||||
var interpolate = 'linear';
|
||||
|
||||
var line = d3.svg.line()
|
||||
.interpolate(interpolate)
|
||||
.x(X)
|
||||
.y(Y);
|
||||
|
||||
var voronoi = d3.geom.voronoi()
|
||||
.x(function (d) { return xScale(d.x); })
|
||||
.y(function (d) { return yScale(d.y); })
|
||||
.clipExtent([
|
||||
[-margin.left, -margin.top],
|
||||
[width + margin.right, height + margin.bottom]
|
||||
]);
|
||||
|
||||
var vis = d3.select(elem);
|
||||
var allLayers = vis.selectAll('path');
|
||||
var allItms = d3.select('.legendwrapper')
|
||||
.selectAll('li.legends');
|
||||
var scrolltop = document.body.scrollTop;
|
||||
var mousemove;
|
||||
|
||||
/* *** Data Manipulation *** */
|
||||
var seriesData = [];
|
||||
|
||||
// adds the label value to each data point
|
||||
// within the values array for displaying in the tooltip
|
||||
data.series.forEach(function (d) {
|
||||
d.values.forEach(function (e) {
|
||||
e.label = d.label;
|
||||
});
|
||||
});
|
||||
|
||||
data.series.map(function (series) {
|
||||
seriesData.push(series);
|
||||
});
|
||||
|
||||
xScale.domain(d3.extent(chart.getBounds(data), function (d) {
|
||||
return d.x;
|
||||
}));
|
||||
|
||||
// setting the y scale domain
|
||||
if (shareYAxis) {
|
||||
yScale
|
||||
.domain([0, yAxisMax])
|
||||
.nice(yTickN);
|
||||
} else {
|
||||
yScale
|
||||
.domain([0, d3.max(chart.getBounds(data), function (d) {
|
||||
return d.y;
|
||||
})])
|
||||
.nice(yTickN);
|
||||
}
|
||||
/* ************************** */
|
||||
|
||||
var svg = d3.select(that)
|
||||
.append('svg')
|
||||
.attr('class', 'canvas')
|
||||
.attr('width', '100%')
|
||||
.attr('height', '100%');
|
||||
|
||||
var g = svg.append('g')
|
||||
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
||||
|
||||
// background rect
|
||||
g.append('rect')
|
||||
.attr('class', 'chart-bkgd')
|
||||
.attr('width', width)
|
||||
.attr('height', height);
|
||||
|
||||
g.append('g')
|
||||
.attr('class', 'x axis')
|
||||
.attr('transform', 'translate(0,' + height + ')')
|
||||
.call(xAxis)
|
||||
.selectAll('text')
|
||||
.call(chart.tickText, width)
|
||||
.on('mouseover', function (d) {
|
||||
if (addTooltip) {
|
||||
var hh = tip[0][0].scrollHeight;
|
||||
|
||||
mousemove = {
|
||||
left: d3.event.pageX,
|
||||
top: d3.event.pageY
|
||||
};
|
||||
|
||||
d3.select(that)
|
||||
.style('cursor', 'default');
|
||||
|
||||
return tip.datum(d)
|
||||
.text(d)
|
||||
.style('top', mousemove.top - scrolltop - hh / 2 + 'px')
|
||||
.style('left', mousemove.left + 20 + 'px')
|
||||
.style('visibility', 'visible');
|
||||
}
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
d3.select(that)
|
||||
.classed('hover', false)
|
||||
.style('stroke', null);
|
||||
if (addTooltip) {
|
||||
tip.style('visibility', 'hidden');
|
||||
}
|
||||
});
|
||||
|
||||
g.append('g')
|
||||
.attr('class', 'y axis')
|
||||
.call(yAxis);
|
||||
|
||||
// Axis labels
|
||||
g.append('text')
|
||||
.attr('class', 'x-axis-label')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('x', width / 2)
|
||||
.attr('y', height + 30)
|
||||
.text(xAxisLabel);
|
||||
|
||||
g.append('text')
|
||||
.attr('class', 'y-axis-label')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('x', -height / 2)
|
||||
.attr('y', -40)
|
||||
.attr('dy', '.75em')
|
||||
.attr('transform', 'rotate(-90)')
|
||||
.text(yAxisLabel);
|
||||
|
||||
// Chart title
|
||||
g.append('text')
|
||||
.attr('class', 'charts-label')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('x', width / 2)
|
||||
.attr('y', -10)
|
||||
.text(chartLabel)
|
||||
.call(chart.tickText, width)
|
||||
.on('mouseover', function (d) {
|
||||
if (addTooltip) {
|
||||
var hh = tip[0][0].scrollHeight;
|
||||
|
||||
mousemove = {
|
||||
left: d3.event.pageX,
|
||||
top: d3.event.pageY
|
||||
};
|
||||
|
||||
d3.select(that)
|
||||
.style('cursor', 'default');
|
||||
|
||||
return tip.text(d.label)
|
||||
.style('top', mousemove.top - scrolltop - hh / 2 + 'px')
|
||||
.style('left', mousemove.left + 20 + 'px')
|
||||
.style('visibility', 'visible');
|
||||
}
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
d3.select(that)
|
||||
.classed('hover', false)
|
||||
.style('stroke', null);
|
||||
if (addTooltip) {
|
||||
tip.style('visibility', 'hidden');
|
||||
}
|
||||
});
|
||||
|
||||
var lines = g.selectAll('.lines')
|
||||
.data(seriesData)
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('class', 'lines');
|
||||
|
||||
lines.append('path')
|
||||
.attr('class', function (d) {
|
||||
return 'rl rl-' + chart.getClassName(d.label, yAxisLabel);
|
||||
})
|
||||
.attr('d', function (d) {
|
||||
return line(d.values);
|
||||
})
|
||||
.attr('fill', 'none')
|
||||
.attr('stroke', function (d) {
|
||||
return colors[d.label];
|
||||
// return d.label ? colors[d.label] : colors[yAxisLabel];
|
||||
})
|
||||
.attr('stroke-width', 3);
|
||||
|
||||
var voronoiGroup = g.append('g')
|
||||
.attr('class', 'voronoi');
|
||||
|
||||
voronoiGroup.selectAll('path')
|
||||
.data(seriesData, function (d) {
|
||||
return voronoi(d.values);
|
||||
})
|
||||
.enter().append('path')
|
||||
.attr('d', function (d) { return line(d.values); })
|
||||
.attr('fill', 'none')
|
||||
.datum(function (d) { return d.values; });
|
||||
// .on('mouseover', mouseover)
|
||||
// .on('mouseout', mouseout);
|
||||
|
||||
var layer = g.selectAll('.layer')
|
||||
.data(seriesData)
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('class', function (d) {
|
||||
return 'rl rl-' + chart.getClassName(d.label, yAxisLabel);
|
||||
})
|
||||
.attr('stroke', function (d) {
|
||||
return colors[d.label];
|
||||
// return d.label ? colors[d.label] : colors[yAxisLabel];
|
||||
});
|
||||
|
||||
var circle = layer.selectAll('.points')
|
||||
.data(function (d) {
|
||||
return d.values;
|
||||
})
|
||||
.enter()
|
||||
.append('circle')
|
||||
.attr('class', 'points')
|
||||
.attr('cx', function (d) {
|
||||
return xScale(d.x);
|
||||
})
|
||||
.attr('cy', function (d) {
|
||||
return yScale(d.y);
|
||||
})
|
||||
.attr('r', 8);
|
||||
|
||||
circle
|
||||
.attr('fill', '#ffffff')
|
||||
.attr('stroke', function (d) {
|
||||
return colors[d.label];
|
||||
// return d.label ? colors[d.label] : colors[yAxisLabel];
|
||||
})
|
||||
.attr('stroke-width', 3.5)
|
||||
.attr('opacity', 0);
|
||||
|
||||
circle
|
||||
.on('mouseover', function (d, i) {
|
||||
var point = d3.select(this);
|
||||
var layerClass = '.rl-' + chart.getClassName(d.label, yAxisLabel);
|
||||
|
||||
point.attr('opacity', 1)
|
||||
.classed('hover', true)
|
||||
.style('cursor', 'pointer');
|
||||
|
||||
// highlight chart layer
|
||||
allLayers = vis.selectAll('path');
|
||||
allLayers.style('opacity', 0.3);
|
||||
|
||||
vis.selectAll(layerClass)
|
||||
.style('opacity', 1);
|
||||
|
||||
// highlight legend item
|
||||
if (allItms) {
|
||||
allItms.style('opacity', 0.3);
|
||||
|
||||
var itm = d3.select('.legendwrapper')
|
||||
.select(layerClass)
|
||||
.style('opacity', 1);
|
||||
}
|
||||
|
||||
dispatch.hover({
|
||||
value: yValue(d, i),
|
||||
point: d,
|
||||
pointIndex: i,
|
||||
series: data.series,
|
||||
config: config,
|
||||
data: latestData,
|
||||
e: d3.event
|
||||
});
|
||||
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('mousemove', function (d) {
|
||||
if (addTooltip) {
|
||||
var hh = tip[0][0].scrollHeight,
|
||||
datum;
|
||||
|
||||
mousemove = {
|
||||
left: d3.event.pageX,
|
||||
top: d3.event.pageY
|
||||
};
|
||||
|
||||
if (typeof d.label !== 'undefined') {
|
||||
datum = {
|
||||
label: d.label,
|
||||
x: d.x,
|
||||
y: d.y
|
||||
};
|
||||
} else {
|
||||
datum = {
|
||||
x: d.x,
|
||||
y: d.y
|
||||
};
|
||||
}
|
||||
|
||||
tip.datum(datum)
|
||||
.text(tooltipFormatter)
|
||||
.style('top', mousemove.top - scrolltop - hh / 2 + 'px')
|
||||
.style('left', mousemove.left + 20 + 'px')
|
||||
.style('visibility', 'visible');
|
||||
}
|
||||
})
|
||||
.on('click', function (d, i) {
|
||||
dispatch.click({
|
||||
value: yValue(d, i),
|
||||
point: d,
|
||||
pointIndex: i,
|
||||
series: data.series,
|
||||
config: config,
|
||||
data: latestData,
|
||||
e: d3.event
|
||||
});
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
var point = d3.select(this);
|
||||
point.attr('opacity', 0);
|
||||
if (addTooltip) {
|
||||
tip.style('visibility', 'hidden');
|
||||
}
|
||||
allLayers.style('opacity', 1);
|
||||
allItms.style('opacity', 1);
|
||||
});
|
||||
|
||||
if (addTooltip) {
|
||||
// **** hilite series on hover
|
||||
allLayers = vis.selectAll('path');
|
||||
lines.on('mouseover', function (d) {
|
||||
// highlight chart layer
|
||||
allLayers.style('opacity', 0.3);
|
||||
var layerClass = '.rl-' + chart.getClassName(d.label, yAxisLabel);
|
||||
var myLayer = vis.selectAll(layerClass)
|
||||
.style('opacity', 1);
|
||||
|
||||
// stroke this rect
|
||||
d3.select(this)
|
||||
.classed('hover', true)
|
||||
.style('stroke', '#333')
|
||||
.style('cursor', 'pointer');
|
||||
|
||||
// hilite legend item
|
||||
if (allItms) {
|
||||
allItms.style('opacity', 0.3);
|
||||
var itm = d3.select('.legendwrapper')
|
||||
.select(layerClass)
|
||||
.style('opacity', 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Event Selection: BRUSH */
|
||||
var brush = d3.svg.brush()
|
||||
.x(xScale)
|
||||
.on('brushend', brushend);
|
||||
|
||||
if (dispatch.on('brush')) {
|
||||
g.append('g')
|
||||
.attr('class', 'brush')
|
||||
.call(brush)
|
||||
.selectAll('rect')
|
||||
.attr('height', height);
|
||||
}
|
||||
/* ************************** */
|
||||
|
||||
lines.on('mouseout', function () {
|
||||
allLayers.style('opacity', 1);
|
||||
allItms.style('opacity', 1);
|
||||
});
|
||||
|
||||
return svg;
|
||||
|
||||
} catch (error) {
|
||||
console.error('chart.createLineChart: ' + error);
|
||||
}
|
||||
|
||||
function X(d) {
|
||||
return xScale(d.x);
|
||||
}
|
||||
|
||||
function Y(d) {
|
||||
return yScale(d.y);
|
||||
}
|
||||
|
||||
function brushend() {
|
||||
var selected;
|
||||
var start;
|
||||
var lastVal;
|
||||
var end;
|
||||
var selectedRange;
|
||||
|
||||
// selected is used to determine the range for ordinal scales
|
||||
selected = xScale.domain()
|
||||
.filter(function (d) {
|
||||
return (brush.extent()[0] <= xScale(d)) && (xScale(d) <= brush.extent()[1]);
|
||||
});
|
||||
|
||||
start = selected[0];
|
||||
lastVal = selected.length - 1;
|
||||
end = selected[lastVal];
|
||||
selectedRange = [start, end];
|
||||
|
||||
return brush.extent()[0] instanceof Date ?
|
||||
dispatch.brush({
|
||||
range: brush.extent(),
|
||||
config: config,
|
||||
e: d3.event,
|
||||
data: latestData
|
||||
}) :
|
||||
dispatch.brush({
|
||||
range: selectedRange,
|
||||
config: config,
|
||||
e: d3.event,
|
||||
data: latestData
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// getters / setters
|
||||
chart.resize = _.debounce(function () {
|
||||
try {
|
||||
if (!latestData) {
|
||||
throw new Error('No valid data');
|
||||
}
|
||||
chart.render(latestData);
|
||||
} catch (error) {
|
||||
console.error('chart.resize: ' + error);
|
||||
}
|
||||
}, 200);
|
||||
|
||||
// enable auto-resize
|
||||
chart.checkSize = function checkSize() {
|
||||
try {
|
||||
var size = $elem.width() + ':' + $elem.height();
|
||||
|
||||
if (prevSize !== size) {
|
||||
chart.resize();
|
||||
}
|
||||
prevSize = size;
|
||||
|
||||
setTimeout(checkSize, 250);
|
||||
} catch (error) {
|
||||
console.error('chart.checkSize: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
/* Function for truncating x axis tick labels */
|
||||
chart.tickText = function (text, width) {
|
||||
try {
|
||||
if (!text) {
|
||||
throw new Error('No text was given');
|
||||
}
|
||||
if (!width) {
|
||||
throw new Error('No width was given');
|
||||
}
|
||||
|
||||
var n = text[0].length,
|
||||
maxw = width / n * 0.9,
|
||||
tickn = Math.floor(maxw);
|
||||
|
||||
text.each(function () {
|
||||
var text = d3.select(this),
|
||||
length = this.getComputedTextLength(),
|
||||
tspan = text.text();
|
||||
|
||||
if (length > maxw) {
|
||||
var str = text.text(),
|
||||
avg = length / str.length,
|
||||
end = Math.floor(maxw / avg);
|
||||
str = str.substr(0, end) + '...';
|
||||
tspan = text.text(str);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('chart.tickText: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getClassName = function (label, yAxisLabel) {
|
||||
try {
|
||||
return label ? chart.classifyString(label) : chart.classifyString(yAxisLabel);
|
||||
} catch (error) {
|
||||
console.error('chart.getClassName: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.classifyString = function (string) {
|
||||
try {
|
||||
if (!chart.isString(string)) {
|
||||
string = chart.stringify(string);
|
||||
}
|
||||
return string.replace(/[.]+|[/]+|[\s]+|[*]+|[;]+|[(]+|[)]+|[:]+|[,]+/g, '');
|
||||
} catch (error) {
|
||||
console.error('chart.classifyString: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.isString = function (value) {
|
||||
try {
|
||||
if (typeof value === 'string') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('chart.isString: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.stringify = function (value) {
|
||||
try {
|
||||
var string = value + '';
|
||||
return string;
|
||||
} catch (error) {
|
||||
console.error('chart.stringify: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.error = function () {
|
||||
try {
|
||||
// Removes the legend container
|
||||
d3.select(elem)
|
||||
.selectAll('*')
|
||||
.remove();
|
||||
|
||||
var errorWrapper = d3.select(elem)
|
||||
.append('div')
|
||||
.attr('class', 'errorWrapper')
|
||||
.style('height', function () {
|
||||
return $(elem)
|
||||
.height() + 'px';
|
||||
})
|
||||
.style('text-align', 'center');
|
||||
|
||||
errorWrapper.append('p')
|
||||
.style('font-size', '18px')
|
||||
.style('margin-top', function () {
|
||||
return $(elem)
|
||||
.height() / 3 + 'px';
|
||||
})
|
||||
.style('line-height', '18px')
|
||||
.text('The container is too small for this chart.');
|
||||
|
||||
return chart;
|
||||
} catch (error) {
|
||||
console.error('chart.error: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.off = function (event) {
|
||||
try {
|
||||
dispatch.on(event, null);
|
||||
return chart;
|
||||
} catch (error) {
|
||||
console.error('chart.off: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Destroys all charts associated with the parent element
|
||||
* if the argument passed is true. By default the argument
|
||||
* is true.
|
||||
*/
|
||||
chart.destroy = function (_) {
|
||||
try {
|
||||
if (!arguments.length || _) {
|
||||
destroyFlag = _ || true;
|
||||
|
||||
// Removing chart and all elements associated with it
|
||||
d3.select(elem)
|
||||
.selectAll('*')
|
||||
.remove();
|
||||
|
||||
// Cleaning up event listeners
|
||||
chart.off('click');
|
||||
chart.off('hover');
|
||||
chart.off('brush');
|
||||
d3.select(window)
|
||||
.on('resize', null);
|
||||
}
|
||||
|
||||
destroyFlag = _;
|
||||
return chart;
|
||||
} catch (error) {
|
||||
console.error('chart.destroy: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.dispatch = dispatch;
|
||||
|
||||
d3.rebind(chart, dispatch, 'on');
|
||||
d3.select(window)
|
||||
.on('resize', chart.resize);
|
||||
|
||||
chart.checkSize();
|
||||
|
||||
return chart;
|
||||
};
|
||||
});
|
|
@ -1,658 +0,0 @@
|
|||
define(function (require) {
|
||||
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
var d3 = require('d3');
|
||||
|
||||
var getSelection = require('components/vislib/utils/selection');
|
||||
var getLegend = require('components/vislib/modules/legend');
|
||||
var getColor = require('components/vislib/utils/colorspace');
|
||||
|
||||
return function getPieChart(elem, config) {
|
||||
if (typeof config === 'undefined') {
|
||||
config = {};
|
||||
}
|
||||
|
||||
var chart = {};
|
||||
|
||||
/* ***** Chart Options ***** */
|
||||
var addLegend = config.addLegend || false;
|
||||
var addTooltip = config.addTooltip || false;
|
||||
var shareYAxis = config.shareYAxis || false;
|
||||
/* ************************* */
|
||||
|
||||
/* ***** Chart Flags ******* */
|
||||
var destroyFlag = false;
|
||||
/* ************************* */
|
||||
|
||||
/* ***** Chart Globals ******* */
|
||||
var dispatch = d3.dispatch('hover', 'click', 'mouseenter', 'mouseleave', 'mouseout', 'mouseover', 'brush');
|
||||
var $elem = $(elem); // cached jquery version of element
|
||||
var latestData;
|
||||
var prevSize;
|
||||
var xValue = function (d, i) {
|
||||
return d.x;
|
||||
};
|
||||
var yValue = function (d, i) {
|
||||
return d.y;
|
||||
};
|
||||
/* ************************* */
|
||||
|
||||
/*
|
||||
Renders the chart to the HTML element
|
||||
*/
|
||||
chart.render = function (data) {
|
||||
try {
|
||||
if (!data) {
|
||||
throw new Error('No valid data');
|
||||
}
|
||||
if (!elem) {
|
||||
throw new Error('No elem provided');
|
||||
}
|
||||
|
||||
// store a copy of the data sent to render, so that it can be resent with .resize()
|
||||
latestData = data;
|
||||
|
||||
// removes elements to redraw the chart on subsequent calls
|
||||
d3.select(elem)
|
||||
.selectAll('*')
|
||||
.remove();
|
||||
|
||||
var chartWrapper = chart.getChartWrapper(elem)[0][0],
|
||||
selection = chart.getSelection(chartWrapper, latestData);
|
||||
|
||||
return chart.getVisualization(selection);
|
||||
} catch (error) {
|
||||
console.error('chart.render: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
/* Function for truncating x axis tick labels */
|
||||
chart.tickText = function (text, width) {
|
||||
var n = text[0].length,
|
||||
maxw = width / n * 0.9,
|
||||
tickn = Math.floor(maxw);
|
||||
text.each(function () {
|
||||
var text = d3.select(this),
|
||||
length = this.getComputedTextLength(),
|
||||
tspan = text.text();
|
||||
if (length > maxw) {
|
||||
var str = text.text(),
|
||||
avg = length / str.length,
|
||||
end = Math.floor(maxw / avg);
|
||||
str = str.substr(0, end) + '...';
|
||||
tspan = text.text(str);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Creates the d3 visualization
|
||||
*/
|
||||
chart.getVisualization = function (selection) {
|
||||
try {
|
||||
if (!selection) {
|
||||
throw new Error('No valid selection');
|
||||
}
|
||||
|
||||
if (destroyFlag) {
|
||||
throw new Error('You destroyed the chart and tried to use it again');
|
||||
}
|
||||
|
||||
var colors = chart.getColors(selection);
|
||||
|
||||
// Calculates the max Y axis value for all Charts
|
||||
if (shareYAxis) {
|
||||
var yAxisMax = chart.getYAxisMax(selection);
|
||||
}
|
||||
|
||||
// Adds the legend
|
||||
if (addLegend) {
|
||||
var legend = getLegend(elem, colors, chart);
|
||||
}
|
||||
|
||||
// Adds tooltips
|
||||
if (addTooltip) {
|
||||
var tip = chart.getTooltip(elem);
|
||||
}
|
||||
|
||||
return selection.each(function (d, i) {
|
||||
var that = this;
|
||||
|
||||
chart.createPieChart({
|
||||
'data': d,
|
||||
'index': i,
|
||||
'this': that,
|
||||
'colors': colors,
|
||||
'tip': tip
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('chart.getVisualization: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getTooltip = function (elem) {
|
||||
try {
|
||||
if (!elem) {
|
||||
throw new Error('No valid elem');
|
||||
}
|
||||
|
||||
var tooltipDiv;
|
||||
|
||||
tooltipDiv = d3.select(elem)
|
||||
.append('div')
|
||||
.attr('class', 'k4tip');
|
||||
|
||||
return tooltipDiv;
|
||||
} catch (error) {
|
||||
console.error('chart.getTooltip: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getChartWrapper = function (elem) {
|
||||
try {
|
||||
if (!elem) {
|
||||
throw new Error('No valid elem');
|
||||
}
|
||||
|
||||
var chartWrapper = d3.select(elem)
|
||||
.append('div');
|
||||
|
||||
chartWrapper
|
||||
.attr('class', 'chartwrapper')
|
||||
.style('height', $(elem)
|
||||
.height() + 'px');
|
||||
|
||||
return chartWrapper;
|
||||
} catch (error) {
|
||||
console.error('chart.getChartWrapper: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getSelection = function (elem, data) {
|
||||
try {
|
||||
if (!elem) {
|
||||
throw new Error('No valid elem');
|
||||
}
|
||||
if (!data) {
|
||||
throw new Error('No valid data');
|
||||
}
|
||||
|
||||
var selection = d3.selectAll(getSelection(elem, data));
|
||||
|
||||
return selection;
|
||||
} catch (error) {
|
||||
console.error('chart.getSelection: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getColors = function (selection) {
|
||||
try {
|
||||
if (!selection) {
|
||||
throw new Error('No valid selection');
|
||||
}
|
||||
|
||||
var colorDomain = chart.getColorDomain(selection),
|
||||
lengthOfColorDomain = colorDomain.length,
|
||||
colorArray = getColor(lengthOfColorDomain),
|
||||
colorDict;
|
||||
|
||||
colorDict = chart.getColorDict(colorDomain, colorArray);
|
||||
|
||||
return colorDict;
|
||||
} catch (error) {
|
||||
console.error('chart.getColors: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getColorDict = function (colorDomain, colorArray) {
|
||||
try {
|
||||
if (!colorDomain) {
|
||||
throw new Error('No valid colorDomain');
|
||||
}
|
||||
if (!colorArray) {
|
||||
throw new Error('No valid colorArray');
|
||||
}
|
||||
|
||||
var colorDict;
|
||||
|
||||
colorDict = _.zipObject(colorDomain, colorArray);
|
||||
|
||||
return colorDict;
|
||||
} catch (error) {
|
||||
console.error('chart.getColorDict' + error);
|
||||
}
|
||||
};
|
||||
|
||||
/* Color domain */
|
||||
chart.getColorDomain = function (selection) {
|
||||
try {
|
||||
if (!selection) {
|
||||
throw new Error('No valid selection');
|
||||
}
|
||||
|
||||
var items = [];
|
||||
|
||||
selection.each(function (d) {
|
||||
d.series.forEach(function (label) {
|
||||
label.values.forEach(function (value) {
|
||||
items.push(value.x);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
items = _.uniq(items);
|
||||
return items;
|
||||
} catch (error) {
|
||||
console.error('chart.getColorDomain: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.createPieChart = function (args) {
|
||||
try {
|
||||
if (typeof args === 'undefined') {
|
||||
args = {};
|
||||
}
|
||||
|
||||
var data = args.data;
|
||||
var that = args.this;
|
||||
var colors = args.colors;
|
||||
var tip = args.tip;
|
||||
var yAxisLabel = data.yAxisLabel;
|
||||
var chartLabel = data.label;
|
||||
var tooltipFormatter = data.tooltipFormatter;
|
||||
|
||||
var elemWidth = parseInt(d3.select(that)
|
||||
.style('width'), 10);
|
||||
var elemHeight = parseInt(d3.select(that)
|
||||
.style('height'), 10);
|
||||
|
||||
if (!elemWidth) {
|
||||
throw new Error('The visualization element has no width');
|
||||
}
|
||||
if (!elemHeight) {
|
||||
throw new Error('The visualization element has no height');
|
||||
}
|
||||
|
||||
var width = elemWidth;
|
||||
var height = elemHeight;
|
||||
var radius = Math.min(width, height) / 2;
|
||||
|
||||
var arc = d3.svg.arc()
|
||||
.outerRadius(radius - 20)
|
||||
.innerRadius(0);
|
||||
|
||||
var pie = d3.layout.pie()
|
||||
.sort(null)
|
||||
.value(function (d) {
|
||||
return d.y;
|
||||
});
|
||||
|
||||
var vis = d3.select(elem);
|
||||
var allLayers = vis.selectAll('path');
|
||||
var allItms = d3.select('.legendwrapper')
|
||||
.selectAll('li.legends');
|
||||
var scrolltop = document.body.scrollTop;
|
||||
var mousemove;
|
||||
|
||||
/* *** Data Manipulation *** */
|
||||
var seriesData = [];
|
||||
|
||||
// adds the label value to each data point
|
||||
// within the values array for displaying in the tooltip
|
||||
data.series.forEach(function (d) {
|
||||
d.values.forEach(function (e) {
|
||||
d.label ? e.label = d.label : e.label = e.x;
|
||||
});
|
||||
});
|
||||
|
||||
data.series.map(function (series) {
|
||||
seriesData.push(series);
|
||||
});
|
||||
/* ************************** */
|
||||
|
||||
var svg = d3.select(that)
|
||||
.append('svg')
|
||||
.attr('class', 'canvas')
|
||||
.attr('width', '100%')
|
||||
.attr('height', '100%');
|
||||
|
||||
// background rect
|
||||
svg.append('rect')
|
||||
.attr('class', 'chart-bkgd')
|
||||
.attr('width', width)
|
||||
.attr('height', height);
|
||||
|
||||
var g = svg.append('g')
|
||||
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
|
||||
|
||||
// Chart title
|
||||
g.append('text')
|
||||
.attr('class', 'charts-label')
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('x', 0)
|
||||
.attr('y', -radius + 10)
|
||||
.text(chartLabel)
|
||||
.call(chart.tickText, width)
|
||||
.on('mouseover', function (d) {
|
||||
if (addTooltip) {
|
||||
var hh = tip[0][0].scrollHeight;
|
||||
|
||||
mousemove = {
|
||||
left: d3.event.pageX,
|
||||
top: d3.event.pageY
|
||||
};
|
||||
|
||||
d3.select(that)
|
||||
.style('cursor', 'default');
|
||||
|
||||
return tip.text(d.label)
|
||||
.style('top', mousemove.top - scrolltop - hh / 2 + 'px')
|
||||
.style('left', mousemove.left + 20 + 'px')
|
||||
.style('visibility', 'visible');
|
||||
}
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
d3.select(that)
|
||||
.classed('hover', false)
|
||||
.style('stroke', null);
|
||||
if (addTooltip) {
|
||||
tip.style('visibility', 'hidden');
|
||||
}
|
||||
});
|
||||
|
||||
g.data(seriesData);
|
||||
|
||||
var wedge = g.selectAll('.arc')
|
||||
.data(function (d) {
|
||||
return pie(d.values);
|
||||
})
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('class', 'arc');
|
||||
|
||||
wedge.append('path')
|
||||
.attr('d', arc)
|
||||
.attr('class', function (d) {
|
||||
return 'rl rl-' + chart.getClassName(d.data.label, yAxisLabel);
|
||||
})
|
||||
.style('fill', function (d) {
|
||||
return colors[d.data.x];
|
||||
})
|
||||
.on('mouseover', function (d, i) {
|
||||
var point = d3.select(this);
|
||||
var layerClass = '.rl-' + chart.getClassName(d.data.label, yAxisLabel);
|
||||
|
||||
point.attr('opacity', 1)
|
||||
.classed('hover', true)
|
||||
.style('cursor', 'pointer');
|
||||
|
||||
// highlight chart layer
|
||||
allLayers = vis.selectAll('.rl');
|
||||
allLayers.style('opacity', 0.3);
|
||||
|
||||
vis.selectAll(layerClass)
|
||||
.style('opacity', 1);
|
||||
|
||||
// highlight legend item
|
||||
if (allItms) {
|
||||
allItms.style('opacity', 0.3);
|
||||
|
||||
var itm = d3.select('.legendwrapper')
|
||||
.select(layerClass)
|
||||
.style('opacity', 1);
|
||||
}
|
||||
|
||||
dispatch.hover({
|
||||
value: yValue(d, i),
|
||||
point: d,
|
||||
pointIndex: i,
|
||||
series: data.series,
|
||||
config: config,
|
||||
data: latestData,
|
||||
e: d3.event
|
||||
});
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('mousemove', function (d) {
|
||||
var datum;
|
||||
var hh = tip[0][0].scrollHeight;
|
||||
|
||||
mousemove = {
|
||||
left: d3.event.pageX,
|
||||
top: d3.event.pageY
|
||||
};
|
||||
scrolltop = document.body.scrollTop;
|
||||
|
||||
if (typeof d.data.label !== 'undefined') {
|
||||
datum = {
|
||||
label: d.data.label,
|
||||
x: d.data.x,
|
||||
y: d.data.y
|
||||
};
|
||||
} else {
|
||||
datum = {
|
||||
x: d.data.x,
|
||||
y: d.data.y
|
||||
};
|
||||
}
|
||||
|
||||
tip.datum(datum)
|
||||
.text(tooltipFormatter)
|
||||
.style('top', mousemove.top - scrolltop - hh / 2 + 'px')
|
||||
.style('left', mousemove.left + 20 + 'px')
|
||||
.style('visibility', 'visible');
|
||||
})
|
||||
.on('click', function (d, i) {
|
||||
dispatch.click({
|
||||
value: yValue(d, i),
|
||||
point: d,
|
||||
pointIndex: i,
|
||||
series: data.series,
|
||||
config: config,
|
||||
data: latestData,
|
||||
e: d3.event
|
||||
});
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('mouseout', function () {
|
||||
var point = d3.select(this);
|
||||
point.attr('opacity', 0.3);
|
||||
if (addTooltip) {
|
||||
tip.style('visibility', 'hidden');
|
||||
}
|
||||
allLayers.style('opacity', 1);
|
||||
allItms.style('opacity', 1);
|
||||
});
|
||||
|
||||
if (addTooltip) {
|
||||
// **** hilite series on hover
|
||||
// allLayers = vis.selectAll('path');
|
||||
wedge.on('mouseover', function (d) {
|
||||
// highlight chart layer
|
||||
allLayers.style('opacity', 0.3);
|
||||
var layerClass = '.rl-' + chart.getClassName(d.label, yAxisLabel);
|
||||
var myLayer = vis.selectAll(layerClass)
|
||||
.style('opacity', 1);
|
||||
|
||||
// stroke this rect
|
||||
d3.select(this)
|
||||
.classed('hover', true)
|
||||
.style('stroke', '#333')
|
||||
.style('cursor', 'pointer');
|
||||
|
||||
// hilite legend item
|
||||
if (allItms) {
|
||||
allItms.style('opacity', 0.3);
|
||||
var itm = d3.select('.legendwrapper')
|
||||
.select(layerClass)
|
||||
.style('opacity', 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// wedge.append('text')
|
||||
// .attr('transform', function (d) {
|
||||
// return 'translate(' + arc.centroid(d) + ')';
|
||||
// })
|
||||
// .attr('dy', '.35em')
|
||||
// .style('text-anchor', 'middle')
|
||||
// .text(function (d) {
|
||||
// return d.data.x;
|
||||
// });
|
||||
|
||||
return svg;
|
||||
} catch (error) {
|
||||
console.error('chart.createPieChart: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
// getters / setters
|
||||
chart.resize = _.debounce(function () {
|
||||
try {
|
||||
if (!latestData) {
|
||||
throw new Error('No valid data');
|
||||
}
|
||||
chart.render(latestData);
|
||||
} catch (error) {
|
||||
console.error('chart.resize: ' + error);
|
||||
}
|
||||
}, 200);
|
||||
|
||||
// enable auto-resize
|
||||
chart.checkSize = function checkSize() {
|
||||
try {
|
||||
var size = $elem.width() + ':' + $elem.height();
|
||||
|
||||
if (prevSize !== size) {
|
||||
chart.resize();
|
||||
}
|
||||
prevSize = size;
|
||||
|
||||
setTimeout(checkSize, 250);
|
||||
} catch (error) {
|
||||
console.error('chart.checkSize: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.getClassName = function (label, yAxisLabel) {
|
||||
try {
|
||||
return label ? chart.classifyString(label) : chart.classifyString(yAxisLabel);
|
||||
} catch (error) {
|
||||
console.error('chart.getClassName: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.classifyString = function (string) {
|
||||
try {
|
||||
if (!chart.isString(string)) {
|
||||
string = chart.stringify(string);
|
||||
}
|
||||
return string.replace(/[.]+|[/]+|[\s]+|[*]+|[;]+|[(]+|[)]+|[:]+|[,]+/g, '');
|
||||
} catch (error) {
|
||||
console.error('chart.classifyString: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.isString = function (value) {
|
||||
try {
|
||||
if (typeof value === 'string') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('chart.isString: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.stringify = function (value) {
|
||||
try {
|
||||
var string = value + '';
|
||||
return string;
|
||||
} catch (error) {
|
||||
console.error('chart.stringify: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.error = function () {
|
||||
try {
|
||||
// Removes the legend container
|
||||
d3.select(elem)
|
||||
.selectAll('*')
|
||||
.remove();
|
||||
|
||||
var errorWrapper = d3.select(elem)
|
||||
.append('div')
|
||||
.attr('class', 'errorWrapper')
|
||||
.style('height', function () {
|
||||
return $(elem)
|
||||
.height() + 'px';
|
||||
})
|
||||
.style('text-align', 'center');
|
||||
|
||||
errorWrapper.append('p')
|
||||
.style('font-size', '18px')
|
||||
.style('margin-top', function () {
|
||||
return $(elem)
|
||||
.height() / 3 + 'px';
|
||||
})
|
||||
.style('line-height', '18px')
|
||||
.text('The container is too small for this chart.');
|
||||
|
||||
return chart;
|
||||
} catch (error) {
|
||||
console.error('chart.error: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.off = function (event) {
|
||||
try {
|
||||
dispatch.on(event, null);
|
||||
return chart;
|
||||
} catch (error) {
|
||||
console.error('chart.off: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Destroys all charts associated with the parent element
|
||||
* if the argument passed is true. By default the argument
|
||||
* is true.
|
||||
*/
|
||||
chart.destroy = function (_) {
|
||||
try {
|
||||
if (!arguments.length || _) {
|
||||
destroyFlag = _ || true;
|
||||
|
||||
// Removing chart and all elements associated with it
|
||||
d3.select(elem)
|
||||
.selectAll('*')
|
||||
.remove();
|
||||
|
||||
// Cleaning up event listeners
|
||||
chart.off('click');
|
||||
chart.off('hover');
|
||||
d3.select(window)
|
||||
.on('resize', null);
|
||||
}
|
||||
|
||||
destroyFlag = _;
|
||||
return chart;
|
||||
} catch (error) {
|
||||
console.error('chart.destroy: ' + error);
|
||||
}
|
||||
};
|
||||
|
||||
chart.dispatch = dispatch;
|
||||
|
||||
d3.rebind(chart, dispatch, 'on');
|
||||
d3.select(window)
|
||||
.on('resize', chart.resize);
|
||||
|
||||
chart.checkSize();
|
||||
|
||||
return chart;
|
||||
};
|
||||
});
|
|
@ -1,225 +0,0 @@
|
|||
/* Stylings that will blow your mind! */
|
||||
|
||||
.arc path {
|
||||
stroke: #fff;
|
||||
/* stroke-width: 3px; */
|
||||
}
|
||||
|
||||
.arc path {
|
||||
stroke: #fff;
|
||||
/* stroke-width: 3px; */
|
||||
}
|
||||
|
||||
div.col {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div.rows {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.row-labels, .column-labels {
|
||||
margin: 0;
|
||||
padding: 10;
|
||||
}
|
||||
|
||||
visualize {
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
.chartwrapper {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.x-axis-label {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
bottom: 15px;
|
||||
font-size: 7pt;
|
||||
color: #848e96;
|
||||
}
|
||||
|
||||
div.y-axis-label {
|
||||
position: absolute;
|
||||
left: -25px;
|
||||
top: 42.5%;
|
||||
font-size: 7pt;
|
||||
color: #848e96;
|
||||
-webkit-transform: rotate(270deg);
|
||||
-moz-transform: rotate(270deg);
|
||||
-o-transform: rotate(270deg);
|
||||
-ms-transform: rotate(270deg);
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
|
||||
/* legends */
|
||||
.legendwrapper {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
/*border: 1px solid #ddd;*/
|
||||
position: absolute;
|
||||
float: right;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
top: 34px;
|
||||
right: 10px;
|
||||
z-index: 10;
|
||||
overflow: hidden;
|
||||
}
|
||||
.header {
|
||||
width: 100%;
|
||||
height: 26px;
|
||||
margin: 0 0 6px 0;
|
||||
}
|
||||
.legend-ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 150px;
|
||||
|
||||
}
|
||||
.legend-ul li {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 150px;
|
||||
min-height: 22px;
|
||||
margin: 0 18px 0 18px;
|
||||
padding: 4px 0 4px 0;
|
||||
text-align: left;
|
||||
word-wrap: break-word;
|
||||
font-size: 12px;
|
||||
line-height: 13px;
|
||||
list-style: none;
|
||||
color: #666;
|
||||
|
||||
}
|
||||
.dots {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
float: left;
|
||||
margin: 2px 0 0 -16px;
|
||||
padding: 0;
|
||||
}
|
||||
.legend-toggle {
|
||||
position: relative;
|
||||
float: right;
|
||||
right: 2px;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.legend-open {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
||||
.column-labels {
|
||||
color: #777;
|
||||
font-size: 10pt;
|
||||
font-weight: normal;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-left: 1.0em;
|
||||
line-height: 2.0em;
|
||||
}
|
||||
|
||||
/* histogram axis and label styles */
|
||||
.vis-canvas {
|
||||
/* background-color: #fff; */
|
||||
}
|
||||
|
||||
.chart-bkgd {
|
||||
fill: #ffffff;
|
||||
/*fill: #eff3f4;*/
|
||||
/*stroke: #ddd;*/
|
||||
/*shape-rendering: crispEdges;*/
|
||||
}
|
||||
|
||||
/*.rows-label, .columns-label {
|
||||
font-size: 10pt;
|
||||
fill: #46525d;
|
||||
text-align: center;
|
||||
line-height: 1.5em;
|
||||
}*/
|
||||
|
||||
p.rows-label, p.columns-label {
|
||||
display: block;
|
||||
background: #eff3f4;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 9pt;
|
||||
fill: #46525d;
|
||||
text-align: center;
|
||||
line-height: 1.9em;
|
||||
}
|
||||
|
||||
.charts-label {
|
||||
font-size: 8pt;
|
||||
fill: #848e96;
|
||||
}
|
||||
|
||||
.x-axis-label, .y-axis-label {
|
||||
font-size: 7pt;
|
||||
fill: #848e96;
|
||||
}
|
||||
|
||||
.tick text {
|
||||
font-size: 7pt;
|
||||
fill: #848e96;
|
||||
/*cursor: pointer;*/
|
||||
}
|
||||
|
||||
.axis {
|
||||
shape-rendering: crispEdges;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.axis line, .axis path {
|
||||
stroke: #ddd;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.legend-box {
|
||||
fill: #ffffff;
|
||||
}
|
||||
|
||||
.brush .extent {
|
||||
stroke: #fff;
|
||||
fill-opacity: .125;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.layer .rect:hover {
|
||||
stroke: 3px;
|
||||
}
|
||||
|
||||
.k4tip {
|
||||
line-height: 1;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
padding: 8px;
|
||||
background: rgba(70, 82, 93, 0.95);
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
position: fixed;
|
||||
z-index: 20;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.rect {
|
||||
/*shape-rendering: crispEdges;*/
|
||||
stroke: transparent;
|
||||
stroke-width: 2;
|
||||
}
|
||||
.rect.hover {
|
||||
/*shape-rendering: crispEdges;*/
|
||||
stroke: #333;
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var d3 = require('d3');
|
||||
|
||||
/* Returns an array of usedColors. The length of usedColors
|
||||
is trimmed or extended with generated colors to match length of colDom.
|
||||
*/
|
||||
|
||||
// 72 seed colors
|
||||
var seedColors = [
|
||||
'#57c17b', '#006e8a', '#6f87d8', '#663db8', '#bc52bc', '#9e3533', '#daa05d', '#967b17',
|
||||
'#a0caae', '#73a4b0', '#acb5d8', '#9b8bbb', '#c19fc1', '#b88484', '#e0cbb2', '#bfb282',
|
||||
'#336c46', '#00455c', '#394e93', '#422c6d', '#783678', '#6a2424', '#936734', '#60521f',
|
||||
'#b3d5bf', '#85adb7', '#bdc5e0', '#aa9dc3', '#c9acc9', '#c39898', '#e8d7c5', '#cbc09a',
|
||||
'#2c593b', '#003252', '#34457f', '#352456', '#673267', '#591d1d', '#7f592f', '#443b17',
|
||||
'#c7e0cf', '#a8c5cc', '#d2d7ea', '#c2b9d4', '#dbc7db', '#d5b9b9', '#f2e9de', '#d9d1b5',
|
||||
'#254b32', '#002c47', '#2b3969', '#30214f', '#562956', '#491818', '#654625', '#393114',
|
||||
'#dbebe0', '#bcd2d7', '#d0d6eb', '#cdc4de', '#e8d9e8', '#e0cccc', '#f5eee5', '#e4dec9',
|
||||
'#20412b', '#001f33', '#232f57', '#281b41', '#482348', '#3e1414', '#563c20', '#2e2810'
|
||||
];
|
||||
|
||||
var usedColors = [];
|
||||
|
||||
return function (colDom) {
|
||||
// check if more colors needed
|
||||
var dif = colDom - seedColors.length;
|
||||
if (dif > 0) {
|
||||
// generate more colors
|
||||
usedColors = _.clone(seedColors);
|
||||
for (var newcol, i = 0; i < dif; i++) {
|
||||
newcol = d3.rgb(usedColors[i])
|
||||
.darker(1.3)
|
||||
.toString();
|
||||
usedColors.push(newcol);
|
||||
}
|
||||
} else {
|
||||
// trim to length of colDomain labels
|
||||
usedColors = _.first(seedColors, colDom);
|
||||
}
|
||||
return usedColors;
|
||||
};
|
||||
|
||||
});
|
|
@ -1,97 +0,0 @@
|
|||
define(function (require) {
|
||||
var $ = require('jquery');
|
||||
var d3 = require('d3');
|
||||
|
||||
// Dynamically adds css file
|
||||
require('css!components/vislib/styles/k4.d3');
|
||||
|
||||
// adds an array to another array
|
||||
function addTo(to, array) {
|
||||
[].push.apply(to, array);
|
||||
}
|
||||
|
||||
/*
|
||||
Accepts a DOM element(s) and data.
|
||||
Returns an array of DOM elements on which charts
|
||||
will be rendered.
|
||||
*/
|
||||
function placeChart(elem, data) {
|
||||
var $el = elem instanceof Array ? elem : d3.select(elem)
|
||||
.datum(data),
|
||||
charts = [];
|
||||
|
||||
if (data.rows) {
|
||||
addTo(charts, split($el, 'height', 'width', data.rows, 'rows'));
|
||||
} else if (data.columns) {
|
||||
addTo(charts, split($el, 'width', 'height', data.columns, 'columns'));
|
||||
} else {
|
||||
if (!data.series || data.series.length === 0) {
|
||||
throw new Error('No valid data');
|
||||
}
|
||||
|
||||
addTo(charts, $el.append('div')
|
||||
.attr('class', 'chart')
|
||||
.style('width', '100%')
|
||||
.style('height', '100%')[0]);
|
||||
}
|
||||
|
||||
return charts;
|
||||
}
|
||||
|
||||
/*
|
||||
Accepts a DOM element(s), 'width' and 'height', data and class name.
|
||||
Returns a DOM element array that has been split by class name,
|
||||
i.e. rows or columns.
|
||||
*/
|
||||
function split(elem, by, inherit, data, name) {
|
||||
var charts = [],
|
||||
$el = elem instanceof Array ? elem : d3.select(elem),
|
||||
node = elem instanceof Array ? $(elem[0]) : $(elem),
|
||||
// need to refactor
|
||||
size = ($(node)
|
||||
.parent()[by]() / data.length) / $(node)
|
||||
.parent()[by]() * 100,
|
||||
inheritedSize = node[inherit]() / node[inherit]() * 100;
|
||||
|
||||
if (!size || !inheritedSize || size === 0 || inheritedSize === 0) {
|
||||
if (!size || size === 0) {
|
||||
throw new Error('Chart cannot be rendered because ' + by + ' is ' + size + '.');
|
||||
} else {
|
||||
throw new Error('Chart cannot be rendered because ' + inherit + ' is ' + inheritedSize + '.');
|
||||
}
|
||||
}
|
||||
|
||||
$el.selectAll('div')
|
||||
.data(data)
|
||||
.enter()
|
||||
.append('div')
|
||||
.attr('class', name)
|
||||
.style('width', function () {
|
||||
return by === 'width' ? size + '%' : inheritedSize + '%';
|
||||
})
|
||||
.style('height', function () {
|
||||
return by === 'height' ? size + '%' : inheritedSize + '%';
|
||||
})
|
||||
.style('display', function () {
|
||||
return name === 'rows' ? 'block' : 'inline-block';
|
||||
})
|
||||
.each(function (d) {
|
||||
var selection = d3.select(this);
|
||||
|
||||
if (!d.series) {
|
||||
selection.append('div')
|
||||
.attr('class', name + '-label')
|
||||
.attr('height', '10%')
|
||||
.text(d.label);
|
||||
}
|
||||
|
||||
addTo(charts, placeChart(selection, d));
|
||||
});
|
||||
|
||||
return charts;
|
||||
}
|
||||
|
||||
return function (elem, data) {
|
||||
return placeChart(elem, data);
|
||||
};
|
||||
});
|
|
@ -1,83 +0,0 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
|
||||
/* Returns an array of ordered keys for a data series
|
||||
* or several arrays of data values.
|
||||
*/
|
||||
|
||||
function orderKeys(data) {
|
||||
var uniqueXObjs = {},
|
||||
orderedKeys;
|
||||
|
||||
data.forEach(function (series) {
|
||||
series.values.forEach(function (d, i) {
|
||||
var key = d.x;
|
||||
uniqueXObjs[key] = uniqueXObjs[key] === void 0 ? i : Math.max(i, uniqueXObjs[key]);
|
||||
});
|
||||
});
|
||||
|
||||
orderedKeys = _.chain(uniqueXObjs)
|
||||
.pairs()
|
||||
.sortBy(1)
|
||||
.pluck(0)
|
||||
.value();
|
||||
return orderedKeys;
|
||||
}
|
||||
|
||||
/* Returns the indexed position of a value in an array. */
|
||||
function getIndexOf(val, arr) {
|
||||
var i, max = arr.length;
|
||||
for (i = 0; i < max; i++) {
|
||||
/* jshint eqeqeq:false */
|
||||
// intentional double equals
|
||||
if (val == arr[i].x) {
|
||||
return i;
|
||||
}
|
||||
/* jshint eqeqeq:true */
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function createZeroFilledArray(orderedKeys) {
|
||||
var max = orderedKeys.length,
|
||||
i, arr = [];
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
var val = orderedKeys[i];
|
||||
arr.push({
|
||||
x: val,
|
||||
y: 0
|
||||
});
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
function modifyZeroFilledArray(zeroArray, dataArray) {
|
||||
var i, max = dataArray.length;
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
var val = dataArray[i],
|
||||
index = getIndexOf(val.x, zeroArray);
|
||||
|
||||
zeroArray.splice(index, 1);
|
||||
zeroArray.splice(index, 0, val);
|
||||
}
|
||||
|
||||
return zeroArray;
|
||||
}
|
||||
|
||||
return function (data) {
|
||||
var i;
|
||||
var max = data.series.length;
|
||||
var orderedKeys = orderKeys(data.series);
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
var zeroArray = createZeroFilledArray(orderedKeys),
|
||||
dataArray = data.series[i].values;
|
||||
data.series[i].values = modifyZeroFilledArray(zeroArray, dataArray);
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
});
|
96
src/kibana/components/vislib/vis.js
Normal file
96
src/kibana/components/vislib/vis.js
Normal file
|
@ -0,0 +1,96 @@
|
|||
define(function (require) {
|
||||
return function VisFactory(d3, Private) {
|
||||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
|
||||
var Handler = Private(require('components/vislib/lib/handler'));
|
||||
var ResizeChecker = Private(require('components/vislib/lib/resize_checker'));
|
||||
var Events = Private(require('factories/events'));
|
||||
var chartTypes = Private(require('components/vislib/vis_types'));
|
||||
require('css!components/vislib/components/styles/main.css');
|
||||
|
||||
/*
|
||||
* Visualization controller. Exposed API for creating visualizations.
|
||||
* arguments:
|
||||
* $el => jquery reference to a DOM element
|
||||
* config => object of params for the chart.
|
||||
* e.g. type: 'column', addLegend: true, ...
|
||||
*/
|
||||
_(Vis).inherits(Events);
|
||||
function Vis($el, config) {
|
||||
if (!(this instanceof Vis)) {
|
||||
return new Vis($el, config);
|
||||
}
|
||||
Vis.Super.apply(this, arguments);
|
||||
|
||||
this.el = $el.get ? $el.get(0) : $el;
|
||||
this.ChartClass = chartTypes[config.type];
|
||||
this._attr = _.defaults(config || {}, {});
|
||||
|
||||
// bind the resize function so it can be used as an event handler
|
||||
this.resize = _.bind(this.resize, this);
|
||||
|
||||
this.resizeChecker = new ResizeChecker(this.el);
|
||||
this.resizeChecker.on('resize', this.resize);
|
||||
}
|
||||
|
||||
// Exposed API for rendering charts.
|
||||
Vis.prototype.render = function (data) {
|
||||
if (!data) {
|
||||
throw new Error('No valid data!');
|
||||
}
|
||||
|
||||
// Save data to this object and new up the Handler constructor
|
||||
this.data = data;
|
||||
this.handler = new Handler(this);
|
||||
|
||||
try {
|
||||
this.handler.render();
|
||||
} catch (error) {
|
||||
// if involving height and width of the container, log error to screen
|
||||
// Because we have to wait for the DOM element to initialize, we do not
|
||||
// want to throw an error when the DOM `el` is zero
|
||||
if ($(this.el).height() > 0 &&
|
||||
error.message === 'The height and/or width of this container is too small for this chart.') {
|
||||
this.handler.error(error.message);
|
||||
} else {
|
||||
console.error(error.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Resize the chart
|
||||
Vis.prototype.resize = function () {
|
||||
if (!this.data) {
|
||||
// TODO: need to come up with a solution for resizing when no data is available
|
||||
return;
|
||||
}
|
||||
this.render(this.data);
|
||||
};
|
||||
|
||||
// Destroy the chart
|
||||
Vis.prototype.destroy = function () {
|
||||
// Removing chart and all elements associated with it
|
||||
d3.select(this.el).selectAll('*').remove();
|
||||
|
||||
// remove event listeners
|
||||
this.resizeChecker.off('resize', this.resize);
|
||||
|
||||
// pass destroy call down to owned objects
|
||||
this.resizeChecker.destroy();
|
||||
};
|
||||
|
||||
// Set attributes on the chart
|
||||
Vis.prototype.set = function (name, val) {
|
||||
this._attr[name] = val;
|
||||
this.render(this.data);
|
||||
};
|
||||
|
||||
// Get attributes from the chart
|
||||
Vis.prototype.get = function (name) {
|
||||
return this._attr[name];
|
||||
};
|
||||
|
||||
return Vis;
|
||||
};
|
||||
});
|
8
src/kibana/components/vislib/vis_types.js
Normal file
8
src/kibana/components/vislib/vis_types.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
define(function (require) {
|
||||
return function VisTypeFactory(Private) {
|
||||
// visLib visualization types
|
||||
return {
|
||||
histogram: Private(require('components/vislib/visualizations/column_chart'))
|
||||
};
|
||||
};
|
||||
});
|
27
src/kibana/components/vislib/visualizations/_chart.js
Normal file
27
src/kibana/components/vislib/visualizations/_chart.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
define(function (require) {
|
||||
return function ChartBaseClass(d3, Private) {
|
||||
var _ = require('lodash');
|
||||
|
||||
/*
|
||||
* Base Class for all visualizations.
|
||||
* Exposes a render method.
|
||||
*/
|
||||
function Chart(vis, el, chartData) {
|
||||
if (!(this instanceof Chart)) {
|
||||
return new Chart(vis, el, chartData);
|
||||
}
|
||||
|
||||
this.vis = vis;
|
||||
this.chartEl = el;
|
||||
this.chartData = chartData;
|
||||
this._attr = _.defaults(vis._attr || {}, {});
|
||||
}
|
||||
|
||||
// Render the visualization.
|
||||
Chart.prototype.render = function () {
|
||||
return d3.select(this.chartEl).call(this.draw());
|
||||
};
|
||||
|
||||
return Chart;
|
||||
};
|
||||
});
|
254
src/kibana/components/vislib/visualizations/column_chart.js
Normal file
254
src/kibana/components/vislib/visualizations/column_chart.js
Normal file
|
@ -0,0 +1,254 @@
|
|||
define(function (require) {
|
||||
return function ColumnChartFactory(d3, Private) {
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
|
||||
var Chart = Private(require('components/vislib/visualizations/_chart'));
|
||||
var Legend = Private(require('components/vislib/lib/legend'));
|
||||
|
||||
// Dynamically adds css file
|
||||
require('css!components/vislib/components/styles/main');
|
||||
|
||||
/*
|
||||
* Column chart visualization => vertical bars, stacked bars
|
||||
*/
|
||||
_(ColumnChart).inherits(Chart);
|
||||
function ColumnChart(vis, chartEl, chartData) {
|
||||
if (!(this instanceof ColumnChart)) {
|
||||
return new ColumnChart(vis, chartEl, chartData);
|
||||
}
|
||||
|
||||
ColumnChart.Super.apply(this, arguments);
|
||||
// Column chart specific attributes
|
||||
this._attr = _.defaults(vis._attr || {}, {
|
||||
xValue: function (d, i) { return d.x; },
|
||||
yValue: function (d, i) { return d.y; },
|
||||
dispatch: d3.dispatch('brush', 'click', 'hover', 'mouseenter', 'mouseleave', 'mouseover', 'mouseout'),
|
||||
});
|
||||
}
|
||||
|
||||
// Response to `click` and `hover` events
|
||||
ColumnChart.prototype.eventResponse = function (d, i) {
|
||||
return {
|
||||
value : this._attr.yValue(d, i),
|
||||
point : d,
|
||||
label : d.label,
|
||||
color : this.vis.data.getColorFunc()(d.label),
|
||||
pointIndex: i,
|
||||
series : this.chartData.series,
|
||||
config : this._attr,
|
||||
data : this.chartData,
|
||||
e : d3.event
|
||||
};
|
||||
};
|
||||
|
||||
// Stack data
|
||||
// TODO: refactor so that this is called from the data module
|
||||
ColumnChart.prototype.stackData = function (data) {
|
||||
var self = this;
|
||||
|
||||
return this._attr.stack(data.series.map(function (d) {
|
||||
var label = d.label;
|
||||
return d.values.map(function (e, i) {
|
||||
return {
|
||||
label: label,
|
||||
x : self._attr.xValue.call(d.values, e, i),
|
||||
y : self._attr.yValue.call(d.values, e, i)
|
||||
};
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
// Add brush to the svg
|
||||
ColumnChart.prototype.addBrush = function (xScale, svg) {
|
||||
var self = this;
|
||||
|
||||
// Brush scale
|
||||
var brush = d3.svg.brush()
|
||||
.x(xScale)
|
||||
.on('brushend', function brushEnd() {
|
||||
// response returned on brush
|
||||
return self._attr.dispatch.brush({
|
||||
range: brush.extent(),
|
||||
config: self._attr,
|
||||
e: d3.event,
|
||||
data: self.chartData
|
||||
});
|
||||
});
|
||||
|
||||
// if `addBrushing` is true, add brush canvas
|
||||
if (self._attr.addBrushing) {
|
||||
svg.append('g')
|
||||
.attr('class', 'brush')
|
||||
.call(brush)
|
||||
.selectAll('rect')
|
||||
.attr('height', this._attr.height - this._attr.margin.top - this._attr.margin.bottom);
|
||||
}
|
||||
};
|
||||
|
||||
ColumnChart.prototype.addBars = function (svg, layers) {
|
||||
var data = this.chartData;
|
||||
var color = this.vis.data.getColorFunc();
|
||||
var xScale = this.vis.xAxis.xScale;
|
||||
var yScale = this.vis.yAxis.yScale;
|
||||
var layer;
|
||||
var bars;
|
||||
|
||||
// Data layers
|
||||
layer = svg.selectAll('.layer')
|
||||
.data(layers)
|
||||
.enter().append('g')
|
||||
.attr('class', function (d, i) {
|
||||
return i;
|
||||
});
|
||||
|
||||
// Append the bars
|
||||
bars = layer.selectAll('rect')
|
||||
.data(function (d) {
|
||||
return d;
|
||||
});
|
||||
|
||||
// exit
|
||||
bars.exit().remove();
|
||||
|
||||
// enter
|
||||
bars.enter()
|
||||
.append('rect')
|
||||
.attr('class', function (d) {
|
||||
return 'color ' + Legend.prototype.colorToClass.call(this, color(d.label));
|
||||
})
|
||||
.attr('fill', function (d) {
|
||||
return color(d.label);
|
||||
});
|
||||
|
||||
// update
|
||||
bars
|
||||
.attr('x', function (d) {
|
||||
return xScale(d.x);
|
||||
})
|
||||
.attr('width', function () {
|
||||
var barWidth;
|
||||
var barSpacing;
|
||||
|
||||
if (data.ordered && data.ordered.date) {
|
||||
barWidth = xScale(data.ordered.min + data.ordered.interval) - xScale(data.ordered.min);
|
||||
barSpacing = barWidth * 0.25;
|
||||
|
||||
// if (barWidth <= 1) {
|
||||
// throw new Error('The height and/or width of this container is too small for this chart.');
|
||||
// }
|
||||
return barWidth - barSpacing;
|
||||
}
|
||||
|
||||
// if (xScale.rangeBand() <= 1) {
|
||||
// throw new Error('The height and/or width of this container is too small for this chart.');
|
||||
// }
|
||||
return xScale.rangeBand();
|
||||
})
|
||||
.attr('y', function (d) {
|
||||
return yScale(d.y0 + d.y);
|
||||
})
|
||||
.attr('height', function (d) {
|
||||
return yScale(d.y0) - yScale(d.y0 + d.y);
|
||||
});
|
||||
|
||||
return bars;
|
||||
};
|
||||
|
||||
ColumnChart.prototype.addBarEvents = function (bars) {
|
||||
var self = this;
|
||||
var tooltip = this.vis.tooltip;
|
||||
var isTooltip = this._attr.addTooltip;
|
||||
var dispatch = this._attr.dispatch;
|
||||
|
||||
bars
|
||||
.on('mouseover.bar', function (d, i) {
|
||||
d3.select(this)
|
||||
.classed('hover', true)
|
||||
.style('stroke', '#333')
|
||||
.style('cursor', 'pointer');
|
||||
|
||||
dispatch.hover(self.eventResponse(d, i));
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('click.bar', function (d, i) {
|
||||
dispatch.click(self.eventResponse(d, i));
|
||||
d3.event.stopPropagation();
|
||||
})
|
||||
.on('mouseout.bar', function () {
|
||||
d3.select(this).classed('hover', false)
|
||||
.style('stroke', null);
|
||||
});
|
||||
|
||||
// Add tooltip
|
||||
if (isTooltip) {
|
||||
bars.call(tooltip.render());
|
||||
}
|
||||
};
|
||||
|
||||
ColumnChart.prototype.draw = function () {
|
||||
// Attributes
|
||||
var self = this;
|
||||
var xScale = this.vis.xAxis.xScale;
|
||||
var $elem = $(this.chartEl);
|
||||
var margin = this._attr.margin;
|
||||
var elWidth = this._attr.width = $elem.width();
|
||||
var elHeight = this._attr.height = $elem.height();
|
||||
var div;
|
||||
var svg;
|
||||
var width;
|
||||
var height;
|
||||
var layers;
|
||||
var bars;
|
||||
|
||||
return function (selection) {
|
||||
selection.each(function (data) {
|
||||
// Stack data
|
||||
layers = self.stackData(data);
|
||||
|
||||
// Get the width and height
|
||||
width = elWidth;
|
||||
height = elHeight - margin.top - margin.bottom;
|
||||
|
||||
// if height or width < 20 or NaN, throw error
|
||||
if (_.isNaN(width) || width < 20 || _.isNaN(height) || height < 20) {
|
||||
throw new Error('The height and/or width of this container is too ' +
|
||||
'small for this chart.');
|
||||
}
|
||||
|
||||
// Select the current DOM element
|
||||
div = d3.select(this);
|
||||
|
||||
// Create the canvas for the visualization
|
||||
svg = div.append('svg')
|
||||
.attr('width', width)
|
||||
.attr('height', height + margin.top + margin.bottom)
|
||||
.append('g')
|
||||
.attr('transform', 'translate(0,' + margin.top + ')');
|
||||
|
||||
// addBrush canvas
|
||||
self.addBrush(xScale, svg);
|
||||
|
||||
// add bars
|
||||
bars = self.addBars(svg, layers);
|
||||
|
||||
// add events to bars
|
||||
self.addBarEvents(bars);
|
||||
|
||||
// chart base line
|
||||
var line = svg.append('line')
|
||||
.attr('x1', 0)
|
||||
.attr('y1', height)
|
||||
.attr('x2', width)
|
||||
.attr('y2', height)
|
||||
.style('stroke', '#ddd')
|
||||
.style('stroke-width', 1);
|
||||
|
||||
return svg;
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
return ColumnChart;
|
||||
};
|
||||
});
|
|
@ -1,11 +1,10 @@
|
|||
define(function (require) {
|
||||
require('modules')
|
||||
.get('kibana/directive')
|
||||
.directive('visualize', function (Notifier, SavedVis, indexPatterns, Private) {
|
||||
.directive('visualize', function (Notifier, SavedVis, indexPatterns, Private, visLib) {
|
||||
|
||||
require('components/visualize/spy/spy');
|
||||
require('css!components/visualize/visualize.css');
|
||||
var vislib = require('components/vislib/index');
|
||||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
var visTypes = Private(require('components/vis_types/index'));
|
||||
|
@ -93,7 +92,7 @@ define(function (require) {
|
|||
}
|
||||
);
|
||||
|
||||
chart = new vislib.Chart($visualize[0], vislibParams);
|
||||
chart = new visLib.Vis($visualize[0], vislibParams);
|
||||
|
||||
_.each(vis.listeners, function (listener, event) {
|
||||
chart.on(event, listener);
|
||||
|
|
7898
src/kibana/styles/main.css
Normal file
7898
src/kibana/styles/main.css
Normal file
File diff suppressed because it is too large
Load diff
|
@ -150,6 +150,15 @@ define(function (require) {
|
|||
// always call flush, it might not do anything
|
||||
flush(this, args);
|
||||
};
|
||||
},
|
||||
chunk: function (arr, count) {
|
||||
var size = Math.ceil(arr.length / count);
|
||||
var chunks = new Array(count);
|
||||
for (var i = 0; i < count; i ++) {
|
||||
var start = i * size;
|
||||
chunks[i] = arr.slice(start, start + size);
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
95
src/kibana/utils/sequencer.js
Normal file
95
src/kibana/utils/sequencer.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
|
||||
function create(min, max, length, mod) {
|
||||
var seq = new Array(length);
|
||||
|
||||
var valueDist = max - min;
|
||||
|
||||
// range of values that the mod creates
|
||||
var modRange = [mod(0, length), mod(length - 1, length)];
|
||||
|
||||
// distance between
|
||||
var modRangeDist = modRange[1] - modRange[0];
|
||||
|
||||
_.times(length, function (i) {
|
||||
var modIPercent = (mod(i, length) - modRange[0]) / modRangeDist;
|
||||
|
||||
// percent applied to distance and added to min to
|
||||
// produce value
|
||||
seq[i] = min + (valueDist * modIPercent);
|
||||
});
|
||||
|
||||
seq.min = min;
|
||||
seq.max = max;
|
||||
|
||||
return seq;
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* Create an exponential sequence of numbers.
|
||||
*
|
||||
* Creates a curve resembling:
|
||||
*
|
||||
* ;
|
||||
* /
|
||||
* /
|
||||
* .-'
|
||||
* _.-"
|
||||
* _.-'"
|
||||
* _,.-'"
|
||||
* _,..-'"
|
||||
* _,..-'""
|
||||
* _,..-'""
|
||||
* ____,..--'""
|
||||
*
|
||||
* @param {number} min - the min value to produce
|
||||
* @param {number} max - the max value to produce
|
||||
* @param {number} length - the number of values to produce
|
||||
* @return {number[]} - an array containing the sequence
|
||||
*/
|
||||
createEaseIn: _.partialRight(create, function (i, length) {
|
||||
// generates numbers from 1 to +Infinity
|
||||
return i * Math.pow(i, 1.1111);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Create an sequence of numbers using sine.
|
||||
*
|
||||
* Create a curve resembling:
|
||||
*
|
||||
* ____,..--'""
|
||||
* _,..-'""
|
||||
* _,..-'""
|
||||
* _,..-'"
|
||||
* _,.-'"
|
||||
* _.-'"
|
||||
* _.-"
|
||||
* .-'
|
||||
* /
|
||||
* /
|
||||
* ;
|
||||
*
|
||||
*
|
||||
* @param {number} min - the min value to produce
|
||||
* @param {number} max - the max value to produce
|
||||
* @param {number} length - the number of values to produce
|
||||
* @return {number[]} - an array containing the sequence
|
||||
*/
|
||||
createEaseOut: _.partialRight(create, function (i, length) {
|
||||
// adapted from output of http://www.timotheegroleau.com/Flash/experiments/easing_function_generator.htm
|
||||
// generates numbers from 0 to 100
|
||||
|
||||
var ts = (i /= length) * i;
|
||||
var tc = ts * i;
|
||||
return 100 * (
|
||||
0.5 * tc * ts +
|
||||
-3 * ts * ts +
|
||||
6.5 * tc +
|
||||
-7 * ts +
|
||||
4 * i
|
||||
);
|
||||
})
|
||||
};
|
||||
});
|
|
@ -11,6 +11,7 @@ module.exports = {
|
|||
'<%= src %>/kibana/apps/visualize/styles/visualization.less',
|
||||
'<%= src %>/kibana/apps/visualize/styles/main.less',
|
||||
'<%= src %>/kibana/styles/main.less',
|
||||
'<%= src %>/kibana/components/vislib/components/styles/main.less',
|
||||
'<%= src %>/kibana/components/**/*.less'
|
||||
],
|
||||
expand: true,
|
||||
|
|
32
tasks/config/saucelabs-mocha.js
Normal file
32
tasks/config/saucelabs-mocha.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
module.exports = {
|
||||
unit: {
|
||||
options: {
|
||||
urls: [
|
||||
'http://localhost:8000/test/unit/?saucelabs=true'
|
||||
],
|
||||
testname: 'Kibana Browser Tests',
|
||||
build: process.env.TRAVIS_BUILD_ID || 'test build',
|
||||
concurrency: 10,
|
||||
username: 'kibana',
|
||||
key: process.env.SAUCE_ACCESS_KEY,
|
||||
browsers: [
|
||||
{
|
||||
browserName: 'chrome',
|
||||
platform: 'Windows 7'
|
||||
},
|
||||
{
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.9',
|
||||
},
|
||||
{
|
||||
browserName: 'firefox',
|
||||
platform: 'Linux',
|
||||
},
|
||||
{
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 8'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
|
@ -10,7 +10,8 @@ module.exports = function (grunt) {
|
|||
files: [
|
||||
'<%= app %>/**/*.less',
|
||||
'<%= app %>/**/styles/**/*.less',
|
||||
'<%= app %>/**/components/**/*.less'
|
||||
'<%= app %>/**/components/**/*.less',
|
||||
'<%= app %>/**/components/vislib/components/styles/**/*.less'
|
||||
],
|
||||
tasks: ['less']
|
||||
},
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
var _ = require('lodash');
|
||||
module.exports = function (grunt) {
|
||||
/* jshint scripturl:true */
|
||||
|
||||
var testTask = process.env.TRAVIS ? 'saucelabs-mocha:unit' : 'mocha:unit';
|
||||
|
||||
grunt.registerTask('test', [
|
||||
'jshint',
|
||||
'ruby_server',
|
||||
'maybe_start_server',
|
||||
'jade',
|
||||
'mocha:unit',
|
||||
'jshint'
|
||||
testTask
|
||||
]);
|
||||
|
||||
grunt.registerTask('coverage', [
|
||||
|
|
|
@ -16,6 +16,7 @@ module.exports = function (grunt) {
|
|||
grunt.registerTask('todos', function () {
|
||||
var files = grunt.file.expand([
|
||||
'src/kibana/**/*.js',
|
||||
'!src/kibana/bower_components',
|
||||
'test/unit/specs/**/*.js'
|
||||
]);
|
||||
var matches = [];
|
||||
|
|
|
@ -2,17 +2,23 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Kibana4 Tests</title>
|
||||
|
||||
<link rel="stylesheet" href="/node_modules/mocha/mocha.css" />
|
||||
<script src="/node_modules/expect.js/expect.js"></script>
|
||||
<link rel="stylesheet" href="/node_modules/mocha-screencast-reporter/screencast-reporter.css" />
|
||||
|
||||
<script src="/node_modules/expect.js/index.js"></script>
|
||||
<script src="/node_modules/mocha/mocha.js"></script>
|
||||
<script src="/node_modules/mocha-screencast-reporter/screencast-reporter.js"></script>
|
||||
<script src="/src/kibana/bower_components/requirejs/require.js"></script>
|
||||
<script src="/src/kibana/require.config.js"></script>
|
||||
|
||||
<script>(function () {
|
||||
var COVERAGE = !!(/coverage/i.test(window.location.search));
|
||||
var SAUCELABS = !!(/saucelabs/i.test(window.location.search));
|
||||
|
||||
mocha.setup({
|
||||
ui: 'bdd',
|
||||
reporter: 'html'
|
||||
reporter: SAUCELABS ? ScreencastReporter : 'html'
|
||||
});
|
||||
|
||||
require.config({
|
||||
|
@ -83,26 +89,82 @@
|
|||
'specs/utils/interval',
|
||||
'specs/utils/versionmath',
|
||||
'specs/utils/routes/index',
|
||||
'specs/utils/sequencer',
|
||||
'specs/courier/search_source/_get_normalized_sort',
|
||||
'specs/factories/base_object',
|
||||
'specs/state_management/state',
|
||||
'specs/state_management/global_state',
|
||||
'specs/utils/diff_object',
|
||||
'specs/factories/events',
|
||||
'specs/vislib/color',
|
||||
'specs/vislib/zero_injection',
|
||||
'specs/vislib/labels',
|
||||
'specs/vislib/x_axis',
|
||||
'specs/vislib/y_axis',
|
||||
'specs/vislib/axis_title',
|
||||
'specs/vislib/chart_title',
|
||||
'specs/vislib/layout_types',
|
||||
'specs/vislib/vis_types',
|
||||
'specs/vislib/splits',
|
||||
'specs/vislib/layout',
|
||||
'specs/vislib/_chart',
|
||||
'specs/vislib/column_layout',
|
||||
'specs/vislib/index',
|
||||
'specs/vislib/vis',
|
||||
'specs/vislib/handler',
|
||||
'specs/vislib/_error_handler',
|
||||
'specs/vislib/data',
|
||||
'specs/vislib/resize_checker',
|
||||
'specs/utils/diff_time_picker_vals',
|
||||
'specs/factories/events',
|
||||
'specs/index_patterns/_flatten_search_response',
|
||||
'specs/utils/registry/index',
|
||||
'specs/directives/filter_bar',
|
||||
'specs/components/agg_types/index',
|
||||
'specs/components/vis/index'
|
||||
'specs/components/vis/index',
|
||||
'specs/components/reflow_watcher'
|
||||
], function (kibana, sinon) {
|
||||
kibana.load(function () {
|
||||
var xhr = sinon.useFakeXMLHttpRequest();
|
||||
|
||||
window.mochaRunner = mocha.run().on('end', function () {
|
||||
window.mochaResults = this.stats;
|
||||
window.mochaResults.details = getFailedTests(this);
|
||||
xhr.restore();
|
||||
});
|
||||
|
||||
function getFailedTests(runner) {
|
||||
var fails = [];
|
||||
var suiteStack = [];
|
||||
|
||||
(function recurse(suite) {
|
||||
suiteStack.push(suite);
|
||||
|
||||
(suite.tests || [])
|
||||
.filter(function (test) {
|
||||
return test.state !== 'passed' && test.state !== 'pending';
|
||||
})
|
||||
.forEach(function (test) {
|
||||
fails.push({
|
||||
title: suiteStack.concat(test).reduce(function (title, suite) {
|
||||
if (suite.title) {
|
||||
return (title ? title + ' ' : '') + suite.title;
|
||||
} else {
|
||||
return title;
|
||||
}
|
||||
}, ''),
|
||||
err: test.err ? (test.err.stack || test.err.message) : 'unknown error'
|
||||
});
|
||||
});
|
||||
|
||||
(suite.suites || []).forEach(recurse);
|
||||
|
||||
suiteStack.pop();
|
||||
}(runner.suite));
|
||||
|
||||
return fails;
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
78
test/unit/specs/components/reflow_watcher.js
Normal file
78
test/unit/specs/components/reflow_watcher.js
Normal file
|
@ -0,0 +1,78 @@
|
|||
define(function (require) {
|
||||
describe('Reflow watcher', function () {
|
||||
require('angular');
|
||||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
|
||||
var $body = $(document.body);
|
||||
var $window = $(window);
|
||||
var expectStubbedEventAndEl = function (stub, event, $el) {
|
||||
expect(stub.getCalls().some(function (call) {
|
||||
var events = call.args[0].split(' ');
|
||||
return _.contains(events, event) && $el.is(call.thisValue);
|
||||
})).to.be(true);
|
||||
};
|
||||
|
||||
var EventEmitter;
|
||||
var reflowWatcher;
|
||||
var $rootScope;
|
||||
var $onStub;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function (Private, $injector) {
|
||||
$rootScope = $injector.get('$rootScope');
|
||||
EventEmitter = Private(require('factories/events'));
|
||||
|
||||
// stub jQuery's $.on method while creating the reflowWatcher
|
||||
$onStub = sinon.stub($.fn, 'on');
|
||||
reflowWatcher = Private(require('components/reflow_watcher'));
|
||||
$onStub.restore();
|
||||
|
||||
// setup the reflowWatchers $http watcher
|
||||
$rootScope.$apply();
|
||||
}));
|
||||
|
||||
it('is an event emitter', function () {
|
||||
expect(reflowWatcher).to.be.an(EventEmitter);
|
||||
});
|
||||
|
||||
describe('listens', function () {
|
||||
it('to "mouseup" on the body', function () {
|
||||
expectStubbedEventAndEl($onStub, 'mouseup', $body);
|
||||
});
|
||||
|
||||
it('to "resize" on the window', function () {
|
||||
expectStubbedEventAndEl($onStub, 'resize', $window);
|
||||
});
|
||||
});
|
||||
|
||||
describe('un-listens in #destroy()', function () {
|
||||
var $offStub;
|
||||
|
||||
beforeEach(function () {
|
||||
$offStub = sinon.stub($.fn, 'off');
|
||||
reflowWatcher.destroy();
|
||||
$offStub.restore();
|
||||
});
|
||||
|
||||
it('to "mouseup" on the body', function () {
|
||||
expectStubbedEventAndEl($offStub, 'mouseup', $body);
|
||||
});
|
||||
|
||||
it('to "resize" on the window', function () {
|
||||
expectStubbedEventAndEl($offStub, 'resize', $window);
|
||||
});
|
||||
});
|
||||
|
||||
it('triggers the "reflow" event within a new angular tick', function () {
|
||||
var stub = sinon.stub();
|
||||
reflowWatcher.on('reflow', stub);
|
||||
reflowWatcher.trigger();
|
||||
|
||||
expect(stub).to.have.property('callCount', 0);
|
||||
$rootScope.$apply();
|
||||
expect(stub).to.have.property('callCount', 1);
|
||||
});
|
||||
});
|
||||
});
|
101
test/unit/specs/utils/sequencer.js
Normal file
101
test/unit/specs/utils/sequencer.js
Normal file
|
@ -0,0 +1,101 @@
|
|||
define(function (require) {
|
||||
describe('sequencer util', function () {
|
||||
var _ = require('lodash');
|
||||
var sequencer = require('utils/sequencer');
|
||||
|
||||
var args = [
|
||||
{ min: 500, max: 7500, length: 1500 },
|
||||
{ min: 50, max: 500, length: 1000 },
|
||||
{ min: 5, max: 50, length: 100 }
|
||||
];
|
||||
|
||||
function eachSeqFor(method, fn) {
|
||||
args.forEach(function (args) {
|
||||
fn(method(args.min, args.max, args.length), args);
|
||||
});
|
||||
}
|
||||
|
||||
function getSlopes(seq, count) {
|
||||
return _.chunk(seq, count).map(function (chunk) {
|
||||
return (_.last(chunk) - _.first(chunk)) / chunk.length;
|
||||
});
|
||||
}
|
||||
|
||||
// using expect() here causes massive GC runs because seq can be +1000 elements
|
||||
function expectedChange(seq, up) {
|
||||
up = !!up;
|
||||
|
||||
if (seq.length < 2) {
|
||||
throw new Error('unable to reach change without at least two elements');
|
||||
}
|
||||
|
||||
seq.forEach(function (n, i) {
|
||||
if (i > 0 && (seq[i - 1] < n) !== up) {
|
||||
throw new Error('expected values to ' + (up ? 'increase': 'decrease'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function generalTests(seq, args) {
|
||||
it('obeys the min arg', function () {
|
||||
expect(Math.min.apply(Math, seq)).to.be(args.min);
|
||||
});
|
||||
|
||||
it('obeys the max arg', function () {
|
||||
expect(Math.max.apply(Math, seq)).to.be(args.max);
|
||||
});
|
||||
|
||||
it('obeys the length arg', function () {
|
||||
expect(seq).to.have.length(args.length);
|
||||
});
|
||||
|
||||
it('always creates increasingly larger values', function () {
|
||||
expectedChange(seq, true);
|
||||
});
|
||||
}
|
||||
|
||||
describe('#createEaseIn', function () {
|
||||
eachSeqFor(sequencer.createEaseIn, function (seq, args) {
|
||||
describe('with args: ' + JSON.stringify(args), function () {
|
||||
generalTests(seq, args);
|
||||
|
||||
it('produces increasing slopes', function () {
|
||||
expectedChange(getSlopes(seq, 2), true);
|
||||
expectedChange(getSlopes(seq, 4), true);
|
||||
expectedChange(getSlopes(seq, 6), true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createEaseOut', function () {
|
||||
eachSeqFor(sequencer.createEaseOut, function (seq, args) {
|
||||
describe('with args: ' + JSON.stringify(args), function () {
|
||||
generalTests(seq, args);
|
||||
|
||||
it('produces decreasing slopes', function () {
|
||||
expectedChange(getSlopes(seq, 2), false);
|
||||
expectedChange(getSlopes(seq, 4), false);
|
||||
expectedChange(getSlopes(seq, 6), false);
|
||||
});
|
||||
|
||||
// Flipped version of previous test to ensure that expectedChange()
|
||||
// and friends are behaving properly
|
||||
it('doesn\'t produce increasing slopes', function () {
|
||||
expect(function () {
|
||||
expectedChange(getSlopes(seq, 2), true);
|
||||
}).to.throwError();
|
||||
|
||||
expect(function () {
|
||||
expectedChange(getSlopes(seq, 4), true);
|
||||
}).to.throwError();
|
||||
|
||||
expect(function () {
|
||||
expectedChange(getSlopes(seq, 6), true);
|
||||
}).to.throwError();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
48
test/unit/specs/vislib/_chart.js
Normal file
48
test/unit/specs/vislib/_chart.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
|
||||
angular.module('ChartBaseClass', ['kibana']);
|
||||
angular.module('ColumnChartFactory', ['kibana']);
|
||||
|
||||
describe('VisLib _chart Test Suite', function () {
|
||||
var ColumnChart;
|
||||
var Chart;
|
||||
var chartData = {};
|
||||
var vis;
|
||||
var el;
|
||||
var myChart;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ChartBaseClass');
|
||||
module('ColumnChartFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
ColumnChart = Private(require('components/vislib/visualizations/column_chart'));
|
||||
Chart = Private(require('components/vislib/visualizations/_chart'));
|
||||
|
||||
el = d3.select('body').append('div').attr('class', 'column-chart');
|
||||
vis = {
|
||||
_attr: {
|
||||
stack: d3.layout.stack()
|
||||
}
|
||||
};
|
||||
myChart = new ColumnChart(vis, el, chartData);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
el.remove();
|
||||
});
|
||||
|
||||
it('should be a constructor for visualization modules', function () {
|
||||
expect(myChart instanceof Chart).to.be(true);
|
||||
});
|
||||
|
||||
it('should have a render method', function () {
|
||||
expect(typeof myChart.render === 'function').to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
42
test/unit/specs/vislib/_error_handler.js
Normal file
42
test/unit/specs/vislib/_error_handler.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
define(function (require) {
|
||||
|
||||
var angular = require('angular');
|
||||
angular.module('ErrorHandlerFactory', ['kibana']);
|
||||
|
||||
describe('VisLib ErrorHandler Test Suite', function () {
|
||||
var ErrorHandler;
|
||||
var errorHandler;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ErrorHandlerFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (Private) {
|
||||
ErrorHandler = Private(require('components/vislib/lib/_error_handler'));
|
||||
errorHandler = new ErrorHandler();
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateWidthandHeight Method', function () {
|
||||
it('should throw an error when width and/or height is 0', function () {
|
||||
expect(function () {
|
||||
errorHandler.validateWidthandHeight(0, 200);
|
||||
}).to.throwError();
|
||||
expect(function () {
|
||||
errorHandler.validateWidthandHeight(200, 0);
|
||||
}).to.throwError();
|
||||
});
|
||||
|
||||
it('should throw an error when width and/or height is NaN', function () {
|
||||
expect(function () {
|
||||
errorHandler.validateWidthandHeight(null, 200);
|
||||
}).to.throwError();
|
||||
expect(function () {
|
||||
errorHandler.validateWidthandHeight(200, null);
|
||||
}).to.throwError();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
137
test/unit/specs/vislib/axis_title.js
Normal file
137
test/unit/specs/vislib/axis_title.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
|
||||
angular.module('AxisTitleFactory', ['kibana']);
|
||||
|
||||
describe('Vislib AxisTitle Class Test Suite', function () {
|
||||
var AxisTitle;
|
||||
var Data;
|
||||
var axisTitle;
|
||||
var el;
|
||||
var dataObj;
|
||||
var xTitle;
|
||||
var yTitle;
|
||||
var data = {
|
||||
hits: 621,
|
||||
label: '',
|
||||
ordered: {
|
||||
date: true,
|
||||
interval: 30000,
|
||||
max: 1408734982458,
|
||||
min: 1408734082458
|
||||
},
|
||||
series: [
|
||||
{
|
||||
values: [
|
||||
{
|
||||
x: 1408734060000,
|
||||
y: 8
|
||||
},
|
||||
{
|
||||
x: 1408734090000,
|
||||
y: 23
|
||||
},
|
||||
{
|
||||
x: 1408734120000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734150000,
|
||||
y: 28
|
||||
},
|
||||
{
|
||||
x: 1408734180000,
|
||||
y: 36
|
||||
},
|
||||
{
|
||||
x: 1408734210000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734240000,
|
||||
y: 26
|
||||
},
|
||||
{
|
||||
x: 1408734270000,
|
||||
y: 22
|
||||
},
|
||||
{
|
||||
x: 1408734300000,
|
||||
y: 29
|
||||
},
|
||||
{
|
||||
x: 1408734330000,
|
||||
y: 24
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
xAxisLabel: 'Date Histogram',
|
||||
yAxisLabel: 'Count'
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
module('AxisTitleFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
AxisTitle = Private(require('components/vislib/lib/axis_title'));
|
||||
Data = Private(require('components/vislib/lib/data'));
|
||||
|
||||
el = d3.select('body').append('div')
|
||||
.attr('class', 'vis-wrapper');
|
||||
|
||||
el.append('div')
|
||||
.attr('class', 'y-axis-title')
|
||||
.style('height', '20px')
|
||||
.style('width', '20px');
|
||||
|
||||
el.append('div')
|
||||
.attr('class', 'x-axis-title')
|
||||
.style('height', '20px')
|
||||
.style('width', '20px');
|
||||
|
||||
|
||||
dataObj = new Data(data, {});
|
||||
xTitle = dataObj.get('xAxisLabel');
|
||||
yTitle = dataObj.get('yAxisLabel');
|
||||
axisTitle = new AxisTitle($('.vis-wrapper')[0], xTitle, yTitle);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
el.remove();
|
||||
});
|
||||
|
||||
describe('render Method', function () {
|
||||
beforeEach(function () {
|
||||
axisTitle.render();
|
||||
});
|
||||
|
||||
it('should append an svg to div', function () {
|
||||
expect(el.select('.x-axis-title').selectAll('svg').length).to.be(1);
|
||||
expect(el.select('.y-axis-title').selectAll('svg').length).to.be(1);
|
||||
});
|
||||
|
||||
it('should append a g element to the svg', function () {
|
||||
expect(el.select('.x-axis-title').selectAll('svg').select('g').length).to.be(1);
|
||||
expect(el.select('.y-axis-title').selectAll('svg').select('g').length).to.be(1);
|
||||
});
|
||||
|
||||
it('should append text', function () {
|
||||
expect(!!el.select('.x-axis-title').selectAll('svg').selectAll('text')).to.be(true);
|
||||
expect(!!el.select('.y-axis-title').selectAll('svg').selectAll('text')).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('draw Method', function () {
|
||||
it('should be a function', function () {
|
||||
expect(_.isFunction(axisTitle.draw())).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
120
test/unit/specs/vislib/chart_title.js
Normal file
120
test/unit/specs/vislib/chart_title.js
Normal file
|
@ -0,0 +1,120 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
|
||||
angular.module('ChartTitleFactory', ['kibana']);
|
||||
|
||||
describe('Vislib ChartTitle Class Test Suite', function () {
|
||||
var ChartTitle;
|
||||
var Data;
|
||||
var chartTitle;
|
||||
var el;
|
||||
var dataObj;
|
||||
var data = {
|
||||
hits: 621,
|
||||
label: '',
|
||||
ordered: {
|
||||
date: true,
|
||||
interval: 30000,
|
||||
max: 1408734982458,
|
||||
min: 1408734082458
|
||||
},
|
||||
series: [
|
||||
{
|
||||
values: [
|
||||
{
|
||||
x: 1408734060000,
|
||||
y: 8
|
||||
},
|
||||
{
|
||||
x: 1408734090000,
|
||||
y: 23
|
||||
},
|
||||
{
|
||||
x: 1408734120000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734150000,
|
||||
y: 28
|
||||
},
|
||||
{
|
||||
x: 1408734180000,
|
||||
y: 36
|
||||
},
|
||||
{
|
||||
x: 1408734210000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734240000,
|
||||
y: 26
|
||||
},
|
||||
{
|
||||
x: 1408734270000,
|
||||
y: 22
|
||||
},
|
||||
{
|
||||
x: 1408734300000,
|
||||
y: 29
|
||||
},
|
||||
{
|
||||
x: 1408734330000,
|
||||
y: 24
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
xAxisLabel: 'Date Histogram',
|
||||
yAxisLabel: 'Count'
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
module('ChartTitleFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
ChartTitle = Private(require('components/vislib/lib/chart_title'));
|
||||
Data = Private(require('components/vislib/lib/data'));
|
||||
|
||||
el = d3.select('body').append('div')
|
||||
.attr('class', 'vis-wrapper')
|
||||
.datum(data);
|
||||
|
||||
el.append('div')
|
||||
.attr('class', 'chart-title')
|
||||
.style('height', '20px');
|
||||
|
||||
dataObj = new Data(data, {});
|
||||
chartTitle = new ChartTitle($('.vis-wrapper')[0], 'rows');
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
el.remove();
|
||||
});
|
||||
|
||||
describe('render Method', function () {
|
||||
beforeEach(function () {
|
||||
chartTitle.render();
|
||||
});
|
||||
|
||||
it('should append an svg to div', function () {
|
||||
expect(el.select('.chart-title').selectAll('svg').length).to.be(1);
|
||||
});
|
||||
|
||||
it('should append text', function () {
|
||||
expect(!!el.select('.chart-title').selectAll('svg').selectAll('text')).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('draw Method', function () {
|
||||
it('should be a function', function () {
|
||||
expect(_.isFunction(chartTitle.draw())).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
139
test/unit/specs/vislib/color.js
Normal file
139
test/unit/specs/vislib/color.js
Normal file
|
@ -0,0 +1,139 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
|
||||
angular.module('ColorUtilService', ['kibana']);
|
||||
angular.module('SeedColorUtilService', ['kibana']);
|
||||
angular.module('ColorObjUtilService', ['kibana']);
|
||||
angular.module('ColorPaletteUtilService', ['kibana']);
|
||||
|
||||
describe('Vislib Color Module Test Suite', function () {
|
||||
var seedColors;
|
||||
|
||||
describe('Color (main)', function () {
|
||||
var getColors;
|
||||
var arr = ['good', 'better', 'best', 'never', 'let', 'it', 'rest'];
|
||||
var str = 'test';
|
||||
var error;
|
||||
var color;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ColorUtilService');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
seedColors = Private(require('components/vislib/components/_functions/color/seed_colors'));
|
||||
getColors = Private(require('components/vislib/components/_functions/color/color'));
|
||||
// error = getColors(str);
|
||||
color = getColors(arr);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be a function', function () {
|
||||
expect(typeof getColors).to.be('function');
|
||||
});
|
||||
|
||||
it('should return a function', function () {
|
||||
expect(typeof color).to.be('function');
|
||||
});
|
||||
|
||||
it('should return the first hex color in the seed colors array', function () {
|
||||
expect(color(arr[0])).to.be(seedColors[0]);
|
||||
});
|
||||
|
||||
// it('should throw a TypeError when the input value is not an array', function () {
|
||||
// console.log(error);
|
||||
// expect(error).to.throwException(typeof str + ' should be an array of strings or numbers');
|
||||
// });
|
||||
});
|
||||
|
||||
describe('Seed Colors', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
module('SeedColorUtilService');
|
||||
});
|
||||
|
||||
it('should return an array', function () {
|
||||
expect(seedColors instanceof Array).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array of length 72', function () {
|
||||
expect(seedColors.length).to.be(72);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Color Object', function () {
|
||||
var createColorObj;
|
||||
var arr1 = ['rashid', 'juan', 'chris', 'spencer'];
|
||||
var arr2 = ['guru', 'datavis', 'architect', 'javascript'];
|
||||
var dict;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ColorObjUtilService');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
createColorObj = Private(require('components/vislib/components/_functions/color/color_obj'));
|
||||
dict = createColorObj(arr1, arr2);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be a function', function () {
|
||||
expect(typeof createColorObj).to.be('function');
|
||||
});
|
||||
|
||||
it('should return an object', function () {
|
||||
expect(dict instanceof Object).to.be(true);
|
||||
});
|
||||
|
||||
it('should return the correct value', function () {
|
||||
expect(dict[arr1[0]]).to.be(arr2[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Color Palette', function () {
|
||||
var num1 = 45;
|
||||
var num2 = 72;
|
||||
var num3 = 90;
|
||||
var createColorPalette;
|
||||
var colorPalette;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ColorPaletteUtilService');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
createColorPalette = Private(require('components/vislib/components/_functions/color/color_palette'));
|
||||
colorPalette = createColorPalette(num1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be a function', function () {
|
||||
expect(typeof createColorPalette).to.be('function');
|
||||
});
|
||||
|
||||
it('should return an array', function () {
|
||||
expect(colorPalette instanceof Array).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array of the same length as the input', function () {
|
||||
expect(colorPalette.length).to.be(num1);
|
||||
});
|
||||
|
||||
it('should return the seed color array when input length is 72', function () {
|
||||
expect(createColorPalette(num2)[71]).to.be(seedColors[71]);
|
||||
});
|
||||
|
||||
it('should return an array of the same length as the input when input is greater than 72', function () {
|
||||
expect(createColorPalette(num3).length).to.be(num3);
|
||||
});
|
||||
|
||||
it('should create new darker colors when input is greater than 72', function () {
|
||||
expect(createColorPalette(num3)[72]).not.to.equal(seedColors[0]);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
97
test/unit/specs/vislib/column_chart.js
Normal file
97
test/unit/specs/vislib/column_chart.js
Normal file
|
@ -0,0 +1,97 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
|
||||
angular.module('ColumnChartFactory', ['kibana']);
|
||||
|
||||
describe('Vislib ColumnChart Class Test Suite', function () {
|
||||
var ColumnChart;
|
||||
var Data;
|
||||
var chart;
|
||||
var el;
|
||||
var mydata;
|
||||
var data = {
|
||||
hits: 621,
|
||||
label: '',
|
||||
ordered: {
|
||||
date: true,
|
||||
interval: 30000,
|
||||
max: 1408734982458,
|
||||
min: 1408734082458
|
||||
},
|
||||
series: [
|
||||
{
|
||||
values: [
|
||||
{
|
||||
x: 1408734060000,
|
||||
y: 8
|
||||
},
|
||||
{
|
||||
x: 1408734090000,
|
||||
y: 23
|
||||
},
|
||||
{
|
||||
x: 1408734120000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734150000,
|
||||
y: 28
|
||||
},
|
||||
{
|
||||
x: 1408734180000,
|
||||
y: 36
|
||||
},
|
||||
{
|
||||
x: 1408734210000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734240000,
|
||||
y: 26
|
||||
},
|
||||
{
|
||||
x: 1408734270000,
|
||||
y: 22
|
||||
},
|
||||
{
|
||||
x: 1408734300000,
|
||||
y: 29
|
||||
},
|
||||
{
|
||||
x: 1408734330000,
|
||||
y: 24
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
xAxisLabel: 'Date Histogram',
|
||||
yAxisLabel: 'Count'
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
module('ColumnChartFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
ColumnChart = Private(require('components/vislib/visualizations/column_chart'));
|
||||
Data = Private(require('components/vislib/lib/data'));
|
||||
|
||||
el = d3.select('body').append('div')
|
||||
.attr('class', 'vis-wrapper')
|
||||
.datum(data);
|
||||
|
||||
el.append('div')
|
||||
.attr('class', 'chart-title')
|
||||
.style('height', '20px');
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
el.remove();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
96
test/unit/specs/vislib/column_layout.js
Normal file
96
test/unit/specs/vislib/column_layout.js
Normal file
|
@ -0,0 +1,96 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
|
||||
angular.module('ColumnLayoutFactory', ['kibana']);
|
||||
|
||||
describe('Vislib Column Layout Test Suite', function () {
|
||||
var layoutType;
|
||||
var columnLayout;
|
||||
var el;
|
||||
var data = {
|
||||
hits: 621,
|
||||
label: '',
|
||||
ordered: {
|
||||
date: true,
|
||||
interval: 30000,
|
||||
max: 1408734982458,
|
||||
min: 1408734082458
|
||||
},
|
||||
series: [
|
||||
{
|
||||
values: [
|
||||
{
|
||||
x: 1408734060000,
|
||||
y: 8
|
||||
},
|
||||
{
|
||||
x: 1408734090000,
|
||||
y: 23
|
||||
},
|
||||
{
|
||||
x: 1408734120000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734150000,
|
||||
y: 28
|
||||
},
|
||||
{
|
||||
x: 1408734180000,
|
||||
y: 36
|
||||
},
|
||||
{
|
||||
x: 1408734210000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734240000,
|
||||
y: 26
|
||||
},
|
||||
{
|
||||
x: 1408734270000,
|
||||
y: 22
|
||||
},
|
||||
{
|
||||
x: 1408734300000,
|
||||
y: 29
|
||||
},
|
||||
{
|
||||
x: 1408734330000,
|
||||
y: 24
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
xAxisLabel: 'Date Histogram',
|
||||
yAxisLabel: 'Count'
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
module('ColumnLayoutFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
layoutType = Private(require('components/vislib/layout_types'));
|
||||
el = d3.select('body').append('div').attr('class', 'visualization');
|
||||
columnLayout = layoutType.histogram(el, data);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
el.remove();
|
||||
});
|
||||
|
||||
it('should return an array of objects', function () {
|
||||
expect(_.isArray(columnLayout)).to.be(true);
|
||||
expect(_.isObject(columnLayout[0])).to.be(true);
|
||||
});
|
||||
|
||||
it('should throw an error when the wrong number or no arguments provided', function () {
|
||||
expect(function () { layoutType.histogram(el); }).to.throwError();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
251
test/unit/specs/vislib/data.js
Normal file
251
test/unit/specs/vislib/data.js
Normal file
|
@ -0,0 +1,251 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
|
||||
var seriesData = {
|
||||
'label': '',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var rowsData = {
|
||||
'rows': [
|
||||
{
|
||||
'label': 'a',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'label': 'b',
|
||||
'series': [
|
||||
{
|
||||
'label': '300',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'label': 'c',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'label': 'd',
|
||||
'series': [
|
||||
{
|
||||
'label': '200',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var colsData = {
|
||||
'columns': [
|
||||
{
|
||||
'label': 'a',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'label': 'b',
|
||||
'series': [
|
||||
{
|
||||
'label': '300',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'label': 'c',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'label': 'd',
|
||||
'series': [
|
||||
{
|
||||
'label': '200',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var seriesData2 = {
|
||||
'label': '',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
},
|
||||
{
|
||||
'label': '200',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var rowsData2 = {
|
||||
'rows': [
|
||||
{
|
||||
'label': 'a',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
},
|
||||
{
|
||||
'label': '200',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'label': 'b',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
},
|
||||
{
|
||||
'label': '200',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var colsData2 = {
|
||||
'columns': [
|
||||
{
|
||||
'label': 'a',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
},
|
||||
{
|
||||
'label': '200',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'label': 'b',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
},
|
||||
{
|
||||
'label': '200',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var flattenedData = [
|
||||
[{x: 0, y: 0}, {x: 1, y: 2}, {x: 2, y: 4}, {x: 3, y: 6}, {x: 4, y: 8}],
|
||||
[{x: 0, y: 0}, {x: 1, y: 2}, {x: 2, y: 4}, {x: 3, y: 6}, {x: 4, y: 8}],
|
||||
[{x: 0, y: 0}, {x: 1, y: 2}, {x: 2, y: 4}, {x: 3, y: 6}, {x: 4, y: 8}]
|
||||
];
|
||||
|
||||
angular.module('DataFactory', ['kibana']);
|
||||
|
||||
describe('Vislib Data Class Test Suite', function () {
|
||||
|
||||
describe('Data Class (main)', function () {
|
||||
var dataFactory;
|
||||
var rowIn;
|
||||
|
||||
beforeEach(function () {
|
||||
module('DataFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
dataFactory = Private(require('components/vislib/lib/data'));
|
||||
});
|
||||
rowIn = new dataFactory(rowsData, {});
|
||||
});
|
||||
|
||||
it('should be a function', function () {
|
||||
expect(_.isFunction(dataFactory)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an object', function () {
|
||||
expect(_.isObject(rowIn)).to.be(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Data.flatten', function () {
|
||||
var dataFactory;
|
||||
var serIn;
|
||||
var rowIn;
|
||||
var colIn;
|
||||
var serOut;
|
||||
var rowOut;
|
||||
var colOut;
|
||||
|
||||
beforeEach(function () {
|
||||
module('DataFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
dataFactory = Private(require('components/vislib/lib/data'));
|
||||
});
|
||||
serIn = new dataFactory(seriesData, {});
|
||||
rowIn = new dataFactory(rowsData, {});
|
||||
colIn = new dataFactory(colsData, {});
|
||||
serOut = serIn.flatten();
|
||||
rowOut = rowIn.flatten();
|
||||
colOut = colIn.flatten();
|
||||
});
|
||||
|
||||
it('should return an array of arrays', function () {
|
||||
expect(_.isArray(serOut)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return array length 3', function () {
|
||||
expect(serOut[0][0].length).to.be(3);
|
||||
});
|
||||
|
||||
it('should return array length 3', function () {
|
||||
expect(rowOut[0][0].length).to.be(3);
|
||||
});
|
||||
|
||||
it('should return array length 3', function () {
|
||||
expect(colOut[0][0].length).to.be(3);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
180
test/unit/specs/vislib/handler.js
Normal file
180
test/unit/specs/vislib/handler.js
Normal file
|
@ -0,0 +1,180 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
|
||||
var angular = require('angular');
|
||||
angular.module('VisFactory', ['kibana']);
|
||||
angular.module('DataFactory', ['kibana']);
|
||||
angular.module('HandlerFactory', ['kibana']);
|
||||
|
||||
describe('VisLib Handler Test Suite', function () {
|
||||
var Vis;
|
||||
var Data;
|
||||
var Handler;
|
||||
var handler;
|
||||
var vis;
|
||||
var el;
|
||||
var example;
|
||||
var config;
|
||||
var data = {
|
||||
hits : 621,
|
||||
label : '',
|
||||
ordered : {
|
||||
date: true,
|
||||
interval: 30000,
|
||||
max : 1408734982458,
|
||||
min : 1408734082458
|
||||
},
|
||||
series : [
|
||||
{
|
||||
values: [
|
||||
{
|
||||
x: 1408734060000,
|
||||
y: 8
|
||||
},
|
||||
{
|
||||
x: 1408734090000,
|
||||
y: 23
|
||||
},
|
||||
{
|
||||
x: 1408734120000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734150000,
|
||||
y: 28
|
||||
},
|
||||
{
|
||||
x: 1408734180000,
|
||||
y: 36
|
||||
},
|
||||
{
|
||||
x: 1408734210000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734240000,
|
||||
y: 26
|
||||
},
|
||||
{
|
||||
x: 1408734270000,
|
||||
y: 22
|
||||
},
|
||||
{
|
||||
x: 1408734300000,
|
||||
y: 29
|
||||
},
|
||||
{
|
||||
x: 1408734330000,
|
||||
y: 24
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
tooltipFormatter: function (datapoint) {
|
||||
return datapoint;
|
||||
},
|
||||
xAxisFormatter: function (thing) {
|
||||
return thing;
|
||||
},
|
||||
xAxisLabel: 'Date Histogram',
|
||||
yAxisLabel: 'Count'
|
||||
};
|
||||
|
||||
|
||||
beforeEach(function () {
|
||||
module('VisFactory');
|
||||
module('HandlerFactory');
|
||||
module('DataFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
Vis = Private(require('components/vislib/vis'));
|
||||
Data = Private(require('components/vislib/lib/data'));
|
||||
Handler = Private(require('components/vislib/lib/handler'));
|
||||
|
||||
el = d3.select('body').append('div')
|
||||
.attr('class', 'visualize');
|
||||
|
||||
config = {
|
||||
type: 'histogram',
|
||||
shareYAxis: true,
|
||||
addTooltip: true,
|
||||
addLegend: true
|
||||
};
|
||||
|
||||
vis = new Vis(el[0][0], config);
|
||||
|
||||
handler = new Handler({
|
||||
vis: vis,
|
||||
el: el[0][0],
|
||||
data: data,
|
||||
ChartClass: vis.ChartClass,
|
||||
_attr: config
|
||||
});
|
||||
|
||||
// handler.render(data);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
el.remove();
|
||||
});
|
||||
|
||||
// describe('render Method', function () {
|
||||
// it('should instantiate all constructors ', function () {
|
||||
// expect(!!handler.layout).to.be(true);
|
||||
// expect(!!handler.legend).to.be(true);
|
||||
// expect(!!handler.tooltip).to.be(true);
|
||||
// expect(!!handler.xAxis).to.be(true);
|
||||
// expect(!!handler.yAxis).to.be(true);
|
||||
// expect(!!handler.axisTitle).to.be(true);
|
||||
// expect(!!handler.chartTitle).to.be(true);
|
||||
// });
|
||||
//
|
||||
// it('should append all DOM Elements for the visualization', function () {
|
||||
// expect($('.vis-wrapper').length).to.be(1);
|
||||
// expect($('.y-axis-col-wrapper').length).to.be(1);
|
||||
// expect($('.vis-col-wrapper').length).to.be(1);
|
||||
// expect($('.legend-col-wrapper').length).to.be(1);
|
||||
// expect($('.k4tip').length).to.be(1);
|
||||
// expect($('.y-axis-col').length).to.be(1);
|
||||
// expect($('.y-axis-title').length).to.be(1);
|
||||
// expect($('.y-axis-chart-title').length).to.be(0);
|
||||
// expect($('.y-axis-div-wrapper').length).to.be(1);
|
||||
// expect($('.y-axis-spacer-block').length).to.be(1);
|
||||
// expect($('.chart-wrapper').length).to.be(1);
|
||||
// expect($('.x-axis-wrapper').length).to.be(1);
|
||||
// expect($('.x-axis-div-wrapper').length).to.be(1);
|
||||
// expect($('.x-axis-chart-title').length).to.be(0);
|
||||
// expect($('.x-axis-title').length).to.be(1);
|
||||
// expect($('svg').length).to.be(5);
|
||||
// });
|
||||
// });
|
||||
|
||||
describe('removeAll Method', function () {
|
||||
beforeEach(function () {
|
||||
inject(function () {
|
||||
handler.removeAll(el[0][0]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove all DOM elements from the el', function () {
|
||||
expect($(el).children().length).to.be(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error Method', function () {
|
||||
beforeEach(function () {
|
||||
handler.error('This is an error!');
|
||||
});
|
||||
|
||||
it('should return an error classed DOM element with a text message', function () {
|
||||
expect($('.visualize').find('.error').length).to.be(1);
|
||||
expect($('.error p').html()).to.be('This is an error!');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
29
test/unit/specs/vislib/index.js
Normal file
29
test/unit/specs/vislib/index.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
|
||||
var angular = require('angular');
|
||||
|
||||
angular.module('kibana/vislib', ['kibana']);
|
||||
|
||||
describe('VisLib Index Test Suite', function () {
|
||||
var chart;
|
||||
|
||||
beforeEach(function () {
|
||||
module('kibana/vislib');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, visLib) {
|
||||
chart = visLib;
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an object', function () {
|
||||
expect(_.isObject(chart)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return a Vis function', function () {
|
||||
expect(_.isFunction(chart.Vis)).to.be(true);
|
||||
});
|
||||
});
|
||||
});
|
298
test/unit/specs/vislib/labels.js
Normal file
298
test/unit/specs/vislib/labels.js
Normal file
|
@ -0,0 +1,298 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
|
||||
angular.module('LabelUtilService', ['kibana']);
|
||||
angular.module('GetArrayUtilService', ['kibana']);
|
||||
angular.module('UniqLabelUtilService', ['kibana']);
|
||||
angular.module('GetSeriesUtilService', ['kibana']);
|
||||
|
||||
var getLabels;
|
||||
var seriesLabels;
|
||||
var rowsLabels;
|
||||
var seriesArr;
|
||||
var rowsArr;
|
||||
var uniqLabels;
|
||||
var error;
|
||||
|
||||
var seriesData = {
|
||||
'label': '',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var rowsData = {
|
||||
'rows': [
|
||||
{
|
||||
'label': 'a',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'label': 'b',
|
||||
'series': [
|
||||
{
|
||||
'label': '300',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'label': 'c',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'label': 'd',
|
||||
'series': [
|
||||
{
|
||||
'label': '200',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var columnsData = {
|
||||
'columns': [
|
||||
{
|
||||
'label': 'a',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'label': 'b',
|
||||
'series': [
|
||||
{
|
||||
'label': '300',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'label': 'c',
|
||||
'series': [
|
||||
{
|
||||
'label': '100',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
'label': 'd',
|
||||
'series': [
|
||||
{
|
||||
'label': '200',
|
||||
'values': [{x: 0, y: 1}, {x: 1, y: 2}, {x: 2, y: 3}]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
describe('Vislib Labels Module Test Suite', function () {
|
||||
|
||||
describe('Labels (main)', function () {
|
||||
beforeEach(function () {
|
||||
module('LabelUtilService');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
getLabels = Private(require('components/vislib/components/_functions/labels/labels'));
|
||||
seriesLabels = getLabels(seriesData);
|
||||
rowsLabels = getLabels(rowsData);
|
||||
seriesArr = _.isArray(seriesLabels);
|
||||
rowsArr = _.isArray(rowsLabels);
|
||||
uniqLabels = _.chain(rowsData.rows)
|
||||
.pluck('series')
|
||||
.flatten()
|
||||
.pluck('label')
|
||||
.uniq()
|
||||
.value();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be a function', function () {
|
||||
expect(typeof getLabels).to.be('function');
|
||||
});
|
||||
|
||||
it('should return an array if input is data.series', function () {
|
||||
expect(seriesArr).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array if input is data.rows', function () {
|
||||
expect(rowsArr).to.be(true);
|
||||
});
|
||||
|
||||
it('should return empty array if input is not an object', function () {
|
||||
error = getLabels('string not object');
|
||||
expect(error.length).to.be(0);
|
||||
});
|
||||
|
||||
it('should return unique label values', function () {
|
||||
expect(rowsLabels[0]).to.equal(uniqLabels[0]);
|
||||
expect(rowsLabels[1]).to.equal(uniqLabels[1]);
|
||||
expect(rowsLabels[2]).to.equal(uniqLabels[2]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Data array', function () {
|
||||
var dataArray;
|
||||
var testSeries;
|
||||
var testRows;
|
||||
|
||||
beforeEach(function () {
|
||||
module('GetArrayUtilService');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
dataArray = Private(require('components/vislib/components/_functions/labels/data_array'));
|
||||
seriesLabels = dataArray(seriesData);
|
||||
rowsLabels = dataArray(rowsData);
|
||||
testSeries = _.isArray(seriesLabels);
|
||||
testRows = _.isArray(rowsLabels);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be a function', function () {
|
||||
expect(typeof dataArray).to.equal('function');
|
||||
});
|
||||
|
||||
it('should return an array of objects if input is data.series', function () {
|
||||
expect(testSeries).to.equal(true);
|
||||
});
|
||||
|
||||
it('should return an array of objects if input is data.rows', function () {
|
||||
expect(testRows).to.equal(true);
|
||||
});
|
||||
|
||||
it('should return an array of same length as input data.series', function () {
|
||||
expect(seriesLabels.length).to.equal(seriesData.series.length);
|
||||
});
|
||||
|
||||
it('should return an array of same length as input data.rows', function () {
|
||||
expect(rowsLabels.length).to.equal(rowsData.rows.length);
|
||||
});
|
||||
|
||||
it('should return an array of objects with obj.labels and obj.values', function () {
|
||||
expect(seriesLabels[0].label).to.equal('100');
|
||||
expect(seriesLabels[0].values[0].x).to.equal(0);
|
||||
expect(seriesLabels[0].values[0].y).to.equal(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Unique labels', function () {
|
||||
var uniqLabels;
|
||||
var arrObj = [
|
||||
{'label': 'a'},
|
||||
{'label': 'b'},
|
||||
{'label': 'b'},
|
||||
{'label': 'c'},
|
||||
{'label': 'c'},
|
||||
{'label': 'd'},
|
||||
{'label': 'f'}
|
||||
];
|
||||
var uniq;
|
||||
var testArr;
|
||||
|
||||
beforeEach(function () {
|
||||
module('UniqLabelUtilService');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
uniqLabels = Private(require('components/vislib/components/_functions/labels/uniq_labels'));
|
||||
uniq = uniqLabels(arrObj);
|
||||
testArr = _.isArray(uniq);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be a function', function () {
|
||||
expect(typeof uniqLabels).to.be('function');
|
||||
});
|
||||
|
||||
it('should return an array', function () {
|
||||
expect(testArr).to.be(true);
|
||||
});
|
||||
|
||||
it('should return array of 5 unique values', function () {
|
||||
expect(uniq.length).to.be(5);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Get series', function () {
|
||||
var getSeries;
|
||||
var columnsLabels;
|
||||
var rowsLabels;
|
||||
var seriesLabels;
|
||||
var columnsArr;
|
||||
var rowsArr;
|
||||
var seriesArr;
|
||||
var error;
|
||||
|
||||
beforeEach(function () {
|
||||
module('GetSeriesUtilService');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
getSeries = Private(require('components/vislib/components/_functions/labels/get_series'));
|
||||
columnsLabels = getSeries(columnsData);
|
||||
rowsLabels = getSeries(rowsData);
|
||||
seriesLabels = getSeries(seriesData);
|
||||
columnsArr = _.isArray(columnsLabels);
|
||||
rowsArr = _.isArray(rowsLabels);
|
||||
seriesArr = _.isArray(seriesLabels);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be a function', function () {
|
||||
expect(typeof getSeries).to.be('function');
|
||||
});
|
||||
|
||||
it('should return an array if input is data.columns', function () {
|
||||
expect(columnsArr).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array if input is data.rows', function () {
|
||||
expect(rowsArr).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an empty array if input is data.series', function () {
|
||||
expect(seriesLabels.length).to.be(0);
|
||||
});
|
||||
|
||||
it('should return an array of the same length as as input data.columns', function () {
|
||||
expect(columnsLabels.length).to.be(columnsData.columns.length);
|
||||
});
|
||||
|
||||
it('should return an array of the same length as as input data.rows', function () {
|
||||
expect(rowsLabels.length).to.be(rowsData.rows.length);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
203
test/unit/specs/vislib/layout.js
Normal file
203
test/unit/specs/vislib/layout.js
Normal file
|
@ -0,0 +1,203 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
|
||||
angular.module('LayoutFactory', ['kibana']);
|
||||
|
||||
describe('Vislib Layout Class Test Suite', function () {
|
||||
var Layout;
|
||||
var layout;
|
||||
var xAxisSplit;
|
||||
var el;
|
||||
var chartType = 'histogram';
|
||||
var data = {
|
||||
hits: 621,
|
||||
label: '',
|
||||
ordered: {
|
||||
date: true,
|
||||
interval: 30000,
|
||||
max: 1408734982458,
|
||||
min: 1408734082458
|
||||
},
|
||||
series: [
|
||||
{
|
||||
values: [
|
||||
{
|
||||
x: 1408734060000,
|
||||
y: 8
|
||||
},
|
||||
{
|
||||
x: 1408734090000,
|
||||
y: 23
|
||||
},
|
||||
{
|
||||
x: 1408734120000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734150000,
|
||||
y: 28
|
||||
},
|
||||
{
|
||||
x: 1408734180000,
|
||||
y: 36
|
||||
},
|
||||
{
|
||||
x: 1408734210000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734240000,
|
||||
y: 26
|
||||
},
|
||||
{
|
||||
x: 1408734270000,
|
||||
y: 22
|
||||
},
|
||||
{
|
||||
x: 1408734300000,
|
||||
y: 29
|
||||
},
|
||||
{
|
||||
x: 1408734330000,
|
||||
y: 24
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
xAxisLabel: 'Date Histogram',
|
||||
yAxisLabel: 'Count'
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
module('LayoutFactory');
|
||||
module('XAxisSplitFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
Layout = Private(require('components/vislib/lib/layout'));
|
||||
xAxisSplit = Private(require('components/vislib/components/layouts/splits/column_chart/x_axis_split'));
|
||||
|
||||
el = d3.select('body').append('div')
|
||||
.attr('class', 'visualize-chart');
|
||||
|
||||
layout = new Layout(el[0][0], data, chartType);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
el.remove();
|
||||
});
|
||||
|
||||
describe('createLayout Method', function () {
|
||||
beforeEach(function () {
|
||||
layout.createLayout(layout.layoutType);
|
||||
});
|
||||
|
||||
it('should append all the divs', function () {
|
||||
expect(el.selectAll('.vis-wrapper').length).to.be(1);
|
||||
expect(el.selectAll('.y-axis-col-wrapper').length).to.be(1);
|
||||
expect(el.selectAll('.vis-col-wrapper').length).to.be(1);
|
||||
expect(el.selectAll('.legend-col-wrapper').length).to.be(1);
|
||||
expect(el.selectAll('.k4tip').length).to.be(1);
|
||||
expect(el.selectAll('.y-axis-col').length).to.be(1);
|
||||
expect(el.selectAll('.y-axis-title').length).to.be(1);
|
||||
expect(el.selectAll('.y-axis-chart-title').length).to.be(1);
|
||||
expect(el.selectAll('.y-axis-div-wrapper').length).to.be(1);
|
||||
expect(el.selectAll('.y-axis-spacer-block').length).to.be(1);
|
||||
expect(el.selectAll('.chart-wrapper').length).to.be(1);
|
||||
expect(el.selectAll('.x-axis-wrapper').length).to.be(1);
|
||||
expect(el.selectAll('.x-axis-div-wrapper').length).to.be(1);
|
||||
expect(el.selectAll('.x-axis-chart-title').length).to.be(1);
|
||||
expect(el.selectAll('.x-axis-title').length).to.be(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('layout Method', function () {
|
||||
beforeEach(function () {
|
||||
layout.layout({
|
||||
parent: layout.el,
|
||||
type: 'div',
|
||||
class: 'chart',
|
||||
datum: layout.data,
|
||||
children: [
|
||||
{
|
||||
type: 'div',
|
||||
class: 'x-axis',
|
||||
splits: xAxisSplit
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('should append a div with the correct class name', function () {
|
||||
expect(el.select('.chart').length).to.be(1);
|
||||
});
|
||||
|
||||
it('should bind data to the DOM element', function () {
|
||||
expect(!!el.select('.chart').data()).to.be(true);
|
||||
});
|
||||
|
||||
it('should create children', function () {
|
||||
expect(el.select('.x-axis').length).to.be(1);
|
||||
});
|
||||
|
||||
it('should call split function when provided', function () {
|
||||
expect(el.select('.x-axis-div').length).to.be(1);
|
||||
});
|
||||
|
||||
it('should throw an errors when incorrect arguments provided', function () {
|
||||
expect(function () {
|
||||
layout.layout({
|
||||
parent: null,
|
||||
type: 'div',
|
||||
class: 'chart'
|
||||
});
|
||||
}).to.throwError();
|
||||
|
||||
expect(function () {
|
||||
layout.layout({
|
||||
parent: layout.el,
|
||||
type: undefined,
|
||||
class: 'chart'
|
||||
});
|
||||
}).to.throwError();
|
||||
|
||||
expect(function () {
|
||||
layout.layout({
|
||||
parent: el,
|
||||
type: xAxisSplit,
|
||||
class: 'chart'
|
||||
});
|
||||
}).to.throwError();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('appendElem Method', function () {
|
||||
beforeEach(function () {
|
||||
layout.appendElem(layout.el, 'svg', 'column');
|
||||
});
|
||||
|
||||
it('should append DOM element to el with a class name', function () {
|
||||
expect(el.select('svg').attr('class')).to.be('column');
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeAll Method', function () {
|
||||
beforeEach(function () {
|
||||
inject(function (d3) {
|
||||
d3.select(layout.el).append('div').attr('class', 'visualize');
|
||||
layout.removeAll(layout.el);
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove all DOM elements from the el', function () {
|
||||
expect($(el).children().length).to.be(0);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
31
test/unit/specs/vislib/layout_types.js
Normal file
31
test/unit/specs/vislib/layout_types.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
|
||||
angular.module('LayoutTypeFactory', ['kibana']);
|
||||
|
||||
describe('Vislib Layout Types Test Suite', function () {
|
||||
var layoutType;
|
||||
var layoutFunc;
|
||||
|
||||
beforeEach(function () {
|
||||
module('LayoutTypeFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
layoutType = Private(require('components/vislib/layout_types'));
|
||||
layoutFunc = layoutType.histogram;
|
||||
});
|
||||
});
|
||||
|
||||
it('should be an object', function () {
|
||||
expect(_.isObject(layoutType)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return a function', function () {
|
||||
expect(typeof layoutFunc).to.be('function');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
199
test/unit/specs/vislib/resize_checker.js
Normal file
199
test/unit/specs/vislib/resize_checker.js
Normal file
|
@ -0,0 +1,199 @@
|
|||
define(function (require) {
|
||||
describe('Vislib Resize Checker', function () {
|
||||
var $ = require('jquery');
|
||||
var _ = require('lodash');
|
||||
var Promise = require('bluebird');
|
||||
|
||||
var sinon = require('test_utils/auto_release_sinon');
|
||||
require('test_utils/no_digest_promises').activateForSuite();
|
||||
|
||||
var ResizeChecker;
|
||||
var EventEmitter;
|
||||
var checker;
|
||||
var reflowWatcher;
|
||||
var spyReflowOn;
|
||||
|
||||
beforeEach(module('kibana'));
|
||||
beforeEach(inject(function (Private) {
|
||||
ResizeChecker = Private(require('components/vislib/lib/resize_checker'));
|
||||
EventEmitter = Private(require('factories/events'));
|
||||
reflowWatcher = Private(require('components/reflow_watcher'));
|
||||
|
||||
spyReflowOn = sinon.spy(reflowWatcher, 'on');
|
||||
checker = new ResizeChecker(
|
||||
$(document.createElement('div'))
|
||||
.appendTo('body')
|
||||
.css('visibility', 'hidden')
|
||||
.get(0)
|
||||
);
|
||||
spyReflowOn.restore();
|
||||
}));
|
||||
|
||||
afterEach(function () {
|
||||
checker.$el.remove();
|
||||
checker.destroy();
|
||||
});
|
||||
|
||||
it('is an event emitter', function () {
|
||||
expect(checker).to.be.a(EventEmitter);
|
||||
});
|
||||
|
||||
it('emits a "resize" event when the el is resized', function (done) {
|
||||
checker.on('resize', function () {
|
||||
done();
|
||||
});
|
||||
|
||||
checker.$el.text('haz contents');
|
||||
checker.check();
|
||||
});
|
||||
|
||||
it('listens for the "reflow" event of the reflowWatchers', function () {
|
||||
expect(spyReflowOn).to.have.property('callCount', 1);
|
||||
var call = spyReflowOn.getCall(0);
|
||||
expect(call.args[0]).to.be('reflow');
|
||||
});
|
||||
|
||||
describe('#read', function () {
|
||||
it('uses jquery to get the width and height of the element', function () {
|
||||
var stubw = sinon.spy($.fn, 'width');
|
||||
var stubh = sinon.spy($.fn, 'height');
|
||||
|
||||
checker.read();
|
||||
|
||||
expect(stubw).to.have.property('callCount', 1);
|
||||
expect(stubw.getCall(0)).to.have.property('thisValue', checker.$el);
|
||||
|
||||
expect(stubh).to.have.property('callCount', 1);
|
||||
expect(stubh.getCall(0)).to.have.property('thisValue', checker.$el);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#saveSize', function () {
|
||||
it('calls #read() when no arg is passed', function () {
|
||||
var stub = sinon.stub(checker, 'read').returns({});
|
||||
|
||||
checker.saveSize();
|
||||
|
||||
expect(stub).to.have.property('callCount', 1);
|
||||
});
|
||||
|
||||
it('saves the size of the element', function () {
|
||||
var football = {};
|
||||
checker.saveSize(football);
|
||||
expect(checker).to.have.property('_savedSize', football);
|
||||
});
|
||||
|
||||
it('returns false if the size matches the previous value', function () {
|
||||
expect(checker.saveSize(checker._savedSize)).to.be(false);
|
||||
});
|
||||
|
||||
it('returns true if the size is different than previous value', function () {
|
||||
expect(checker.saveSize({})).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#check()', function () {
|
||||
var emit;
|
||||
|
||||
beforeEach(function () {
|
||||
emit = sinon.stub(checker, 'emit');
|
||||
|
||||
// prevent the checker from auto-checking
|
||||
checker.destroy();
|
||||
checker.startSchedule = checker.continueSchedule = _.noop;
|
||||
});
|
||||
|
||||
it('does not emit "resize" immediately after a resize, but waits for changes to stop', function () {
|
||||
expect(checker).to.have.property('_isDirty', false);
|
||||
|
||||
checker.$el.css('height', 100);
|
||||
checker.check();
|
||||
|
||||
expect(checker).to.have.property('_isDirty', true);
|
||||
expect(emit).to.have.property('callCount', 0);
|
||||
|
||||
// no change in el size
|
||||
checker.check();
|
||||
|
||||
expect(checker).to.have.property('_isDirty', false);
|
||||
expect(emit).to.have.property('callCount', 1);
|
||||
});
|
||||
|
||||
it('emits "resize" based on MS_MAX_RESIZE_DELAY, even if el\'s constantly changing size', function () {
|
||||
var steps = _.random(5, 10);
|
||||
this.slow(steps * 10);
|
||||
|
||||
// we are going to fake the delay using the fake clock
|
||||
var msStep = Math.floor(ResizeChecker.MS_MAX_RESIZE_DELAY / (steps - 1));
|
||||
var clock = sinon.useFakeTimers();
|
||||
|
||||
_.times(steps, function step(i) {
|
||||
checker.$el.css('height', 100 + i);
|
||||
checker.check();
|
||||
|
||||
expect(checker).to.have.property('_isDirty', true);
|
||||
expect(emit).to.have.property('callCount', i > steps ? 1 : 0);
|
||||
|
||||
clock.tick(msStep); // move the clock forward one step
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('scheduling', function () {
|
||||
var clock;
|
||||
var schedule;
|
||||
|
||||
beforeEach(function () {
|
||||
// prevent the checker from running automatically
|
||||
checker.destroy();
|
||||
clock = sinon.useFakeTimers();
|
||||
|
||||
schedule = [];
|
||||
_.times(25, function () {
|
||||
schedule.push(_.random(3, 250));
|
||||
});
|
||||
});
|
||||
|
||||
it('walks the schedule, using each value as it\'s next timeout', function () {
|
||||
var timerId = checker.startSchedule(schedule);
|
||||
|
||||
// start at 0 even though "start" used the first slot, we will still check it
|
||||
for (var i = 0; i < schedule.length; i++) {
|
||||
expect(clock.timeouts[timerId]).to.have.property('callAt', schedule[i]);
|
||||
timerId = checker.continueSchedule();
|
||||
}
|
||||
});
|
||||
|
||||
it('repeats the last value in the schedule', function () {
|
||||
var timerId = checker.startSchedule(schedule);
|
||||
|
||||
// start at 1, and go until there is one left
|
||||
for (var i = 1; i < schedule.length - 1; i++) {
|
||||
timerId = checker.continueSchedule();
|
||||
}
|
||||
|
||||
var last = _.last(schedule);
|
||||
_.times(5, function () {
|
||||
var timer = clock.timeouts[checker.continueSchedule()];
|
||||
expect(timer).to.have.property('callAt', last);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#destroy()', function () {
|
||||
it('removes the "reflow" event from the reflowWatcher', function () {
|
||||
var stub = sinon.stub(reflowWatcher, 'off');
|
||||
checker.destroy();
|
||||
expect(stub).to.have.property('callCount', 1);
|
||||
expect(stub.calledWith('reflow')).to.be.ok();
|
||||
});
|
||||
|
||||
it('clears the timeout', function () {
|
||||
var spy = sinon.spy(window, 'clearTimeout');
|
||||
checker.destroy();
|
||||
expect(spy).to.have.property('callCount', 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
278
test/unit/specs/vislib/splits.js
Normal file
278
test/unit/specs/vislib/splits.js
Normal file
|
@ -0,0 +1,278 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var $ = require('jquery');
|
||||
|
||||
angular.module('ChartSplitFactory', ['kibana']);
|
||||
angular.module('ChartTitleSplitFactory', ['kibana']);
|
||||
angular.module('XAxisSplitFactory', ['kibana']);
|
||||
angular.module('YAxisSplitFactory', ['kibana']);
|
||||
|
||||
describe('Vislib Split Function Test Suite', function () {
|
||||
describe('Column Chart', function () {
|
||||
var chartSplit;
|
||||
var chartTitleSplit;
|
||||
var xAxisSplit;
|
||||
var yAxisSplit;
|
||||
var el;
|
||||
var data = {
|
||||
rows: [
|
||||
{
|
||||
hits : 621,
|
||||
label : '',
|
||||
ordered : {
|
||||
date : true,
|
||||
interval: 30000,
|
||||
max : 1408734982458,
|
||||
min : 1408734082458
|
||||
},
|
||||
series : [
|
||||
{
|
||||
values: [
|
||||
{
|
||||
x: 1408734060000,
|
||||
y: 8
|
||||
},
|
||||
{
|
||||
x: 1408734090000,
|
||||
y: 23
|
||||
},
|
||||
{
|
||||
x: 1408734120000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734150000,
|
||||
y: 28
|
||||
},
|
||||
{
|
||||
x: 1408734180000,
|
||||
y: 36
|
||||
},
|
||||
{
|
||||
x: 1408734210000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734240000,
|
||||
y: 26
|
||||
},
|
||||
{
|
||||
x: 1408734270000,
|
||||
y: 22
|
||||
},
|
||||
{
|
||||
x: 1408734300000,
|
||||
y: 29
|
||||
},
|
||||
{
|
||||
x: 1408734330000,
|
||||
y: 24
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
xAxisLabel: 'Date Histogram',
|
||||
yAxisLabel: 'Count'
|
||||
},
|
||||
{
|
||||
hits : 621,
|
||||
label : '',
|
||||
ordered : {
|
||||
date : true,
|
||||
interval: 30000,
|
||||
max : 1408734982458,
|
||||
min : 1408734082458
|
||||
},
|
||||
series : [
|
||||
{
|
||||
values: [
|
||||
{
|
||||
x: 1408734060000,
|
||||
y: 8
|
||||
},
|
||||
{
|
||||
x: 1408734090000,
|
||||
y: 23
|
||||
},
|
||||
{
|
||||
x: 1408734120000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734150000,
|
||||
y: 28
|
||||
},
|
||||
{
|
||||
x: 1408734180000,
|
||||
y: 36
|
||||
},
|
||||
{
|
||||
x: 1408734210000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734240000,
|
||||
y: 26
|
||||
},
|
||||
{
|
||||
x: 1408734270000,
|
||||
y: 22
|
||||
},
|
||||
{
|
||||
x: 1408734300000,
|
||||
y: 29
|
||||
},
|
||||
{
|
||||
x: 1408734330000,
|
||||
y: 24
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
xAxisLabel: 'Date Histogram',
|
||||
yAxisLabel: 'Count'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
module('ChartSplitFactory');
|
||||
module('ChartTitleSplitFactory');
|
||||
module('XAxisSplitFactory');
|
||||
module('YAxisSplitFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
chartSplit = Private(require('components/vislib/components/layouts/splits/column_chart/chart_split'));
|
||||
chartTitleSplit = Private(require('components/vislib/components/layouts/splits/column_chart/chart_title_split'));
|
||||
xAxisSplit = Private(require('components/vislib/components/layouts/splits/column_chart/x_axis_split'));
|
||||
yAxisSplit = Private(require('components/vislib/components/layouts/splits/column_chart/y_axis_split'));
|
||||
|
||||
el = d3.select('body').append('div')
|
||||
.attr('class', 'visualization')
|
||||
.datum(data);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
el.remove();
|
||||
});
|
||||
|
||||
describe('chart split function', function () {
|
||||
var fixture;
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3) {
|
||||
fixture = d3.select('.visualization').call(chartSplit);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
fixture.remove();
|
||||
});
|
||||
|
||||
it('should append the correct number of divs', function () {
|
||||
expect($('.chart').length).to.be(2);
|
||||
});
|
||||
|
||||
it('should add the correct class name', function () {
|
||||
expect(!!$('.chart-wrapper-row').length).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('chart title split function', function () {
|
||||
var newEl;
|
||||
var fixture;
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3) {
|
||||
el.append('div').attr('class', 'x-axis-chart-title');
|
||||
el.append('div').attr('class', 'y-axis-chart-title');
|
||||
d3.select('.x-axis-chart-title').call(chartTitleSplit);
|
||||
d3.select('.y-axis-chart-title').call(chartTitleSplit);
|
||||
|
||||
newEl = d3.select('body').append('div')
|
||||
.attr('class', 'series')
|
||||
.datum({ series: []});
|
||||
|
||||
newEl.append('div').attr('class', 'x-axis-chart-title');
|
||||
newEl.append('div').attr('class', 'y-axis-chart-title');
|
||||
newEl.select('.x-axis-chart-title').call(chartTitleSplit);
|
||||
newEl.select('.y-axis-chart-title').call(chartTitleSplit);
|
||||
|
||||
fixture = newEl.selectAll(this.childNodes)[0].length;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
newEl.remove();
|
||||
});
|
||||
|
||||
it('should append the correct number of divs', function () {
|
||||
expect($('.chart-title').length).to.be(2);
|
||||
});
|
||||
|
||||
it('should remove the correct div', function () {
|
||||
expect($('.y-axis-chart-title').length).to.be(1);
|
||||
expect($('.x-axis-chart-title').length).to.be(0);
|
||||
});
|
||||
|
||||
it('should remove all chart title divs when only one chart is rendered', function () {
|
||||
expect(fixture).to.be(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('x axis split function', function () {
|
||||
var fixture;
|
||||
var divs;
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3) {
|
||||
fixture = d3.select('body').append('div')
|
||||
.attr('class', 'columns')
|
||||
.datum({ columns: [{}, {}] });
|
||||
d3.select('.columns').call(xAxisSplit);
|
||||
divs = d3.selectAll('.x-axis-div')[0];
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
fixture.remove();
|
||||
$(divs).remove();
|
||||
});
|
||||
|
||||
it('should append the correct number of divs', function () {
|
||||
expect(divs.length).to.be(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('y axis split function', function () {
|
||||
var fixture;
|
||||
var divs;
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3) {
|
||||
fixture = d3.select('body').append('div')
|
||||
.attr('class', 'rows')
|
||||
.datum({ rows: [{}, {}] });
|
||||
|
||||
d3.select('.rows').call(yAxisSplit);
|
||||
|
||||
divs = d3.selectAll('.y-axis-div')[0];
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
fixture.remove();
|
||||
$(divs).remove();
|
||||
});
|
||||
|
||||
it('should append the correct number of divs', function () {
|
||||
expect(divs.length).to.be(2);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
157
test/unit/specs/vislib/vis.js
Normal file
157
test/unit/specs/vislib/vis.js
Normal file
|
@ -0,0 +1,157 @@
|
|||
define(function (require) {
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
|
||||
var angular = require('angular');
|
||||
angular.module('VisFactory', ['kibana']);
|
||||
|
||||
describe('VisLib Vis Test Suite', function () {
|
||||
var Vis;
|
||||
var vis;
|
||||
var el;
|
||||
var config;
|
||||
var data = {
|
||||
hits: 621,
|
||||
label: '',
|
||||
ordered: {
|
||||
date: true,
|
||||
interval: 30000,
|
||||
max: 1408734982458,
|
||||
min: 1408734082458
|
||||
},
|
||||
series: [{
|
||||
values: [{
|
||||
x: 1408734060000,
|
||||
y: 8
|
||||
}, {
|
||||
x: 1408734090000,
|
||||
y: 23
|
||||
}, {
|
||||
x: 1408734120000,
|
||||
y: 30
|
||||
}, {
|
||||
x: 1408734150000,
|
||||
y: 28
|
||||
}, {
|
||||
x: 1408734180000,
|
||||
y: 36
|
||||
}, {
|
||||
x: 1408734210000,
|
||||
y: 30
|
||||
}, {
|
||||
x: 1408734240000,
|
||||
y: 26
|
||||
}, {
|
||||
x: 1408734270000,
|
||||
y: 22
|
||||
}, {
|
||||
x: 1408734300000,
|
||||
y: 29
|
||||
}, {
|
||||
x: 1408734330000,
|
||||
y: 24
|
||||
}]
|
||||
}],
|
||||
tooltipFormatter: function (datapoint) {
|
||||
return datapoint;
|
||||
},
|
||||
xAxisFormatter: function (thing) {
|
||||
return thing;
|
||||
},
|
||||
xAxisLabel: 'Date Histogram',
|
||||
yAxisLabel: 'Count'
|
||||
};
|
||||
|
||||
|
||||
beforeEach(function () {
|
||||
module('VisFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
Vis = Private(require('components/vislib/vis'));
|
||||
|
||||
el = d3.select('body').append('div')
|
||||
.attr('class', 'visualize');
|
||||
|
||||
config = {
|
||||
type: 'histogram',
|
||||
shareYAxis: true,
|
||||
addTooltip: true,
|
||||
addLegend: true
|
||||
};
|
||||
|
||||
vis = new Vis(el[0][0], config);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
el.remove();
|
||||
vis.destroy();
|
||||
});
|
||||
|
||||
describe('render Method', function () {
|
||||
beforeEach(function () {
|
||||
vis.render(data);
|
||||
});
|
||||
|
||||
it('should bind data to this object', function () {
|
||||
expect(_.isObject(vis.data)).to.be(true);
|
||||
});
|
||||
|
||||
it('should instantiate a handler object', function () {
|
||||
expect(_.isObject(vis.handler)).to.be(true);
|
||||
});
|
||||
|
||||
it('should append a chart', function () {
|
||||
expect($('.chart').length).to.be(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resize Method', function () {
|
||||
it('should resize the chart', function () {
|
||||
vis.render(data);
|
||||
$('.visualize').width(500);
|
||||
vis.resize();
|
||||
expect($('.chart').width()).to.be.lessThan(500);
|
||||
});
|
||||
});
|
||||
|
||||
describe('destroy Method', function () {
|
||||
beforeEach(function () {
|
||||
vis.destroy();
|
||||
});
|
||||
|
||||
it('should remove all DOM elements from el', function () {
|
||||
expect($('.vis-wrapper').length).to.be(0);
|
||||
});
|
||||
|
||||
it('should turn off events', function () {});
|
||||
});
|
||||
|
||||
describe('set Method', function () {
|
||||
beforeEach(function () {
|
||||
vis.render(data);
|
||||
vis.set('addLegend', false);
|
||||
vis.set('offset', 'wiggle');
|
||||
});
|
||||
|
||||
it('should set an attribute', function () {
|
||||
expect(vis.get('addLegend')).to.be(false);
|
||||
expect(vis.get('offset')).to.be('wiggle');
|
||||
});
|
||||
});
|
||||
|
||||
describe('get Method', function () {
|
||||
beforeEach(function () {
|
||||
vis.render(data);
|
||||
});
|
||||
|
||||
it('should get attribue values', function () {
|
||||
expect(vis.get('addLegend')).to.be(true);
|
||||
expect(vis.get('addTooltip')).to.be(true);
|
||||
expect(vis.get('type')).to.be('histogram');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
31
test/unit/specs/vislib/vis_types.js
Normal file
31
test/unit/specs/vislib/vis_types.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
|
||||
angular.module('VisTypeFactory', ['kibana']);
|
||||
|
||||
describe('Vislib Vis Types Test Suite', function () {
|
||||
var visTypes;
|
||||
var visFunc;
|
||||
|
||||
beforeEach(function () {
|
||||
module('VisTypeFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
visTypes = Private(require('components/vislib/vis_types'));
|
||||
visFunc = visTypes.histogram;
|
||||
});
|
||||
});
|
||||
|
||||
it('should be an object', function () {
|
||||
expect(_.isObject(visTypes)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return a function', function () {
|
||||
expect(typeof visFunc).to.be('function');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
233
test/unit/specs/vislib/x_axis.js
Normal file
233
test/unit/specs/vislib/x_axis.js
Normal file
|
@ -0,0 +1,233 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
var $ = require('jquery');
|
||||
|
||||
angular.module('XAxisFactory', ['kibana']);
|
||||
|
||||
describe('Vislib xAxis Class Test Suite', function () {
|
||||
var XAxis;
|
||||
var Data;
|
||||
var xAxis;
|
||||
var el;
|
||||
var fixture;
|
||||
var dataObj;
|
||||
var data = {
|
||||
hits: 621,
|
||||
label: '',
|
||||
ordered: {
|
||||
date: true,
|
||||
interval: 30000,
|
||||
max: 1408734982458,
|
||||
min: 1408734082458
|
||||
},
|
||||
series: [
|
||||
{
|
||||
values: [
|
||||
{
|
||||
x: 1408734060000,
|
||||
y: 8
|
||||
},
|
||||
{
|
||||
x: 1408734090000,
|
||||
y: 23
|
||||
},
|
||||
{
|
||||
x: 1408734120000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734150000,
|
||||
y: 28
|
||||
},
|
||||
{
|
||||
x: 1408734180000,
|
||||
y: 36
|
||||
},
|
||||
{
|
||||
x: 1408734210000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734240000,
|
||||
y: 26
|
||||
},
|
||||
{
|
||||
x: 1408734270000,
|
||||
y: 22
|
||||
},
|
||||
{
|
||||
x: 1408734300000,
|
||||
y: 29
|
||||
},
|
||||
{
|
||||
x: 1408734330000,
|
||||
y: 24
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
xAxisFormatter: function (thing) {
|
||||
return new Date(thing);
|
||||
},
|
||||
xAxisLabel: 'Date Histogram',
|
||||
yAxisLabel: 'Count'
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
module('XAxisFactory');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (d3, Private) {
|
||||
Data = Private(require('components/vislib/lib/data'));
|
||||
XAxis = Private(require('components/vislib/lib/x_axis'));
|
||||
|
||||
el = d3.select('body').append('div')
|
||||
.attr('class', 'x-axis-wrapper')
|
||||
.style('height', '40px');
|
||||
|
||||
fixture = el.append('div')
|
||||
.attr('class', 'x-axis-div');
|
||||
|
||||
dataObj = new Data(data, {});
|
||||
xAxis = new XAxis({
|
||||
el: $('.x-axis-div')[0],
|
||||
xValues: dataObj.xValues(),
|
||||
ordered: dataObj.get('ordered'),
|
||||
xAxisFormatter: dataObj.get('xAxisFormatter'),
|
||||
_attr: {
|
||||
margin: { top: 0, right: 0, bottom: 0, left: 0 }
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
fixture.remove();
|
||||
el.remove();
|
||||
});
|
||||
|
||||
describe('render Method', function () {
|
||||
beforeEach(function () {
|
||||
xAxis.render();
|
||||
});
|
||||
|
||||
it('should append an svg to div', function () {
|
||||
expect(el.selectAll('svg').length).to.be(1);
|
||||
});
|
||||
|
||||
it('should append a g element to the svg', function () {
|
||||
expect(el.selectAll('svg').select('g').length).to.be(1);
|
||||
});
|
||||
|
||||
it('should append ticks with text', function () {
|
||||
expect(!!el.selectAll('svg').selectAll('.tick text')).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getScale, getDomain, getTimeDomain, getOrdinalDomain, and getRange Methods', function () {
|
||||
var ordered;
|
||||
var timeScale;
|
||||
var timeDomain;
|
||||
var ordinalScale;
|
||||
var ordinalDomain;
|
||||
var width;
|
||||
var range;
|
||||
|
||||
beforeEach(function () {
|
||||
ordered = dataObj.get('ordered');
|
||||
timeScale = xAxis.getScale(ordered);
|
||||
timeDomain = xAxis.getDomain(timeScale, ordered);
|
||||
ordinalScale = xAxis.getScale(false);
|
||||
ordinalDomain = ordinalScale.domain(['this', 'should', 'be', 'an', 'array']);
|
||||
width = $('.x-axis-div').width();
|
||||
range = xAxis.getRange(timeDomain, ordered, width);
|
||||
});
|
||||
|
||||
it('should return a function', function () {
|
||||
expect(_.isFunction(timeScale)).to.be(true);
|
||||
expect(_.isFunction(ordinalScale)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return the correct domain', function () {
|
||||
expect(_.isDate(timeDomain.domain()[0])).to.be(true);
|
||||
expect(_.isDate(timeDomain.domain()[1])).to.be(true);
|
||||
});
|
||||
|
||||
it('should return the min and max dates', function () {
|
||||
expect(timeDomain.domain()[0].toDateString()).to.be(new Date(1408734060000).toDateString());
|
||||
expect(timeDomain.domain()[1].toDateString()).to.be(new Date(1408734330000).toDateString());
|
||||
});
|
||||
|
||||
it('should return an ordinal scale', function () {
|
||||
expect(ordinalDomain.domain()[0]).to.be('this');
|
||||
expect(ordinalDomain.domain()[4]).to.be('array');
|
||||
});
|
||||
|
||||
it('should return an array of values', function () {
|
||||
expect(_.isArray(ordinalDomain.domain())).to.be(true);
|
||||
});
|
||||
|
||||
it('should return the correct range', function () {
|
||||
expect(range.range()[0]).to.be(0);
|
||||
expect(range.range()[1]).to.be(width);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getXScale Method', function () {
|
||||
var ordered;
|
||||
var width;
|
||||
var xScale;
|
||||
|
||||
beforeEach(function () {
|
||||
ordered = dataObj.get('ordered');
|
||||
width = $('.x-axis-div').width();
|
||||
xScale = xAxis.getXScale(ordered, width);
|
||||
});
|
||||
|
||||
it('should return a function', function () {
|
||||
expect(_.isFunction(xScale)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return a domain', function () {
|
||||
expect(_.isDate(xScale.domain()[0])).to.be(true);
|
||||
expect(_.isDate(xScale.domain()[1])).to.be(true);
|
||||
});
|
||||
|
||||
it('should return a range', function () {
|
||||
expect(xScale.range()[0]).to.be(0);
|
||||
expect(xScale.range()[1]).to.be(width);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getXAxis Method', function () {
|
||||
var width;
|
||||
var axis;
|
||||
|
||||
beforeEach(function () {
|
||||
width = $('.x-axis-div').width();
|
||||
xAxis.getXAxis(width);
|
||||
});
|
||||
|
||||
it('should create an xAxis function on the xAxis class', function () {
|
||||
expect(_.isFunction(xAxis.xAxis)).to.be(true);
|
||||
});
|
||||
|
||||
it('should create an xScale function on the xAxis class', function () {
|
||||
expect(_.isFunction(xAxis.xScale)).to.be(true);
|
||||
});
|
||||
|
||||
it('should create an xAxisFormatter function on the xAxis class', function () {
|
||||
expect(_.isFunction(xAxis.xAxisFormatter)).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('draw Method', function () {
|
||||
it('should be a function', function () {
|
||||
expect(_.isFunction(xAxis.draw())).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
214
test/unit/specs/vislib/y_axis.js
Normal file
214
test/unit/specs/vislib/y_axis.js
Normal file
|
@ -0,0 +1,214 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
var d3 = require('d3');
|
||||
var $ = require('jquery');
|
||||
|
||||
angular.module('YAxisFactory', ['kibana']);
|
||||
|
||||
describe('Vislib yAxis Class Test Suite', function () {
|
||||
var YAxis;
|
||||
var Data;
|
||||
var yAxis;
|
||||
var el;
|
||||
var yAxisDiv;
|
||||
var dataObj;
|
||||
var data = {
|
||||
hits: 621,
|
||||
label: 'test',
|
||||
ordered: {
|
||||
date: true,
|
||||
interval: 30000,
|
||||
max: 1408734982458,
|
||||
min: 1408734082458
|
||||
},
|
||||
series: [
|
||||
{
|
||||
values: [
|
||||
{
|
||||
x: 1408734060000,
|
||||
y: 8
|
||||
},
|
||||
{
|
||||
x: 1408734090000,
|
||||
y: 23
|
||||
},
|
||||
{
|
||||
x: 1408734120000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734150000,
|
||||
y: 28
|
||||
},
|
||||
{
|
||||
x: 1408734180000,
|
||||
y: 36
|
||||
},
|
||||
{
|
||||
x: 1408734210000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734240000,
|
||||
y: 26
|
||||
},
|
||||
{
|
||||
x: 1408734270000,
|
||||
y: 22
|
||||
},
|
||||
{
|
||||
x: 1408734300000,
|
||||
y: 29
|
||||
},
|
||||
{
|
||||
x: 1408734330000,
|
||||
y: 24
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
values: [
|
||||
{
|
||||
x: 1408734060000,
|
||||
y: 8
|
||||
},
|
||||
{
|
||||
x: 1408734090000,
|
||||
y: 23
|
||||
},
|
||||
{
|
||||
x: 1408734120000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734150000,
|
||||
y: 28
|
||||
},
|
||||
{
|
||||
x: 1408734180000,
|
||||
y: 36
|
||||
},
|
||||
{
|
||||
x: 1408734210000,
|
||||
y: 30
|
||||
},
|
||||
{
|
||||
x: 1408734240000,
|
||||
y: 26
|
||||
},
|
||||
{
|
||||
x: 1408734270000,
|
||||
y: 22
|
||||
},
|
||||
{
|
||||
x: 1408734300000,
|
||||
y: 29
|
||||
},
|
||||
{
|
||||
x: 1408734330000,
|
||||
y: 24
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
xAxisLabel: 'Date Histogram',
|
||||
yAxisLabel: 'Count'
|
||||
};
|
||||
|
||||
beforeEach(module('YAxisFactory'));
|
||||
beforeEach(inject(function (d3, Private) {
|
||||
Data = Private(require('components/vislib/lib/data'));
|
||||
YAxis = Private(require('components/vislib/lib/y_axis'));
|
||||
|
||||
expect($('.y-axis-wrapper')).to.have.length(0);
|
||||
|
||||
var $node = $('<div>').css({
|
||||
height: 40,
|
||||
width: 40,
|
||||
})
|
||||
.appendTo('body')
|
||||
.addClass('y-axis-wrapper');
|
||||
|
||||
var node = $node.get(0);
|
||||
|
||||
el = d3.select(node).datum(data);
|
||||
|
||||
yAxisDiv = el.append('div')
|
||||
.attr('class', 'y-axis-div');
|
||||
|
||||
dataObj = new Data(data, {});
|
||||
yAxis = new YAxis({
|
||||
el: node,
|
||||
yMax: dataObj.getYMaxValue(),
|
||||
_attr: {
|
||||
margin: { top: 0, right: 0, bottom: 0, left: 0 }
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
afterEach(function () {
|
||||
el.remove();
|
||||
yAxisDiv.remove();
|
||||
});
|
||||
|
||||
describe('render Method', function () {
|
||||
beforeEach(function () {
|
||||
expect(d3.select(yAxis.el).selectAll('.y-axis-div')).to.have.length(1);
|
||||
yAxis.render();
|
||||
});
|
||||
|
||||
it('should append an svg to div', function () {
|
||||
expect(el.selectAll('svg').length).to.be(1);
|
||||
});
|
||||
|
||||
it('should append a g element to the svg', function () {
|
||||
expect(el.selectAll('svg').select('g').length).to.be(1);
|
||||
});
|
||||
|
||||
it('should append ticks with text', function () {
|
||||
expect(!!el.selectAll('svg').selectAll('.tick text')).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getYScale Method', function () {
|
||||
var yScale;
|
||||
var height = 50;
|
||||
|
||||
beforeEach(function () {
|
||||
yScale = yAxis.getYScale(height);
|
||||
});
|
||||
|
||||
it('should return a function', function () {
|
||||
expect(_.isFunction(yScale)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return the correct domain', function () {
|
||||
expect(yScale.domain()[0]).to.be(0);
|
||||
// Should be greater than 36 since we are using .nice()
|
||||
expect(yScale.domain()[1]).to.be.greaterThan(36);
|
||||
});
|
||||
|
||||
it('should return the correct range', function () {
|
||||
expect(yScale.range()[0]).to.be(height);
|
||||
// The yScale range should always start from 0
|
||||
expect(yScale.range()[1]).to.be(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('draw Method', function () {
|
||||
it('should be a function', function () {
|
||||
expect(_.isFunction(yAxis.draw())).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tickScale Method', function () {
|
||||
it('should return the correct number of ticks', function () {
|
||||
expect(yAxis.tickScale(1000)).to.be(11);
|
||||
expect(yAxis.tickScale(40)).to.be(3);
|
||||
expect(yAxis.tickScale(20)).to.be(0);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
336
test/unit/specs/vislib/zero_injection.js
Normal file
336
test/unit/specs/vislib/zero_injection.js
Normal file
|
@ -0,0 +1,336 @@
|
|||
define(function (require) {
|
||||
var angular = require('angular');
|
||||
var _ = require('lodash');
|
||||
|
||||
angular.module('ZeroInjectionUtilService', ['kibana']);
|
||||
angular.module('FlattenDataObjectUtilService', ['kibana']);
|
||||
angular.module('OrderedXKeysUtilService', ['kibana']);
|
||||
angular.module('ReplaceIndexUtilService', ['kibana']);
|
||||
angular.module('UniqueXValuesUtilService', ['kibana']);
|
||||
angular.module('ZeroFillDataArrayUtilService', ['kibana']);
|
||||
angular.module('ZeroFilledArrayUtilService', ['kibana']);
|
||||
|
||||
describe('Vislib Zero Injection Module Test Suite', function () {
|
||||
var seriesData = {
|
||||
series: [
|
||||
{
|
||||
label: '200',
|
||||
values: [
|
||||
{x: 'v1', y: 234}, {x: 'v2', y: 34}, {x: 'v3', y: 834}, {x: 'v4', y: 1234}, {x: 'v5', y: 4}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var multiSeriesData = {
|
||||
series: [
|
||||
{
|
||||
label: '200',
|
||||
values: [
|
||||
{x: '1', y: 234}, {x: '2', y: 34}, {x: '3', y: 834}, {x: '4', y: 1234}, {x: '5', y: 4}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '404',
|
||||
values: [
|
||||
{x: '1', y: 1234}, {x: '3', y: 234}, {x: '5', y: 34}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '503',
|
||||
values: [
|
||||
{x: '3', y: 834}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var ordered = {};
|
||||
|
||||
describe('Zero Injection (main)', function () {
|
||||
var injectZeros;
|
||||
var sample1;
|
||||
var sample2;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ZeroInjectionUtilService');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (Private) {
|
||||
injectZeros = Private(require('components/vislib/components/_functions/zero_injection/inject_zeros'));
|
||||
sample1 = injectZeros(seriesData);
|
||||
sample2 = injectZeros(multiSeriesData);
|
||||
});
|
||||
});
|
||||
|
||||
it('should be a function', function () {
|
||||
expect(_.isFunction(injectZeros)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an object with series[0].values"', function () {
|
||||
expect(_.isObject(sample1)).to.be(true);
|
||||
expect(_.isObject(sample1.series[0].values)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return the same array of objects when the length of the series array is 1', function () {
|
||||
expect(sample1.series[0].values[0].x).to.be(seriesData.series[0].values[0].x);
|
||||
expect(sample1.series[0].values[1].x).to.be(seriesData.series[0].values[1].x);
|
||||
expect(sample1.series[0].values[2].x).to.be(seriesData.series[0].values[2].x);
|
||||
expect(sample1.series[0].values[3].x).to.be(seriesData.series[0].values[3].x);
|
||||
expect(sample1.series[0].values[4].x).to.be(seriesData.series[0].values[4].x);
|
||||
});
|
||||
|
||||
it('should inject zeros in the input array', function () {
|
||||
expect(sample2.series[1].values[1].y).to.be(0);
|
||||
expect(sample2.series[2].values[0].y).to.be(0);
|
||||
expect(sample2.series[2].values[1].y).to.be(0);
|
||||
expect(sample2.series[2].values[4].y).to.be(0);
|
||||
});
|
||||
|
||||
it('should return values arrays with the same x values', function () {
|
||||
expect(sample2.series[1].values[0].x).to.be(sample2.series[2].values[0].x);
|
||||
expect(sample2.series[1].values[1].x).to.be(sample2.series[2].values[1].x);
|
||||
expect(sample2.series[1].values[2].x).to.be(sample2.series[2].values[2].x);
|
||||
expect(sample2.series[1].values[3].x).to.be(sample2.series[2].values[3].x);
|
||||
expect(sample2.series[1].values[4].x).to.be(sample2.series[2].values[4].x);
|
||||
});
|
||||
|
||||
it('should return values arrays of the same length', function () {
|
||||
expect(sample2.series[0].values.length).to.be(sample2.series[1].values.length);
|
||||
expect(sample2.series[0].values.length).to.be(sample2.series[2].values.length);
|
||||
expect(sample2.series[1].values.length).to.be(sample2.series[2].values.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Order X Values', function () {
|
||||
var orderXValues;
|
||||
var results;
|
||||
|
||||
beforeEach(function () {
|
||||
module('OrderedXKeysUtilService');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (Private) {
|
||||
orderXValues = Private(require('components/vislib/components/_functions/zero_injection/ordered_x_keys'));
|
||||
results = orderXValues(multiSeriesData);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a function', function () {
|
||||
expect(_.isFunction(orderXValues)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array', function () {
|
||||
expect(_.isArray(results)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array of values in the correct order', function () {
|
||||
expect(results[0]).to.be('1');
|
||||
expect(results[1]).to.be('2');
|
||||
expect(results[2]).to.be('3');
|
||||
expect(results[3]).to.be('4');
|
||||
expect(results[4]).to.be('5');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Unique Keys', function () {
|
||||
var uniqueKeys;
|
||||
var results;
|
||||
|
||||
beforeEach(function () {
|
||||
module('UniqueXValuesUtilService');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (Private) {
|
||||
uniqueKeys = Private(require('components/vislib/components/_functions/zero_injection/uniq_keys'));
|
||||
results = uniqueKeys(multiSeriesData.series);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a function', function () {
|
||||
expect(_.isFunction(uniqueKeys)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an object', function () {
|
||||
expect(_.isObject(results)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an object of unique keys', function () {
|
||||
expect(_.uniq(_.keys(results)).length).to.be(_.keys(results).length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Replace Index', function () {
|
||||
var replaceIndex;
|
||||
var arr = [
|
||||
{ x: 1, y: 2},
|
||||
{ x: 2, y: 3},
|
||||
{ x: 3, y: 4}
|
||||
];
|
||||
var index = 1;
|
||||
var obj = { x: 2, y: 5 };
|
||||
var results;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ReplaceIndexUtilService');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (Private) {
|
||||
replaceIndex = Private(require('components/vislib/components/_functions/zero_injection/replace_index'));
|
||||
results = replaceIndex(arr, index, obj);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a function', function () {
|
||||
expect(_.isFunction(replaceIndex)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array', function () {
|
||||
expect(_.isArray(results)).to.be(true);
|
||||
});
|
||||
|
||||
it('should replace the object at the index in the array with the new object', function () {
|
||||
expect(results[1].y).to.be(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Flatten Data', function () {
|
||||
var flattenData;
|
||||
var results;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ReplaceIndexUtilService');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (Private) {
|
||||
flattenData = Private(require('components/vislib/components/_functions/zero_injection/flatten_data'));
|
||||
results = flattenData(multiSeriesData);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a function', function () {
|
||||
expect(_.isFunction(flattenData)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array', function () {
|
||||
expect(_.isArray(results)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array of objects', function () {
|
||||
expect(_.isObject(results[0])).to.be(true);
|
||||
expect(_.isObject(results[1])).to.be(true);
|
||||
expect(_.isObject(results[2])).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Zero Filled Array', function () {
|
||||
var createZeroArray;
|
||||
var arr1 = [1, 2, 3, 4, 5];
|
||||
var arr2 = ['1', '2', '3', '4', '5'];
|
||||
var results1;
|
||||
var results2;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ZeroFilledArrayUtilService');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (Private) {
|
||||
createZeroArray = Private(require('components/vislib/components/_functions/zero_injection/zero_filled_array'));
|
||||
results1 = createZeroArray(arr1);
|
||||
results2 = createZeroArray(arr2);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a function', function () {
|
||||
expect(_.isFunction(createZeroArray)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array', function () {
|
||||
expect(_.isArray(results1)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array of objects', function () {
|
||||
expect(_.isObject(results1[0])).to.be(true);
|
||||
expect(_.isObject(results1[1])).to.be(true);
|
||||
expect(_.isObject(results1[2])).to.be(true);
|
||||
expect(_.isObject(results1[3])).to.be(true);
|
||||
expect(_.isObject(results1[4])).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array of objects where each y value is 0', function () {
|
||||
expect(results1[0].y).to.be(0);
|
||||
expect(results1[1].y).to.be(0);
|
||||
expect(results1[2].y).to.be(0);
|
||||
expect(results1[3].y).to.be(0);
|
||||
expect(results1[4].y).to.be(0);
|
||||
});
|
||||
|
||||
it('should return an array of objects where each x values are numbers', function () {
|
||||
expect(_.isNumber(results1[0].x)).to.be(true);
|
||||
expect(_.isNumber(results1[1].x)).to.be(true);
|
||||
expect(_.isNumber(results1[2].x)).to.be(true);
|
||||
expect(_.isNumber(results1[3].x)).to.be(true);
|
||||
expect(_.isNumber(results1[4].x)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array of objects where each x values are strings', function () {
|
||||
expect(_.isString(results2[0].x)).to.be(true);
|
||||
expect(_.isString(results2[1].x)).to.be(true);
|
||||
expect(_.isString(results2[2].x)).to.be(true);
|
||||
expect(_.isString(results2[3].x)).to.be(true);
|
||||
expect(_.isString(results2[4].x)).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Zero Filled Data Array', function () {
|
||||
var zeroFillArray;
|
||||
var xValueArr = [1, 2, 3, 4, 5];
|
||||
var createZeroArray;
|
||||
var arr1;
|
||||
var arr2 = multiSeriesData.series[2].values;
|
||||
var results;
|
||||
|
||||
beforeEach(function () {
|
||||
module('ZeroFillDataArrayUtilService');
|
||||
});
|
||||
|
||||
beforeEach(function () {
|
||||
inject(function (Private) {
|
||||
zeroFillArray = Private(require('components/vislib/components/_functions/zero_injection/zero_fill_data_array'));
|
||||
createZeroArray = Private(require('components/vislib/components/_functions/zero_injection/zero_filled_array'));
|
||||
arr1 = createZeroArray(xValueArr);
|
||||
// Takes zero array as 1st arg and data array as 2nd arg
|
||||
results = zeroFillArray(arr1, arr2);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a function', function () {
|
||||
expect(_.isFunction(zeroFillArray)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array', function () {
|
||||
expect(_.isArray(results)).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array of objects', function () {
|
||||
expect(_.isObject(results[0])).to.be(true);
|
||||
expect(_.isObject(results[1])).to.be(true);
|
||||
expect(_.isObject(results[2])).to.be(true);
|
||||
});
|
||||
|
||||
it('should return an array with zeros injected in the appropriate objects as y values', function () {
|
||||
expect(results[0].y).to.be(0);
|
||||
expect(results[1].y).to.be(0);
|
||||
expect(results[2].y).to.be(0);
|
||||
expect(results[4].y).to.be(0);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue