Merge pull request #15163 from colemanw/eventClone
[civicrm-core.git] / js / crm.backbone.js
index 746ddaf7be3559ee8aa0d9529885de9a8b85d71a..c7960305fa2e1a21a83f4b1eacdcdc6bcc2355fa 100644 (file)
@@ -1,5 +1,4 @@
-(function($) {
-  var CRM = (window.CRM) ? (window.CRM) : (window.CRM = {});
+(function($, _, Backbone) {
   if (!CRM.Backbone) CRM.Backbone = {};
 
   /**
@@ -16,8 +15,9 @@
   CRM.Backbone.sync = function(method, model, options) {
     var isCollection = _.isArray(model.models);
 
+    var apiOptions, params;
     if (isCollection) {
-      var apiOptions = {
+      apiOptions = {
         success: function(data) {
           // unwrap data
           options.success(_.toArray(data.values));
       };
       switch (method) {
         case 'read':
-          CRM.api(model.crmEntityName, 'get', model.toCrmCriteria(), apiOptions);
+          CRM.api(model.crmEntityName, model.toCrmAction('get'), model.toCrmCriteria(), apiOptions);
           break;
         // replace all entities matching "x.crmCriteria" with new entities in "x.models"
         case 'crm-replace':
-          var params = this.toCrmCriteria();
+          params = this.toCrmCriteria();
           params.version = 3;
           params.values = this.toJSON();
-          CRM.api(model.crmEntityName, 'replace', params, apiOptions);
+          CRM.api(model.crmEntityName, model.toCrmAction('replace'), params, apiOptions);
           break;
         default:
           apiOptions.error({is_error: 1, error_message: "CRM.Backbone.sync(" + method + ") not implemented for collections"});
       }
     } else {
       // callback options to pass to CRM.api
-      var apiOptions = {
+      apiOptions = {
         success: function(data) {
           // unwrap data
-          var values = _.toArray(data['values']);
+          var values = _.toArray(data.values);
           if (data.count == 1) {
             options.success(values[0]);
           } else {
       switch (method) {
         case 'create': // pass-through
         case 'update':
-          var params = model.toJSON();
-          params.options || (params.options = {});
+          params = model.toJSON();
+          if (!params.options) params.options = {};
           params.options.reload = 1;
           if (!model._isDuplicate) {
-            CRM.api(model.crmEntityName, 'create', params, apiOptions);
+            CRM.api(model.crmEntityName, model.toCrmAction('create'), params, apiOptions);
           } else {
-            CRM.api(model.crmEntityName, 'duplicate', params, apiOptions);
+            CRM.api(model.crmEntityName, model.toCrmAction('duplicate'), params, apiOptions);
           }
           break;
         case 'read':
         case 'delete':
           var apiAction = (method == 'delete') ? 'delete' : 'get';
-          var params = model.toCrmCriteria();
+          params = model.toCrmCriteria();
           if (!params.id) {
             apiOptions.error({is_error: 1, error_message: 'Missing ID for ' + model.crmEntityName});
             return;
           }
-          CRM.api(model.crmEntityName, apiAction, params, apiOptions);
+          CRM.api(model.crmEntityName, model.toCrmAction(apiAction), params, apiOptions);
           break;
         default:
           apiOptions.error({is_error: 1, error_message: "CRM.Backbone.sync(" + method + ") not implemented for models"});
     // Defaults - if specified in ModelClass, preserve
     _.defaults(ModelClass.prototype, {
       crmEntityName: crmEntityName,
+      crmActions: {}, // map: string backboneActionName => string serverSideActionName
+      crmReturn: null, // array: list of fields to return
+      toCrmAction: function(action) {
+        return this.crmActions[action] ? this.crmActions[action] : action;
+      },
       toCrmCriteria: function() {
-        return (this.get('id')) ? {id: this.get('id')} : {};
+        var result = (this.get('id')) ? {id: this.get('id')} : {};
+        if (!_.isEmpty(this.crmReturn)) {
+          result.return = this.crmReturn;
+        }
+        return result;
       },
       duplicate: function() {
         var newModel = new ModelClass(this.toJSON());
    * });
    * CRM.Backbone.extendCollection(ContactCollection);
    *
-   * // Use class
+   * // Use class (with passive criteria)
    * var c = new ContactCollection([], {
    *   crmCriteria: {contact_type: 'Organization'}
    * });
    * c.get(123).set('property', 'value');
    * c.get(456).setDeleted(true);
    * c.save();
+   *
+   * // Use class (with active criteria)
+   * var criteriaModel = new SomeModel({
+   *     contact_type: 'Organization'
+   * });
+   * var c = new ContactCollection([], {
+   *   crmCriteriaModel: criteriaModel
+   * });
+   * c.fetch();
+   * c.get(123).set('property', 'value');
+   * c.get(456).setDeleted(true);
+   * c.save();
    * @endcode
    *
+   *
    * @param Class CollectionClass
    * @see tests/qunit/crm-backbone
    */
     // Defaults - if specified in CollectionClass, preserve
     _.defaults(CollectionClass.prototype, {
       crmEntityName: CollectionClass.prototype.model.prototype.crmEntityName,
+      crmActions: {}, // map: string backboneActionName => string serverSideActionName
+      toCrmAction: function(action) {
+        return this.crmActions[action] ? this.crmActions[action] : action;
+      },
       toCrmCriteria: function() {
-        return (this.crmCriteria) ? _.extend({}, this.crmCriteria) : {};
+        var result = (this.crmCriteria) ? _.extend({}, this.crmCriteria) : {};
+        if (!_.isEmpty(this.crmReturn)) {
+          result.return = this.crmReturn;
+        } else if (this.model && !_.isEmpty(this.model.prototype.crmReturn)) {
+          result.return = this.model.prototype.crmReturn;
+        }
+        return result;
+      },
+
+      /**
+       * Get an object which represents this collection's criteria
+       * as a live model. Any changes to the model will be applied
+       * to the collection, and the collection will be refreshed.
+       *
+       * @param criteriaModelClass
+       */
+      setCriteriaModel: function(criteriaModel) {
+        var collection = this;
+        this.crmCriteria = criteriaModel.toJSON();
+        this.listenTo(criteriaModel, 'change', function() {
+          collection.crmCriteria = criteriaModel.toJSON();
+          collection.debouncedFetch();
+        });
       },
 
+      debouncedFetch: _.debounce(function() {
+        this.fetch({reset: true});
+      }, 100),
+
       /**
        * Reconcile the server's collection with the client's collection.
        * New/modified items from the client will be saved/updated on the
        * @param Object options - accepts "success" and "error" callbacks
        */
       save: function(options) {
-        options || (options = {});
+        if (!options) options = {};
         var collection = this;
         var success = options.success;
         options.success = function(resp) {
         };
         wrapError(collection, options);
 
-        return this.sync('crm-replace', this, options)
+        return this.sync('crm-replace', this, options);
       }
     });
     // Overrides - if specified in CollectionClass, replace
     _.extend(CollectionClass.prototype, {
       sync: CRM.Backbone.sync,
       initialize: function(models, options) {
-        options || (options = {});
-        if (options.crmCriteria) {
+        if (!options) options = {};
+        if (options.crmCriteriaModel) {
+          this.setCriteriaModel(options.crmCriteriaModel);
+        } else if (options.crmCriteria) {
           this.crmCriteria = options.crmCriteria;
         }
+        if (options.crmActions) {
+          this.crmActions = _.extend(this.crmActions, options.crmActions);
+        }
         if (origInit) {
           return origInit.apply(this, arguments);
         }
    *   - error: function(collection, error)
    */
    CRM.Backbone.findCreate = function(options) {
-     options || (options = {});
+     if (!options) options = {};
      var collection = new options.CollectionClass([], {
        crmCriteria: options.crmCriteria
      });
      collection.fetch({
       success: function(collection) {
-        if (collection.length == 0) {
+        if (collection.length === 0) {
           var attrs = _.extend({}, collection.crmCriteria, options.defaults || {});
           var model = collection._prepareModel(attrs, options);
           options.success(model);
   var wrapError = function (model, options) {
     var error = options.error;
     options.error = function(resp) {
-      if (error) error(model, resp, options);
+      if (error) error(model, resp, optio);
       model.trigger('error', model, resp, options);
     };
   };
-})(cj);
+})(CRM.$, CRM._, CRM.BB);