Commit d5bc550b by Rashid Khan

New timepicker, collapsable query and filter sections

parent 377aa16f
define(['jquery', 'underscore'],
function($, _) {
define(['jquery', 'underscore','moment'],
function($, _, moment) {
'use strict';
var kbn = {};
......@@ -247,6 +247,139 @@ function($, _) {
return new Date(new Date().getTime() - (kbn.interval_to_ms(string)));
};
/* This is a simplified version of elasticsearch's date parser */
kbn.parseDate = function(text) {
if(_.isDate(text)) {
return text;
}
var time,
mathString = "",
index,
parseString;
if (text.substring(0,3) === "now") {
time = new Date();
mathString = text.substring("now".length);
} else {
index = text.indexOf("||");
parseString;
if (index === -1) {
parseString = text;
mathString = ""; // nothing else
} else {
parseString = text.substring(0, index);
mathString = text.substring(index + 2);
}
// We're going to just require ISO8601 timestamps, k?
time = new Date(parseString);
}
if (!mathString.length) {
return time;
}
//return [time,parseString,mathString];
return kbn.parseDateMath(mathString, time);
};
kbn.parseDateMath = function(mathString, time, roundUp) {
var dateTime = moment(time);
for (var i = 0; i < mathString.length; ) {
var c = mathString.charAt(i++),
type,
num,
unit;
if (c === '/') {
type = 0;
} else if (c === '+') {
type = 1;
} else if (c === '-') {
type = 2;
} else {
console.log("no type");
return false;
}
if (isNaN(mathString.charAt(i))) {
num = 1;
} else {
var numFrom = i;
while (!isNaN(mathString.charAt(i))) {
i++;
}
num = parseInt(mathString.substring(numFrom, i),10);
}
if (type === 0) {
// rounding is only allowed on whole numbers
if (num !== 1) {
console.log("nbad rounding");
return false;
}
}
unit = mathString.charAt(i++);
switch (unit) {
case 'M':
if (type === 0) {
roundUp ? dateTime.endOf('month') : dateTime.startOf('month');
} else if (type === 1) {
dateTime.add('months',num);
} else if (type === 2) {
dateTime.subtract('months',num);
}
break;
case 'w':
if (type === 0) {
roundUp ? dateTime.endOf('week') : dateTime.startOf('week');
} else if (type === 1) {
dateTime.add('weeks',num);
} else if (type === 2) {
dateTime.subtract('weeks',num);
}
break;
case 'd':
if (type === 0) {
roundUp ? dateTime.endOf('day') : dateTime.startOf('day');
} else if (type === 1) {
dateTime.add('days',num);
} else if (type === 2) {
dateTime.subtract('days',num);
}
break;
case 'h':
case 'H':
if (type === 0) {
roundUp ? dateTime.endOf('hour') : dateTime.startOf('hour');
} else if (type === 1) {
dateTime.add('hours',num);
} else if (type === 2) {
dateTime.subtract('hours',num);
}
break;
case 'm':
if (type === 0) {
roundUp ? dateTime.endOf('minute') : dateTime.startOf('minute');
} else if (type === 1) {
dateTime.add('minutes',num);
} else if (type === 2) {
dateTime.subtract('minutes',num);
}
break;
case 's':
if (type === 0) {
roundUp ? dateTime.endOf('second') : dateTime.startOf('second');
} else if (type === 1) {
dateTime.add('seconds',num);
} else if (type === 2) {
dateTime.subtract('seconds',num);
}
break;
default:
console.log("unknown unit");
return false;
}
}
return dateTime.toDate();
};
// LOL. hahahahaha. DIE.
kbn.flatten_json = function(object,root,array) {
if (typeof array === 'undefined') {
......
define([
'./dash',
'./dashLoader',
'./row',
'./row'
], function () {});
\ No newline at end of file
......@@ -16,6 +16,7 @@ function (angular, app, _) {
collapsable: true,
editable: true,
panels: [],
notice: false
};
_.defaults($scope.row,_d);
......@@ -34,6 +35,8 @@ function (angular, app, _) {
$timeout(function() {
$scope.$broadcast('render');
});
} else {
row.notice = false;
}
};
......
......@@ -3,6 +3,7 @@ define([
'./arrayJoin',
'./dashUpload',
'./kibanaPanel',
'./kibanaSimplePanel',
'./ngBlur',
'./ngModelOnBlur',
'./tip',
......
define([
'angular'
],
function (angular) {
'use strict';
angular
.module('kibana.directives')
.directive('kibanaSimplePanel', function($compile) {
return {
restrict: 'E',
link: function($scope, elem, attr) {
// once we have the template, scan it for controllers and
// load the module.js if we have any
// compile the module and uncloack. We're done
function loadModule($module) {
$module.appendTo(elem);
/* jshint indent:false */
$compile(elem.contents())($scope);
elem.removeClass("ng-cloak");
}
$scope.$watch(attr.type, function (name) {
elem.addClass("ng-cloak");
// load the panels module file, then render it in the dom.
$scope.require([
'jquery',
'text!panels/'+name+'/module.html'
], function ($, moduleTemplate) {
var $module = $(moduleTemplate);
// top level controllers
var $controllers = $module.filter('ngcontroller, [ng-controller], .ng-controller');
// add child controllers
$controllers = $controllers.add($module.find('ngcontroller, [ng-controller], .ng-controller'));
if ($controllers.length) {
$scope.require([
'panels/'+name+'/module'
], function() {
loadModule($module);
});
} else {
loadModule($module);
}
});
});
}
};
});
});
\ No newline at end of file
define(['angular', 'jquery', 'underscore'], function (angular, $, _) {
define(['angular', 'jquery', 'underscore', 'moment'], function (angular, $, _, moment) {
'use strict';
var module = angular.module('kibana.filters');
......@@ -42,6 +42,16 @@ define(['angular', 'jquery', 'underscore'], function (angular, $, _) {
};
});
module.filter('moment', function() {
return function(date,mode) {
switch(mode) {
case 'ago':
return moment(date).fromNow();
}
return moment(date).fromNow();
};
});
module.filter('noXml', function() {
var noXml = function(text) {
return _.isString(text)
......
......@@ -55,7 +55,7 @@
<i class="filter-action pointer icon-remove" bs-tooltip="'Remove'" ng-click="remove(id)"></i>
<i class="filter-action pointer" ng-class="{'icon-check': filterSrv.list[id].active,'icon-check-empty': !filterSrv.list[id].active}" bs-tooltip="'Toggle'" ng-click="toggle(id)"></i>
<i class="filter-action pointer icon-edit" ng-hide="filterSrv.list[id].editing" bs-tooltip="'Edit'" ng-click="filterSrv.list[id].editing = true"></i>
<i class="filter-action pointer icon-edit" ng-hide="filterSrv.list[id].editing || !isEditable(filterSrv.list[id])" bs-tooltip="'Edit'" ng-click="filterSrv.list[id].editing = true"></i>
</div>
......
......@@ -27,6 +27,10 @@ function (angular, app, _) {
};
_.defaults($scope.panel,_d);
$scope.$on('filter', function() {
$scope.row.notice = true;
});
$scope.init = function() {
$scope.filterSrv = filterSrv;
};
......
......@@ -137,7 +137,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
* @return {[type]} [description]
*/
$scope.get_time_range = function () {
var range = $scope.range = filterSrv.timeRange('min');
var range = $scope.range = filterSrv.timeRange('last');
return range;
};
......@@ -289,7 +289,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
// function $scope.zoom
// factor :: Zoom factor, so 0.5 = cuts timespan in half, 2 doubles timespan
$scope.zoom = function(factor) {
var _range = filterSrv.timeRange('min');
var _range = filterSrv.timeRange('last');
var _timespan = (_range.to.valueOf() - _range.from.valueOf());
var _center = _range.to.valueOf() - _timespan/2;
......@@ -496,8 +496,8 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
elem.bind("plotselected", function (event, ranges) {
filterSrv.set({
type : 'time',
from : moment.utc(ranges.xaxis.from),
to : moment.utc(ranges.xaxis.to),
from : moment.utc(ranges.xaxis.from).toDate(),
to : moment.utc(ranges.xaxis.to).toDate(),
field : scope.panel.time_field
});
});
......
<div class="modal-body">
<style>
.timepicker-to-column {
margin-top: 10px;
}
.timepicker-input input {
outline: 0 !important;
border: 0px !important;
-webkit-box-shadow: 0;
-moz-box-shadow: 0;
box-shadow: 0;
position: relative;
}
.timepicker-input input::-webkit-outer-spin-button,
.timepicker-input input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input.timepicker-date {
width: 90px;
}
input.timepicker-hms {
width: 20px;
}
input.timepicker-ms {
width: 25px;
}
div.timepicker-now {
float: right;
}
</style>
<div class="timepicker form-horizontal">
<form name="input">
<div class="timepicker-from-column">
<label class="small">From</label>
<div class="fake-input timepicker-input">
<input class="timepicker-date" type="text" ng-change="validate(temptime)" ng-model="temptime.from.date" data-date-format="mm/dd/yyyy" required bs-datepicker />@
<input class="timepicker-hms" type="text" maxlength="2" ng-change="validate(temptime)" ng-model="temptime.from.hour" required ng-pattern="patterns.hour" onClick="this.select();"/>:
<input class="timepicker-hms" type="text" maxlength="2" ng-change="validate(temptime)" ng-model="temptime.from.minute" required ng-pattern="patterns.minute" onClick="this.select();"/>:
<input class="timepicker-hms" type="text" maxlength="2" ng-change="validate(temptime)" ng-model="temptime.from.second" required ng-pattern="patterns.second" onClick="this.select();"/>.
<input class="timepicker-ms" type="text" maxlength="3" ng-change="validate(temptime)" ng-model="temptime.from.millisecond" required ng-pattern="patterns.millisecond" onClick="this.select();"/>
</div>
</div>
<div class="timepicker-to-column">
<label class="small">To (<a class="link" ng-class="{'strong':panel.now}" ng-click="setNow();panel.now=true">now</a>)</label>
<div class="fake-input timepicker-input">
<div ng-hide="panel.now">
<input class="timepicker-date" type="text" ng-change="validate(temptime)" ng-model="temptime.to.date" data-date-format="mm/dd/yyyy" required bs-datepicker />@
<input class="timepicker-hms" type="text" maxlength="2" ng-change="validate(temptime)" ng-model="temptime.to.hour" required ng-pattern="patterns.hour" onClick="this.select();"/>:
<input class="timepicker-hms" type="text" maxlength="2" ng-change="validate(temptime)" ng-model="temptime.to.minute" required ng-pattern="patterns.minute" onClick="this.select();"/>:
<input class="timepicker-hms" type="text" maxlength="2" ng-change="validate(temptime)" ng-model="temptime.to.second" required ng-pattern="patterns.second" onClick="this.select();"/>.
<input class="timepicker-ms" type="text" maxlength="3" ng-change="validate(temptime)" ng-model="temptime.to.millisecond" required ng-pattern="patterns.millisecond" onClick="this.select();"/>
</div>
<span type="text" ng-show="panel.now" ng-disabled="panel.now">&nbsp <i class="pointer icon-remove-sign" ng-click="setNow();panel.now=false"></i> Right Now <input type="text" name="dummy" style="visibility:hidden" /></span>
</div>
</div>
</form>
<div class="clearfix"></div>
</div>
</div>
<div class="modal-footer">
<form name="input" style="margin-bottom:0">
<span class="" ng-hide="input.$valid">Invalid date or range</span>
<button ng-click="setAbsoluteTimeFilter(validate(temptime));dismiss();" ng-disabled="!input.$valid" class="btn btn-success">Apply</button>
<button ng-click="dismiss();" class="btn btn-danger">Cancel</button>
</form>
</div>
<div class="row-fluid">
<div class="span6">
<label class="small">Relative time options <small>comma seperated</small></label>
<input type="text" array-join class="input-large" ng-model="panel.time_options">
</div>
<div class="span3">
<label class="small">Time Field</label>
<input type="text" class="input-small" ng-model="panel.timefield">
</div>
</div>
<div ng-controller='timepicker2' ng-init="init()">
<style>
.timepicker-timestring {
font-weight: normal;
}
.timepicker-dropdown {
margin: 0px !important;
border: 0px !important;
}
</style>
<!-- This is a complete hack. The form actually exists in the modal, but due to transclusion
$scope.input isn't available on the controller unless the form element is in this file -->
<form name="input" style="margin:3px 0 0 0">
<ul class="nav nav-pills timepicker-dropdown">
<li class="dropdown" bs-tooltip="(time.from.date | date:'yyyy-MM-dd HH:mm:ss.sss') + ' to ' +(time.to.date | date:'yyyy-MM-dd HH:mm:ss.sss')" data-placement="bottom" ng-click="dismiss();">
<a class="dropdown-toggle timepicker-dropdown" data-toggle="dropdown" href="#">
<span class="small" ng-show="filterSrv.idsByType('time').length">
<span class="pointer" ng-hide="panel.now">{{time.from.date | date:'MMM d, y HH:mm:ss'}}</span>
<span class="pointer" ng-show="panel.now">{{time.from.date | moment:'ago'}}</span>
to
<span class="pointer" ng-hide="panel.now" >{{time.to.date | date:'MMM d, y HH:mm:ss'}}</span>
<span class="pointer" ng-show="panel.now">{{time.to.date | moment:'ago'}}</span>
</span>
<span ng-hide="filterSrv.idsByType('time').length">Set a time filter</span>
<i class="small icon-caret-down"></i>
</a>
<ul class="dropdown-menu">
<li ng-repeat='timespan in panel.time_options'>
<a ng-click="setRelativeFilter(timespan)">Last {{timespan}}</a>
</li>
<li><a ng-click="customTime()">Custom</a></li>
</ul>
</li>
</ul>
</form>
</div>
\ No newline at end of file
/*
## Timepicker2
### Parameters
* mode :: The default mode of the panel. Options: 'relative', 'absolute' 'since' Default: 'relative'
* time_options :: An array of possible time options. Default: ['5m','15m','1h','6h','12h','24h','2d','7d','30d']
* timespan :: The default options selected for the relative view. Default: '15m'
* timefield :: The field in which time is stored in the document.
* refresh: Object containing refresh parameters
* enable :: true/false, enable auto refresh by default. Default: false
* interval :: Seconds between auto refresh. Default: 30
* min :: The lowest interval a user may set
*/
define([
'angular',
'app',
'underscore',
'moment',
'kbn'
],
function (angular, app, _, moment, kbn) {
'use strict';
var module = angular.module('kibana.panels.timepicker2', []);
app.useModule(module);
module.controller('timepicker2', function($scope, $modal, $q, filterSrv) {
$scope.panelMeta = {
status : "Stable",
description : "A panel for controlling the time range filters. If you have time based data, "+
" or if you're using time stamped indices, you need one of these"
};
// Set and populate defaults
var _d = {
status : "Stable",
mode : "relative",
time_options : ['5m','15m','1h','6h','12h','24h','2d','7d','30d'],
timespan : '15m',
timefield : '@timestamp',
timeformat : "",
refresh : {
enable : false,
interval: 30,
min : 3
}
};
_.defaults($scope.panel,_d);
var customTimeModal = $modal({
template: './app/panels/timepicker2/custom.html',
persist: true,
show: false,
scope: $scope,
keyboard: false
});
$scope.filterSrv = filterSrv;
// ng-pattern regexs
$scope.patterns = {
date: /^[0-9]{2}\/[0-9]{2}\/[0-9]{4}$/,
hour: /^([01]?[0-9]|2[0-3])$/,
minute: /^[0-5][0-9]$/,
second: /^[0-5][0-9]$/,
millisecond: /^[0-9]*$/
};
$scope.$on('refresh', function(){$scope.init();});
$scope.init = function() {
var time = filterSrv.timeRange('last');
if(time) {
$scope.panel.now = filterSrv.timeRange(false).to === "now" ? true : false;
$scope.time = getScopeTimeObj(time.from,time.to);
}
};
$scope.customTime = function() {
// Assume the form is valid since we're setting it to something valid
$scope.input.$setValidity("dummy", true);
$scope.temptime = cloneTime($scope.time);
// Date picker needs the date to be at the start of the day
$scope.temptime.from.date.setHours(0,0,0,0);
$scope.temptime.to.date.setHours(0,0,0,0);
$q.when(customTimeModal).then(function(modalEl) {
modalEl.modal('show');
});
};
// Constantly validate the input of the fields. This function does not change any date variables
// outside of its own scope
$scope.validate = function(time) {
// Assume the form is valid. There is a hidden dummy input for invalidating it programatically.
$scope.input.$setValidity("dummy", true);
var _from = datepickerToLocal(time.from.date),
_to = datepickerToLocal(time.to.date),
_t = time;
if($scope.input.$valid) {
_from.setHours(_t.from.hour,_t.from.minute,_t.from.second,_t.from.millisecond);
_to.setHours(_t.to.hour,_t.to.minute,_t.to.second,_t.to.millisecond);
// Check that the objects are valid and to is after from
if(isNaN(_from.getTime()) || isNaN(_to.getTime()) || _from.getTime() >= _to.getTime()) {
$scope.input.$setValidity("dummy", false);
return false;
}
} else {
return false;
}
return {from:_from,to:_to};
};
$scope.setNow = function() {
$scope.time.to = getTimeObj(new Date());
};
/*
time : {
from: Date
to: Date
}
*/
$scope.setAbsoluteTimeFilter = function (time) {
// Create filter object
var _filter = _.clone(time);
_filter.type = 'time';
_filter.field = $scope.panel.timefield;
if($scope.panel.now) {
_filter.to = "now";
}
// Clear all time filters, set a new one
filterSrv.removeByType('time',true);
// Set the filter
$scope.panel.filter_id = filterSrv.set(_filter);
// Update our representation
$scope.time = getScopeTimeObj(time.from,time.to);
return $scope.panel.filter_id;
};
$scope.setRelativeFilter = function(timespan) {
$scope.panel.now = true;
// Create filter object
var _filter = {
type : 'time',
field : $scope.panel.timefield,
from : "now-"+timespan,
to: "now"
};
// Clear all time filters, set a new one
filterSrv.removeByType('time',true);
// Set the filter
$scope.panel.filter_id = filterSrv.set(_filter);
// Update our representation
$scope.time = getScopeTimeObj(kbn.parseDate(_filter.from),new Date());
return $scope.panel.filter_id;
};
var pad = function(n, width, z) {
z = z || '0';
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
};
var cloneTime = function(time) {
var _n = {
from: _.clone(time.from),
to: _.clone(time.to)
};
// Create new dates as _.clone is shallow.
_n.from.date = new Date(_n.from.date);
_n.to.date = new Date(_n.to.date);
return _n;
};
var getScopeTimeObj = function(from,to) {
return {
from: getTimeObj(from),
to: getTimeObj(to)
};
};
var getTimeObj = function(date) {
return {
date: new Date(date),
hour: pad(date.getHours(),2),
minute: pad(date.getMinutes(),2),
second: pad(date.getSeconds(),2),
millisecond: pad(date.getMilliseconds(),3)
};
};
// Do not use the results of this function unless you plan to use setHour/Minutes/etc on the result
var datepickerToLocal = function(date) {
console.log(date);
date = moment(date).clone().toDate();
console.log(moment(new Date(date.getTime() + date.getTimezoneOffset() * 60000)).toDate());
return moment(new Date(date.getTime() + date.getTimezoneOffset() * 60000)).toDate();
};
});
});
\ No newline at end of file
<form name="refreshPopover" class='form-inline input-append' style="margin:0px">
<label><small>Interval (seconds)</small></label><br>
<input type="number" class="input-mini" ng-model="refresh_interval">
<button type="button" class="btn" ng-click="set_interval(refresh_interval);dismiss()"><i class="icon-ok"></i></button>
</form>
\ No newline at end of file
......@@ -87,7 +87,8 @@ function (angular, app, _, kbn) {
timeField = timeField[0];
}
$scope.time = filterSrv.timeRange('min');
// This logic can be simplifie greatly with the new kbn.parseDate
$scope.time = filterSrv.timeRange('last');
$scope.old_time = {
from : new Date($scope.time.from.getTime() - kbn.interval_to_ms($scope.panel.ago)),
to : new Date($scope.time.to.getTime() - kbn.interval_to_ms($scope.panel.ago))
......
<li ng-controller="RowCtrl"><kibana-simple-panel type="'timepicker2'" ng-cloak></kibana-simple-panel></li>
<li><a bs-tooltip="'Goto saved default'" data-placement="bottom" href='#/dashboard'><i class='icon-home'></i></a></li>
......
<div class="row-fluid container" style="margin-top:10px; width:98%">
<!-- is there a better way to repeat without actually affecting the page? -->
<nil ng-repeat="row in dashboard.current.pulldowns" ng-controller="RowCtrl">
<div class="top-row-open" ng-hide="row.collapse">
<kibana-simple-panel type="row.type" ng-cloak></kibana-simple-panel>
</div>
<div class="top-row-close pointer" ng-click="toggle_row(row);dismiss();" bs-tooltip="'Toggle '+row.type" data-placement="bottom">
<span class="small row-text">{{row.type}}</span>
<i class="small" ng-class="{'icon-expand':row.collapse,'icon-collapse-top':!row.collapse}"></i>
<i class="small icon-circle text-warning" ng-show="row.notice && row.collapse"></i>
</div>
</nil>
<!-- Rows -->
<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" style="padding:0px;margin:0px;position:relative;">
<div class="container-fluid main">
<div class="row-fluid">
<div class="row-fluid container" style="margin-top:10px; width:98%">
<!-- Rows -->
<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" style="padding:0px;margin:0px;position:relative;">
<div class="row-close span12" ng-show="row.collapse" data-placement="bottom" >
<span class="row-button" bs-modal="'app/partials/roweditor.html'" class="pointer">
<i bs-tooltip="'Configure row'" data-placement="right" ng-show="row.editable" class="icon-cog pointer"></i>
</span>
<span class="row-button" ng-click="toggle_row(row)" ng-show="row.collapsable">
<i bs-tooltip="'Expand row'" data-placement="right" ng-show="row.editable" class="icon-expand pointer" ></i>
</span>
<span class="row-button row-text" ng-click="toggle_row(row)" ng-class="{'pointer':row.collapsable}">{{row.title || 'Row '+$index}}</span>
</div>
<div class="row-close span12" ng-show="row.collapse" data-placement="bottom" >
<span class="row-button" bs-modal="'app/partials/roweditor.html'" class="pointer">
<i bs-tooltip="'Configure row'" data-placement="right" ng-show="row.editable" class="icon-cog pointer"></i>
</span>
<span class="row-button" ng-click="toggle_row(row)" ng-show="row.collapsable">
<i bs-tooltip="'Expand row'" data-placement="right" ng-show="row.editable" class="icon-expand pointer" ></i>
</span>
<span class="row-button row-text" ng-click="toggle_row(row)" ng-class="{'pointer':row.collapsable}">{{row.title || 'Row '+$index}}</span>
</div>
<div style="text-align:left" class="row-open" ng-show="!row.collapse">
<span ng-show="row.collapsable">
<i bs-tooltip="'Hide row'" data-placement="right" class="icon-collapse-top" ng-click="toggle_row(row)"></i>
<br>
</span>
<span bs-modal="'app/partials/roweditor.html'" ng-show="row.editable">
<i bs-tooltip="'Configure row'" data-placement="right" class="icon-cog pointer"></i>
<br>
</span>
<span ng-show="rowSpan(row) == 12 && row.editable">
<i bs-tooltip="'Row full. Create a new row to add more panels'" data-placement="right" class="icon-trello"></i>
<br>
</span>
<span ng-show="rowSpan(row) > 12">
<i bs-tooltip="'Total span > 12. This row may format poorly'" data-placement="right" class="icon-warning-sign text-warning"></i>
<br>
</span>
</div>
<div style="text-align:left" class="row-open" ng-show="!row.collapse">
<span ng-show="row.collapsable">
<i bs-tooltip="'Hide row'" data-placement="right" class="icon-collapse-top" ng-click="toggle_row(row)"></i>
<br>
</span>
<span bs-modal="'app/partials/roweditor.html'" ng-show="row.editable">
<i bs-tooltip="'Configure row'" data-placement="right" class="icon-cog pointer"></i>
<br>
</span>
<span ng-show="rowSpan(row) == 12 && row.editable">
<i bs-tooltip="'Row full. Create a new row to add more panels'" data-placement="right" class="icon-trello"></i>
<br>
</span>
<span ng-show="rowSpan(row) > 12">
<i bs-tooltip="'Total span > 12. This row may format poorly'" data-placement="right" class="icon-warning-sign text-warning"></i>
<br>
</span>
</div>
</div>
<div class="row-fluid" style="padding-top:0px" ng-if="!row.collapse">
</div>
<div class="row-fluid" style="padding-top:0px" ng-if="!row.collapse">
<!-- Panels -->
<div ng-repeat="(name, panel) in row.panels|filter:isPanel" ng-hide="panel.span == 0 || panel.hide" class="span{{panel.span}} panel nospace" style="min-height:{{row.height}}; position:relative" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:$index,mutate:false,onDrop:'panelMoveDrop',onOver:'panelMoveOver(true)',onOut:'panelMoveOut'}">
<!-- Error Panel -->
<div class="row-fluid">
<div class="span12 alert alert-error panel-error" ng-hide="!panel.error">
<a class="close" ng-click="panel.error=false">&times;</a>
<i class="icon-exclamation-sign"></i> <strong>Oops!</strong> {{panel.error}}
</div>
</div>
<!-- Panels -->
<div ng-repeat="(name, panel) in row.panels|filter:isPanel" ng-hide="panel.span == 0 || panel.hide" class="span{{panel.span}} panel nospace" style="min-height:{{row.height}}; position:relative" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:$index,mutate:false,onDrop:'panelMoveDrop',onOver:'panelMoveOver(true)',onOut:'panelMoveOut'}">
<!-- Error Panel -->
<div class="row-fluid">
<div class="span12 alert alert-error panel-error" ng-hide="!panel.error">
<a class="close" ng-click="panel.error=false">&times;</a>
<i class="icon-exclamation-sign"></i> <strong>Oops!</strong> {{panel.error}}
<!-- Content Panel -->
<div class="row-fluid" style="position:relative" ng-class="{'dragInProgress':dashboard.panelDragging}" >
<kibana-panel type="panel.type" ng-cloak></kibana-panel>
</div>
</div>
</div>
<!-- Content Panel -->
<div class="row-fluid" style="position:relative" ng-class="{'dragInProgress':dashboard.panelDragging}" >
<kibana-panel type="panel.type" ng-cloak></kibana-panel>
</div>
</div>
<div ng-hide="(12-rowSpan(row)) < 1 || !dashboard.current.panel_hints" class="panel span{{(12-rowSpan(row))}}" ng-class="{'dragInProgress':dashboard.panelDragging}" style="height:{{row.height}};" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:row.panels.length,mutate:false,onDrop:'panelMoveDrop',onOver:'panelMoveOver',onOut:'panelMoveOut'}">
<div ng-hide="(12-rowSpan(row)) < 1 || !dashboard.current.panel_hints" class="panel span{{(12-rowSpan(row))}}" ng-class="{'dragInProgress':dashboard.panelDragging}" style="height:{{row.height}};" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:row.panels.length,mutate:false,onDrop:'panelMoveDrop',onOver:'panelMoveOver',onOut:'panelMoveOut'}">
<span bs-modal="'app/partials/roweditor.html'" ng-show="row.editable && !dashboard.panelDragging">
<i ng-hide="rowSpan(row) == 0" class="pointer icon-plus-sign" ng-click="editor.index = 2" bs-tooltip="'Add a panel to this row'" data-placement="right"></i>
<span ng-click="editor.index = 2" style="margin-top: 8px; margin-left: 3px" ng-show="rowSpan(row) == 0" class="btn btn-mini">Add panel to empty row</btn>
</span>
<span bs-modal="'app/partials/roweditor.html'" ng-show="row.editable && !dashboard.panelDragging">
<i ng-hide="rowSpan(row) == 0" class="pointer icon-plus-sign" ng-click="editor.index = 2" bs-tooltip="'Add a panel to this row'" data-placement="right"></i>
<span ng-click="editor.index = 2" style="margin-top: 8px; margin-left: 3px" ng-show="rowSpan(row) == 0" class="btn btn-mini">Add panel to empty row</btn>
</span>
</div>
</div>
</div>
</div>
<div class="row-fluid" ng-show='dashboard.current.editable'>
<div class="span12" style="text-align:right;">
<span style="margin-left: 0px;" class="pointer btn btn-mini" bs-modal="'app/partials/dasheditor.html'">
<span ng-click="editor.index = 2"><i class="icon-plus-sign"></i> ADD A ROW</span>
</span>
</div>
</div>
</div>
</div>
<div class="row-fluid" ng-show='dashboard.current.editable'>
<div class="span12" style="text-align:right;">
<span style="margin-left: 0px;" class="pointer btn btn-mini" bs-modal="'app/partials/dasheditor.html'">
<span ng-click="editor.index = 2"><i class="icon-plus-sign"></i> ADD A ROW</span>
</span>
</div>
</div>
</div>
\ No newline at end of file
......@@ -25,6 +25,14 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
failover: false,
panel_hints: true,
rows: [],
pulldowns: [
{
type: 'query',
},
{
type: 'filtering'
}
],
services: {},
loader: {
save_gist: false,
......@@ -109,7 +117,7 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
this.refresh = function() {
if(self.current.index.interval !== 'none') {
if(filterSrv.idsByType('time').length > 0) {
var _range = filterSrv.timeRange('min');
var _range = filterSrv.timeRange('last');
kbnIndex.indices(_range.from,_range.to,
self.current.index.pattern,self.current.index.interval
).then(function (p) {
......@@ -175,7 +183,7 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
filterSrv.init();
// If there's an index interval set and no existing time filter, send a refresh to set one
if(dashboard.index.interval !== 'none' && filterSrv.idsByType('time').length === 0) {
if(dashboard.index.interval !== 'none') {
self.refresh();
}
......
define([
'angular',
'underscore',
'config'
], function (angular, _, config) {
'config',
'kbn'
], function (angular, _, config, kbn) {
'use strict';
var module = angular.module('kibana.services');
......@@ -35,10 +36,13 @@ define([
self.ids = dashboard.current.services.filter.ids;
_f = dashboard.current.services.filter;
// Date filters hold strings now, not dates
/*
_.each(self.getByType('time',true),function(time) {
self.list[time.id].from = new Date(time.from);
self.list[time.id].to = new Date(time.to);
});
*/
};
......@@ -78,6 +82,7 @@ define([
dashboard.refresh();
},0);
}
$rootScope.$broadcast('filter');
return _r;
};
......@@ -101,6 +106,7 @@ define([
dashboard.refresh();
},0);
}
$rootScope.$broadcast('filter');
return _r;
};
......@@ -150,10 +156,11 @@ define([
switch(filter.type)
{
case 'time':
return ejs.RangeFilter(filter.field)
.from(filter.from.valueOf())
//.from("now-1d")
.to(filter.to.valueOf());
var _f = ejs.RangeFilter(filter.field).from(kbn.parseDate(filter.from).valueOf());
if(!_.isUndefined(filter.to)) {
_f = _f.to(filter.to.valueOf());
}
return _f;
case 'range':
return ejs.RangeFilter(filter.field)
.from(filter.from)
......@@ -187,26 +194,26 @@ define([
return _.pluck(self.getByType('time'),'field');
};
// This special function looks for all time filters, and returns a time range according to the mode
// No idea when max would actually be used
this.timeRange = function(mode) {
var _t = _.where(self.list,{type:'time',active:true});
if(_t.length === 0) {
// Parse is used when you need to know about the raw filter
this.timeRange = function(parse) {
var _t = _.last(_.where(self.list,{type:'time',active:true}));
if(_.isUndefined(_t)) {
return false;
}
switch(mode) {
case "min":
if(parse === false) {
return {
from: new Date(_.max(_.pluck(_t,'from'))),
to: new Date(_.min(_.pluck(_t,'to')))
from: _t.from,
to: _t.to
};
case "max":
} else {
var
_from = _t.from,
_to = _t.to || new Date();
return {
from: new Date(_.min(_.pluck(_t,'from'))),
to: new Date(_.max(_.pluck(_t,'to')))
from : kbn.parseDate(_from),
to : kbn.parseDate(_to)
};
default:
return false;
}
};
......
......@@ -51,4 +51,4 @@ function (Settings) {
'terms'
]
});
});
\ No newline at end of file
});
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.
......@@ -34,16 +34,14 @@
<div class="navbar-inner">
<div class="container-fluid">
<span class="brand">{{dashboard.current.title}}</span>
<ul class="nav pull-right" ng-controller='dashLoader' ng-init="init()" ng-include="'app/partials/dashLoader.html'">
</ul>
</div>
</div>
</div>
<div class="container-fluid main">
<div class="row-fluid">
<div ng-view></div>
</div>
</div>
</body>
<div ng-view></div>
</body>
</html>
......@@ -16,6 +16,10 @@ div.fake-input {
.border-radius(@inputBorderRadius @inputBorderRadius @inputBorderRadius @inputBorderRadius);
}
form input.ng-invalid {
color: @errorText;
}
.editor-title {
margin-right: 10px;
font-size: 1.7em;
......@@ -80,6 +84,22 @@ div.fake-input {
font-weight: 200;
}
.top-row-open {
background: darken(@bodyBackground, 3%);
padding: 5px 25px 5px 25px;
}
.top-row-close {
outline: 1px solid darken(@bodyBackground, 10%);
border-top: 1px solid lighten(@bodyBackground, 10%);
padding: 0px;
margin: 1px 0px 0px 0px;
text-align: center;
min-height: 16px !important;
line-height: 16px;
background: darken(@bodyBackground, 3%);
}
.row-open i {
font-size: 10pt;
}
......@@ -146,9 +166,15 @@ div.fake-input {
}
.link {
color: @linkColor;
cursor: pointer;
}
.link:hover {
color: @linkColorHover;
}
.pointer:hover {
color: @linkColorHover;
}
......@@ -157,10 +183,6 @@ div.fake-input {
cursor: pointer;
}
.link:hover {
color: @linkColorHover;
}
.popover {
max-width: 480px;
}
......@@ -191,6 +213,10 @@ div.fake-input {
font-weight: bold;
}
a {
cursor: pointer;
}
.normal {
font-weight: normal;
}
......
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