unsavedChangesSrv.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. define([
  2. 'angular',
  3. 'lodash',
  4. 'config',
  5. ],
  6. function(angular, _, config) {
  7. 'use strict';
  8. if (!config.unsaved_changes_warning) {
  9. return;
  10. }
  11. var module = angular.module('grafana.services');
  12. module.service('unsavedChangesSrv', function($rootScope, $modal, $q, $location, $timeout) {
  13. var self = this;
  14. var modalScope = $rootScope.$new();
  15. $rootScope.$on("dashboard-loaded", function(event, newDashboard) {
  16. // wait for different services to patch the dashboard (missing properties)
  17. $timeout(function() {
  18. self.original = newDashboard.getSaveModelClone();
  19. self.current = newDashboard;
  20. }, 1200);
  21. });
  22. $rootScope.$on("dashboard-saved", function(event, savedDashboard) {
  23. self.original = savedDashboard.getSaveModelClone();
  24. self.current = savedDashboard;
  25. self.orignalPath = $location.path();
  26. });
  27. $rootScope.$on("$routeChangeSuccess", function() {
  28. self.original = null;
  29. self.originalPath = $location.path();
  30. });
  31. this.ignoreChanges = function() {
  32. if (!self.current) { return true; }
  33. var meta = self.current.meta;
  34. return !meta.canSave || meta.fromScript || meta.fromFile;
  35. };
  36. window.onbeforeunload = function() {
  37. if (self.ignoreChanges()) { return; }
  38. if (self.has_unsaved_changes()) {
  39. return "There are unsaved changes to this dashboard";
  40. }
  41. };
  42. this.init = function() {
  43. $rootScope.$on("$locationChangeStart", function(event, next) {
  44. // check if we should look for changes
  45. if (self.originalPath === $location.path()) { return true; }
  46. if (self.ignoreChanges()) { return true; }
  47. if (self.has_unsaved_changes()) {
  48. event.preventDefault();
  49. self.next = next;
  50. $timeout(self.open_modal);
  51. }
  52. });
  53. };
  54. this.open_modal = function() {
  55. var confirmModal = $modal({
  56. template: './app/partials/unsaved-changes.html',
  57. modalClass: 'confirm-modal',
  58. persist: true,
  59. show: false,
  60. scope: modalScope,
  61. keyboard: false
  62. });
  63. $q.when(confirmModal).then(function(modalEl) {
  64. modalEl.modal('show');
  65. });
  66. };
  67. this.has_unsaved_changes = function() {
  68. if (!self.original) {
  69. return false;
  70. }
  71. var current = self.current.getSaveModelClone();
  72. var original = self.original;
  73. // ignore timespan changes
  74. current.time = original.time = {};
  75. current.refresh = original.refresh;
  76. // ignore version
  77. current.version = original.version;
  78. // ignore template variable values
  79. _.each(current.templating.list, function(value, index) {
  80. value.current = null;
  81. value.options = null;
  82. if (original.templating.list.length > index) {
  83. original.templating.list[index].current = null;
  84. original.templating.list[index].options = null;
  85. }
  86. });
  87. // ignore some panel and row stuff
  88. current.forEachPanel(function(panel, panelIndex, row, rowIndex) {
  89. var originalRow = original.rows[rowIndex];
  90. var originalPanel = original.getPanelById(panel.id);
  91. // ignore row collapse state
  92. if (originalRow) {
  93. row.collapse = originalRow.collapse;
  94. }
  95. if (originalPanel) {
  96. // ignore graph legend sort
  97. if (originalPanel.legend && panel.legend) {
  98. delete originalPanel.legend.sortDesc;
  99. delete originalPanel.legend.sort;
  100. delete panel.legend.sort;
  101. delete panel.legend.sortDesc;
  102. }
  103. }
  104. });
  105. var currentTimepicker = _.findWhere(current.nav, { type: 'timepicker' });
  106. var originalTimepicker = _.findWhere(original.nav, { type: 'timepicker' });
  107. if (currentTimepicker && originalTimepicker) {
  108. currentTimepicker.now = originalTimepicker.now;
  109. }
  110. var currentJson = angular.toJson(current);
  111. var originalJson = angular.toJson(original);
  112. if (currentJson !== originalJson) {
  113. return true;
  114. }
  115. return false;
  116. };
  117. this.goto_next = function() {
  118. var baseLen = $location.absUrl().length - $location.url().length;
  119. var nextUrl = self.next.substring(baseLen);
  120. $location.url(nextUrl);
  121. };
  122. modalScope.ignore = function() {
  123. self.original = null;
  124. self.goto_next();
  125. };
  126. modalScope.save = function() {
  127. var unregister = $rootScope.$on('dashboard-saved', function() {
  128. self.goto_next();
  129. });
  130. $timeout(unregister, 2000);
  131. $rootScope.$emit('save-dashboard');
  132. };
  133. }).run(function(unsavedChangesSrv) {
  134. unsavedChangesSrv.init();
  135. });
  136. });