Merge remote-tracking branch 'upstream/4.4' into 4.4-master-2013-12-30-11-03-32
[civicrm-core.git] / js / crm.backbone.js
index 01ec9e847ab8ef1c81fd7714ad38c12354a6cb35..87c1c5cbc3f0aa0df8e8ce680c570aa72e0522ea 100644 (file)
       switch (method) {
         case 'create': // pass-through
         case 'update':
-          CRM.api(model.crmEntityName, 'create', model.toJSON(), apiOptions);
+          var params = model.toJSON();
+          params.options || (params.options = {});
+          params.options.reload = 1;
+          if (!model._isDuplicate) {
+            CRM.api(model.crmEntityName, 'create', params, apiOptions);
+          } else {
+            CRM.api(model.crmEntityName, 'duplicate', params, apiOptions);
+          }
           break;
         case 'read':
         case 'delete':
       crmEntityName: crmEntityName,
       toCrmCriteria: function() {
         return (this.get('id')) ? {id: this.get('id')} : {};
+      },
+      duplicate: function() {
+        var newModel = new ModelClass(this.toJSON());
+        newModel._isDuplicate = true;
+        if (newModel.setModified) newModel.setModified();
+        newModel.listenTo(newModel, 'sync', function(){
+          // may get called on subsequent resaves -- don't care!
+          delete newModel._isDuplicate;
+        });
+        return newModel;
       }
     });
     // Overrides - if specified in ModelClass, replace
     // Defaults - if specified in ModelClass, preserve
     _.defaults(ModelClass.prototype, {
       isSaved: function() {
-        var result = !this.isNew() && !this._modified;
+        var result = !this.isNew() && !this.isModified();
         return result;
       },
+      isModified: function() {
+        return this._modified;
+      },
       _saved_onchange: function(model, options) {
         if (options.parse) return;
+        // console.log('change', model.changedAttributes(), model.previousAttributes());
         this.setModified();
       },
       setModified: function() {
    * });
    * 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
    */
         return (this.crmCriteria) ? _.extend({}, this.crmCriteria) : {};
       },
 
+      /**
+       * 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});
+      }, 500),
+
       /**
        * Reconcile the server's collection with the client's collection.
        * New/modified items from the client will be saved/updated on the
       sync: CRM.Backbone.sync,
       initialize: function(models, options) {
         options || (options = {});
-        if (options.crmCriteria) {
+        if (options.crmCriteriaModel) {
+          this.setCriteriaModel(options.crmCriteriaModel);
+        } else if (options.crmCriteria) {
           this.crmCriteria = options.crmCriteria;
         }
         if (origInit) {