time_srv.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import moment from 'moment';
  2. import _ from 'lodash';
  3. import coreModule from 'app/core/core_module';
  4. import kbn from 'app/core/utils/kbn';
  5. import * as dateMath from 'app/core/utils/datemath';
  6. export class TimeSrv {
  7. time: any;
  8. refreshTimer: any;
  9. refresh: boolean;
  10. oldRefresh: boolean;
  11. dashboard: any;
  12. timeAtLoad: any;
  13. private autoRefreshBlocked: boolean;
  14. /** @ngInject */
  15. constructor(private $rootScope, private $timeout, private $location, private timer, private contextSrv) {
  16. // default time
  17. this.time = { from: '6h', to: 'now' };
  18. $rootScope.$on('zoom-out', this.zoomOut.bind(this));
  19. $rootScope.$on('$routeUpdate', this.routeUpdated.bind(this));
  20. document.addEventListener('visibilitychange', () => {
  21. if (this.autoRefreshBlocked && document.visibilityState === 'visible') {
  22. this.autoRefreshBlocked = false;
  23. this.refreshDashboard();
  24. }
  25. });
  26. }
  27. init(dashboard) {
  28. this.timer.cancelAll();
  29. this.dashboard = dashboard;
  30. this.time = dashboard.time;
  31. this.refresh = dashboard.refresh;
  32. this.initTimeFromUrl();
  33. this.parseTime();
  34. // remember time at load so we can go back to it
  35. this.timeAtLoad = _.cloneDeep(this.time);
  36. if (this.refresh) {
  37. this.setAutoRefresh(this.refresh);
  38. }
  39. }
  40. private parseTime() {
  41. // when absolute time is saved in json it is turned to a string
  42. if (_.isString(this.time.from) && this.time.from.indexOf('Z') >= 0) {
  43. this.time.from = moment(this.time.from).utc();
  44. }
  45. if (_.isString(this.time.to) && this.time.to.indexOf('Z') >= 0) {
  46. this.time.to = moment(this.time.to).utc();
  47. }
  48. }
  49. private parseUrlParam(value) {
  50. if (value.indexOf('now') !== -1) {
  51. return value;
  52. }
  53. if (value.length === 8) {
  54. return moment.utc(value, 'YYYYMMDD');
  55. }
  56. if (value.length === 15) {
  57. return moment.utc(value, 'YYYYMMDDTHHmmss');
  58. }
  59. if (!isNaN(value)) {
  60. const epoch = parseInt(value);
  61. return moment.utc(epoch);
  62. }
  63. return null;
  64. }
  65. private initTimeFromUrl() {
  66. const params = this.$location.search();
  67. if (params.from) {
  68. this.time.from = this.parseUrlParam(params.from) || this.time.from;
  69. }
  70. if (params.to) {
  71. this.time.to = this.parseUrlParam(params.to) || this.time.to;
  72. }
  73. if (params.refresh) {
  74. this.refresh = params.refresh || this.refresh;
  75. }
  76. }
  77. private routeUpdated() {
  78. const params = this.$location.search();
  79. const urlRange = this.timeRangeForUrl();
  80. // check if url has time range
  81. if (params.from && params.to) {
  82. // is it different from what our current time range?
  83. if (params.from !== urlRange.from || params.to !== urlRange.to) {
  84. // issue update
  85. this.initTimeFromUrl();
  86. this.setTime(this.time, true);
  87. }
  88. } else if (this.timeHasChangedSinceLoad()) {
  89. this.setTime(this.timeAtLoad, true);
  90. }
  91. }
  92. private timeHasChangedSinceLoad() {
  93. return this.timeAtLoad.from !== this.time.from || this.timeAtLoad.to !== this.time.to;
  94. }
  95. setAutoRefresh(interval) {
  96. this.dashboard.refresh = interval;
  97. this.cancelNextRefresh();
  98. if (interval) {
  99. const intervalMs = kbn.interval_to_ms(interval);
  100. this.refreshTimer = this.timer.register(
  101. this.$timeout(() => {
  102. this.startNextRefreshTimer(intervalMs);
  103. this.refreshDashboard();
  104. }, intervalMs)
  105. );
  106. }
  107. // update url
  108. const params = this.$location.search();
  109. if (interval) {
  110. params.refresh = interval;
  111. this.$location.search(params);
  112. } else if (params.refresh) {
  113. delete params.refresh;
  114. this.$location.search(params);
  115. }
  116. }
  117. refreshDashboard() {
  118. this.$rootScope.$broadcast('refresh');
  119. }
  120. private startNextRefreshTimer(afterMs) {
  121. this.cancelNextRefresh();
  122. this.refreshTimer = this.timer.register(
  123. this.$timeout(() => {
  124. this.startNextRefreshTimer(afterMs);
  125. if (this.contextSrv.isGrafanaVisible()) {
  126. this.refreshDashboard();
  127. } else {
  128. this.autoRefreshBlocked = true;
  129. }
  130. }, afterMs)
  131. );
  132. }
  133. private cancelNextRefresh() {
  134. this.timer.cancel(this.refreshTimer);
  135. }
  136. setTime(time, fromRouteUpdate?) {
  137. _.extend(this.time, time);
  138. // disable refresh if zoom in or zoom out
  139. if (moment.isMoment(time.to)) {
  140. this.oldRefresh = this.dashboard.refresh || this.oldRefresh;
  141. this.setAutoRefresh(false);
  142. } else if (this.oldRefresh && this.oldRefresh !== this.dashboard.refresh) {
  143. this.setAutoRefresh(this.oldRefresh);
  144. this.oldRefresh = null;
  145. }
  146. // update url
  147. if (fromRouteUpdate !== true) {
  148. const urlRange = this.timeRangeForUrl();
  149. const urlParams = this.$location.search();
  150. urlParams.from = urlRange.from;
  151. urlParams.to = urlRange.to;
  152. this.$location.search(urlParams);
  153. }
  154. this.$rootScope.appEvent('time-range-changed', this.time);
  155. this.$timeout(this.refreshDashboard.bind(this), 0);
  156. }
  157. timeRangeForUrl() {
  158. const range = this.timeRange().raw;
  159. if (moment.isMoment(range.from)) {
  160. range.from = range.from.valueOf().toString();
  161. }
  162. if (moment.isMoment(range.to)) {
  163. range.to = range.to.valueOf().toString();
  164. }
  165. return range;
  166. }
  167. timeRange() {
  168. // make copies if they are moment (do not want to return out internal moment, because they are mutable!)
  169. const raw = {
  170. from: moment.isMoment(this.time.from) ? moment(this.time.from) : this.time.from,
  171. to: moment.isMoment(this.time.to) ? moment(this.time.to) : this.time.to,
  172. };
  173. const timezone = this.dashboard && this.dashboard.getTimezone();
  174. return {
  175. from: dateMath.parse(raw.from, false, timezone),
  176. to: dateMath.parse(raw.to, true, timezone),
  177. raw: raw,
  178. };
  179. }
  180. zoomOut(e, factor) {
  181. const range = this.timeRange();
  182. const timespan = range.to.valueOf() - range.from.valueOf();
  183. const center = range.to.valueOf() - timespan / 2;
  184. let to = center + timespan * factor / 2;
  185. let from = center - timespan * factor / 2;
  186. if (to > Date.now() && range.to <= Date.now()) {
  187. const offset = to - Date.now();
  188. from = from - offset;
  189. to = Date.now();
  190. }
  191. this.setTime({ from: moment.utc(from), to: moment.utc(to) });
  192. }
  193. }
  194. coreModule.service('timeSrv', TimeSrv);