/**
 * @author Ed Spencer
 * @class Ext.data.Model
 *
 * <p>A Model represents some object that your application manages. For example, one might define a Model for Users, Products,
 * Cars, or any other real-world object that we want to model in the system. Models are registered via the {@link Ext.ModelManager model manager},
 * and are used by {@link Ext.data.Store stores}, which are in turn used by many of the data-bound components in Ext.</p>
 *
 * <p>Models are defined as a set of fields and any arbitrary methods and properties relevant to the model. For example:</p>
 *
<pre><code>
Ext.define('User', {
    extend: 'Ext.data.Model',
    fields: [
        {name: 'name',  type: 'string'},
        {name: 'age',   type: 'int'},
        {name: 'phone', type: 'string'},
        {name: 'alive', type: 'boolean', defaultValue: true}
    ],

    changeName: function() {
        var oldName = this.get('name'),
            newName = oldName + " The Barbarian";

        this.set('name', newName);
    }
});
</code></pre>
*
* <p>The fields array is turned into a {@link Ext.util.MixedCollection MixedCollection} automatically by the {@link Ext.ModelManager ModelManager}, and all
* other functions and properties are copied to the new Model's prototype.</p>
*
* <p>Now we can create instances of our User model and call any model logic we defined:</p>
*
<pre><code>
var user = Ext.ModelManager.create({
    name : 'Conan',
    age  : 24,
    phone: '555-555-5555'
}, 'User');

user.changeName();
user.get('name'); //returns "Conan The Barbarian"
</code></pre>
 *
 * <p><u>Validations</u></p>
 *
 * <p>Models have built-in support for validations, which are executed against the validator functions in
 * {@link Ext.data.validations} ({@link Ext.data.validations see all validation functions}). Validations are easy to add to models:</p>
 *
<pre><code>
Ext.define('User', {
    extend: 'Ext.data.Model',
    fields: [
        {name: 'name',     type: 'string'},
        {name: 'age',      type: 'int'},
        {name: 'phone',    type: 'string'},
        {name: 'gender',   type: 'string'},
        {name: 'username', type: 'string'},
        {name: 'alive',    type: 'boolean', defaultValue: true}
    ],

    validations: [
        {type: 'presence',  field: 'age'},
        {type: 'length',    field: 'name',     min: 2},
        {type: 'inclusion', field: 'gender',   list: ['Male', 'Female']},
        {type: 'exclusion', field: 'username', list: ['Admin', 'Operator']},
        {type: 'format',    field: 'username', matcher: /([a-z]+)[0-9]{2,3}/}
    ]
});
</code></pre>
 *
 * <p>The validations can be run by simply calling the {@link #validate} function, which returns a {@link Ext.data.Errors}
 * object:</p>
 *
<pre><code>
var instance = Ext.ModelManager.create({
    name: 'Ed',
    gender: 'Male',
    username: 'edspencer'
}, 'User');

var errors = instance.validate();
</code></pre>
 *
 * <p><u>Associations</u></p>
 *
 * <p>Models can have associations with other Models via {@link Ext.data.BelongsToAssociation belongsTo} and
 * {@link Ext.data.HasManyAssociation hasMany} associations. For example, let's say we're writing a blog administration
 * application which deals with Users, Posts and Comments. We can express the relationships between these models like this:</p>
 *
<pre><code>
Ext.define('Post', {
    extend: 'Ext.data.Model',
    fields: ['id', 'user_id'],

    belongsTo: 'User',
    hasMany  : {model: 'Comment', name: 'comments'}
});

Ext.define('Comment', {
    extend: 'Ext.data.Model',
    fields: ['id', 'user_id', 'post_id'],

    belongsTo: 'Post'
});

Ext.define('User', {
    extend: 'Ext.data.Model',
    fields: ['id'],

    hasMany: [
        'Post',
        {model: 'Comment', name: 'comments'}
    ]
});
</code></pre>
 *
 * <p>See the docs for {@link Ext.data.BelongsToAssociation} and {@link Ext.data.HasManyAssociation} for details on the usage
 * and configuration of associations. Note that associations can also be specified like this:</p>
 *
<pre><code>
Ext.define('User', {
    extend: 'Ext.data.Model',
    fields: ['id'],

    associations: [
        {type: 'hasMany', model: 'Post',    name: 'posts'},
        {type: 'hasMany', model: 'Comment', name: 'comments'}
    ]
});
</code></pre>
 *
 * <p><u>Using a Proxy</u></p>
 *
 * <p>Models are great for representing types of data and relationships, but sooner or later we're going to want to
 * load or save that data somewhere. All loading and saving of data is handled via a {@link Ext.data.proxy.Proxy Proxy},
 * which can be set directly on the Model:</p>
 *
<pre><code>
Ext.define('User', {
    extend: 'Ext.data.Model',
    fields: ['id', 'name', 'email'],

    proxy: {
        type: 'rest',
        url : '/users'
    }
});
</code></pre>
 *
 * <p>Here we've set up a {@link Ext.data.proxy.Rest Rest Proxy}, which knows how to load and save data to and from a
 * RESTful backend. Let's see how this works:</p>
 *
<pre><code>
var user = Ext.ModelManager.create({name: 'Ed Spencer', email: 'ed@sencha.com'}, 'User');

user.save(); //POST /users
</code></pre>
 *
 * <p>Calling {@link #save} on the new Model instance tells the configured RestProxy that we wish to persist this
 * Model's data onto our server. RestProxy figures out that this Model hasn't been saved before because it doesn't
 * have an id, and performs the appropriate action - in this case issuing a POST request to the url we configured
 * (/users). We configure any Proxy on any Model and always follow this API - see {@link Ext.data.proxy.Proxy} for a full
 * list.</p>
 *
 * <p>Loading data via the Proxy is equally easy:</p>
 *
<pre><code>
//get a reference to the User model class
var User = Ext.ModelManager.getModel('User');

//Uses the configured RestProxy to make a GET request to /users/123
User.load(123, {
    success: function(user) {
        console.log(user.getId()); //logs 123
    }
});
</code></pre>
 *
 * <p>Models can also be updated and destroyed easily:</p>
 *
<pre><code>
//the user Model we loaded in the last snippet:
user.set('name', 'Edward Spencer');

//tells the Proxy to save the Model. In this case it will perform a PUT request to /users/123 as this Model already has an id
user.save({
    success: function() {
        console.log('The User was updated');
    }
});

//tells the Proxy to destroy the Model. Performs a DELETE request to /users/123
user.destroy({
    success: function() {
        console.log('The User was destroyed!');
    }
});
</code></pre>
 *
 * <p><u>Usage in Stores</u></p>
 *
 * <p>It is very common to want to load a set of Model instances to be displayed and manipulated in the UI. We do this
 * by creating a {@link Ext.data.Store Store}:</p>
 *
<pre><code>
var store = new Ext.data.Store({
    model: 'User'
});

//uses the Proxy we set up on Model to load the Store data
store.load();
</code></pre>
 *
 * <p>A Store is just a collection of Model instances - usually loaded from a server somewhere. Store can also maintain
 * a set of added, updated and removed Model instances to be synchronized with the server via the Proxy. See the
 * {@link Ext.data.Store Store docs} for more information on Stores.</p>
 *
 * @constructor
 * @param {Object} data An object containing keys corresponding to this model's fields, and their associated values
 * @param {Number} id Optional unique ID to assign to this model instance
 */

