Merge branch 'vislib/refactor'

This commit is contained in:
Spencer Alger 2014-09-15 10:27:15 -07:00
commit 305efe8647
93 changed files with 15727 additions and 4198 deletions

View file

@ -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=

View file

@ -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)**

View file

@ -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",

View 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;
}

View file

@ -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: [

View 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;
}

View 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;
}

View file

@ -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;

View 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;
}

View 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;
}

View 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;
}

View 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();
};
});

View file

@ -9,7 +9,8 @@ define(function (require) {
vislibParams: {
shareYAxis: true,
addTooltip: true,
addLegend: true
addLegend: true,
addBrushing: true
},
schemas: new Schemas([
{

View file

@ -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];
};
};
};
});

View file

@ -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);
};
};
});

View file

@ -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;
};
};
});

View file

@ -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'
];
};
});

View file

@ -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;
};
};
});

View file

@ -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();
};
};
});

View file

@ -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));
};
};
});

View file

@ -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'));
};
};
});

View file

@ -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');
};
};
});

View file

@ -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;
};
};
});

View file

@ -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();
};
};
});

View file

@ -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;
};
};
});

View file

@ -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;
};
};
});

View file

@ -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;
};
};
});

View file

@ -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;
};
};
});

View file

@ -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);
}
});
};
};
});

View file

@ -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();
});
};
};
});

View file

@ -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');
});
};
};
});

View file

@ -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');
});
};
};
});

View file

@ -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'
}
]
}
];
};
};
});

View 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;
}

View file

@ -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;
};
});

View file

@ -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'))
};
});
});

View 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'))
};
};
});

View 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;
};
});

View 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;
};
});

View 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;
};
});

View 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;
};
});

View 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;
};
});

View 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;
};
});

View 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;
};
});

View 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;
};
});

View 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;
};
});

View 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;
};
});

View 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;
};
});

View file

@ -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

View file

@ -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>&nbsp;');
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);
};
});

View file

@ -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;
};
});

View file

@ -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;
};
});

View file

@ -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;
}

View file

@ -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;
};
});

View file

@ -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);
};
});

View file

@ -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;
};
});

View 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;
};
});

View file

@ -0,0 +1,8 @@
define(function (require) {
return function VisTypeFactory(Private) {
// visLib visualization types
return {
histogram: Private(require('components/vislib/visualizations/column_chart'))
};
};
});

View 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;
};
});

View 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;
};
});

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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;
}
});

View 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
);
})
};
});

View file

@ -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,

View 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'
}
]
}
}
};

View file

@ -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']
},

View file

@ -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', [

View file

@ -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 = [];

View file

@ -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;
}
});
});
}

View 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);
});
});
});

View 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();
});
});
});
});
});
});

View 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);
});
});
});

View 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();
});
});
});
});

View 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);
});
});
});
});

View 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);
});
});
});
});

View 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]);
});
});
});
});

View 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();
});
});
});

View 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();
});
});
});

View 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);
});
});
});
});

View 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!');
});
});
});
});

View 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);
});
});
});

View 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);
});
});
});
});

View 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);
});
});
});
});

View 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');
});
});
});

View 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);
});
});
});
});

View 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);
});
});
});
});
});

View 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');
});
});
});
});

View 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');
});
});
});

View 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);
});
});
});
});

View 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);
});
});
});
});

View 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);
});
});
});
});