    /**
     * @author Ed Spencer
     *
     * 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.data.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.
     *
     * Models are defined as a set of fields and any arbitrary methods and properties relevant to the model. For example:
     *
     *     Ext.define('User', {
     *         extend: 'Ext.data.Model',
     *
     *         config: {
     *             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);
     *         }
     *     });
     *
     * The fields array is turned into a {@link Ext.util.MixedCollection MixedCollection} automatically by the {@link
     * Ext.data.ModelManager ModelManager}, and all other functions and properties are copied to the new Model's prototype.
     *
     * Now we can create instances of our User model and call any model logic we defined:
     *
     *     var user = Ext.create('User', {
     *         name : 'Conan',
     *         age  : 24,
     *         phone: '555-555-5555'
     *     });
     *
     *     user.changeName();
     *     user.get('name'); //returns "Conan The Barbarian"
     *
     * # Validations
     *
     * 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:
     *
     *     Ext.define('User', {
     *         extend: 'Ext.data.Model',
     *
     *         config: {
     *             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}/}
     *             ]
     *         }
     *     });
     *
     * The validations can be run by simply calling the {@link #validate} function, which returns a {@link Ext.data.Errors}
     * object:
     *
     *     var instance = Ext.create('User', {
     *         name: 'Ed',
     *         gender: 'Male',
     *         username: 'edspencer'
     *     });
     *
     *     var errors = instance.validate();
     *
     * # Associations
     *
     * Models can have associations with other Models via {@link Ext.data.association.HasOne},
     * {@link Ext.data.association.BelongsTo belongsTo} and {@link Ext.data.association.HasMany 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:
     *
     *     Ext.define('Post', {
     *         extend: 'Ext.data.Model',
     *
     *         config: {
     *             fields: ['id', 'user_id'],
     *             belongsTo: 'User',
     *             hasMany  : {model: 'Comment', name: 'comments'}
     *         }
     *     });
     *
     *     Ext.define('Comment', {
     *         extend: 'Ext.data.Model',
     *
     *         config: {
     *             fields: ['id', 'user_id', 'post_id'],
     *             belongsTo: 'Post'
     *         }
     *     });
     *
     *     Ext.define('User', {
     *         extend: 'Ext.data.Model',
     *
     *         config: {
     *             fields: ['id'],
     *             hasMany: [
     *                 'Post',
     *                 {model: 'Comment', name: 'comments'}
     *             ]
     *         }
     *     });
     *
     * See the docs for {@link Ext.data.association.HasOne}, {@link Ext.data.association.BelongsTo} and
     * {@link Ext.data.association.HasMany} for details on the usage and configuration of associations.
     * Note that associations can also be specified like this:
     *
     *     Ext.define('User', {
     *         extend: 'Ext.data.Model',
     *
     *         config: {
     *             fields: ['id'],
     *             associations: [
     *                 {type: 'hasMany', model: 'Post',    name: 'posts'},
     *                 {type: 'hasMany', model: 'Comment', name: 'comments'}
     *             ]
     *         }
     *     });
     *
     * # Using a Proxy
     *
     * 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:
     *
     *     Ext.define('User', {
     *         extend: 'Ext.data.Model',
     *
     *         config: {
     *             fields: ['id', 'name', 'email'],
     *             proxy: {
     *                 type: 'rest',
     *                 url : '/users'
     *             }
     *         }
     *     });
     *
     * 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:
     *
     *     var user = Ext.create('User', {name: 'Ed Spencer', email: 'ed@sencha.com'});
     *
     *     user.save(); //POST /users
     *
     * 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.
     *
     * Loading data via the Proxy is equally easy:
     *
     *     //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
     *         }
     *     });
     *
     * Models can also be updated and destroyed easily:
     *
     *     //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!');
     *         }
     *     });
     *
     * # Usage in Stores
     *
     * 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}:
     *
     *     var store = Ext.create('Ext.data.Store', {
     *         model: 'User'
     *     });
     *
     *     //uses the Proxy we set up on Model to load the Store data
     *     store.load();
     *
     * 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.
     *
     * @constructor
     * Creates new Model instance.
     * @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.mixin.Observable'
        },

        isModel: true,

        requires: [
            'Ext.util.Collection',
            'Ext.data.Field',
            'Ext.data.identifier.Simple',
            'Ext.data.ModelManager',
            'Ext.data.proxy.Ajax',
            'Ext.data.association.HasMany',
            'Ext.data.association.BelongsTo',
            'Ext.data.association.HasOne',
            'Ext.data.Errors'
        ],

        config: {
            /**
             * @cfg {String} idProperty
             * The name of the field treated as this Model's unique id. Defaults to 'id'. Note that this field
             * needs to have a type of 'auto'. Setting the field type to anything else will be undone by the
             * framework. This is because new records that are created without an id, will have one generated.
             * This generated id is a string.
             */
            idProperty: 'id',

            data: null,

            /**
             * @cfg {Object[]/String[]} fields
             * The field definitions for all instances of this Model. **Note:** this does not set the *values* of each
             * field on an instance, it sets the collection of fields itself. Sample usage:
             *
             *     Ext.define('MyApp.model.User', {
             *         extend: 'Ext.data.Model',
             *
             *         config: {
             *             fields: [
             *                 'id',
             *                 {name: 'age', type: 'int'},
             *                 {name: 'taxRate', type: 'float'}
             *             ]
             *         }
             *     });
             * @accessor
             */
            fields: undefined,

            /**
             * @cfg {Object[]} validations
             * An array of {@link Ext.data.Validations validations} for this model.
             */
            validations: null,

            /**
             * @cfg {Object[]} associations
             * An array of {@link Ext.data.association.Association associations} for this model.
             */
            associations: null,

            /**
             * @cfg {String/Object/String[]/Object[]} hasMany
             * One or more {@link Ext.data.association.HasMany HasMany associations} for this model.
             */
            hasMany: null,

            /**
             * @cfg {String/Object/String[]/Object[]} hasOne
             * One or more {@link Ext.data.association.HasOne HasOne associations} for this model.
             */
            hasOne: null,

            /**
             * @cfg {String/Object/String[]/Object[]} belongsTo
             * One or more {@link Ext.data.association.BelongsTo BelongsTo associations} for this model.
             */
            belongsTo: null,

            /**
             * @cfg {Object/Ext.data.Proxy} proxy
             * The string type of the default Model Proxy. Defaults to null.
             * @accessor
             */
            proxy: null,

            identifier: {
                type: 'simple'
            },

            /**
             * @cfg {String} clientIdProperty
             * The name of a property that is used for submitting this Model's unique client-side identifier
             * to the server when multiple phantom records are saved as part of the same {@link Ext.data.Operation Operation}.
             * In such a case, the server response should include the client id for each record
             * so that the server response data can be used to update the client-side records if necessary.
             * This property cannot have the same name as any of this Model's fields.
             * Defaults to 'clientId'.
             * @accessor
             */
            clientIdProperty: 'clientId',

            /**
             * @method getIsErased Returns true if the record has been erased on the server.
             */
            isErased: false
        },

        staticConfigs: [
            'idProperty',
            'fields',
            'validations',
            'associations',
            'hasMany',
            'hasOne',
            'belongsTo',
            'clientIdProperty',
            'identifier',
            'proxy'
        ],

        statics: {
            EDIT   : 'edit',
            REJECT : 'reject',
            COMMIT : 'commit',

            generateProxyMethod: function(name) {
                return function() {
                    var prototype = this.prototype;

                    return prototype[name].apply(prototype, arguments);
                };
            },

            generateCacheId: function(record, id) {
                return record.modelName.replace(/\./g, '-').toLowerCase() + '-' + (id !== undefined ? id : record.getId());
            }
        },

        inheritableStatics: {
            /**
             * Asynchronously loads a model instance by id. Sample usage:
             *
             *     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
             *         }
             *     });
             *
             * @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
             * @static
             * @inheritable
             */
            load: function(id, config, scope) {
                scope = config.scope || this;
                if (Ext.isFunction(config)) {
                    config = {
                        callback: config,
                        scope: scope
                    };
                }

                config = Ext.apply({}, config);
                config = Ext.applyIf(config, {
                    action: 'read',
                    params: {
                        id: id
                    },
                    model: this
                });

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

                if (!proxy) {
                    Ext.Logger.error('You are trying to load a model that doesn\'t have a Proxy specified');
                }

                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]);
                };

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

        /**
         * @property {Boolean} editing
         * Internal flag used to track whether or not the model instance is currently being edited. Read-only.
         */
        editing : false,

        /**
         * @property {Boolean} dirty
         * True if this Record has been modified. Read-only.
         */
        dirty : false,

        /**
         * @property {Boolean} phantom
         * True 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.
         */
        phantom : false,

        constructor: function(data, id, raw, convertedData) {
            var me = this,
                cached = null,
                idProperty = this.getIdProperty();

            /**
             * @property {Object} modified key/value pairs of all fields whose values have changed.
             * The value is the original value for the field.
             */
            me.modified = {};

            /**
             * @property {Object} raw The raw data used to create this model if created via a reader.
             */
            me.raw = raw || data || {};

            /**
             * @property {Array} stores
             * An array of {@link Ext.data.Store} objects that this record is bound to.
             */
            me.stores = [];

            if (id || id === 0) {
                cached = Ext.data.Model.cache.get(Ext.data.Model.generateCacheId(this, id));
                if (cached) {
                    return cached.mergeData(convertedData || data || {});
                }
            }

            data = data || convertedData || {};

            // We begin by checking if an id is passed to the constructor. If this is the case we override
            // any possible id value that was passed in the data.
            if (id || id === 0) {
                // Lets skip using set here since it's so much faster
                data[idProperty] = me.internalId = id;
            }

            if (convertedData) {
                me.setConvertedData(data);
            } else {
                me.setData(data);
            }

            // If it does not have an id at this point, we generate it using the id strategy. This means
            // that we will treat this record as a phantom record from now on
            id = me.data[idProperty];
            if (!id && id !== 0) {
                me.data[idProperty] = me.internalId = me.id = me.getIdentifier().generate(me);
                me.phantom = true;

                if (this.associations.length) {
                    this.handleInlineAssociationData(data);
                }
            } else {
                me.id = me.getIdentifier().generate(me);
            }

            Ext.data.Model.cache.add(me);

            if (this.init && typeof this.init == 'function') {
                this.init();
            }
        },

        /**
         * Private function that is used when you create a record that already exists in the model cache.
         * In this case we loop over each field, and apply any data to the current instance that is not already
         * marked as being dirty on that instance.
         * @param data
         * @private
         */
        mergeData: function(rawData) {
            var me = this,
                fields = me.getFields().items,
                ln = fields.length,
                data = me.data,
                i, field, fieldName, value, convert;

            for (i = 0; i < ln; i++) {
                field = fields[i];
                fieldName = field.getName();
                convert = field.getConvert();
                value = rawData[fieldName];

                if (value !== undefined && !me.isModified(fieldName)) {
                    if (convert) {
                        value = convert.call(field, value, me);
                    }

                    data[fieldName] = value;
                }
            }

            return this;
        },

        /**
         * @method getData
         * Returns the current field data for this Model instance.
         */

        /**
         * This method is used to set the data for this Record instance.
         * Note that the existing data is removed. If a field is not specified
         * in the passed data it will use the field's default value. If a convert
         * method is specified for the field it will be called on the value.
         * @param rawData
         * @return {Ext.data.Model} this This Record
         */
        setData: function(rawData) {
            var fields = this.fields.items,
                ln = fields.length,
                isArray = Ext.isArray(rawData),
                data = this._data = this.data = {},
                i, field, name, value, convert, id;

            if (!rawData) {
                return this;
            }

            for (i = 0; i < ln; i++) {
                field = fields[i];
                name = field.getName();
                convert = field.getConvert();

                if (isArray) {
                    value = rawData[i];
                }
                else {
                    value = rawData[name];
                    if (typeof value == 'undefined') {
                        value = field.getDefaultValue();
                    }
                }

                if (convert) {
                    value = convert.call(field, value, this);
                }

                data[name] = value;
            }

            id = this.getId();
            if (this.associations.length && (id || id === 0)) {
                this.handleInlineAssociationData(rawData);
            }

            return this;
        },

        handleInlineAssociationData: function(rawData) {
            var associations = this.associations.items,
                ln = associations.length,
                i, association, associationData, reader, proxy;

            for (i = 0; i < ln; i++) {
                association = associations[i];
                associationData = rawData[association.getAssociationKey()];

                if (associationData) {
                    reader = association.getReader();
                    if (!reader) {
                        proxy = association.getAssociatedModel().getProxy();
                        // if the associated model has a Reader already, use that, otherwise attempt to create a sensible one
                        if (proxy) {
                            reader = proxy.getReader();
                        } else {
                            reader = new Ext.data.JsonReader({
                                model: association.getAssociatedModel()
                            });
                            if (!Ext.isArray(associationData)) {
                                associationData = [associationData];
                            }
                        }
                    }
                    association.read(this, reader, associationData);
                }
            }
        },

        /**
         * Sets the model instance's id field to the given id.
         * @param {Number/String} id The new id
         */
        setId: function(id) {
            var currentId = this.getId();

            // Lets use the direct property instead of getter here
            this.set(this.getIdProperty(), id);

            // We don't update the this.id since we don't want to break listeners that already
            // exist on the record instance.
            this.internalId = id;

            Ext.data.Model.cache.replace(currentId, this);
        },

        /**
         * Returns the unique ID allocated to this model instance as defined by {@link #idProperty}.
         * @return {Number/String} The id
         */
        getId: function() {
            // Lets use the direct property instead of getter here
            return this.get(this.getIdProperty());
        },

        /**
         * This sets the data directly without converting and applying default values.
         * This method is used when a Record gets instantiated by a Reader. Only use
         * this when you are sure you are passing correctly converted data.
         * @param data
         * @return this This Record
         */
        setConvertedData: function(data) {
            this._data = this.data = data;
            return this;
        },

        /**
         * Returns the value of the given field
         * @param {String} fieldName The field to fetch the value for
         * @return {Object} The value
         */
        get: function(fieldName) {
            return this.data[fieldName];
        },

        /**
         * 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 {Object} value The value to set
         */
        set: function(fieldName, value) {
            var me = this,
                // We are using the fields map since it saves lots of function calls
                fieldMap = me.fields.map,
                modified = me.modified,
                notEditing = !me.editing,
                associations = me.associations.items,
                modifiedCount = 0,
                modifiedFieldNames = [],
                field, key, i, currentValue, ln, convert;

            /*
             * 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) {
                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 = fieldMap[key];
                        if (field && field.hasCustomConvert()) {
                            modifiedFieldNames.push(key);
                            continue;
                        }

                        if (!modifiedCount && notEditing) {
                            me.beginEdit();
                        }
                        ++modifiedCount;
                        me.set(key, fieldName[key]);
                    }
                }

                ln = modifiedFieldNames.length;
                if (ln) {
                    if (!modifiedCount && notEditing) {
                        me.beginEdit();
                    }
                    modifiedCount += ln;
                    for (i = 0; i < ln; i++) {
                        field = modifiedFieldNames[i];
                        me.set(field, fieldName[field]);
                    }
                }

                if (notEditing && modifiedCount) {
                    me.endEdit(false, modifiedFieldNames);
                }
            } else {
                field = fieldMap[fieldName];
                convert = field && field.getConvert();
                if (convert) {
                    value = convert.call(field, value, me);
                }

                currentValue = me.data[fieldName];
                me.data[fieldName] = value;

                if (field && !me.isEqual(currentValue, value)) {
                    if (modified.hasOwnProperty(fieldName)) {
                        if (me.isEqual(modified[fieldName], value)) {
                            // the original value in me.modified equals the new value, so the
                            // field is no longer modified
                            delete modified[fieldName];
                            // we might have removed the last modified field, so check to see if
                            // there are any modified fields remaining and correct me.dirty:
                            me.dirty = false;
                            for (key in modified) {
                                if (modified.hasOwnProperty(key)) {
                                    me.dirty = true;
                                    break;
                                }
                            }
                        }
                    } else {
                        me.dirty = true;
                        // We only go one level back?
                        modified[fieldName] = currentValue;
                    }
                }

                if (notEditing) {
                    me.afterEdit([fieldName], modified);
                }
            }
        },

        /**
         * 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;
        },

        /**
         * Begins an edit. While in edit mode, no events (e.g.. the `update` 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;

                // We save the current states of dirty, data and modified so that when we
                // cancel the edit, we can put it back to this state
                me.dirtySave    = me.dirty;
                me.dataSave     = Ext.apply({}, me.data);
                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.data = me.dataSave;
                me.dirty = me.dirtySave;

                // Delete the saved states
                delete me.modifiedSave;
                delete me.dataSave;
                delete me.dirtySave;
            }
        },

        /**
         * Ends an edit. If any data was modified, the containing store is notified (ie, the store's `update` event will
         * fire).
         * @param {Boolean} silent True to not notify the store of the change
         * @param {String[]} modifiedFieldNames Array of field names changed during edit.
         */
        endEdit: function(silent, modifiedFieldNames) {
            var me = this;

            if (me.editing) {
                me.editing = false;

                if (silent !== true && (me.changedWhileEditing())) {
                    me.afterEdit(modifiedFieldNames || Ext.Object.getKeys(this.modified), this.modified);
                }

                delete me.modifiedSave;
                delete me.dataSave;
                delete me.dirtySave;
            }
        },

        /**
         * Checks if the underlying data has changed during an edit. This doesn't necessarily
         * mean the record is dirty, however we still need to notify the store since it may need
         * to update any views.
         * @private
         * @return {Boolean} True if the underlying data has changed during an edit.
         */
        changedWhileEditing: function() {
            var me = this,
                saved = me.dataSave,
                data = me.data,
                key;

            for (key in data) {
                if (data.hasOwnProperty(key)) {
                    if (!me.isEqual(data[key], saved[key])) {
                        return true;
                    }
                }
            }

            return false;
        },

        /**
         * 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 true if the passed field name has been `{@link #modified}` since the load or last commit.
         * @param {String} fieldName {@link Ext.data.Field#name}
         * @return {Boolean}
         */
        isModified : function(fieldName) {
            return this.modified.hasOwnProperty(fieldName);
        },

        /**
         * Saves the model instance using the configured proxy.
         * @param {Object} options Options to pass to the proxy. Config object for {@link Ext.data.Operation}.
         * @return {Ext.data.Model} The Model instance
         */
        save: function(options, scope) {
            var me     = this,
                action = me.phantom ? 'create' : 'update',
                proxy  = me.getProxy(),
                operation,
                callback;

            if (!proxy) {
                Ext.Logger.error('You are trying to save a model instance that doesn\'t have a Proxy specified');
            }

            options = options || {};
            scope = scope || me;

            if (Ext.isFunction(options)) {
                options = {
                    callback: options,
                    scope: scope
                };
            }

            Ext.applyIf(options, {
                records: [me],
                action : action,
                model: me.self
            });

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

            callback = function(operation) {
                if (operation.wasSuccessful()) {
                    Ext.callback(options.success, scope, [me, operation]);
                } else {
                    Ext.callback(options.failure, scope, [me, operation]);
                }

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

            proxy[action](operation, callback, me);

            return me;
        },

        /**
         * Destroys the record using the configured proxy. This will create a 'destroy' operation.
         * Note that this doesn't destroy this instance after the server comes back with a response.
         * It will however call afterErase on any Stores it is joined to. Stores by default will
         * automatically remove this instance from their data collection.
         * @param {Object} options Options to pass to the proxy. Config object for {@link Ext.data.Operation}.
         * @return {Ext.data.Model} The Model instance
         */
        erase: function(options, scope) {
            var me     = this,
                proxy  = this.getProxy(),
                operation,
                callback;

            if (!proxy) {
                Ext.Logger.error('You are trying to erase a model instance that doesn\'t have a Proxy specified');
            }

            options = options || {};
            scope = scope || me;

            if (Ext.isFunction(options)) {
                options = {
                    callback: options,
                    scope: scope
                };
            }

            Ext.applyIf(options, {
                records: [me],
                action : 'destroy',
                model: this.self
            });

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

            callback = function(operation) {
                if (operation.wasSuccessful()) {
                    Ext.callback(options.success, scope, [me, operation]);
                } else {
                    Ext.callback(options.failure, scope, [me, operation]);
                }

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

            proxy.destroy(operation, callback, me);

            return me;
        },

        /**
         * 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.
         *
         * Developers should subscribe to the {@link Ext.data.Store#update} event to have their code notified of reject
         * operations.
         *
         * @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.data[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.
         *
         * Developers should subscribe to the {@link Ext.data.Store#update} event to have their code notified of commit
         * operations.
         *
         * @param {Boolean} silent (optional) True to skip notification of the owning store of the change.
         * Defaults to false.
         */
        commit: function(silent) {
            var me = this,
                modified = this.modified;

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

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

        /**
         * @private
         * If this Model instance has been {@link #join joined} to a {@link Ext.data.Store store}, the store's
         * afterEdit method is called
         * @param {String[]} modifiedFieldNames Array of field names changed during edit.
         */
        afterEdit : function(modifiedFieldNames, modified) {
            this.notifyStores('afterEdit', modifiedFieldNames, modified);
        },

        /**
         * @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.notifyStores("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(modified) {
            this.notifyStores('afterCommit', modified);
        },

        /**
         * @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
         */
        notifyStores: function(fn) {
            var args = Ext.Array.clone(arguments),
                stores = this.stores,
                ln = stores.length,
                i, store;

            args[0] = this;
            for (i = 0; i < ln; ++i) {
                store = stores[i];
                if (store !== undefined && typeof store[fn] == "function") {
                    store[fn].apply(store, args);
                }
            }
        },

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

            return new me.self(null, newId, me.raw, Ext.apply({}, me.data));
        },

        getData: function(includeAssociated) {
            var data = this.data;

            if (includeAssociated === true) {
                Ext.apply(data, this.getAssociatedData());
            }

            return data;
        },

        /**
         * 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 {String[]} 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, associationName, associatedRecords, associatedRecord,
                associatedRecordCount, association, id, i, j, type, allow;

            for (i = 0; i < associationCount; i++) {
                association = associations[i];
                associationName = association.getName();
                type = association.getType();
                allow = true;

                if (associationType) {
                    allow = type == associationType;
                }

                if (allow && type == 'hasMany') {
                    //this is the hasMany store filled with the associated data
                    associatedStore = record[association.getStoreName()];

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

                    //if it's loaded, put it into the association data
                    if (associatedStore && associatedStore.getCount() > 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[associationName][j] = associatedRecord.getData();
                                Ext.apply(associationData[associationName][j], this.prepareAssociatedData(associatedRecord, ids, type));
                            }
                        }
                    }
                } else if (allow && (type == 'belongsTo' || type == 'hasOne')) {
                    associatedRecord = record[association.getInstanceName()];
                    if (associatedRecord !== undefined) {
                        id = associatedRecord.id;
                        if (Ext.Array.indexOf(ids, id) === -1) {
                            ids.push(id);
                            associationData[associationName] = associatedRecord.getData();
                            Ext.apply(associationData[associationName], this.prepareAssociatedData(associatedRecord, ids, type));
                        }
                    }
                }
            }

            return associationData;
        },

        /**
         * Tells this model instance that it has been added to a store.
         * @param {Ext.data.Store} store The store to which this model has been added.
         * @private
         */
        join: function(store) {
            Ext.Array.include(this.stores, store);
        },

        /**
         * Tells this model instance that it has been removed from the store.
         * @param {Ext.data.Store} store The store from which this model has been removed.
         * @private
         */
        unjoin: function(store) {
            Ext.Array.remove(this.stores, store);
        },

        /**
         * Marks this **Record** as `{@link #dirty}`. This method is used interally when adding `{@link #phantom}` records
         * to a {@link Ext.data.proxy.Server#writer writer enabled store}.
         *
         * Marking a record `{@link #dirty}` causes the phantom to be returned by {@link Ext.data.Store#getUpdatedRecords}
         * where it will have a create action composed for it during {@link Ext.data.Model#save model save} operations.
         */
        setDirty : function() {
            var me = this,
                name;

            me.dirty = true;

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

        /**
         * Validates the current data against all of its configured {@link #cfg-validations}.
         * @return {Ext.data.Errors} The errors object
         */
        validate: function() {
            var errors      = Ext.create('Ext.data.Errors'),
                validations = this.getValidations().items,
                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(Ext.create('Ext.data.Error', {
                            field  : field,
                            message: validation.message || validators.getMessage(type)
                        }));
                    }
                }
            }

            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();
        },

        /**
         * Returns a url-suitable string for this model instance. By default this just returns the name of the Model class
         * followed by the instance ID - for example an instance of MyApp.model.User with ID 123 will return 'user/123'.
         * @return {String} The url string for this model instance
         */
        toUrl: function() {
            var pieces = this.$className.split('.'),
                name   = pieces[pieces.length - 1].toLowerCase();

            return name + '/' + this.getId();
        },

        /**
         * Destroys this model instance. Note that this doesn't do a 'destroy' operation. If you want to destroy
         * the record in your localstorage or on the server you should use the {@link #erase} method.
         */
        destroy: function() {
            var me = this;
            me.notifyStores('afterErase', me);
            Ext.data.Model.cache.remove(me);
            me.data = me._data = me.raw = me.stores = me.modified = null;
            me.callParent(arguments);
        },

        //<debug>
        markDirty : function() {
            if (Ext.isDefined(Ext.Logger)) {
                Ext.Logger.deprecate('Ext.data.Model: markDirty has been deprecated. Use setDirty instead.');
            }
            return this.setDirty.apply(this, arguments);
        },
        //</debug>

        applyProxy: function(proxy, currentProxy) {
            return Ext.factory(proxy, Ext.data.Proxy, currentProxy, 'proxy');
        },

        updateProxy: function(proxy) {
            if (proxy) {
                proxy.setModel(this.self);
            }
        },

        applyAssociations: function(associations) {
            if (associations) {
                this.addAssociations(associations, 'hasMany');
            }
        },

        applyBelongsTo: function(belongsTo) {
            if (belongsTo) {
                this.addAssociations(belongsTo, 'belongsTo');
            }
        },

        applyHasMany: function(hasMany) {
            if (hasMany) {
                this.addAssociations(hasMany, 'hasMany');
            }
        },

        applyHasOne: function(hasOne) {
            if (hasOne) {
                this.addAssociations(hasOne, 'hasOne');
            }
        },

        addAssociations: function(associations, defaultType) {
            var ln, i, association,
                name = this.self.modelName,
                associationsCollection = this.self.associations;

            associations = Ext.Array.from(associations);

            for (i = 0, ln = associations.length; i < ln; i++) {
                association = associations[i];
                if (!Ext.isObject(association)) {
                    association = {model: association};
                }

                Ext.applyIf(association, {
                    type: defaultType,
                    ownerModel: name,
                    associatedModel: association.model
                });

                delete association.model;

                Ext.ClassManager.onCreated(function() {
                    associationsCollection.add(Ext.data.association.Association.create(association));
                }, this, (typeof association.associatedModel === 'string') ? association.associatedModel : Ext.getClassName(association.associatedModel));
            }
        },

        applyValidations: function(validations) {
            if (validations) {
                if (!Ext.isArray(validations)) {
                    validations = [validations];
                }
                this.addValidations(validations);
            }
        },

        addValidations: function(validations) {
            this.self.validations.addAll(validations);
        },

        /**
         * @method setFields
         * Updates the collection of Fields that all instances of this Model use. **Does not** update field values in a Model
         * instance (use {@link #set} for that), instead this updates which fields are available on the Model class. This
         * is normally used when creating or updating Model definitions dynamically, for example if you allow your users to
         * define their own Models and save the fields configuration to a database, this method allows you to change those
         * fields later.
         */

        applyFields: function(fields) {
            var superFields = this.superclass.fields;
            if (superFields) {
                fields = superFields.items.concat(fields || []);
            }
            return fields || [];
        },

        updateFields: function(fields) {
            var ln = fields.length,
                me = this,
                prototype = me.self.prototype,
                idProperty = this.getIdProperty(),
                idField, fieldsCollection, field, i;

            /**
             * @property {Ext.util.MixedCollection} fields
             * The fields defined on this model.
             */
            fieldsCollection = me._fields = me.fields = new Ext.util.Collection(prototype.getFieldName);

            for (i = 0; i < ln; i++) {
                field = fields[i];
                if (!field.isField) {
                    field = new Ext.data.Field(fields[i]);
                }
                fieldsCollection.add(field);
            }

            // We want every Model to have an id property field
            idField = fieldsCollection.get(idProperty);
            if (!idField) {
                fieldsCollection.add(new Ext.data.Field(idProperty));
            } else {
                idField.setType('auto');
            }

            fieldsCollection.addSorter(prototype.sortConvertFields);
        },

        applyIdentifier: function(identifier) {
            if (typeof identifier === 'string') {
                identifier = {
                    type: identifier
                };
            }
            return Ext.factory(identifier, Ext.data.identifier.Simple, this.getIdentifier(), 'data.identifier');
        },

        /**
         * This method is used by the fields collection to retrieve the key for a field
         * based on it's name.
         * @param field
         * @private
         */
        getFieldName: function(field) {
            return field.getName();
        },

        /**
         * This method is being used to sort the fields based on their convert method. If
         * a field has a custom convert method, we ensure its more to the bottom of the collection.
         * @param field1
         * @param field2
         * @private
         */
        sortConvertFields: function(field1, field2) {
            var f1SpecialConvert = field1.hasCustomConvert(),
                f2SpecialConvert = field2.hasCustomConvert();

            if (f1SpecialConvert && !f2SpecialConvert) {
                return 1;
            }
            if (!f1SpecialConvert && f2SpecialConvert) {
                return -1;
            }
            return 0;
        },

        /**
         * WARNING: Only Jacky Nguyen and Tommy Maintz (might) understand this.
         * Please don't read this code. If you do, don't judge us.
         * @private
         */
        onClassExtended: function(cls, data, hooks) {
            var onBeforeClassCreated = hooks.onBeforeCreated,
                Model = this,
                prototype = Model.prototype,
                configNameCache = Ext.Class.configNameCache,
                staticConfigs = prototype.staticConfigs.concat(data.staticConfigs || []),
                defaultConfig = prototype.config,
                config = data.config || {},
                key;

            // Convert old properties in data into a config object
            // <deprecated product=touch since=2.0>
            if (data.idgen || config.idgen) {
                config.identifier = data.idgen || config.idgen;
                // <debug warn>
                console.warn('idgen is deprecated as a property. Please put it inside the config object under the new "identifier" configuration');
                // </debug>
            }

            for (key in defaultConfig) {
                if (key in data) {
                    config[key] = data[key];
                    delete data[key];
                    // <debug warn>
                    console.warn(key + ' is deprecated as a property directly on the Model prototype. Please put it inside the config object.');
                    // </debug>
                }
            }
            // </deprecated>
            data.config = config;

            hooks.onBeforeCreated = function(cls, data) {
                var dependencies = [],
                    prototype = cls.prototype,
                    statics = {},
                    config = prototype.config,
                    staticConfigsLn = staticConfigs.length,
                    copyMethods = ['set', 'get'],
                    copyMethodsLn = copyMethods.length,
                    associations = config.associations || [],
                    name = Ext.getClassName(cls),
                    key, methodName, i, j, ln;

                // Create static setters and getters for each config option
                for (i = 0; i < staticConfigsLn; i++) {
                    key = staticConfigs[i];

                    for (j = 0; j < copyMethodsLn; j++) {
                        methodName = configNameCache[key][copyMethods[j]];
                        if (methodName in prototype) {
                            statics[methodName] = Model.generateProxyMethod(methodName);
                        }
                    }
                }

                cls.addStatics(statics);

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

                // Take out dependencies on other associations and the proxy type
                if (config.belongsTo) {
                    dependencies.push('association.belongsto');
                }
                if (config.hasMany) {
                    dependencies.push('association.hasmany');
                }
                if (config.hasOne) {
                    dependencies.push('association.hasone');
                }

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

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

                if (config.validations) {
                    dependencies.push('Ext.data.Validations');
                }

                Ext.require(dependencies, function() {
                    Ext.Function.interceptBefore(hooks, 'onCreated', function() {
                        Ext.data.ModelManager.registerType(name, cls);

                        var superCls = cls.prototype.superclass;

                        /**
                         * @property {Ext.util.Collection} associations
                         * The associations defined on this model.
                         */
                        cls.prototype.associations = cls.associations = cls.prototype._associations = (superCls && superCls.associations)
                            ? superCls.associations.clone()
                            : new Ext.util.Collection(function(association) {
                                return association.getName();
                            });

                        /**
                         * @property {Ext.util.Collection} validations
                         * The validations defined on this model.
                         */
                        cls.prototype.validations = cls.validations = cls.prototype._validations = (superCls && superCls.validations)
                            ? superCls.validations.clone()
                            : new Ext.util.Collection(function(validation) {
                                return validation.field || validation.name;
                            });

                        cls.prototype = Ext.Object.chain(cls.prototype);
                        cls.prototype.initConfig.call(cls.prototype, config);

                        delete cls.prototype.initConfig;
                    });

                    onBeforeClassCreated.call(Model, cls, data, hooks);
                });
            };
        }
    }, function() {
        this.cache = new Ext.util.Collection(this.generateCacheId);
    });