module.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  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. timeformat : "",
  36. group : "default",
  37. refresh : {
  38. enable : false,
  39. interval: 30,
  40. min : 3
  41. }
  42. }
  43. _.defaults($scope.panel,_d)
  44. var _groups = _.isArray($scope.panel.group) ?
  45. $scope.panel.group : [$scope.panel.group];
  46. $scope.init = function() {
  47. // Private refresh interval that we can use for view display without causing
  48. // unnecessary refreshes during changes
  49. $scope.refresh_interval = $scope.panel.refresh.interval
  50. // Init a private time object with Date() objects depending on mode
  51. switch($scope.panel.mode) {
  52. case 'absolute':
  53. $scope.time = {
  54. from : moment($scope.panel.time.from,'YYYY-MM-DD HH:mm:ss') || moment(time_ago($scope.panel.timespan)),
  55. to : moment($scope.panel.time.to,'YYYY-MM-DD HH:mm:ss') || moment()
  56. }
  57. break;
  58. case 'since':
  59. $scope.time = {
  60. from : moment($scope.panel.time.from,'YYYY-MM-DD HH:mm:ss') || moment(time_ago($scope.panel.timespan)),
  61. to : moment()
  62. }
  63. break;
  64. case 'relative':
  65. $scope.time = {
  66. from : moment(time_ago($scope.panel.timespan)),
  67. to : moment()
  68. }
  69. break;
  70. }
  71. $scope.time.field = $scope.panel.timefield;
  72. $scope.time_apply();
  73. // Start refresh timer if enabled
  74. if ($scope.panel.refresh.enable)
  75. $scope.set_interval($scope.panel.refresh.interval);
  76. // In the case that a panel is not ready to receive a time event, it may
  77. // request one be sent by broadcasting a 'get_time' with its _id to its group
  78. // This panel can handle multiple groups
  79. eventBus.register($scope,"get_time", function(event,id) {
  80. eventBus.broadcast($scope.$id,id,'time',unmoment($scope.time))
  81. });
  82. // In case some other panel broadcasts a time, set us to an absolute range
  83. eventBus.register($scope,"set_time", function(event,time) {
  84. $scope.panel.mode = 'absolute';
  85. set_timepicker(moment(time.from),moment(time.to))
  86. $scope.time_apply()
  87. });
  88. eventBus.register($scope,"zoom", function(event,factor) {
  89. var _timespan = ($scope.time.to.valueOf() - $scope.time.from.valueOf());
  90. try {
  91. if($scope.panel.mode != 'absolute') {
  92. $scope.panel.mode = 'since'
  93. set_timepicker(moment($scope.time.to.valueOf() - _timespan*factor),$scope.time.to)
  94. } else {
  95. var _center = $scope.time.to.valueOf() - _timespan/2
  96. set_timepicker(moment(_center - (_timespan*factor)/2),
  97. moment(_center + (_timespan*factor)/2))
  98. }
  99. } catch (e) {
  100. console.log(e)
  101. }
  102. $scope.time_apply();
  103. });
  104. }
  105. $scope.set_interval = function (refresh_interval) {
  106. $scope.panel.refresh.interval = refresh_interval
  107. if(_.isNumber($scope.panel.refresh.interval)) {
  108. if($scope.panel.refresh.interval < $scope.panel.refresh.min) {
  109. $scope.panel.refresh.interval = $scope.panel.refresh.min
  110. timer.cancel($scope.refresh_timer)
  111. return;
  112. }
  113. timer.cancel($scope.refresh_timer)
  114. $scope.refresh()
  115. } else {
  116. timer.cancel($scope.refresh_timer)
  117. }
  118. }
  119. $scope.refresh = function() {
  120. if ($scope.panel.refresh.enable) {
  121. timer.cancel($scope.refresh_timer)
  122. $scope.refresh_timer = timer.register($timeout(function() {
  123. $scope.refresh();
  124. $scope.time_apply();
  125. },$scope.panel.refresh.interval*1000
  126. ));
  127. } else {
  128. timer.cancel($scope.refresh_timer)
  129. }
  130. }
  131. $scope.set_mode = function(mode) {
  132. $scope.panel.mode = mode;
  133. $scope.panel.refresh.enable = mode === 'absolute' ?
  134. false : $scope.panel.refresh.enable
  135. }
  136. $scope.to_now = function() {
  137. $scope.timepicker.to = {
  138. time : moment().format("HH:mm:ss"),
  139. date : moment().format("MM/DD/YYYY")
  140. }
  141. }
  142. $scope.set_timespan = function(timespan) {
  143. $scope.panel.timespan = timespan;
  144. $scope.timepicker.from = {
  145. time : moment(time_ago(timespan)).format("HH:mm:ss"),
  146. date : moment(time_ago(timespan)).format("MM/DD/YYYY")
  147. }
  148. $scope.time_apply();
  149. }
  150. $scope.close_edit = function() {
  151. $scope.time_apply();
  152. }
  153. //
  154. $scope.time_calc = function(){
  155. // If time picker is defined (usually is)
  156. if(!(_.isUndefined($scope.timepicker))) {
  157. var from = $scope.panel.mode === 'relative' ? moment(time_ago($scope.panel.timespan)) :
  158. moment($scope.timepicker.from.date + " " + $scope.timepicker.from.time,'MM/DD/YYYY HH:mm:ss')
  159. var to = $scope.panel.mode !== 'absolute' ? moment() :
  160. moment($scope.timepicker.to.date + " " + $scope.timepicker.to.time,'MM/DD/YYYY HH:mm:ss')
  161. // Otherwise (probably initialization)
  162. } else {
  163. var from = $scope.panel.mode === 'relative' ? moment(time_ago($scope.panel.timespan)) :
  164. $scope.time.from;
  165. var to = $scope.panel.mode !== 'absolute' ? moment() :
  166. $scope.time.to;
  167. }
  168. if (from.valueOf() >= to.valueOf())
  169. from = moment(to.valueOf() - 1000)
  170. $timeout(function(){
  171. set_timepicker(from,to)
  172. });
  173. return {
  174. from : from,
  175. to : to
  176. };
  177. }
  178. $scope.time_apply = function() {
  179. // Update internal time object
  180. $scope.time = $scope.time_calc();
  181. $scope.time.field = $scope.panel.timefield
  182. // Get indices for the time period, then broadcast time range and index list
  183. // in a single object. Not sure if I like this.
  184. if($scope.panel.index_interval !== 'none') {
  185. indices($scope.time.from,$scope.time.to).then(function (p) {
  186. $scope.time.index = p;
  187. eventBus.broadcast($scope.$id,$scope.panel.group,'time',unmoment($scope.time))
  188. });
  189. } else {
  190. $scope.time.index = [$scope.panel.index];
  191. eventBus.broadcast($scope.$id,$scope.panel.group,'time',unmoment($scope.time))
  192. }
  193. // Update panel's string representation of the time object.Don't update if
  194. // we're in relative mode since we dont want to store the time object in the
  195. // json for relative periods
  196. if($scope.panel.mode !== 'relative') {
  197. $scope.panel.time = {
  198. from : $scope.time.from.format("MM/DD/YYYY HH:mm:ss"),
  199. to : $scope.time.to.format("MM/DD/YYYY HH:mm:ss"),
  200. index : $scope.time.index,
  201. };
  202. } else {
  203. delete $scope.panel.time;
  204. }
  205. };
  206. // Prefer to pass around Date() objects in the EventBus since interacting with
  207. // moment objects in libraries that are expecting Date()s can be tricky
  208. function unmoment(time) {
  209. time = _.clone(time)
  210. time.from = time.from.toDate()
  211. time.to = time.to.toDate()
  212. return time;
  213. }
  214. function set_timepicker(from,to) {
  215. // Janky 0s timeout to get around $scope queue processing view issue
  216. $scope.timepicker = {
  217. from : {
  218. time : from.format("HH:mm:ss"),
  219. date : from.format("MM/DD/YYYY")
  220. },
  221. to : {
  222. time : to.format("HH:mm:ss"),
  223. date : to.format("MM/DD/YYYY")
  224. }
  225. }
  226. }
  227. // returns a promise containing an array of all indices matching the index
  228. // pattern that exist in a given range
  229. function indices(from,to) {
  230. var possible = [];
  231. _.each(expand_range(fake_utc(from),fake_utc(to),$scope.panel.index_interval),function(d){
  232. possible.push(d.format($scope.panel.index));
  233. });
  234. return all_indices().then(function(p) {
  235. var indices = _.intersection(possible,p);
  236. indices.reverse();
  237. return indices.length == 0 ? [$scope.panel.defaultindex] : indices;
  238. })
  239. };
  240. // returns a promise containing an array of all indices in an elasticsearch
  241. // cluster
  242. function all_indices() {
  243. var something = $http({
  244. url: config.elasticsearch + "/_aliases",
  245. method: "GET"
  246. }).error(function(data, status, headers, config) {
  247. $scope.error = status;
  248. });
  249. return something.then(function(p) {
  250. var indices = [];
  251. _.each(p.data, function(v,k) {
  252. indices.push(k)
  253. });
  254. return indices;
  255. });
  256. }
  257. // this is stupid, but there is otherwise no good way to ensure that when
  258. // I extract the date from an object that I'm get the UTC date. Stupid js.
  259. // I die a little inside every time I call this function.
  260. // Update: I just read this again. I died a little more inside.
  261. // Update2: More death.
  262. function fake_utc(date) {
  263. date = date.clone().toDate()
  264. return moment(new Date(date.getTime() + date.getTimezoneOffset() * 60000));
  265. }
  266. // Create an array of date objects by a given interval
  267. function expand_range(start, end, interval) {
  268. if(_.contains(['hour','day','week','month','year'],interval)) {
  269. var range;
  270. start = start.clone();
  271. range = [];
  272. while (start.isBefore(end)) {
  273. range.push(start.clone());
  274. switch (interval) {
  275. case 'hour':
  276. start.add('hours',1)
  277. break
  278. case 'day':
  279. start.add('days',1)
  280. break
  281. case 'week':
  282. start.add('weeks',1)
  283. break
  284. case 'month':
  285. start.add('months',1)
  286. break
  287. case 'year':
  288. start.add('years',1)
  289. break
  290. }
  291. }
  292. range.push(end.clone());
  293. return range;
  294. } else {
  295. return false;
  296. }
  297. }
  298. })