Commit 50370f21 by Rashid Khan

Renamed new time picker, deprecated old one

parent e54c868e
...@@ -98,7 +98,7 @@ ...@@ -98,7 +98,7 @@
], ],
"nav": [ "nav": [
{ {
"type": "timepicker2", "type": "timepicker",
"collapse": false, "collapse": false,
"notice": false, "notice": false,
"status": "Stable", "status": "Stable",
...@@ -145,4 +145,4 @@ ...@@ -145,4 +145,4 @@
"hide": false "hide": false
}, },
"refresh": false "refresh": false
} }
\ No newline at end of file
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
"panels": [ "panels": [
{ {
"error": false, "error": false,
"span": 8, "span": 7,
"editable": true, "editable": true,
"group": [ "group": [
"default" "default"
...@@ -50,39 +50,13 @@ ...@@ -50,39 +50,13 @@
"type": "text", "type": "text",
"status": "Stable", "status": "Stable",
"mode": "markdown", "mode": "markdown",
"content": "If you have a field with a timestamp in it, you might want to add a 'timepicker' panel here. Click the plus sign over to the right to do so. You can also remove these information text panels there by clicking the 'x' icon above", "content": "If you have a field with a timestamp in it, you can set a time filter using the control in the navigation bar. You'll need to click the cog icon to configure the field that your timestamp is in.",
"style": {}, "style": {},
"title": "Have a timestamp somewhere?" "title": "Have a timestamp somewhere?"
}
]
},
{
"title": "Query",
"height": "50px",
"editable": true,
"collapse": false,
"collapsable": true,
"panels": [
{
"error": false,
"span": 5,
"editable": true,
"group": [
"default"
],
"type": "query",
"label": "Search",
"history": [
"*"
],
"remember": 10,
"pinned": true,
"query": "*",
"title": "Search"
}, },
{ {
"error": false, "error": false,
"span": 7, "span": 5,
"editable": true, "editable": true,
"group": [ "group": [
"default" "default"
...@@ -90,29 +64,12 @@ ...@@ -90,29 +64,12 @@
"type": "text", "type": "text",
"status": "Stable", "status": "Stable",
"mode": "markdown", "mode": "markdown",
"content": "See the *Filters* bar to the below? Click it to expand the filters row. Right now there are none. click on one of the icons in the document types list to filter down to only that document type", "content": "See the *Filters* bar above? Click it to expand the filters panel. Right now there are none. click on one of the icons in the document types list to filter down to only that document type",
"style": {}, "style": {},
"title": "About filters" "title": "About filters"
} }
] ],
}, "notice": false
{
"title": "Filters",
"height": "50px",
"editable": true,
"collapse": true,
"collapsable": true,
"panels": [
{
"error": false,
"span": 12,
"editable": true,
"group": [
"default"
],
"type": "filtering"
}
]
}, },
{ {
"title": "Graph", "title": "Graph",
...@@ -199,7 +156,8 @@ ...@@ -199,7 +156,8 @@
"style": {}, "style": {},
"title": "The most generic dashboard ever" "title": "The most generic dashboard ever"
} }
] ],
"notice": false
}, },
{ {
"title": "Events", "title": "Events",
...@@ -245,7 +203,8 @@ ...@@ -245,7 +203,8 @@
"normTimes": true, "normTimes": true,
"title": "Documents" "title": "Documents"
} }
] ],
"notice": false
} }
], ],
"editable": true, "editable": true,
...@@ -270,5 +229,54 @@ ...@@ -270,5 +229,54 @@
"load_elasticsearch_size": 20, "load_elasticsearch_size": 20,
"load_local": true, "load_local": true,
"hide": false "hide": false
} },
"pulldowns": [
{
"type": "query",
"collapse": false,
"notice": false,
"query": "*",
"pinned": true,
"history": [],
"remember": 10
},
{
"type": "filtering",
"collapse": true,
"notice": false
}
],
"nav": [
{
"type": "timepicker",
"collapse": false,
"notice": false,
"status": "Stable",
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
],
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"timefield": "@timestamp"
}
],
"refresh": false
} }
\ No newline at end of file
...@@ -181,7 +181,7 @@ ...@@ -181,7 +181,7 @@
], ],
"nav": [ "nav": [
{ {
"type": "timepicker2", "type": "timepicker",
"collapse": false, "collapse": false,
"notice": false, "notice": false,
"status": "Stable", "status": "Stable",
...@@ -228,4 +228,4 @@ ...@@ -228,4 +228,4 @@
"hide": false "hide": false
}, },
"refresh": false "refresh": false
} }
\ No newline at end of file
...@@ -34,88 +34,6 @@ ...@@ -34,88 +34,6 @@
}, },
"rows": [ "rows": [
{ {
"title": "Options",
"height": "50px",
"editable": true,
"collapse": false,
"collapsable": true,
"panels": [
{
"error": false,
"span": 8,
"editable": true,
"group": [
"default"
],
"type": "text",
"status": "Stable",
"mode": "markdown",
"content": "If you have a field with a timestamp in it, you might want to add a 'timepicker' panel here. Click the cog icon over to the left to do so. You can also remove these information text panels there",
"style": {},
"title": "Have a timestamp somewhere?"
}
]
},
{
"title": "Query",
"height": "50px",
"editable": true,
"collapse": false,
"collapsable": true,
"panels": [
{
"error": false,
"span": 12,
"editable": true,
"group": [
"default"
],
"type": "query",
"label": "Search",
"history": [
"*"
],
"remember": 10,
"pinned": true,
"query": "*",
"title": "Search"
}
]
},
{
"title": "Filters",
"height": "50px",
"editable": true,
"collapse": true,
"collapsable": true,
"panels": [
{
"error": false,
"span": 5,
"editable": true,
"group": [
"default"
],
"type": "text",
"status": "Stable",
"mode": "markdown",
"content": "You found the filter row! This row has a 'filtering' panel in it that lists any active filters. You usually want one of these on any dashboard.",
"style": {},
"title": "Found me!"
},
{
"error": false,
"span": 7,
"editable": true,
"group": [
"default"
],
"type": "filtering",
"title": "Filters (applied globally)"
}
]
},
{
"title": "Graph", "title": "Graph",
"height": "250px", "height": "250px",
"editable": true, "editable": true,
...@@ -124,7 +42,7 @@ ...@@ -124,7 +42,7 @@
"panels": [ "panels": [
{ {
"error": false, "error": false,
"span": 4, "span": 3,
"editable": true, "editable": true,
"group": [ "group": [
"default" "default"
...@@ -138,7 +56,7 @@ ...@@ -138,7 +56,7 @@
}, },
{ {
"error": false, "error": false,
"span": 8, "span": 9,
"editable": true, "editable": true,
"group": [ "group": [
"default" "default"
...@@ -150,7 +68,8 @@ ...@@ -150,7 +68,8 @@
"style": {}, "style": {},
"title": "Welcome!" "title": "Welcome!"
} }
] ],
"notice": false
}, },
{ {
"title": "Table", "title": "Table",
...@@ -173,7 +92,8 @@ ...@@ -173,7 +92,8 @@
"style": {}, "style": {},
"title": "Put a table here maybe?" "title": "Put a table here maybe?"
} }
] ],
"notice": false
} }
], ],
"editable": true, "editable": true,
...@@ -198,5 +118,54 @@ ...@@ -198,5 +118,54 @@
"load_elasticsearch_size": 20, "load_elasticsearch_size": 20,
"load_local": true, "load_local": true,
"hide": false "hide": false
} },
"pulldowns": [
{
"type": "query",
"collapse": false,
"notice": false,
"query": "*",
"pinned": true,
"history": [],
"remember": 10
},
{
"type": "filtering",
"collapse": true,
"notice": false
}
],
"nav": [
{
"type": "timepicker",
"collapse": false,
"notice": false,
"status": "Stable",
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
],
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"timefield": "@timestamp"
}
],
"refresh": false
} }
\ No newline at end of file
...@@ -15,18 +15,20 @@ ...@@ -15,18 +15,20 @@
.filter-panel-filter ul { .filter-panel-filter ul {
margin-bottom: 3px; margin-bottom: 3px;
} }
.filter-must { .filter-must {
border-bottom: #7EB26D 3px solid; border-top: #7EB26D 3px solid;
} }
.filter-mustNot { .filter-mustNot {
border-bottom: #E24D42 3px solid; border-top: #E24D42 3px solid;
}
.filter-either {
border-top: #EF843C 3px solid;
} }
.filter-deselected { .filter-deselected {
opacity: 0.5; opacity: 0.5;
} }
.filter-either {
border-bottom: #EF843C 3px solid;
}
.filter-action { .filter-action {
float:right; float:right;
margin-bottom: 0px !important; margin-bottom: 0px !important;
...@@ -43,11 +45,18 @@ ...@@ -43,11 +45,18 @@
</style> </style>
<div class='filtering-container'> <div class='filtering-container'>
<span ng-show="filterSrv.ids.length == 0"><h5>No filters available</h5></span> <span ng-show="filterSrv.ids.length == 0">
<div ng-repeat="id in filterSrv.ids" class="small filter-panel-filter" ng-class="{'filter-deselected': !filterSrv.list[id].active}"> <h5>No filters available</h5>
<div class="filter-{{filterSrv.list[id].mandate}}" > </span>
<div ng-repeat="id in filterSrv.ids" class="small filter-panel-filter filter-{{filterSrv.list[id].mandate}}" ng-class="{'filter-deselected': !filterSrv.list[id].active}">
<div>
<strong>{{filterSrv.list[id].type}}</strong> <strong>{{filterSrv.list[id].type}}</strong>
<span ng-show="!filterSrv.list[id].editing" class="filter-mandate" ng-click="filterSrv.list[id].editing = true">{{filterSrv.list[id].mandate}}</span> <span ng-show="!filterSrv.list[id].editing && isEditable(filterSrv.list[id])" class="filter-mandate" ng-click="filterSrv.list[id].editing = true">
{{filterSrv.list[id].mandate}}
</span>
<span ng-show="!isEditable(filterSrv.list[id])">
{{filterSrv.list[id].mandate}}
</span>
<span ng-show="filterSrv.list[id].editing"> <span ng-show="filterSrv.list[id].editing">
<select class="input-small" ng-model="filterSrv.list[id].mandate" ng-options="f for f in ['must','mustNot','either']"></select> <select class="input-small" ng-model="filterSrv.list[id].mandate" ng-options="f for f in ['must','mustNot','either']"></select>
...@@ -56,17 +65,20 @@ ...@@ -56,17 +65,20 @@
<i class="filter-action pointer icon-remove" bs-tooltip="'Remove'" ng-click="remove(id)"></i> <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" 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 || !isEditable(filterSrv.list[id])" 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> </div>
<div ng-hide="filterSrv.list[id].editing && isEditable(filterSrv.list[id])"> <div ng-hide="filterSrv.list[id].editing && isEditable(filterSrv.list[id])">
<ul class="unstyled"> <ul class="unstyled">
<li ng-repeat="(key,value) in filterSrv.list[id] track by $index" ng-show="show_key(key)"><strong>{{key}}</strong> : {{value}}</li> <li ng-repeat="(key,value) in filterSrv.list[id] track by $index" ng-show="show_key(key)">
<strong>{{key}}</strong> : {{value}}
</li>
</ul> </ul>
</div> </div>
<div ng-show="filterSrv.list[id].editing && isEditable(filterSrv.list[id])"> <div ng-show="filterSrv.list[id].editing && isEditable(filterSrv.list[id])">
<ul class="unstyled"> <ul class="unstyled">
<li ng-repeat="key in _.keys(filterSrv.list[id])" ng-show="show_key(key)"><strong>{{key}}</strong> : <input type='text' ng-model="filterSrv.list[id][key]"></li> <li ng-repeat="key in _.keys(filterSrv.list[id])" ng-show="show_key(key)">
<strong>{{key}}</strong> : <input type='text' ng-model="filterSrv.list[id][key]">
</li>
</ul> </ul>
</div> </div>
<div class="filter-apply" ng-show="filterSrv.list[id].editing"> <div class="filter-apply" ng-show="filterSrv.list[id].editing">
......
<div class="row-fluid">
<div class="span3">
<h6>Default Mode</h6>
<select style="width:85%" ng-model="panel.mode" ng-options="f for f in ['relative','absolute','since']"></select>
</div>
<div class="span3">
<h6>Time Field</h6>
<input type="text" class="input-small" ng-model="panel.timefield">
</div>
</div>
<div class="row-fluid"> <div class="row-fluid">
<h5>Relative mode <small>settings</small></h5> <div class="span4">
<div class="span6"> <label class="small">Relative time options <small>comma seperated</small></label>
<h6>Relative time options <small>comma seperated</small></h6>
<input type="text" array-join class="input-large" ng-model="panel.time_options"> <input type="text" array-join class="input-large" ng-model="panel.time_options">
</div> </div>
<div class="span3"> <div class="span4">
<h6>Default timespan</h6> <label class="small">Auto-refresh options <small>comma seperated</small></label>
<select class="input-mini" ng-model="panel.timespan" ng-options="f for f in panel.time_options"></select> <input type="text" array-join class="input-large" ng-model="panel.refresh_intervals">
</div>
<div class="row-fluid">
<h5>Auto-refresh <small>settings</small></h5>
<div class="span1">
<label class="small"> Enable </label><input type="checkbox" ng-model="panel.refresh.enable" ng-checked="panel.refresh.enable">
</div> </div>
<div class="span2"> <div class="span2">
<label class="small"> Interval (seconds) </label> <label class="small">Time Field</label>
<input type="number" class="input-mini" ng-model="panel.refresh.interval"> <input type="text" class="input-small" ng-model="panel.timefield">
</div>
<div class="span3">
<label class="small"> Minimum Interval (seconds) </label>
<input type="number" class="input-mini" ng-model="panel.refresh.min">
</div> </div>
</div> </div>
\ No newline at end of file
<div ng-controller='timepicker' ng-init="init()"> <div ng-controller='timepicker' ng-init="init()">
<style> <style>
.timepicker-block { .timepicker-timestring {
display: inline-block; font-weight: normal;
}
.timepicker-dropdown {
margin: 0px !important;
border: 0px !important;
} }
</style> </style>
<div class="row-fluid form-horizontal" ng-switch="panel.mode" ng-show="filterSrv.idsByType('time').length > 0"> <!-- This is a complete hack. The form actually exists in the modal, but due to transclusion
<div ng-switch-when="absolute" > $scope.input isn't available on the controller unless the form element is in this file -->
<div class="timepicker-block"> <form name="input" style="margin:3px 0 0 0">
<form class="nomargin"> <ul class="nav nav-pills timepicker-dropdown">
<input type="text" class="input-smaller" ng-change="time_check()" ng-model="timepicker.from.date" data-date-format="mm/dd/yyyy" bs-datepicker> <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();">
<input type="text" class="input-mini" ng-change="time_check()" data-show-meridian="false" data-show-seconds="true" ng-model="timepicker.from.time" bs-timepicker>
</form> <a class="dropdown-toggle timepicker-dropdown" data-toggle="dropdown" href="#">
</div>
to <span ng-show="filterSrv.idsByType('time').length">
<div class="timepicker-block" style="margin-left:5px"> <span class="pointer" ng-hide="panel.now">{{time.from.date | date:'MMM d, y HH:mm:ss'}}</span>
<form class="nomargin"> <span class="pointer" ng-show="panel.now">{{time.from.date | moment:'ago'}}</span>
<input type="text" class="input-smaller" ng-change="time_check()" ng-model="timepicker.to.date" data-date-format="mm/dd/yyyy" bs-datepicker> to
<input type="text" class="input-mini" ng-change="time_check()" data-show-meridian="false" data-show-seconds="true" ng-model="timepicker.to.time" bs-timepicker> <!--(<a ng-click="to_now()">now</a>)--> <span class="pointer" ng-hide="panel.now" >{{time.to.date | date:'MMM d, y HH:mm:ss'}}</span>
</form> <span class="pointer" ng-show="panel.now">{{time.to.date | moment:'ago'}}</span>
</div> </span>
<div class="timepicker-block"> <span ng-hide="filterSrv.idsByType('time').length">Set a time filter</span>
<form class="nomargin"> <span ng-show="dashboard.current.refresh" class="text-warning">refreshed every {{dashboard.current.refresh}} </span>
<button class="btn" ng-click="time_apply()"><i class="icon-ok"></i></button> <i class="icon-caret-down"></i>
</form> </a>
</div>
</div> <ul class="dropdown-menu">
<div ng-switch-when="since"> <!-- Relative time options -->
<div class="timepicker-block"> <li ng-repeat='timespan in panel.time_options track by $index'>
<form class="nomargin"> <a ng-click="setRelativeFilter(timespan)">Last {{timespan}}</a>
<input type="text" class="input-smaller" ng-change="time_check()" ng-model="timepicker.from.date" data-date-format="mm/dd/yyyy" bs-datepicker> </li>
<input type="text" class="input-mini" ng-change="time_check()" data-show-meridian="false" data-show-seconds="true" ng-model="timepicker.from.time" bs-timepicker>
</form> <!-- Auto refresh submenu -->
</div> <li class="dropdown-submenu">
<div class="timepicker-block" style="margin-left:5px"> <a href="#">Auto-Refresh</a>
<form class="nomargin"> <ul class="dropdown-menu">
<button class="btn" ng-click="time_apply()" ><i class="icon-ok"></i></button> <li><a ng-click="dashboard.set_interval(false)">Off</a></li>
</form> <li ng-repeat="interval in panel.refresh_intervals track by $index"><a ng-click="dashboard.set_interval(interval)">Every {{interval}}</a></li>
</div> </ul>
</div> </li>
<div ng-switch-when="relative"> <li><a ng-click="customTime()">Custom</a></li>
<div class="timepicker-block"> </ul>
<form class="nomargin input-append">
<button class="btn btn" ng-repeat='timespan in panel.time_options' ng-class="{'btn-success': (panel.timespan == timespan)}" ng-click="set_timespan(timespan)">{{timespan}}</button> </li>
<!--<select ng-model="panel.sort[0]" ng-options="f for f in fields"></select>--> </ul>
</form>
</div> </form>
</div> </div>
</div>
<div class="row-fluid" ng-show="filterSrv.idsByType('time').length < 1">
<div>
<div class="span11">
<h4>No time filter present</h4>
</div>
</div>
</div>
<div class="row-fluid nomargin">
<div class="span12 small" ng-show="filterSrv.idsByType('time').length > 0">
<a class="link" ng-click="set_mode('relative')" ng-class="{'strong': (panel.mode == 'relative')}">Relative</a> |
<a class="link" ng-click="set_mode('absolute')" ng-class="{'strong': (panel.mode == 'absolute')}">Absolute</a> |
<a class="link" ng-click="set_mode('since')" ng-class="{'strong': (panel.mode == 'since')}">Since</a>
<span ng-hide="panel.mode == 'absolute' || panel.mode == 'none'"> |
<input type="checkbox" ng-model="panel.refresh.enable" ng-change='refresh();'> Auto-refresh
<span ng-class="{'ng-cloak': !panel.refresh.enable}">
every <a data-title="<small>Auto-refresh Settings</small>" data-placement="bottom" bs-popover="'app/panels/timepicker/refreshctrl.html'">{{panel.refresh.interval}}s</a>.
</span>
</span>
</div>
<div class="span12 small" ng-show="filterSrv.idsByType('time').length < 1">
<a class='btn btn-small' ng-click="time_apply()">Create a time filter</a>
</div>
</div>
</div>
\ No newline at end of file
/* /*
## Timepicker ## Timepicker2
### Parameters ### Parameters
* mode :: The default mode of the panel. Options: 'relative', 'absolute' 'since' Default: 'relative' * mode :: The default mode of the panel. Options: 'relative', 'absolute' 'since' Default: 'relative'
...@@ -25,7 +25,7 @@ function (angular, app, _, moment, kbn) { ...@@ -25,7 +25,7 @@ function (angular, app, _, moment, kbn) {
var module = angular.module('kibana.panels.timepicker', []); var module = angular.module('kibana.panels.timepicker', []);
app.useModule(module); app.useModule(module);
module.controller('timepicker', function($scope, $rootScope, $timeout, timer, $http, dashboard, filterSrv) { module.controller('timepicker', function($scope, $modal, $q, filterSrv) {
$scope.panelMeta = { $scope.panelMeta = {
status : "Stable", status : "Stable",
description : "A panel for controlling the time range filters. If you have time based data, "+ description : "A panel for controlling the time range filters. If you have time based data, "+
...@@ -36,228 +36,180 @@ function (angular, app, _, moment, kbn) { ...@@ -36,228 +36,180 @@ function (angular, app, _, moment, kbn) {
// Set and populate defaults // Set and populate defaults
var _d = { var _d = {
status : "Stable", status : "Stable",
mode : "relative",
time_options : ['5m','15m','1h','6h','12h','24h','2d','7d','30d'], time_options : ['5m','15m','1h','6h','12h','24h','2d','7d','30d'],
timespan : '15m', refresh_intervals : ['5s','10s','30s','1m','5m','15m','30m','1h','2h','1d'],
timefield : '@timestamp',
timeformat : "", timefield : '@timestamp'
refresh : {
enable : false,
interval: 30,
min : 3
}
}; };
_.defaults($scope.panel,_d); _.defaults($scope.panel,_d);
var customTimeModal = $modal({
template: './app/panels/timepicker/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() { $scope.init = function() {
// Private refresh interval that we can use for view display without causing var time = filterSrv.timeRange('last');
// unnecessary refreshes during changes if(time) {
$scope.refresh_interval = $scope.panel.refresh.interval; $scope.panel.now = filterSrv.timeRange(false).to === "now" ? true : false;
$scope.filterSrv = filterSrv; $scope.time = getScopeTimeObj(time.from,time.to);
// Init a private time object with Date() objects depending on mode
switch($scope.panel.mode) {
case 'absolute':
$scope.time = {
from : moment($scope.panel.time.from,'MM/DD/YYYY HH:mm:ss') || moment(kbn.time_ago($scope.panel.timespan)),
to : moment($scope.panel.time.to,'MM/DD/YYYY HH:mm:ss') || moment()
};
break;
case 'since':
$scope.time = {
from : moment($scope.panel.time.from,'MM/DD/YYYY HH:mm:ss') || moment(kbn.time_ago($scope.panel.timespan)),
to : moment()
};
break;
case 'relative':
$scope.time = {
from : moment(kbn.time_ago($scope.panel.timespan)),
to : moment()
};
break;
}
$scope.time.field = $scope.panel.timefield;
// These 3 statements basicly do everything time_apply() does
set_timepicker($scope.time.from,$scope.time.to);
update_panel();
// If we're in a mode where something must be calculated, clear existing filters
// and set new ones
if($scope.panel.mode !== 'absolute') {
set_time_filter($scope.time);
} }
};
// Start refresh timer if enabled $scope.customTime = function() {
if ($scope.panel.refresh.enable) { // Assume the form is valid since we're setting it to something valid
$scope.set_interval($scope.panel.refresh.interval); $scope.input.$setValidity("dummy", true);
} $scope.temptime = cloneTime($scope.time);
// In case some other panel broadcasts a time, set us to an absolute range // Date picker needs the date to be at the start of the day
$scope.$on('refresh', function() { $scope.temptime.from.date.setHours(0,0,0,0);
if(filterSrv.idsByType('time').length > 0) { $scope.temptime.to.date.setHours(0,0,0,0);
var time = filterSrv.timeRange('min');
$q.when(customTimeModal).then(function(modalEl) {
if($scope.time.from.diff(moment.utc(time.from),'seconds') !== 0 || modalEl.modal('show');
$scope.time.to.diff(moment.utc(time.to),'seconds') !== 0)
{
$scope.set_mode('absolute');
// These 3 statements basicly do everything time_apply() does
set_timepicker(moment(time.from),moment(time.to));
$scope.time = $scope.time_calc();
update_panel();
}
}
}); });
}; };
$scope.set_interval = function (refresh_interval) { // Constantly validate the input of the fields. This function does not change any date variables
$scope.panel.refresh.interval = refresh_interval; // outside of its own scope
if(_.isNumber($scope.panel.refresh.interval)) { $scope.validate = function(time) {
if($scope.panel.refresh.interval < $scope.panel.refresh.min) { // Assume the form is valid. There is a hidden dummy input for invalidating it programatically.
$scope.panel.refresh.interval = $scope.panel.refresh.min; $scope.input.$setValidity("dummy", true);
timer.cancel($scope.refresh_timer);
return; 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;
} }
timer.cancel($scope.refresh_timer);
$scope.refresh();
} else { } else {
timer.cancel($scope.refresh_timer); return false;
} }
return {from:_from,to:_to};
}; };
$scope.refresh = function() { $scope.setNow = function() {
if ($scope.panel.refresh.enable) { $scope.time.to = getTimeObj(new Date());
timer.cancel($scope.refresh_timer);
$scope.refresh_timer = timer.register($timeout(function() {
$scope.refresh();
$scope.time_apply();
},$scope.panel.refresh.interval*1000));
} else {
timer.cancel($scope.refresh_timer);
}
}; };
var update_panel = function() { /*
// Update panel's string representation of the time object.Don't update if time : {
// we're in relative mode since we dont want to store the time object in the from: Date
// json for relative periods to: Date
if($scope.panel.mode !== 'relative') {
$scope.panel.time = {
from : $scope.time.from.format("MM/DD/YYYY HH:mm:ss"),
to : $scope.time.to.format("MM/DD/YYYY HH:mm:ss"),
};
} else {
delete $scope.panel.time;
} }
}; */
$scope.setAbsoluteTimeFilter = function (time) {
$scope.set_mode = function(mode) { // Create filter object
$scope.panel.mode = mode; var _filter = _.clone(time);
$scope.panel.refresh.enable = mode === 'absolute' ?
false : $scope.panel.refresh.enable;
update_panel(); _filter.type = 'time';
}; _filter.field = $scope.panel.timefield;
$scope.to_now = function() { if($scope.panel.now) {
$scope.timepicker.to = { _filter.to = "now";
time : moment().format("HH:mm:ss"), }
date : moment().format("MM/DD/YYYY")
};
};
$scope.set_timespan = function(timespan) { // Clear all time filters, set a new one
$scope.panel.timespan = timespan; filterSrv.removeByType('time',true);
$scope.timepicker.from = {
time : moment(kbn.time_ago(timespan)).format("HH:mm:ss"),
date : moment(kbn.time_ago(timespan)).format("MM/DD/YYYY")
};
$scope.time_apply();
};
$scope.close_edit = function() { // Set the filter
$scope.time_apply(); $scope.panel.filter_id = filterSrv.set(_filter);
};
// // Update our representation
$scope.time_calc = function(){ $scope.time = getScopeTimeObj(time.from,time.to);
var from,to;
// If time picker is defined (usually is) TOFIX: Horrible parsing
if(!(_.isUndefined($scope.timepicker))) {
from = $scope.panel.mode === 'relative' ? moment(kbn.time_ago($scope.panel.timespan)) :
moment(moment($scope.timepicker.from.date).format('MM/DD/YYYY') + " " + $scope.timepicker.from.time,'MM/DD/YYYY HH:mm:ss');
to = $scope.panel.mode !== 'absolute' ? moment() :
moment(moment($scope.timepicker.to.date).format('MM/DD/YYYY') + " " + $scope.timepicker.to.time,'MM/DD/YYYY HH:mm:ss');
// Otherwise (probably initialization)
} else {
from = $scope.panel.mode === 'relative' ? moment(kbn.time_ago($scope.panel.timespan)) :
$scope.time.from;
to = $scope.panel.mode !== 'absolute' ? moment() :
$scope.time.to;
}
if (from.valueOf() >= to.valueOf()) { return $scope.panel.filter_id;
from = moment(to.valueOf() - 1000); };
}
$timeout(function(){ $scope.setRelativeFilter = function(timespan) {
set_timepicker(from,to);
});
return { $scope.panel.now = true;
from : from, // Create filter object
to : to var _filter = {
type : 'time',
field : $scope.panel.timefield,
from : "now-"+timespan,
to: "now"
}; };
};
$scope.time_apply = function() {
$scope.panel.error = "";
// Update internal time object
// Remove all other time filters // Clear all time filters, set a new one
filterSrv.removeByType('time',true); filterSrv.removeByType('time',true);
$scope.time = $scope.time_calc(); // Set the filter
$scope.time.field = $scope.panel.timefield; $scope.panel.filter_id = filterSrv.set(_filter);
update_panel(); // Update our representation
set_time_filter($scope.time); $scope.time = getScopeTimeObj(kbn.parseDate(_filter.from),new Date());
return $scope.panel.filter_id;
}; };
//$scope.$watch('panel.mode', $scope.time_apply); 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;
};
function set_time_filter(time) { var cloneTime = function(time) {
time.type = 'time'; var _n = {
// Clear all time filters, set a new one from: _.clone(time.from),
filterSrv.removeByType('time',true); to: _.clone(time.to)
$scope.panel.filter_id = filterSrv.set(compile_time(time)); };
return $scope.panel.filter_id; // Create new dates as _.clone is shallow.
} _n.from.date = new Date(_n.from.date);
_n.to.date = new Date(_n.to.date);
// Prefer to pass around Date() objects since interacting with return _n;
// moment objects in libraries that are expecting Date()s can be tricky };
function compile_time(time) {
time = _.clone(time); var getScopeTimeObj = function(from,to) {
time.from = time.from.toDate(); return {
time.to = time.to.toDate(); from: getTimeObj(from),
return time; to: getTimeObj(to)
} };
};
function set_timepicker(from,to) {
// Janky 0s timeout to get around $scope queue processing view issue var getTimeObj = function(date) {
$scope.timepicker = { return {
from : { date: new Date(date),
time : from.format("HH:mm:ss"), hour: pad(date.getHours(),2),
date : from.format("MM/DD/YYYY") minute: pad(date.getMinutes(),2),
}, second: pad(date.getSeconds(),2),
to : { millisecond: pad(date.getMilliseconds(),3)
time : to.format("HH:mm:ss"),
date : to.format("MM/DD/YYYY")
}
}; };
} };
// Do not use the results of this function unless you plan to use setHour/Minutes/etc on the result
var datepickerToLocal = function(date) {
date = moment(date).clone().toDate();
return moment(new Date(date.getTime() + date.getTimezoneOffset() * 60000)).toDate();
};
}); });
}); });
\ No newline at end of file
<div class="row-fluid">
<div class="span4">
<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="span4">
<label class="small">Auto-refresh options <small>comma seperated</small></label>
<input type="text" array-join class="input-large" ng-model="panel.refresh_intervals">
</div>
<div class="span2">
<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 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>
<span ng-show="dashboard.current.refresh" class="text-warning">refreshed every {{dashboard.current.refresh}} </span>
<i class="icon-caret-down"></i>
</a>
<ul class="dropdown-menu">
<!-- Relative time options -->
<li ng-repeat='timespan in panel.time_options track by $index'>
<a ng-click="setRelativeFilter(timespan)">Last {{timespan}}</a>
</li>
<!-- Auto refresh submenu -->
<li class="dropdown-submenu">
<a href="#">Auto-Refresh</a>
<ul class="dropdown-menu">
<li><a ng-click="dashboard.set_interval(false)">Off</a></li>
<li ng-repeat="interval in panel.refresh_intervals track by $index"><a ng-click="dashboard.set_interval(interval)">Every {{interval}}</a></li>
</ul>
</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",
time_options : ['5m','15m','1h','6h','12h','24h','2d','7d','30d'],
refresh_intervals : ['5s','10s','30s','1m','5m','15m','30m','1h','2h','1d'],
timefield : '@timestamp'
};
_.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) {
date = moment(date).clone().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
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
</div> </div>
<div class="top-row-close pointer" ng-click="toggle_pulldown(pulldown);dismiss();" bs-tooltip="'Toggle '+pulldown.type" data-placement="bottom"> <div class="top-row-close pointer" ng-click="toggle_pulldown(pulldown);dismiss();" bs-tooltip="'Toggle '+pulldown.type" data-placement="bottom">
<span class="small row-text">{{pulldown.type}}</span> <span class="small row-text">{{pulldown.type}}</span>
<i class="small" ng-class="{'icon-expand':pulldown.collapse,'icon-collapse-top':!pulldown.collapse}"></i> <i class="small" ng-class="{'icon-caret-left':pulldown.collapse,'icon-caret-up':!pulldown.collapse}"></i>
<i class="small icon-star text-warning" ng-show="row.notice && pulldown.collapse"></i> <i class="small icon-star text-warning" ng-show="row.notice && pulldown.collapse"></i>
</div> </div>
</nil> </nil>
...@@ -23,14 +23,14 @@ ...@@ -23,14 +23,14 @@
<i bs-tooltip="'Configure row'" data-placement="right" ng-show="row.editable" class="icon-cog pointer"></i> <i bs-tooltip="'Configure row'" data-placement="right" ng-show="row.editable" class="icon-cog pointer"></i>
</span> </span>
<span class="row-button" ng-click="toggle_row(row)" ng-show="row.collapsable"> <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> <i bs-tooltip="'Expand row'" data-placement="right" ng-show="row.editable" class="icon-caret-right pointer" ></i>
</span> </span>
<span class="row-button row-text" ng-click="toggle_row(row)" ng-class="{'pointer':row.collapsable}">{{row.title || 'Row '+$index}}</span> <span class="row-button row-text" ng-click="toggle_row(row)" ng-class="{'pointer':row.collapsable}">{{row.title || 'Row '+$index}}</span>
</div> </div>
<div style="text-align:left" class="row-open" ng-show="!row.collapse"> <div style="text-align:center" class="row-open" ng-show="!row.collapse">
<span ng-show="row.collapsable"> <span ng-show="row.collapsable">
<i bs-tooltip="'Hide row'" data-placement="right" class="icon-collapse-top" ng-click="toggle_row(row)"></i> <i bs-tooltip="'Hide row'" data-placement="right" class="icon-caret-up" ng-click="toggle_row(row)"></i>
<br> <br>
</span> </span>
<span bs-modal="'app/partials/roweditor.html'" ng-show="row.editable"> <span bs-modal="'app/partials/roweditor.html'" ng-show="row.editable">
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
<br> <br>
</span> </span>
<span ng-show="rowSpan(row) == 12 && row.editable"> <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> <i bs-tooltip="'Row full. Create a new row to add more panels'" data-placement="right" class="icon-columns"></i>
<br> <br>
</span> </span>
<span ng-show="rowSpan(row) > 12"> <span ng-show="rowSpan(row) > 12">
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
</div> </div>
</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}" ng-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"> <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> <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>
......
...@@ -36,7 +36,7 @@ function (angular, $, kbn, _, config, moment, Modernizr) { ...@@ -36,7 +36,7 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
], ],
nav: [ nav: [
{ {
type: 'timepicker2' type: 'timepicker'
} }
], ],
services: {}, services: {},
...@@ -452,4 +452,4 @@ function (angular, $, kbn, _, config, moment, Modernizr) { ...@@ -452,4 +452,4 @@ function (angular, $, kbn, _, config, moment, Modernizr) {
}); });
}); });
\ No newline at end of file
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