Commit 9f3f7a4f by Rashid Khan

Refactored dashboard state/loading into service, css tweaks

parent 4f1b33b3
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
\ No newline at end of file
.jvectormap-label {
position: absolute;
display: none;
border: solid 1px #CDCDCD;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
background: #292929;
color: white;
font-family: sans-serif, Verdana;
font-size: smaller;
padding: 3px;
}
.jvectormap-zoomin, .jvectormap-zoomout {
position: absolute;
left: 10px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
background: #292929;
padding: 3px;
color: white;
width: 10px;
height: 10px;
cursor: pointer;
line-height: 10px;
text-align: center;
}
.jvectormap-zoomin {
display: none;
top: 10px;
}
.jvectormap-zoomout {
display: none;
top: 30px;
}
\ No newline at end of file
......@@ -6,6 +6,12 @@
color: #000;
}
.spy {
position:absolute;
right:0px;
top:0px;
}
.navbar .brand {
color: #eee;
}
......
{
"title": "Logstash Search",
"services": {},
"rows": [
{
"title": "Options",
......
......@@ -6,18 +6,17 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="description" content="Search based application built using ElasticSearch, elastic.js, and Angular.js">
<meta name="viewport" content="width=device-width">
<title>Kibana 3</title>
<link rel="stylesheet" href="common/css/normalize.min.css">
<link rel="stylesheet" href="common/css/bootstrap.dark.min.css">
<link rel="stylesheet" href="common/css/bootstrap.dark.min.css" title="Dark">
<link rel="alternate stylesheet" href="common/css/bootstrap.light.min.css" title="Light">
<link rel="stylesheet" href="common/css/animate.min.css">
<link rel="stylesheet" href="common/css/bootstrap-responsive.min.css">
<link rel="stylesheet" href="common/css/font-awesome.min.css">
<link rel="stylesheet" href="common/css/main.css">
<link rel="stylesheet" href="common/css/elasticjs.css">
<link rel="stylesheet" href="common/css/timepicker.css">
<!-- project dependency libs -->
......@@ -39,8 +38,8 @@
<div class="navbar-inner">
<div class="container-fluid">
<p class="navbar-text pull-right"><small><strong>Kibana 3</strong> <small>milestone 2</small></small></p>
<span class="brand">{{dashboards.title}}</span>
<div class="brand"><i class='icon-cog pointer' ng-show='dashboards.editable' bs-modal="'partials/dasheditor.html'"></i></div>
<span class="brand">{{dashboard.current.title}}</span>
<div class="brand"><i class='icon-cog pointer' ng-show='dashboard.current.editable' bs-modal="'partials/dasheditor.html'"></i></div>
</div>
</div>
</div>
......
......@@ -19,7 +19,6 @@ var scripts = []
var labjs = $LAB
.script("common/lib/jquery-1.8.0.min.js").wait()
.script("common/lib/modernizr-2.6.1.min.js")
.script("common/lib/underscore.min.js")
.script("common/lib/angular.min.js").wait()
.script("common/lib/angular-strap.min.js")
.script("common/lib/angular-sanitize.min.js")
......@@ -47,10 +46,10 @@ labjs.wait(function(){
angular.module('kibana', modules).config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/dashboard', {
templateUrl: 'partials/dashboard.html'
templateUrl: 'partials/dashboard.html',
})
.when('/dashboard/:type/:id', {
templateUrl: 'partials/dashboard.html'
templateUrl: 'partials/dashboard.html',
})
.when('/dashboard/:type/:id/:params', {
templateUrl: 'partials/dashboard.html'
......
......@@ -3,7 +3,7 @@
'use strict';
angular.module('kibana.controllers', [])
.controller('DashCtrl', function($scope, $rootScope, $http, $timeout, ejsResource, eventBus, fields) {
.controller('DashCtrl', function($scope, $rootScope, $http, $timeout, $route, ejsResource, eventBus, fields, dashboard) {
var _d = {
title: "",
......@@ -18,28 +18,18 @@ angular.module('kibana.controllers', [])
// Make underscore.js available to views
$scope._ = _;
$scope.dashboard = dashboard;
// Provide a global list of all see fields
$scope.fields = fields
$scope.reset_row();
$scope.clear_all_alerts();
// Load dashboard by event
eventBus.register($scope,'dashboard', function(event,dashboard){
$scope.dashboards = dashboard.dashboard;
$scope.dashboards.last = dashboard.last;
_.defaults($scope.dashboards,_d)
})
// If the route changes, clear the existing dashboard
$rootScope.$on( "$routeChangeStart", function(event, next, current) {
delete $scope.dashboards
});
var ejs = $scope.ejs = ejsResource(config.elasticsearch);
}
$scope.add_row = function(dashboards,row) {
$scope.dashboards.rows.push(row);
$scope.add_row = function(dash,row) {
dash.rows.push(row);
}
$scope.reset_row = function() {
......
......@@ -49,7 +49,8 @@ angular.module('kibana.directives', [])
}
};
})
.directive('upload', function(timer){
// Is this even used anymore? I don't think is is
.directive('upload', function(timer,dashboard){
return {
restrict: 'A',
link: function(scope, elem, attrs) {
......@@ -62,9 +63,7 @@ angular.module('kibana.directives', [])
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
// Render thumbnail.
scope.dashboards = JSON.parse(e.target.result)
timer.cancel_all();
dashboard.dash_load(JSON.parse(e.target.result))
scope.$apply();
};
})(f);
......
......@@ -52,7 +52,7 @@ angular.module('kibana.services', [])
if(!(_.isArray(_group)))
_group = [_group];
// Transmit even only if the send is not the receiver AND one of the following:
// Transmit event only if the sender is not the receiver AND one of the following:
// 1) Receiver has group in _to 2) Receiver's $id is in _to
// 3) Event is addressed to ALL 4) Receiver is in ALL group
if((_.intersection(_to,_group).length > 0 ||
......@@ -66,10 +66,10 @@ angular.module('kibana.services', [])
}
});
}
})
/* Service: fields
Provides a global list of all seen fields for use in editor panels
/*
Service: fields
Provides a global list of all seen fields for use in editor panels
*/
.factory('fields', function($rootScope) {
var fields = {
......@@ -119,7 +119,7 @@ angular.module('kibana.services', [])
}
// this is stupid, but there is otherwise no good way to ensure that when
// I extract the date from an object that I'm get the UTC date. Stupid js.
// I extract the date from an object that I get the UTC date. Stupid js.
// I die a little inside every time I call this function.
// Update: I just read this again. I died a little more inside.
// Update2: More death.
......@@ -186,17 +186,264 @@ angular.module('kibana.services', [])
}
})
.service('keylistener', function($rootScope) {
var keys = [];
$(document).keydown(function (e) {
keys[e.which] = true;
.service('query', function() {
})
.service('dashboard', function($routeParams, $http, $rootScope, ejsResource, timer) {
// A hash of defaults to use when loading a dashboard
var _dash = {
title: "",
editable: true,
rows: [],
services: {}
};
// An elasticJS client to use
var ejs = ejsResource(config.elasticsearch);
var gist_pattern = /(^\d{5,}$)|(^[a-z0-9]{10,}$)|(gist.github.com(\/*.*)\/[a-z0-9]{5,}\/*$)/;
// Empty dashboard object
this.current = {};
this.last = {};
// Store a reference to this
var self = this;
$rootScope.$on('$routeChangeSuccess',function(){
route();
})
var route = function() {
// Is there a dashboard type and id in the URL?
if(!(_.isUndefined($routeParams.type)) && !(_.isUndefined($routeParams.id))) {
var _type = $routeParams.type;
var _id = $routeParams.id;
if(_type === 'elasticsearch')
self.elasticsearch_load('dashboard',_id)
if(_type === 'temp')
self.elasticsearch_load('temp',_id)
if(_type === 'file')
self.file_load(_id)
// No dashboard in the URL
} else {
// Check if browser supports localstorage, and if there's a dashboard
if (Modernizr.localstorage &&
!(_.isUndefined(localStorage['dashboard'])) &&
localStorage['dashboard'] !== ''
) {
var dashboard = JSON.parse(localStorage['dashboard']);
_.defaults(dashboard,_dash);
self.dash_load(dashboard)
// No? Ok, grab default.json, its all we have now
} else {
self.file_load('default')
}
}
}
this.to_file = function() {
var blob = new Blob([angular.toJson(self.current,true)], {type: "application/json;charset=utf-8"});
// from filesaver.js
saveAs(blob, self.current.title+"-"+new Date().getTime());
return true;
}
this.set_default = function(dashboard) {
if (Modernizr.localstorage) {
localStorage['dashboard'] = angular.toJson(dashboard || self.current);
return true;
} else {
return false;
}
}
this.purge_default = function() {
if (Modernizr.localstorage) {
localStorage['dashboard'] = '';
return true;
} else {
return false;
}
}
// TOFIX: Pretty sure this breaks when you're on a saved dashboard already
this.share_link = function(title,type,id) {
return {
location : location.href.replace(location.hash,""),
type : type,
id : id,
link : location.href.replace(location.hash,"")+"#dashboard/"+type+"/"+id,
title : title
};
}
this.file_load = function(file) {
return $http({
url: "dashboards/"+file,
method: "GET",
}).then(function(result) {
var _dashboard = result.data
_.defaults(_dashboard,_dash);
self.dash_load(_dashboard);
return true;
},function(result) {
return false;
});
}
$(document).keyup(function (e) {
delete keys[e.which];
this.elasticsearch_load = function(type,id) {
var request = ejs.Request().indices(config.kibana_index).types(type);
var results = request.query(
ejs.IdsQuery(id)
).doSearch();
return results.then(function(results) {
if(_.isUndefined(results)) {
return false;
} else {
self.dash_load(angular.fromJson(results.hits.hits[0]['_source']['dashboard']))
return true;
}
});
}
this.elasticsearch_save = function(type,title,ttl) {
// Clone object so we can modify it without influencing the existing obejct
var save = _.clone(self.current)
this.keyActive = function(key) {
return keys[key] == true;
// Change title on object clone
if (type === 'dashboard') {
var id = save.title = _.isUndefined(title) ? self.current.title : title;
}
// Create request with id as title. Rethink this.
var request = ejs.Document(config.kibana_index,type,id).source({
user: 'guest',
group: 'guest',
title: save.title,
dashboard: angular.toJson(save)
})
if (type === 'temp')
request = request.ttl(ttl)
// TOFIX: Implement error handling here
return request.doIndex(
// Success
function(result) {
return result;
},
// Failure
function(result) {
return false;
}
);
}
this.elasticsearch_delete = function(id) {
return ejs.Document(config.kibana_index,'dashboard',id).doDelete(
// Success
function(result) {
return result;
},
// Failure
function(result) {
return false;
}
);
}
this.elasticsearch_list = function(query,count) {
var request = ejs.Request().indices(config.kibana_index).types('dashboard');
return request.query(
ejs.QueryStringQuery(query || '*')
).size(count).doSearch(
// Success
function(result) {
return result;
},
// Failure
function(result) {
return false;
}
);
}
// TOFIX: Gist functionality
this.save_gist = function(title,dashboard) {
var save = _.clone(dashboard || self.current)
save.title = title || self.current.title;
return $http({
url: "https://api.github.com/gists",
method: "POST",
data: {
"description": save.title,
"public": false,
"files": {
"kibana-dashboard.json": {
"content": angular.toJson(save,true)
}
}
}
}).then(function(data, status, headers, config) {
return data.data.html_url;
}, function(data, status, headers, config) {
return false;
});
}
this.gist_list = function(id) {
return $http.jsonp("https://api.github.com/gists/"+id+"?callback=JSON_CALLBACK"
).then(function(response) {
var files = []
_.each(response.data.data.files,function(v,k) {
try {
var file = JSON.parse(v.content)
files.push(file)
} catch(e) {
// Nothing?
}
});
return files;
}, function(data, status, headers, config) {
return false;
});
}
this.dash_load = function(dashboard) {
self.current = dashboard;
timer.cancel_all();
return true;
}
this.gist_id = function(string) {
if(self.is_gist(string))
return string.match(gist_pattern)[0].replace(/.*\//, '');
}
this.is_gist = function(string) {
if(!_.isUndefined(string) && string != '' && !_.isNull(string.match(gist_pattern)))
return string.match(gist_pattern).length > 0 ? true : false;
else
return false
}
})
.service('keylistener', function($rootScope) {
var keys = [];
$(document).keydown(function (e) {
keys[e.which] = true;
});
$(document).keyup(function (e) {
delete keys[e.which];
});
this.keyActive = function(key) {
return keys[key] == true;
}
});
......@@ -11,12 +11,12 @@
<h5>Gist <small>Enter a gist number or url</small></h5>
<form>
<input type="text" ng-model="gist.url"/><br>
<button class="btn" ng-click="gist_dblist(gist_id(gist.url))" ng-show="is_gist(gist.url)"><i class="icon-github-alt"></i> Get gist:{{gist.url | gistid}}</button>
<button class="btn" ng-click="gist_dblist(dashboard.gist_id(gist.url))" ng-show="dashboard.is_gist(gist.url)"><i class="icon-github-alt"></i> Get gist:{{gist.url | gistid}}</button>
<h6 ng-show="gist.files.length">Dashboards in gist:{{gist.url | gistid}} <small>click to load</small></h6>
<h6 ng-hide="gist.files.length">No gist dashboards found</h6>
<table class="table table-condensed table-striped">
<tr ng-repeat="file in gist.files">
<td><a ng-click="dash_load(file)">{{file.title}}</a></td>
<td><a ng-click="dashboard.dash_load(file)">{{file.title}}</a></td>
</tr>
</table>
</form>
......@@ -30,10 +30,10 @@
<h6 ng-show="elasticsearch.dashboards.length">Elasticsearch stored dashboards</h6>
<h6 ng-hide="elasticsearch.dashboards.length">No dashboards matching your query found</h6>
<table class="table table-condensed table-striped">
<tr ng-repeat="dashboard in elasticsearch.dashboards">
<td><a ng-click="elasticsearch_delete(dashboard)"><i class="icon-remove"></i></a></td>
<td><a href="#/dashboard/elasticsearch/{{dashboard._id}}">{{dashboard._id}}</a></td>
<td><a><i class="icon-share" ng-click="share_link(dashboard._id,'elasticsearch',dashboard._id)" bs-modal="'panels/dashcontrol/share.html'"></i></a></td>
<tr ng-repeat="row in elasticsearch.dashboards">
<td><a ng-click="elasticsearch_delete(row._id)"><i class="icon-remove"></i></a></td>
<td><a href="#/dashboard/elasticsearch/{{row._id}}">{{row._id}}</a></td>
<td><a><i class="icon-share" ng-click="share = dashboard.share_link(row._id,'elasticsearch',row._id)" bs-modal="'panels/dashcontrol/share.html'"></i></a></td>
</tr>
</table>
</div>
......
......@@ -2,5 +2,5 @@
<label class='small'>Dashboard Control</label>
<button class='btn' ng-show="panel.load.gist || panel.load.elasticsearch || panel.load.local" data-placement="bottom" data-unique="1" ng-click="elasticsearch_dblist(elasticsearch.query)" bs-popover="'panels/dashcontrol/load.html'"><i class='icon-folder-open'></i> <i class='icon-caret-down'></i></button>
<button class='btn' ng-show="panel.save.gist || panel.save.elasticsearch || panel.save.local || panel.save.default" data-placement="bottom" data-unique="1" bs-popover="'panels/dashcontrol/save.html'"><i class='icon-save'></i> <i class='icon-caret-down'></i></button>
<button ng-show="panel.temp" class='btn' ng-click="elasticsearch_save('temp')" bs-modal="'panels/dashcontrol/share.html'"><i class='icon-share'></i></button>
<button ng-show="panel.temp" class='btn' ng-click="elasticsearch_save('temp',panel.temp_ttl)" bs-modal="'panels/dashcontrol/share.html'"><i class='icon-share'></i></button>
</kibana-panel>
\ No newline at end of file
......@@ -6,9 +6,9 @@
<h5>Locally</h5>
<form>
<ul class="nav nav-list">
<li><a ng-show="panel.save.local" ng-click="to_file()"><i class="icon-download"></i> Export to File</a></li>
<li><a ng-show="panel.save.default" ng-click="default()"><i class="icon-bookmark"></i> Set as My Default</a></li>
<li><a ng-show="panel.save.default" ng-click="purge()"><i class="icon-ban-circle"></i> Clear My Default</a></li>
<li><a ng-show="panel.save.local" ng-click="dashboard.to_file()"><i class="icon-download"></i> Export to File</a></li>
<li><a ng-show="panel.save.default" ng-click="set_default()"><i class="icon-bookmark"></i> Set as My Default</a></li>
<li><a ng-show="panel.save.default" ng-click="purge_default()"><i class="icon-ban-circle"></i> Clear My Default</a></li>
</ul>
</form>
</div>
......
......@@ -4,7 +4,7 @@
</div>
<div class="modal-body">
<label>Share this dashboard with this URL</label>
<input ng-model='share.link' type="text" style="width:90%" onclick="this.select()" onfocus="this.select()" ng-change="share_link(share.title,share.type,share.id)">
<input ng-model='share.link' type="text" style="width:90%" onclick="this.select()" onfocus="this.select()" ng-change="share = dashboard.share_link(share.title,share.type,share.id)">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" ng-click="dismiss();$broadcast('render')">Close</button>
......
<kibana-panel ng-controller='histogram' ng-init="init()" style="height:{{panel.height || row.height}}">
<span ng-show="panel.spyable" style="position:absolute;right:0px;top:0px" class='panelextra pointer'>
<style>
.histogram-legend {
display:inline-block;
padding-right:5px
}
.histogram-legend-dot {
display:inline-block;
height:10px;
width:10px;
border-radius:5px;
}
.histogram-legend-item {
display:inline-block;
}
.histogram-chart {
position:relative;
}
</style>
<span ng-show="panel.spyable" class='spy panelextra pointer'>
<i bs-modal="'partials/modal.html'" class="icon-eye-open"></i>
</span>
<div>
......@@ -7,12 +25,12 @@
<a class='small' ng-click='zoom(0.5)'><i class='icon-zoom-in'></i> Zoom In</a>
<a class='small' ng-click='zoom(2)'><i class='icon-zoom-out'></i> Zoom Out</a> |
</span>
<span ng-show="panel.legend" ng-repeat='series in plot.getData()' style='display:inline-block;padding-right:5px'>
<div style="display:inline-block;background:{{series.color}};height:10px;width:10px;border-radius:5px;"></div>
<div class='small' style='display:inline-block'>{{series.label}} ({{series.hits}})</div>
<span ng-show="panel.legend" ng-repeat='series in plot.getData()' class="histogram-legend">
<div class="histogram-legend-dot" style="background:{{series.color}};"></div>
<div class='small histogram-legend-item'>{{series.label}} ({{series.hits}})</div>
</span>
<span ng-show="panel.legend" class="small"><span ng-show="panel.value_field && panel.mode != 'count'">{{panel.value_field}}</span> {{panel.mode}} per <strong>{{panel.interval}}</strong> | (<strong>{{hits}}</strong> hits)</span>
</div>
<center><img ng-show='panel.loading && _.isUndefined(data)' src="common/img/load_big.gif"></center>
<div histogram-chart params="{{panel}}" style="height:{{panel.height || row.height}};position:relative"></div>
<div histogram-chart class="histogram-chart" params="{{panel}}" style="height:{{panel.height || row.height}};"></div>
</kibana-panel>
\ No newline at end of file
<kibana-panel ng-controller='map' ng-init="init()">
<span ng-show="panel.spyable" style="position:absolute;right:0px;top:0px" class='panelextra pointer'>
<style>
.jvectormap-label {
position: absolute;
display: none;
border: solid 1px #CDCDCD;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
background: #292929;
color: white;
font-family: sans-serif, Verdana;
font-size: smaller;
padding: 3px;
}
.jvectormap-zoomin, .jvectormap-zoomout {
position: absolute;
left: 10px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
background: #292929;
padding: 3px;
color: white;
width: 10px;
height: 10px;
cursor: pointer;
line-height: 10px;
text-align: center;
}
.jvectormap-zoomin {
display: none;
top: 10px;
}
.jvectormap-zoomout {
display: none;
top: 30px;
}
</style>
<span ng-show="panel.spyable" class='spy panelextra pointer'>
<i bs-modal="'partials/modal.html'" class="icon-eye-open"></i>
</span>
<div map params="{{panel}}" style="height:{{panel.height || row.height}}"></div>
......
<div class="row-fluid container" style="margin-top:10px">
<!-- Rows -->
<div ng-controller="dashcontrol" ng-init="init()"></div>
<div class="row-fluid kibana-row" ng-controller="RowCtrl" ng-repeat="(row_name, row) in dashboards.rows" ng-style="row_style(row)">
<div class="row-fluid kibana-row" ng-controller="RowCtrl" ng-repeat="(row_name, row) in dashboard.current.rows" ng-style="row_style(row)">
<div class="row-control">
<div class="row-fluid row-header" style="padding:0px;margin:0px;height:0px">
<div style="vertical-align:bottom">
......
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>{{dashboards.title}} <small> editor</small></h3>
<h3>{{dashboard.current.title}} <small> editor</small></h3>
</div>
<div class="modal-body">
<h4>Dashboard Control</h4>
<div class="row-fluid">
<div class="span8">
<label class="small">Title</label><input type="text" class="input-large" ng-model='dashboards.title'></input>
<label class="small">Title</label><input type="text" class="input-large" ng-model='dashboard.current.title'></input>
</div>
<div class="span1">
<label class="small"> Editable </label><input type="checkbox" ng-model="dashboards.editable" ng-checked="dashboards.editable" />
<label class="small"> Editable </label><input type="checkbox" ng-model="dashboard.current.editable" ng-checked="dashboard.current.editable" />
</div>
</div>
<div class="row-fluid">
......@@ -21,11 +21,11 @@
<th>Delete</th>
<th>Move</th>
</thead>
<tr ng-repeat="row in dashboards.rows">
<tr ng-repeat="row in dashboard.current.rows">
<td>{{row.title}}</td>
<td><i ng-click="dashboards.rows = _.without(dashboards.rows,row)" class="pointer icon-remove"></i></td>
<td><i ng-click="_.move(dashboards.rows,$index,$index-1)" ng-hide="$first" class="pointer icon-arrow-up"></i></td>
<td><i ng-click="_.move(dashboards.rows,$index,$index+1)" ng-hide="$last" class="pointer icon-arrow-down"></i></td>
<td><i ng-click="dashboard.current.rows = _.without(dashboard.current.rows,row)" class="pointer icon-remove"></i></td>
<td><i ng-click="_.move(dashboard.current.rows,$index,$index-1)" ng-hide="$first" class="pointer icon-arrow-up"></i></td>
<td><i ng-click="_.move(dashboard.current.rows,$index,$index+1)" ng-hide="$last" class="pointer icon-arrow-down"></i></td>
</tr>
</table>
</div>
......@@ -45,7 +45,7 @@
<input type="checkbox" ng-model="row.editable" ng-checked="row.editable" />
</div>
</div>
<button ng-click="add_row(dashboards,row); reset_row();" class="btn btn-primary">Create Row</button><br>
<button ng-click="add_row(dashboard.current,row); reset_row();" class="btn btn-primary">Create Row</button><br>
</div>
</div>
<div class="modal-footer">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment