| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- define([
- 'angular',
- 'lodash',
- ],
- function(angular, _) {
- 'use strict';
- var module = angular.module('grafana.services');
- module.service('unsavedChangesSrv', function($rootScope, $q, $location, $timeout, contextSrv, dashboardSrv, $window) {
- function Tracker(dashboard, scope, originalCopyDelay) {
- var self = this;
- this.current = dashboard;
- this.originalPath = $location.path();
- this.scope = scope;
- // register events
- scope.onAppEvent('dashboard-saved', function() {
- this.original = this.current.getSaveModelClone();
- this.originalPath = $location.path();
- }.bind(this));
- $window.onbeforeunload = function() {
- if (self.ignoreChanges()) { return; }
- if (self.hasChanges()) {
- return "There are unsaved changes to this dashboard";
- }
- };
- scope.$on("$locationChangeStart", function(event, next) {
- // check if we should look for changes
- if (self.originalPath === $location.path()) { return true; }
- if (self.ignoreChanges()) { return true; }
- if (self.hasChanges()) {
- event.preventDefault();
- self.next = next;
- $timeout(function() {
- self.open_modal();
- });
- }
- });
- if (originalCopyDelay) {
- $timeout(function() {
- // wait for different services to patch the dashboard (missing properties)
- self.original = dashboard.getSaveModelClone();
- }, originalCopyDelay);
- } else {
- self.original = dashboard.getSaveModelClone();
- }
- }
- var p = Tracker.prototype;
- // for some dashboards and users
- // changes should be ignored
- p.ignoreChanges = function() {
- if (!this.original) { return true; }
- if (!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
- p.cleanDashboardFromIgnoredChanges = function(dash) {
- // ignore time and refresh
- dash.time = 0;
- dash.refresh = 0;
- dash.schemaVersion = 0;
- dash.editMode = false;
- // 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;
- });
- // ignore template variable values
- _.each(dash.templating.list, function(value) {
- value.current = null;
- value.options = null;
- value.filters = null;
- });
- };
- p.hasChanges = function() {
- 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;
- };
- p.open_modal = function() {
- var tracker = this;
- var dashboard = this.current;
- var modalScope = this.scope.$new();
- var clone = dashboard.getSaveModelClone();
- modalScope.clone = clone;
- modalScope.ignore = function() {
- tracker.original = null;
- tracker.goto_next();
- };
- var cancel = $rootScope.$on('dashboard-saved', function() {
- cancel();
- $timeout(function() {
- tracker.goto_next();
- });
- });
- $rootScope.appEvent('show-modal', {
- src: 'public/app/partials/unsaved-changes.html',
- scope: modalScope,
- modalClass: 'modal--narrow'
- });
- };
- p.goto_next = function() {
- var baseLen = $location.absUrl().length - $location.url().length;
- var nextUrl = this.next.substring(baseLen);
- $location.url(nextUrl);
- };
- this.Tracker = Tracker;
- this.init = function(dashboard, scope) {
- return new Tracker(dashboard, scope, 1000);
- };
- });
- });
|