Ext.define('Ext.data.Model', {
    alternateClassName
: 'Ext.data.Record',
   
    mixins
: {
        observable
: 'Ext.util.Observable'
   
},

    requires
: [
       
'Ext.ModelManager',
       
'Ext.data.Field',
       
'Ext.data.Errors',
       
'Ext.data.Operation',
       
'Ext.data.validations',
       
'Ext.data.proxy.Ajax',
       
'Ext.util.MixedCollection'
   
],

    onClassExtended
: function(cls, data) {
       
var onBeforeClassCreated = data.onBeforeClassCreated;

        data
.onBeforeClassCreated = function(cls, data) {
           
var me = this,
                name
= Ext.getClassName(cls),
                prototype
= cls.prototype,
                superCls
= cls.prototype.superclass,

                validations
= data.validations || [],
                fields
= data.fields || [],
                associations
= data.associations || [],
                belongsTo
= data.belongsTo,
                hasMany
= data.hasMany,

                fieldsMixedCollection
= new Ext.util.MixedCollection(false, function(field) {
                   
return field.name;
               
}),

                associationsMixedCollection
= new Ext.util.MixedCollection(false, function(association) {
                   
return association.name;
               
}),

                superValidations
= superCls.validations,
                superFields
= superCls.fields,
                superAssociations
= superCls.associations,

                association
, i, ln,
                dependencies
= [];

           
// Save modelName on class and its prototype
            cls
.modelName = name;
            prototype
.modelName = name;

           
// Merge the validations of the superclass and the new subclass
           
if (superValidations) {
                validations
= superValidations.concat(validations);
           
}

            data
.validations = validations;

           
// Merge the fields of the superclass and the new subclass
           
if (superFields) {
                fields
= superFields.items.concat(fields);
           
}

           
for (i = 0, ln = fields.length; i < ln; ++i) {
                fieldsMixedCollection
.add(new Ext.data.Field(fields[i]));
           
}

            data
.fields = fieldsMixedCollection;

           
//associations can be specified in the more convenient format (e.g. not inside an 'associations' array).
           
//we support that here
           
if (belongsTo) {
                belongsTo
= Ext.Array.from(belongsTo);

               
for (i = 0, ln = belongsTo.length; i < ln; ++i) {
                    association
= belongsTo[i];

                   
if (!Ext.isObject(association)) {
                        association
= {model: association};
                   
}

                    association
.type = 'belongsTo';
                    associations
.push(association);
               
}

               
delete data.belongsTo;
           
}

           
if (hasMany) {
                hasMany
= Ext.Array.from(hasMany);
               
for (i = 0, ln = hasMany.length; i < ln; ++i) {
                    association
= hasMany[i];

                   
if (!Ext.isObject(association)) {
                        association
= {model: association};
                   
}

                    association
.type = 'hasMany';
                    associations
.push(association);
               
}

               
delete data.hasMany;
           
}

           
if (superAssociations) {
                associations
= superAssociations.items.concat(associations);
           
}

           
for (i = 0, ln = associations.length; i < ln; ++i) {
                dependencies
.push('association.' + associations[i].type.toLowerCase());
           
}

           
if (data.proxy) {
               
if (typeof data.proxy === 'string') {
                    dependencies
.push('proxy.' + data.proxy);
               
}
               
else if (typeof data.proxy.type === 'string') {
                    dependencies
.push('proxy.' + data.proxy.type);
               
}
           
}

           
Ext.require(dependencies, function() {
               
Ext.ModelManager.registerType(name, cls);

               
for (i = 0, ln = associations.length; i < ln; ++i) {
                    association
= associations[i];

                   
Ext.apply(association, {
                        ownerModel
: name,
                        associatedModel
: association.model
                   
});

                   
if (Ext.ModelManager.getModel(association.model) === undefined) {
                       
Ext.ModelManager.registerDeferredAssociation(association);
                   
} else {
                        associationsMixedCollection
.add(Ext.data.Association.create(association));
                   
}
               
}

                data
.associations = associationsMixedCollection;

                onBeforeClassCreated
.call(me, cls, data);

                cls
.setProxy(cls.prototype.proxy || cls.prototype.defaultProxyType);

               
// Fire the onModelDefined template method on ModelManager
               
Ext.ModelManager.onModelDefined(cls);
           
});
       
}
   
},

    inheritableStatics
: {
        /**
         * Sets the Proxy to use for this model. Accepts any options that can be accepted by {@link Ext#createByAlias Ext.createByAlias}
         * @param {String/Object/Ext.data.proxy.Proxy} proxy The proxy
         * @static
         */

        setProxy
: function(proxy) {
           
//make sure we have an Ext.data.proxy.Proxy object
           
if (!proxy.isProxy) {
               
if (typeof proxy == "string") {
                    proxy
= {
                        type
: proxy
                   
};
               
}
                proxy
= Ext.createByAlias("proxy." + proxy.type, proxy);
           
}
            proxy
.setModel(this);
           
this.proxy = this.prototype.proxy = proxy;

           
return proxy;
       
},

        /**
         * Returns the configured Proxy for this Model
         * @return {Ext.data.proxy.Proxy} The proxy
         */

        getProxy
: function() {
           
return this.proxy;
       
},

        /**
         * <b>Static</b>. Asynchronously loads a model instance by id. Sample usage:
    <pre><code>
    MyApp.User = Ext.define('User', {
        extend: 'Ext.data.Model',
        fields: [
            {name: 'id', type: 'int'},
            {name: 'name', type: 'string'}
        ]
    });

    MyApp.User.load(10, {
        scope: this,
        failure: function(record, operation) {
            //do something if the load failed
        },
        success: function(record, operation) {
            //do something if the load succeeded
        },
        callback: function(record, operation) {
            //do something whether the load succeeded or failed
        }
    });
    </code></pre>
         * @param {Number} id The id of the model to load
         * @param {Object} config Optional config object containing success, failure and callback functions, plus optional scope
         * @member Ext.data.Model
         * @method load
         * @static
         */

        load
: function(id, config) {
            config
= Ext.apply({}, config);
            config
= Ext.applyIf(config, {
                action
: 'read',
                id    
: id
           
});

           
var operation  = Ext.create('Ext.data.Operation', config),
                scope      
= config.scope || this,
                record    
= null,
                callback
;

            callback
= function(operation) {
               
if (operation.wasSuccessful()) {
                    record
= operation.getRecords()[0];
                   
Ext.callback(config.success, scope, [record, operation]);
               
} else {
                   
Ext.callback(config.failure, scope, [record, operation]);
               
}
               
Ext.callback(config.callback, scope, [record, operation]);
           
};

           
this.proxy.read(operation, callback, this);
       
}
   
},

    statics
: {
        PREFIX
: 'ext-record',
        AUTO_ID
: 1,
        EDIT  
: 'edit',
        REJECT
: 'reject',
        COMMIT
: 'commit',

        /**
         * Generates a sequential id. This method is typically called when a record is {@link #create}d
         * and {@link #Record no id has been specified}. The id will automatically be assigned
         * to the record. The returned id takes the form:
         * <tt>&#123;PREFIX}-&#123;AUTO_ID}</tt>.<div class="mdetail-params"><ul>
         * <li><b><tt>PREFIX</tt></b> : String<p class="sub-desc"><tt>Ext.data.Model.PREFIX</tt>
         * (defaults to <tt>'ext-record'</tt>)</p></li>
         * <li><b><tt>AUTO_ID</tt></b> : String<p class="sub-desc"><tt>Ext.data.Model.AUTO_ID</tt>
         * (defaults to <tt>1</tt> initially)</p></li>
         * </ul></div>
         * @param {Ext.data.Model} rec The record being created.  The record does not exist, it's a {@link #phantom}.
         * @return {String} auto-generated string id, <tt>"ext-record-i++'</tt>;
         * @static
         */

        id
: function(rec) {
           
var id = [this.PREFIX, '-', this.AUTO_ID++].join('');
            rec
.phantom = true;
            rec
.internalId = id;
           
return id;
       
}
   
},
   
    /**
     * Internal flag used to track whether or not the model instance is currently being edited. Read-only
     * @property editing
     * @type Boolean
     */

    editing
: false,

    /**
     * Readonly flag - true if this Record has been modified.
     * @type Boolean
     */

    dirty
: false,

    /**
     * @cfg {String} persistanceProperty The property on this Persistable object that its data is saved to.
     * Defaults to 'data' (e.g. all persistable data resides in this.data.)
     */

    persistanceProperty
: 'data',

    evented
: false,
    isModel
: true,

    /**
     * <tt>true</tt> when the record does not yet exist in a server-side database (see
     * {@link #setDirty}).  Any record which has a real database pk set as its id property
     * is NOT a phantom -- it's real.
     * @property phantom
     * @type {Boolean}
     */

    phantom
: false,

    /**
     * @cfg {String} idProperty The name of the field treated as this Model's unique id (defaults to 'id').
     */

    idProperty
: 'id',

    /**
     * The string type of the default Model Proxy. Defaults to 'ajax'
     * @property defaultProxyType
     * @type String
     */

    defaultProxyType
: 'ajax',

    /**
     * An array of the fields defined on this model
     * @property fields
     * @type {Array}
     */


    constructor
: function(data, id) {
        data
= data || {};
       
       
var me = this,
            fields
,
            length
,
            field
,
            name
,
            i
,
            isArray
= Ext.isArray(data),
            newData
= isArray ? {} : null; // to hold mapped array data if needed

        /**
         * An internal unique ID for each Model instance, used to identify Models that don't have an ID yet
         * @property internalId
         * @type String
         * @private
         */

        me
.internalId = (id || id === 0) ? id : Ext.data.Model.id(me);

       
Ext.applyIf(me, {
            data
: {}    
       
});
       
        /**
         * Key: value pairs of all fields whose values have changed
         * @property modified
         * @type Object
         */

        me
.modified = {};

        me
[me.persistanceProperty] = {};

        me
.mixins.observable.constructor.call(me);

       
//add default field values if present
        fields
= me.fields.items;
        length
= fields.length;

       
for (i = 0; i < length; i++) {
            field
= fields[i];
            name  
= field.name;

           
if (isArray){
               
// Have to map array data so the values get assigned to the named fields
               
// rather than getting set as the field names with undefined values.
                newData
[name] = data[i];
           
}
           
else if (data[name] === undefined) {
                data
[name] = field.defaultValue;
           
}
       
}

        me
.set(newData || data);
       
// clear any dirty/modified since we're initializing
        me
.dirty = false;
        me
.modified = {};

       
if (me.getId()) {
            me
.phantom = false;
       
}

       
if (typeof me.init == 'function') {
            me
.init();
       
}

        me
.id = me.modelName + '-' + me.internalId;

       
Ext.ModelManager.register(me);
   
},
   
    /**
     * Returns the value of the given field
     * @param {String} fieldName The field to fetch the value for
     * @return {Mixed} The value
     */

   
get: function(field) {
       
return this[this.persistanceProperty][field];
   
},
   
    /**
     * Sets the given field to the given value, marks the instance as dirty
     * @param {String|Object} fieldName The field to set, or an object containing key/value pairs
     * @param {Mixed} value The value to set
     */

   
set: function(fieldName, value) {
       
var me = this,
            fields
= me.fields,
            modified
= me.modified,
            convertFields
= [],
            field
, key, i, currentValue;

       
/*
         * If we're passed an object, iterate over that object. NOTE: we pull out fields with a convert function and
         * set those last so that all other possible data is set before the convert function is called
         */

       
if (arguments.length == 1 && Ext.isObject(fieldName)) {
           
for (key in fieldName) {
               
if (fieldName.hasOwnProperty(key)) {
               
                   
//here we check for the custom convert function. Note that if a field doesn't have a convert function,
                   
//we default it to its type's convert function, so we have to check that here. This feels rather dirty.
                    field
= fields.get(key);
                   
if (field && field.convert !== field.type.convert) {
                        convertFields
.push(key);
                       
continue;
                   
}
                   
                    me
.set(key, fieldName[key]);
               
}
           
}

           
for (i = 0; i < convertFields.length; i++) {
                field
= convertFields[i];
                me
.set(field, fieldName[field]);
           
}

       
} else {
           
if (fields) {
                field
= fields.get(fieldName);

               
if (field && field.convert) {
                    value
= field.convert(value, me);
               
}
           
}
            currentValue
= me.get(fieldName);
            me
[me.persistanceProperty][fieldName] = value;
           
           
if (field && field.persist && !me.isEqual(currentValue, value)) {
                me
.dirty = true;
                me
.modified[fieldName] = currentValue;
           
}

           
if (!me.editing) {
                me
.afterEdit();
           
}
       
}
   
},
   
    /**
     * Checks if two values are equal, taking into account certain
     * special factors, for example dates.
     * @private
     * @param {Object} a The first value
     * @param {Object} b The second value
     * @return {Boolean} True if the values are equal
     */

    isEqual
: function(a, b){
       
if (Ext.isDate(a) && Ext.isDate(b)) {
           
return a.getTime() === b.getTime();
       
}
       
return a === b;
   
},
   
    /**
     * Begin an edit. While in edit mode, no events (e.g.. the <code>update</code> event)
     * are relayed to the containing store. When an edit has begun, it must be followed
     * by either {@link #endEdit} or {@link #cancelEdit}.
     */

    beginEdit
: function(){
       
var me = this;
       
if (!me.editing) {
            me
.editing = true;
            me
.dirtySave = me.dirty;
            me
.dataSave = Ext.apply({}, me[me.persistanceProperty]);
            me
.modifiedSave = Ext.apply({}, me.modified);
       
}
   
},
   
    /**
     * Cancels all changes made in the current edit operation.
     */

    cancelEdit
: function(){
       
var me = this;
       
if (me.editing) {
            me
.editing = false;
           
// reset the modified state, nothing changed since the edit began
            me
.modified = me.modifiedSave;
            me
[me.persistanceProperty] = me.dataSave;
            me
.dirty = me.dirtySave;
           
delete me.modifiedSave;
           
delete me.dataSave;
           
delete me.dirtySave;
       
}
   
},
   
    /**
     * End an edit. If any data was modified, the containing store is notified
     * (ie, the store's <code>update</code> event will fire).
     * @param {Boolean} silent True to not notify the store of the change
     */

    endEdit
: function(silent){
       
var me = this;
       
if (me.editing) {
            me
.editing = false;
           
delete me.modifiedSave;
           
delete me.dataSave;
           
delete me.dirtySave;
           
if (silent !== true && me.dirty) {
                me
.afterEdit();
           
}
       
}
   
},
   
    /**
     * Gets a hash of only the fields that have been modified since this Model was created or commited.
     * @return Object
     */

    getChanges
: function(){
       
var modified = this.modified,
            changes  
= {},
            field
;

       
for (field in modified) {
           
if (modified.hasOwnProperty(field)){
                changes
[field] = this.get(field);
           
}
       
}

       
return changes;
   
},
   
    /**
     * Returns <tt>true</tt> if the passed field name has been <code>{@link #modified}</code>
     * since the load or last commit.
     * @param {String} fieldName {@link Ext.data.Field#name}
     * @return {Boolean}
     */

    isModified
: function(fieldName) {
       
return this.modified.hasOwnProperty(fieldName);
   
},
   
    /**
     * <p>Marks this <b>Record</b> as <code>{@link #dirty}</code>.  This method
     * is used interally when adding <code>{@link #phantom}</code> records to a
     * {@link Ext.data.Store#writer writer enabled store}.</p>
     * <br><p>Marking a record <code>{@link #dirty}</code> causes the phantom to
     * be returned by {@link Ext.data.Store#getModifiedRecords} where it will
     * have a create action composed for it during {@link Ext.data.Store#save store save}
     * operations.</p>
     */

    setDirty
: function() {
       
var me = this,
            name
;
       
        me
.dirty = true;

        me
.fields.each(function(field) {
           
if (field.persist) {
                name
= field.name;
                me
.modified[name] = me.get(name);
           
}
       
}, me);
   
},

   
//<debug>
    markDirty
: function() {
       
if (Ext.isDefined(Ext.global.console)) {
           
Ext.global.console.warn('Ext.data.Model: markDirty has been deprecated. Use setDirty instead.');
       
}
       
return this.setDirty.apply(this, arguments);
   
},
   
//</debug>
   
    /**
     * Usually called by the {@link Ext.data.Store} to which this model instance has been {@link #join joined}.
     * Rejects all changes made to the model instance since either creation, or the last commit operation.
     * Modified fields are reverted to their original values.
     * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
     * to have their code notified of reject operations.</p>
     * @param {Boolean} silent (optional) True to skip notification of the owning
     * store of the change (defaults to false)
     */

    reject
: function(silent) {
       
var me = this,
            modified
= me.modified,
            field
;

       
for (field in modified) {
           
if (modified.hasOwnProperty(field)) {
               
if (typeof modified[field] != "function") {
                    me
[me.persistanceProperty][field] = modified[field];
               
}
           
}
       
}

        me
.dirty = false;
        me
.editing = false;
        me
.modified = {};

       
if (silent !== true) {
            me
.afterReject();
       
}
   
},

    /**
     * Usually called by the {@link Ext.data.Store} which owns the model instance.
     * Commits all changes made to the instance since either creation or the last commit operation.
     * <p>Developers should subscribe to the {@link Ext.data.Store#update} event
     * to have their code notified of commit operations.</p>
     * @param {Boolean} silent (optional) True to skip notification of the owning
     * store of the change (defaults to false)
     */

    commit
: function(silent) {
       
var me = this;
       
        me
.dirty = false;
        me
.editing = false;

        me
.modified = {};

       
if (silent !== true) {
            me
.afterCommit();
       
}
   
},

    /**
     * Creates a copy (clone) of this Model instance.
     * @param {String} id (optional) A new id, defaults to the id
     * of the instance being copied. See <code>{@link #id}</code>.
     * To generate a phantom instance with a new id use:<pre><code>
var rec = record.copy(); // clone the record
Ext.data.Model.id(rec); // automatically generate a unique sequential id
     * </code></pre>
     * @return {Record}
     */

    copy
: function(newId) {
       
var me = this;
       
       
return new me.self(Ext.apply({}, me[me.persistanceProperty]), newId || me.internalId);
   
},

    /**
     * Sets the Proxy to use for this model. Accepts any options that can be accepted by {@link Ext#createByAlias Ext.createByAlias}
     * @param {String/Object/Ext.data.proxy.Proxy} proxy The proxy
     * @static
     */

    setProxy
: function(proxy) {
       
//make sure we have an Ext.data.proxy.Proxy object
       
if (!proxy.isProxy) {
           
if (typeof proxy === "string") {
                proxy
= {
                    type
: proxy
               
};
           
}
            proxy
= Ext.createByAlias("proxy." + proxy.type, proxy);
       
}
        proxy
.setModel(this.self);
       
this.proxy = proxy;

       
return proxy;
   
},

    /**
     * Returns the configured Proxy for this Model
     * @return {Ext.data.proxy.Proxy} The proxy
     */

    getProxy
: function() {
       
return this.proxy;
   
},

    /**
     * Validates the current data against all of its configured {@link #validations} and returns an
     * {@link Ext.data.Errors Errors} object
     * @return {Ext.data.Errors} The errors object
     */

    validate
: function() {
       
var errors      = Ext.create('Ext.data.Errors'),
            validations
= this.validations,
            validators  
= Ext.data.validations,
            length
, validation, field, valid, type, i;

       
if (validations) {
            length
= validations.length;

           
for (i = 0; i < length; i++) {
                validation
= validations[i];
                field
= validation.field || validation.name;
                type  
= validation.type;
                valid
= validators[type](validation, this.get(field));

               
if (!valid) {
                    errors
.add({
                        field  
: field,
                        message
: validation.message || validators[type + 'Message']
                   
});
               
}
           
}
       
}

       
return errors;
   
},

    /**
     * Checks if the model is valid. See {@link #validate}.
     * @return {Boolean} True if the model is valid.
     */

    isValid
: function(){
       
return this.validate().isValid();
   
},

    /**
     * Saves the model instance using the configured proxy
     * @param {Object} options Options to pass to the proxy
     * @return {Ext.data.Model} The Model instance
     */

    save
: function(options) {
        options
= Ext.apply({}, options);

       
var me     = this,
            action
= me.phantom ? 'create' : 'update',
            record
= null,
            scope  
= options.scope || me,
            operation
,
            callback
;

       
Ext.apply(options, {
            records
: [me],
            action
: action
       
});

        operation
= Ext.create('Ext.data.Operation', options);

        callback
= function(operation) {
           
if (operation.wasSuccessful()) {
                record
= operation.getRecords()[0];
               
//we need to make sure we've set the updated data here. Ideally this will be redundant once the
               
//ModelCache is in place
                me
.set(record.data);
                record
.dirty = false;

               
Ext.callback(options.success, scope, [record, operation]);
           
} else {
               
Ext.callback(options.failure, scope, [record, operation]);
           
}

           
Ext.callback(options.callback, scope, [record, operation]);
       
};

        me
.getProxy()[action](operation, callback, me);

       
return me;
   
},

    /**
     * Destroys the model using the configured proxy
     * @param {Object} options Options to pass to the proxy
     * @return {Ext.data.Model} The Model instance
     */

    destroy
: function(options){
        options
= Ext.apply({}, options);

       
var me     = this,
            record
= null,
            scope  
= options.scope || me,
            operation
,
            callback
;

       
Ext.apply(options, {
            records
: [me],
            action
: 'destroy'
       
});

        operation
= Ext.create('Ext.data.Operation', options);
        callback
= function(operation) {
           
if (operation.wasSuccessful()) {
               
Ext.callback(options.success, scope, [record, operation]);
           
} else {
               
Ext.callback(options.failure, scope, [record, operation]);
           
}
           
Ext.callback(options.callback, scope, [record, operation]);
       
};

        me
.getProxy().destroy(operation, callback, me);
       
return me;
   
},

    /**
     * Returns the unique ID allocated to this model instance as defined by {@link #idProperty}
     * @return {Number} The id
     */

    getId
: function() {
       
return this.get(this.idProperty);
   
},

    /**
     * Sets the model instance's id field to the given id
     * @param {Number} id The new id
     */

    setId
: function(id) {
       
this.set(this.idProperty, id);
   
},

    /**
     * Tells this model instance that it has been added to a store
     * @param {Ext.data.Store} store The store that the model has been added to
     */

    join
: function(store) {
        /**
         * The {@link Ext.data.Store} to which this Record belongs.
         * @property store
         * @type {Ext.data.Store}
         */

       
this.store = store;
   
},

    /**
     * Tells this model instance that it has been removed from the store
     */

    unjoin
: function() {
       
delete this.store;
   
},

    /**
     * @private
     * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
     * afterEdit method is called
     */

    afterEdit
: function() {
       
this.callStore('afterEdit');
   
},

    /**
     * @private
     * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
     * afterReject method is called
     */

    afterReject
: function() {
       
this.callStore("afterReject");
   
},

    /**
     * @private
     * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
     * afterCommit method is called
     */

    afterCommit
: function() {
       
this.callStore('afterCommit');
   
},

    /**
     * @private
     * Helper function used by afterEdit, afterReject and afterCommit. Calls the given method on the
     * {@link Ext.data.Store store} that this instance has {@link #join joined}, if any. The store function
     * will always be called with the model instance as its single argument.
     * @param {String} fn The function to call on the store
     */

    callStore
: function(fn) {
       
var store = this.store;

       
if (store !== undefined && typeof store[fn] == "function") {
            store
[fn](this);
       
}
   
},

    /**
     * Gets all of the data from this Models *loaded* associations.
     * It does this recursively - for example if we have a User which
     * hasMany Orders, and each Order hasMany OrderItems, it will return an object like this:
     * {
     *     orders: [
     *         {
     *             id: 123,
     *             status: 'shipped',
     *             orderItems: [
     *                 ...
     *             ]
     *         }
     *     ]
     * }
     * @return {Object} The nested data set for the Model's loaded associations
     */

    getAssociatedData
: function(){
       
return this.prepareAssociatedData(this, [], null);
   
},

    /**
     * @private
     * This complex-looking method takes a given Model instance and returns an object containing all data from
     * all of that Model's *loaded* associations. See (@link #getAssociatedData}
     * @param {Ext.data.Model} record The Model instance
     * @param {Array} ids PRIVATE. The set of Model instance internalIds that have already been loaded
     * @param {String} associationType (optional) The name of the type of association to limit to.
     * @return {Object} The nested data set for the Model's loaded associations
     */

    prepareAssociatedData
: function(record, ids, associationType) {
       
//we keep track of all of the internalIds of the models that we have loaded so far in here
       
var associations     = record.associations.items,
            associationCount
= associations.length,
            associationData  
= {},
            associatedStore
, associatedName, associatedRecords, associatedRecord,
            associatedRecordCount
, association, id, i, j, type, allow;

       
for (i = 0; i < associationCount; i++) {
            association
= associations[i];
            type
= association.type;
            allow
= true;
           
if (associationType) {
                allow
= type == associationType;
           
}
           
if (allow && type == 'hasMany') {

               
//this is the hasMany store filled with the associated data
                associatedStore
= record[association.storeName];

               
//we will use this to contain each associated record's data
                associationData
[association.name] = [];

               
//if it's loaded, put it into the association data
               
if (associatedStore && associatedStore.data.length > 0) {
                    associatedRecords
= associatedStore.data.items;
                    associatedRecordCount
= associatedRecords.length;

                   
//now we're finally iterating over the records in the association. We do this recursively
                   
for (j = 0; j < associatedRecordCount; j++) {
                        associatedRecord
= associatedRecords[j];
                       
// Use the id, since it is prefixed with the model name, guaranteed to be unique
                        id
= associatedRecord.id;

                       
//when we load the associations for a specific model instance we add it to the set of loaded ids so that
                       
//we don't load it twice. If we don't do this, we can fall into endless recursive loading failures.
                       
if (Ext.Array.indexOf(ids, id) == -1) {
                            ids
.push(id);

                            associationData
[association.name][j] = associatedRecord.data;
                           
Ext.apply(associationData[association.name][j], this.prepareAssociatedData(associatedRecord, ids, type));
                       
}
                   
}
               
}
           
} else if (allow && type == 'belongsTo') {
                associatedRecord
= record[association.instanceName];
               
if (associatedRecord !== undefined) {
                    id
= associatedRecord.id;
                   
if (Ext.Array.indexOf(ids, id) == -1) {
                        ids
.push(id);
                        associationData
[association.name] = associatedRecord.data;
                       
Ext.apply(associationData[association.name], this.prepareAssociatedData(associatedRecord, ids, type));
                   
}
               
}
           
}
       
}

       
return associationData;
   
}
});