summaryrefslogtreecommitdiff
path: root/www/crm/wp-content/plugins/civicrm/civicrm/ang/crmAutosave.js
diff options
context:
space:
mode:
Diffstat (limited to 'www/crm/wp-content/plugins/civicrm/civicrm/ang/crmAutosave.js')
-rw-r--r--www/crm/wp-content/plugins/civicrm/civicrm/ang/crmAutosave.js118
1 files changed, 118 insertions, 0 deletions
diff --git a/www/crm/wp-content/plugins/civicrm/civicrm/ang/crmAutosave.js b/www/crm/wp-content/plugins/civicrm/civicrm/ang/crmAutosave.js
new file mode 100644
index 00000000..359ccfeb
--- /dev/null
+++ b/www/crm/wp-content/plugins/civicrm/civicrm/ang/crmAutosave.js
@@ -0,0 +1,118 @@
+/// 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._);