module.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /*
  2. ## Timepicker
  3. The timepicker panel is used to select time ranges and inform other panel of
  4. them. It also handles searching for indices that match the given time range and
  5. a pattern
  6. ### Parameters
  7. * mode :: The default mode of the panel. Options: 'relative', 'absolute' 'since' Default: 'relative'
  8. * time_options :: An array of possible time options. Default: ['5m','15m','1h','6h','12h','24h','2d','7d','30d']
  9. * timespan :: The default options selected for the relative view. Default: '15m'
  10. * timefield :: The field in which time is stored in the document.
  11. * index :: Index pattern to match. Literals should be double quoted. Default: '_all'
  12. * defaultindex :: Index to failover to if index not found
  13. * index_interval :: Time between timestamped indices (can be 'none') for static index
  14. * refresh: Object containing refresh parameters
  15. * enable :: true/false, enable auto refresh by default. Default: false
  16. * interval :: Seconds between auto refresh. Default: 30
  17. * min :: The lowest interval a user may set
  18. ### Group Events
  19. #### Sends
  20. * time :: Object Includes from, to and index
  21. #### Receives
  22. * get_time :: Receives an object containing a $id, broadcasts back to it.
  23. */
  24. angular.module('kibana.timepicker', [])
  25. .controller('timepicker', function($scope, eventBus, $timeout, timer, $http) {
  26. // Set and populate defaults
  27. var _d = {
  28. mode : "relative",
  29. time_options : ['5m','15m','1h','6h','12h','24h','2d','7d','30d'],
  30. timespan : '15m',
  31. timefield : '@timestamp',
  32. index : '_all',
  33. defaultindex : "_all",
  34. index_interval: "none",
  35. group : "default",
  36. refresh : {
  37. enable : false,
  38. interval: 30,
  39. min : 3
  40. }
  41. }
  42. _.defaults($scope.panel,_d)
  43. var _groups = _.isArray($scope.panel.group) ?
  44. $scope.panel.group : [$scope.panel.group];
  45. $scope.init = function() {
  46. // Private refresh interval that we can use for view display without causing
  47. // unnecessary refreshes during changes
  48. $scope.refresh_interval = $scope.panel.refresh.interval
  49. // Init a private time object with Date() objects depending on mode
  50. switch($scope.panel.mode) {
  51. case 'absolute':
  52. $scope.time = {
  53. from : new Date(Date.parse($scope.panel.time.from)) || time_ago($scope.panel.timespan),
  54. to : new Date(Date.parse($scope.panel.time.to)) || new Date()
  55. }
  56. break;
  57. case 'since':
  58. $scope.time = {
  59. from : new Date(Date.parse($scope.panel.time.from)) || time_ago($scope.panel.timespan),
  60. to : new Date() || new Date()
  61. }
  62. break;
  63. case 'relative':
  64. $scope.time = {
  65. from : time_ago($scope.panel.timespan),
  66. to : new Date()
  67. }
  68. break;
  69. }
  70. $scope.time.field = $scope.panel.timefield;
  71. $scope.time_apply();
  72. // Start refresh timer if enabled
  73. if ($scope.panel.refresh.enable)
  74. $scope.set_interval($scope.panel.refresh.interval);
  75. // In the case that a panel is not ready to receive a time event, it may
  76. // request one be sent by broadcasting a 'get_time' with its _id to its group
  77. // This panel can handle multiple groups
  78. eventBus.register($scope,"get_time", function(event,id) {
  79. eventBus.broadcast($scope.$id,id,'time',$scope.time)
  80. });
  81. // In case some other panel broadcasts a time, set us to an absolute range
  82. eventBus.register($scope,"set_time", function(event,time) {
  83. $scope.panel.mode = 'absolute';
  84. set_timepicker(time.from,time.to)
  85. $scope.time_apply()
  86. });
  87. eventBus.register($scope,"zoom", function(event,factor) {
  88. var _timespan = ($scope.time.to.getTime() - $scope.time.from.getTime());
  89. try {
  90. if($scope.panel.mode != 'absolute') {
  91. $scope.panel.mode = 'since'
  92. set_timepicker(new Date($scope.time.to.getTime() - _timespan*factor),$scope.time.to)
  93. } else {
  94. var _center = $scope.time.to - _timespan/2
  95. set_timepicker(new Date(_center - (_timespan*factor)/2),
  96. new Date(_center + (_timespan*factor)/2))
  97. }
  98. } catch (e) {
  99. console.log(e)
  100. }
  101. $scope.time_apply();
  102. });
  103. }
  104. $scope.set_interval = function (refresh_interval) {
  105. $scope.panel.refresh.interval = refresh_interval
  106. if(_.isNumber($scope.panel.refresh.interval)) {
  107. if($scope.panel.refresh.interval < $scope.panel.refresh.min) {
  108. $scope.panel.refresh.interval = $scope.panel.refresh.min
  109. timer.cancel($scope.refresh_timer)
  110. return;
  111. }
  112. timer.cancel($scope.refresh_timer)
  113. $scope.refresh()
  114. } else {
  115. timer.cancel($scope.refresh_timer)
  116. }
  117. }
  118. $scope.refresh = function() {
  119. if ($scope.panel.refresh.enable) {
  120. timer.cancel($scope.refresh_timer)
  121. $scope.refresh_timer = timer.register($timeout(function() {
  122. $scope.refresh();
  123. $scope.time_apply();
  124. },$scope.panel.refresh.interval*1000
  125. ));
  126. } else {
  127. timer.cancel($scope.refresh_timer)
  128. }
  129. }
  130. $scope.set_mode = function(mode) {
  131. $scope.panel.mode = mode;
  132. $scope.panel.refresh.enable = mode === 'absolute' ?
  133. false : $scope.panel.refresh.enable
  134. }
  135. $scope.to_now = function() {
  136. $scope.timepicker.to = {
  137. time : new Date().format("HH:MM:ss"),
  138. date : new Date().format("mm/dd/yyyy")
  139. }
  140. }
  141. $scope.set_timespan = function(timespan) {
  142. $scope.panel.timespan = timespan;
  143. $scope.timepicker.from = {
  144. time : time_ago(timespan).format("HH:MM:ss"),
  145. date : time_ago(timespan).format("mm/dd/yyyy")
  146. }
  147. $scope.time_apply();
  148. }
  149. $scope.close_edit = function() {
  150. $scope.time_apply();
  151. }
  152. //
  153. $scope.time_calc = function(){
  154. // If time picker is defined (on initialization)
  155. if(!(_.isUndefined($scope.timepicker))) {
  156. var from = $scope.panel.mode === 'relative' ? time_ago($scope.panel.timespan) :
  157. new Date(Date.parse($scope.timepicker.from.date + " " + $scope.timepicker.from.time))
  158. var to = $scope.panel.mode !== 'absolute' ? new Date() :
  159. new Date(Date.parse($scope.timepicker.to.date + " " + $scope.timepicker.to.time))
  160. // Otherwise
  161. } else {
  162. var from = $scope.panel.mode === 'relative' ? time_ago($scope.panel.timespan) :
  163. $scope.time.from;
  164. var to = $scope.panel.mode !== 'absolute' ? new Date() :
  165. $scope.time.to;
  166. }
  167. if (from.getTime() >= to.getTime())
  168. from = new Date(to.getTime() - 1000)
  169. $timeout(function(){
  170. set_timepicker(from,to)
  171. });
  172. return {
  173. from : from,
  174. to : to
  175. };
  176. }
  177. $scope.time_apply = function() {
  178. // Update internal time object
  179. $scope.time = $scope.time_calc();
  180. $scope.time.field = $scope.panel.timefield
  181. // Get indices for the time period, then broadcast time range and index list
  182. // in a single object. Not sure if I like this.
  183. if($scope.panel.index_interval !== 'none') {
  184. indices($scope.time.from,$scope.time.to).then(function (p) {
  185. $scope.time.index = p;
  186. eventBus.broadcast($scope.$id,$scope.panel.group,'time',$scope.time)
  187. });
  188. } else {
  189. $scope.time.index = [$scope.panel.index];
  190. eventBus.broadcast($scope.$id,$scope.panel.group,'time',$scope.time)
  191. }
  192. // Update panel's string representation of the time object.Don't update if
  193. // we're in relative mode since we dont want to store the time object in the
  194. // json for relative periods
  195. if($scope.panel.mode !== 'relative') {
  196. $scope.panel.time = {
  197. from : $scope.time.from.format("mm/dd/yyyy HH:MM:ss"),
  198. to : $scope.time.to.format("mm/dd/yyyy HH:MM:ss"),
  199. index : $scope.time.index,
  200. };
  201. } else {
  202. delete $scope.panel.time;
  203. }
  204. };
  205. function set_timepicker(from,to) {
  206. // Janky 0s timeout to get around $scope queue processing view issue
  207. $scope.timepicker = {
  208. from : {
  209. time : from.format("HH:MM:ss"),
  210. date : from.format("mm/dd/yyyy")
  211. },
  212. to : {
  213. time : to.format("HH:MM:ss"),
  214. date : to.format("mm/dd/yyyy")
  215. }
  216. }
  217. }
  218. // returns a promise containing an array of all indices matching the index
  219. // pattern that exist in a given range
  220. function indices(from,to) {
  221. var possible = [];
  222. _.each(expand_range(fake_utc(from),fake_utc(to),$scope.panel.index_interval),function(d){
  223. possible.push(d.format($scope.panel.index));
  224. });
  225. return all_indices().then(function(p) {
  226. var indices = _.intersection(possible,p);
  227. indices.reverse();
  228. return indices.length == 0 ? [$scope.panel.defaultindex] : indices;
  229. })
  230. };
  231. // returns a promise containing an array of all indices in an elasticsearch
  232. // cluster
  233. function all_indices() {
  234. var something = $http({
  235. url: config.elasticsearch + "/_aliases",
  236. method: "GET"
  237. }).error(function(data, status, headers, config) {
  238. $scope.error = status;
  239. });
  240. return something.then(function(p) {
  241. var indices = [];
  242. _.each(p.data, function(v,k) {
  243. indices.push(k)
  244. });
  245. return indices;
  246. });
  247. }
  248. // this is stupid, but there is otherwise no good way to ensure that when
  249. // I extract the date from an object that I'm get the UTC date. Stupid js.
  250. // I die a little inside every time I call this function.
  251. // Update: I just read this again. I died a little more inside.
  252. function fake_utc(date) {
  253. return new Date(date.getTime() + date.getTimezoneOffset() * 60000);
  254. }
  255. // Create an array of date objects by a given interval
  256. function expand_range(start, end, interval) {
  257. if(_.contains(['hour','day','week','month','year'],interval)) {
  258. var range;
  259. start = start.clone();
  260. range = [];
  261. while (start.isBefore(end)) {
  262. range.push(start.clone());
  263. switch (interval) {
  264. case 'hour':
  265. start.addHours(1)
  266. break
  267. case 'day':
  268. start.addDays(1)
  269. break
  270. case 'week':
  271. start.addWeeks(1)
  272. break
  273. case 'month':
  274. start.addMonths(1)
  275. break
  276. case 'year':
  277. start.addYears(1)
  278. break
  279. }
  280. }
  281. range.push(end.clone());
  282. return range;
  283. } else {
  284. return false;
  285. }
  286. }
  287. })