Source: backbone-x/source/settings.js

/*jshint indent:2, curly:true, eqeqeq:true, immed:true, latedef:true,
newcap:true, noarg:true, regexp:true, undef:true, strict:true, trailing:true,
white:true*/
/*global XT:true, XM:true, Backbone:true, _:true */

(function () {
  "use strict";

  /**
    @class
    @name XM.Settings
    @extends XM.Model
  */
  XM.Settings = XM.Model.extend(/** @lends XM.Settings# */ {
    recordType: 'XM.Settings',
    autoFetchId: false,

    /**
     * @override
     */
    fetch: function (_options) {
      var options = _.extend({ }, _options),
        userCallback = options.success,

        //Handle a dispatch response as a Backbone sync response.
        done = function (model, resp, options) {
          model.set(resp, options);
          model.setStatus(XM.Model.READY_CLEAN, options);

          if (_.isFunction(userCallback)) {
            userCallback(model, resp, options);
          }
        },
        fetchOptions = this._fetchHelper(_.extend(options, {
          success: done
        }));

      return fetchOptions && this.sync('read', this, fetchOptions);
    },

    /**
      Practically identical to the backbone method, but we have to change
      the signature of the success function. This is unfortunate.
      XXX decompose using same strategy as the late prototypeFetch function
     */
    prototypeSave: function (key, val, options) {
      var that = this,
        attributes = this.attributes,
        attrs, success, method, xhr;

      // Handle both `"key", value` and `{key: value}` -style arguments.
      if (key === null || typeof key === 'object') {
        attrs = key;
        options = val;
      } else {
        (attrs = {})[key] = val;
      }

      // If we're not waiting and attributes exist, save acts as `set(attr).save(null, opts)`.
      if (attrs && (!options || !options.wait) && !this.set(attrs, options)) {
        return false;
      }

      options = _.extend({validate: true}, options);

      // Do not persist invalid models.
      if (!this._validate(attrs, options)) {
        return false;
      }

      // Set temporary attributes if `{wait: true}`.
      if (attrs && options.wait) {
        this.attributes = _.extend({}, attributes, attrs);
      }

      // After a successful server-side save, the client is (optionally)
      // updated with the server-side state.
      if (options.parse === void 0) {
        options.parse = true;
      }
      success = options.success;
      options.success = function (resp, options) {
        var model = that;
        // Ensure attributes are restored during synchronous saves.
        model.attributes = attributes;
        var serverAttrs = model.parse(resp, options);
        if (options.wait) {
          serverAttrs = _.extend(attrs || {}, serverAttrs);
        }
        if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
          return false;
        }
        if (success) {
          success(model, resp, options);
        }
      };

      // Finish configuring and sending the Ajax request.
      method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
      if (method === 'patch') {
        options.attrs = attrs;
      }
      xhr = this.sync(method, this, options);

      // Restore attributes.
      if (attrs && options.wait) {
        this.attributes = attributes;
      }

      return xhr;
    },

    /**
      Does the usual stuff and also updates the local settings. Also passes
      in our prototype save function.
     */
    save: function (key, value, options) {
      options = options ? _.clone(options) : {};
      var that = this,
        success;

      // Handle both `"key", value` and `{key: value}` -style arguments.
      if (_.isObject(key) || _.isEmpty(key)) {
        options = value ? _.clone(value) : {};
      } else {
        options = options ? _.clone(options) : {};
      }
      success = options.success;

      options.success = function (resp) {
        XT.session.settings.set(that.attributes);
        if (success) { success(that, resp, options); }
      };

      options.prototypeSave = this.prototypeSave;

      // Handle both `"key", value` and `{key: value}` -style arguments.
      if (_.isObject(key) || _.isEmpty(key)) { value = options; }
      return XM.Model.prototype.save.call(this, key, value, options);
    },

    /**
      Reimplemented to discard `dataState`.

      @param {Number} Status
    */
    setStatus: function (status, options) {
      XM.Model.prototype.setStatus.apply(this, arguments);
      delete this.attributes.dataState;
    },

    /**
      Reimplemented to invoke `settings` and `commitSettings` functions.
    */
    sync: function (method, model, options) {
      options = options ? _.clone(options) : {};
      var that = this,
        recordType = this.recordType,
        result,
        error = options.error;

      options.error = function (resp) {
        var K = XM.Model;
        that.setStatus(K.ERROR);
        if (error) { error(model, resp, options); }
      };

      // Read
      if (method === 'read' && recordType && options.success) {
        result = this.dispatch(this.dispatchRecordType || this.recordType,
          this.dispatchFetchFunction || 'settings',
          this.dispatchRecordType || this.recordType,
          options);

      // Write
      } else if (method === 'update') {
        result = this.dispatch(this.dispatchRecordType || this.recordType,
          this.dispatchCommitFunction || 'commitSettings',
          [this.generatePatches()],
          options);
      }
      return result || false;
    },

    validate: function () {
    }

  });

  // ..........................................................
  // CLASS METHODS
  //

  _.extend(XM.Settings, /** @lends XM.Settings# */{
    /**
      Reimplemented to always return false.

      @returns {Boolean}
    */
    canCreate: function () {
      return false;
    },

    /**
      Reimplemented to always return false.

      @returns {Boolean}
    */
    canDelete: function () {
      return false;
    },

    /**
      Reimplemented to always return false.

      @returns {Boolean}
    */
    canRead: function () {
      return XM.Settings.canUpdate.call(this);
    },

    /**
      Reimplemented.

      @returns {Boolean}
    */
    canUpdate: function () {
      return XT.session.privileges.get(this.prototype.privileges);
    }

  });


}());