119 lines
3.9 KiB
JavaScript
119 lines
3.9 KiB
JavaScript
/// crmAutosave
|
|
(function(angular, $, _) {
|
|
|
|
angular.module('crmAutosave', CRM.angRequires('crmAutosave'));
|
|
|
|
// usage:
|
|
// var autosave = new CrmAutosaveCtrl({
|
|
// save: function -- A function to handle saving. Should return a promise.
|
|
// If it's not a promise, then we'll assume that it completes successfully.
|
|
// saveIf: function -- Only allow autosave when conditional returns true. Default: !form.$invalid
|
|
// model: object|function -- (Re)schedule saves based on observed changes to object. We perform deep
|
|
// inspection on the model object. This could be a performance issue you
|
|
// had many concurrent autosave forms or a particularly large model, but
|
|
// it should be fine with typical usage.
|
|
// interval: object -- Interval spec. Default: {poll: 250, save: 5000}
|
|
// form: object|function -- FormController or its getter
|
|
// });
|
|
// autosave.start();
|
|
// $scope.$on('$destroy', autosave.stop);
|
|
// Note: if the save operation itself
|
|
angular.module('crmAutosave').service('CrmAutosaveCtrl', function($interval, $timeout, $q) {
|
|
function CrmAutosaveCtrl(options) {
|
|
var intervals = angular.extend({poll: 250, save: 5000}, options.interval);
|
|
var jobs = {poll: null, save: null}; // job handles used ot cancel/reschedule timeouts/intervals
|
|
var lastSeenModel = null;
|
|
var saving = false;
|
|
|
|
// Determine if model has changed; (re)schedule the save.
|
|
// This is a bit expensive and doesn't need to be continuous, so we use polling instead of watches.
|
|
function checkChanges() {
|
|
if (saving) {
|
|
return;
|
|
}
|
|
var currentModel = _.isFunction(options.model) ? options.model() : options.model;
|
|
if (!angular.equals(currentModel, lastSeenModel)) {
|
|
lastSeenModel = angular.copy(currentModel);
|
|
if (jobs.save) {
|
|
$timeout.cancel(jobs.save);
|
|
}
|
|
jobs.save = $timeout(doAutosave, intervals.save);
|
|
}
|
|
}
|
|
|
|
function doAutosave() {
|
|
jobs.save = null;
|
|
if (saving) {
|
|
return;
|
|
}
|
|
|
|
var form = _.isFunction(options.form) ? options.form() : options.form;
|
|
|
|
if (options.saveIf) {
|
|
if (!options.saveIf()) {
|
|
return;
|
|
}
|
|
}
|
|
else if (form && form.$invalid) {
|
|
return;
|
|
}
|
|
|
|
saving = true;
|
|
lastSeenModel = angular.copy(_.isFunction(options.model) ? options.model() : options.model);
|
|
|
|
// Set to pristine before saving -- not after saving.
|
|
// If an eager user continues editing concurrent with the
|
|
// save process, then the form should become dirty again.
|
|
if (form) {
|
|
form.$setPristine();
|
|
}
|
|
var res = options.save();
|
|
if (res && res.then) {
|
|
res.then(
|
|
function() {
|
|
saving = false;
|
|
},
|
|
function() {
|
|
saving = false;
|
|
if (form) {
|
|
form.$setDirty();
|
|
}
|
|
}
|
|
);
|
|
}
|
|
else {
|
|
saving = false;
|
|
}
|
|
}
|
|
|
|
var self = this;
|
|
|
|
this.start = function() {
|
|
if (!jobs.poll) {
|
|
lastSeenModel = angular.copy(_.isFunction(options.model) ? options.model() : options.model);
|
|
jobs.poll = $interval(checkChanges, intervals.poll);
|
|
}
|
|
};
|
|
|
|
this.stop = function() {
|
|
if (jobs.poll) {
|
|
$interval.cancel(jobs.poll);
|
|
jobs.poll = null;
|
|
}
|
|
if (jobs.save) {
|
|
$timeout.cancel(jobs.save);
|
|
jobs.save = null;
|
|
}
|
|
};
|
|
|
|
this.suspend = function(p) {
|
|
self.stop();
|
|
return p.finally(self.start);
|
|
};
|
|
}
|
|
|
|
return CrmAutosaveCtrl;
|
|
});
|
|
|
|
})(angular, CRM.$, CRM._);
|