| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- import angular from 'angular';
- import _ from 'lodash';
- export class Tracker {
- current: any;
- originalPath: any;
- scope: any;
- original: any;
- next: any;
- $window: any;
- /** @ngInject */
- constructor(
- dashboard,
- scope,
- originalCopyDelay,
- private $location,
- $window,
- private $timeout,
- private contextSrv,
- private $rootScope
- ) {
- this.$location = $location;
- this.$window = $window;
- this.current = dashboard;
- this.originalPath = $location.path();
- this.scope = scope;
- // register events
- scope.onAppEvent('dashboard-saved', () => {
- this.original = this.current.getSaveModelClone();
- this.originalPath = $location.path();
- });
- $window.onbeforeunload = () => {
- if (this.ignoreChanges()) {
- return '';
- }
- if (this.hasChanges()) {
- return 'There are unsaved changes to this dashboard';
- }
- return '';
- };
- scope.$on('$locationChangeStart', (event, next) => {
- // check if we should look for changes
- if (this.originalPath === $location.path()) {
- return true;
- }
- if (this.ignoreChanges()) {
- return true;
- }
- if (this.hasChanges()) {
- event.preventDefault();
- this.next = next;
- this.$timeout(() => {
- this.open_modal();
- });
- }
- return false;
- });
- if (originalCopyDelay) {
- this.$timeout(() => {
- // wait for different services to patch the dashboard (missing properties)
- this.original = dashboard.getSaveModelClone();
- }, originalCopyDelay);
- } else {
- this.original = dashboard.getSaveModelClone();
- }
- }
- // for some dashboards and users
- // changes should be ignored
- ignoreChanges() {
- if (!this.original) {
- return true;
- }
- if (!this.contextSrv.isEditor) {
- return true;
- }
- if (!this.current || !this.current.meta) {
- return true;
- }
- var meta = this.current.meta;
- return !meta.canSave || meta.fromScript || meta.fromFile;
- }
- // remove stuff that should not count in diff
- cleanDashboardFromIgnoredChanges(dash) {
- // ignore time and refresh
- dash.time = 0;
- dash.refresh = 0;
- dash.schemaVersion = 0;
- // filter row and panels properties that should be ignored
- dash.rows = _.filter(dash.rows, function(row) {
- if (row.repeatRowId) {
- return false;
- }
- row.panels = _.filter(row.panels, function(panel) {
- if (panel.repeatPanelId) {
- return false;
- }
- // remove scopedVars
- panel.scopedVars = null;
- // ignore span changes
- panel.span = null;
- // ignore panel legend sort
- if (panel.legend) {
- delete panel.legend.sort;
- delete panel.legend.sortDesc;
- }
- return true;
- });
- // ignore collapse state
- row.collapse = false;
- return true;
- });
- dash.panels = _.filter(dash.panels, panel => {
- if (panel.repeatPanelId) {
- return false;
- }
- // remove scopedVars
- panel.scopedVars = null;
- // ignore panel legend sort
- if (panel.legend) {
- delete panel.legend.sort;
- delete panel.legend.sortDesc;
- }
- return true;
- });
- // ignore template variable values
- _.each(dash.templating.list, function(value) {
- value.current = null;
- value.options = null;
- value.filters = null;
- });
- }
- hasChanges() {
- var current = this.current.getSaveModelClone();
- var original = this.original;
- this.cleanDashboardFromIgnoredChanges(current);
- this.cleanDashboardFromIgnoredChanges(original);
- var currentTimepicker = _.find(current.nav, { type: 'timepicker' });
- var originalTimepicker = _.find(original.nav, { type: 'timepicker' });
- if (currentTimepicker && originalTimepicker) {
- currentTimepicker.now = originalTimepicker.now;
- }
- var currentJson = angular.toJson(current);
- var originalJson = angular.toJson(original);
- return currentJson !== originalJson;
- }
- discardChanges() {
- this.original = null;
- this.gotoNext();
- }
- open_modal() {
- this.$rootScope.appEvent('show-modal', {
- templateHtml:
- '<unsaved-changes-modal dismiss="dismiss()"></unsaved-changes-modal>',
- modalClass: 'modal--narrow confirm-modal',
- });
- }
- saveChanges() {
- var self = this;
- var cancel = this.$rootScope.$on('dashboard-saved', () => {
- cancel();
- this.$timeout(() => {
- self.gotoNext();
- });
- });
- this.$rootScope.appEvent('save-dashboard');
- }
- gotoNext() {
- var baseLen = this.$location.absUrl().length - this.$location.url().length;
- var nextUrl = this.next.substring(baseLen);
- this.$location.url(nextUrl);
- }
- }
- /** @ngInject */
- export function unsavedChangesSrv(
- $rootScope,
- $q,
- $location,
- $timeout,
- contextSrv,
- dashboardSrv,
- $window
- ) {
- this.Tracker = Tracker;
- this.init = function(dashboard, scope) {
- this.tracker = new Tracker(
- dashboard,
- scope,
- 1000,
- $location,
- $window,
- $timeout,
- contextSrv,
- $rootScope
- );
- return this.tracker;
- };
- }
- angular
- .module('grafana.services')
- .service('unsavedChangesSrv', unsavedChangesSrv);
|