Merge pull request #19736 from colemanw/empty
[civicrm-core.git] / ang / crmAutosave.js
CommitLineData
2386ec88
TO
1/// crmAutosave
2(function(angular, $, _) {
3
0b199194 4 angular.module('crmAutosave', CRM.angRequires('crmAutosave'));
2386ec88 5
06e1fe0a
TO
6 // usage:
7 // var autosave = new CrmAutosaveCtrl({
8 // save: function -- A function to handle saving. Should return a promise.
9 // If it's not a promise, then we'll assume that it completes successfully.
10 // saveIf: function -- Only allow autosave when conditional returns true. Default: !form.$invalid
11 // model: object|function -- (Re)schedule saves based on observed changes to object. We perform deep
12 // inspection on the model object. This could be a performance issue you
13 // had many concurrent autosave forms or a particularly large model, but
14 // it should be fine with typical usage.
15 // interval: object -- Interval spec. Default: {poll: 250, save: 5000}
16 // form: object|function -- FormController or its getter
17 // });
18 // autosave.start();
19 // $scope.$on('$destroy', autosave.stop);
20 // Note: if the save operation itself
6877567a 21 angular.module('crmAutosave').service('CrmAutosaveCtrl', function($interval, $timeout, $q) {
06e1fe0a
TO
22 function CrmAutosaveCtrl(options) {
23 var intervals = angular.extend({poll: 250, save: 5000}, options.interval);
24 var jobs = {poll: null, save: null}; // job handles used ot cancel/reschedule timeouts/intervals
25 var lastSeenModel = null;
26 var saving = false;
2386ec88 27
06e1fe0a
TO
28 // Determine if model has changed; (re)schedule the save.
29 // This is a bit expensive and doesn't need to be continuous, so we use polling instead of watches.
30 function checkChanges() {
31 if (saving) {
32 return;
33 }
34 var currentModel = _.isFunction(options.model) ? options.model() : options.model;
35 if (!angular.equals(currentModel, lastSeenModel)) {
36 lastSeenModel = angular.copy(currentModel);
37 if (jobs.save) {
38 $timeout.cancel(jobs.save);
2386ec88 39 }
06e1fe0a 40 jobs.save = $timeout(doAutosave, intervals.save);
2386ec88 41 }
06e1fe0a 42 }
2386ec88 43
06e1fe0a
TO
44 function doAutosave() {
45 jobs.save = null;
46 if (saving) {
47 return;
48 }
2386ec88 49
06e1fe0a
TO
50 var form = _.isFunction(options.form) ? options.form() : options.form;
51
52 if (options.saveIf) {
53 if (!options.saveIf()) {
2386ec88
TO
54 return;
55 }
06e1fe0a
TO
56 }
57 else if (form && form.$invalid) {
58 return;
59 }
2386ec88 60
06e1fe0a
TO
61 saving = true;
62 lastSeenModel = angular.copy(_.isFunction(options.model) ? options.model() : options.model);
2386ec88 63
06e1fe0a
TO
64 // Set to pristine before saving -- not after saving.
65 // If an eager user continues editing concurrent with the
66 // save process, then the form should become dirty again.
67 if (form) {
2386ec88 68 form.$setPristine();
06e1fe0a
TO
69 }
70 var res = options.save();
71 if (res && res.then) {
72 res.then(
73 function() {
74 saving = false;
75 },
76 function() {
77 saving = false;
78 if (form) {
2386ec88
TO
79 form.$setDirty();
80 }
06e1fe0a
TO
81 }
82 );
2386ec88 83 }
06e1fe0a
TO
84 else {
85 saving = false;
86 }
87 }
2386ec88 88
6877567a
TO
89 var self = this;
90
06e1fe0a
TO
91 this.start = function() {
92 if (!jobs.poll) {
93 lastSeenModel = angular.copy(_.isFunction(options.model) ? options.model() : options.model);
94 jobs.poll = $interval(checkChanges, intervals.poll);
95 }
96 };
2386ec88 97
06e1fe0a
TO
98 this.stop = function() {
99 if (jobs.poll) {
100 $interval.cancel(jobs.poll);
101 jobs.poll = null;
102 }
103 if (jobs.save) {
104 $timeout.cancel(jobs.save);
105 jobs.save = null;
106 }
107 };
6877567a
TO
108
109 this.suspend = function(p) {
110 self.stop();
111 return p.finally(self.start);
112 };
06e1fe0a
TO
113 }
114
115 return CrmAutosaveCtrl;
2386ec88
TO
116 });
117
118})(angular, CRM.$, CRM._);