| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735 | <!DOCTYPE html><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  <title>The source code</title>  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>  <style type="text/css">    .highlight { display: block; background-color: #ddd; }  </style>  <script type="text/javascript">    function highlight() {      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";    }  </script></head><body onload="prettyPrint(); highlight();">  <pre class="prettyprint lang-js"><span id='Ext-data-Model'>/**</span> * @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.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', *         fields: [ *             {name: 'name',  type: 'string'}, *             {name: 'age',   type: 'int', convert: null}, *             {name: 'phone', type: 'string'}, *             {name: 'alive', type: 'boolean', defaultValue: true, convert: null} *         ], * *         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.ModelManager ModelManager}, and all other functions and properties are copied to the new Model's prototype. *  * By default, the built in numeric and boolean field types have a (@link Ext.data.Field#convert} function which coerces string * values in raw data into the field's type. For better performance with {@link Ext.data.reader.Json Json} or {@link Ext.data.reader.Array Array} * readers *if you are in control of the data fed into this Model*, you can null out the default convert function which will cause * the raw property to be copied directly into the Field's value. * * 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', *         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', *         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'} *         ] *     }); * * 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', *         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', *         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. */Ext.define('Ext.data.Model', {    alternateClassName: 'Ext.data.Record',    mixins: {        observable: 'Ext.util.Observable'    },        requires: [        'Ext.ModelManager',        'Ext.data.IdGenerator',        'Ext.data.Field',        'Ext.data.Errors',        'Ext.data.Operation',        'Ext.data.validations',        'Ext.util.MixedCollection'    ],    compareConvertFields: function(f1, f2) {        var f1SpecialConvert = f1.convert && f1.type && f1.convert !== f1.type.convert,            f2SpecialConvert = f2.convert && f2.type && f2.convert !== f2.type.convert;        if (f1SpecialConvert && !f2SpecialConvert) {            return 1;        }        if (!f1SpecialConvert && f2SpecialConvert) {            return -1;        }        return 0;    },    itemNameFn: function(item) {        return item.name;    },    onClassExtended: function(cls, data, hooks) {        var onBeforeClassCreated = hooks.onBeforeCreated;        hooks.onBeforeCreated = function(cls, data) {            var me = this,                name = Ext.getClassName(cls),                prototype = cls.prototype,                superCls = cls.prototype.superclass,                validations = data.validations || [],                fields = data.fields || [],                field,                associationsConfigs = data.associations || [],                addAssociations = function(items, type) {                    var i = 0,                        len,                        item;                    if (items) {                        items = Ext.Array.from(items);                        for (len = items.length; i < len; ++i) {                            item = items[i];                            if (!Ext.isObject(item)) {                                item = {model: item};                            }                            item.type = type;                            associationsConfigs.push(item);                        }                    }                },                idgen = data.idgen,                fieldsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn),                associationsMixedCollection = new Ext.util.MixedCollection(false, prototype.itemNameFn),                superValidations = superCls.validations,                superFields = superCls.fields,                superAssociations = superCls.associations,                associationConfig, i, ln,                dependencies = [],                idProperty = data.idProperty || cls.prototype.idProperty,                // Process each Field upon add into the collection                onFieldAddReplace = function(arg0, arg1, arg2) {                    var newField,                        pos;                    if (fieldsMixedCollection.events.add.firing) {                        // Add event signature is (position, value, key);                        pos = arg0;                        newField  = arg1;                    } else {                        // Replace event signature is (key, oldValue, newValue);                        newField = arg2;                        pos = arg1.originalIndex;                    }                    // Set the originalIndex for ArrayReader to get the default mapping from in case                    // compareConvertFields changes the order due to some fields having custom convert functions.                    newField.originalIndex = pos;                    // The field(s) which encapsulates the idProperty must never have a default value set                    // if no value arrives from the server side. So override any possible prototype-provided                    // defaultValue with undefined which will inhibit generation of defaulting code in Reader.buildRecordDataExtractor                    if (newField.mapping === idProperty || (newField.mapping == null && newField.name === idProperty)) {                        newField.defaultValue = undefined;                    }                },                // Use the proxy from the class definition object if present, otherwise fall back to the inherited one, or the default                    clsProxy = data.proxy || cls.prototype.proxy || cls.prototype.defaultProxyType,                // Sort upon add function to be used in case of dynamically added Fields                fieldConvertSortFn = function() {                    fieldsMixedCollection.sortBy(prototype.compareConvertFields);                };            // 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);            }            fieldsMixedCollection.on({                add:     onFieldAddReplace,                replace: onFieldAddReplace            });              for (i = 0, ln = fields.length; i < ln; ++i) {                field = fields[i];                fieldsMixedCollection.add(field.isField ? field : new Ext.data.Field(field));            }            if (!fieldsMixedCollection.get(idProperty)) {                fieldsMixedCollection.add(new Ext.data.Field(idProperty));            }            // Ensure the Fields are on correct order: Fields with custom convert function last            fieldConvertSortFn();            fieldsMixedCollection.on({                add:     fieldConvertSortFn,                replace: fieldConvertSortFn            });            data.fields = fieldsMixedCollection;            if (idgen) {                data.idgen = Ext.data.IdGenerator.get(idgen);            }            //associations can be specified in the more convenient format (e.g. not inside an 'associations' array).            //we support that here            addAssociations(data.belongsTo, 'belongsTo');            delete data.belongsTo;            addAssociations(data.hasMany, 'hasMany');            delete data.hasMany;            addAssociations(data.hasOne, 'hasOne');            delete data.hasOne;            if (superAssociations) {                associationsConfigs = superAssociations.items.concat(associationsConfigs);            }            for (i = 0, ln = associationsConfigs.length; i < ln; ++i) {                dependencies.push('association.' + associationsConfigs[i].type.toLowerCase());            }            // If we have not been supplied with a Proxy *instance*, then add the proxy type to our dependency list            if (clsProxy && !clsProxy.isProxy) {                //<debug>                if (typeof clsProxy !== 'string' && !clsProxy.type) {                    Ext.log.warn(name + ': proxy type is ' + clsProxy.type);                }                //</debug>                dependencies.push('proxy.' + (typeof clsProxy === 'string' ? clsProxy : clsProxy.type));            }            Ext.require(dependencies, function() {                Ext.ModelManager.registerType(name, cls);                for (i = 0, ln = associationsConfigs.length; i < ln; ++i) {                    associationConfig = associationsConfigs[i];                    if (associationConfig.isAssociation) {                        associationConfig = Ext.applyIf({                            ownerModel: name,                            associatedModel: associationConfig.model                        }, associationConfig.initialConfig);                    } else {                        Ext.apply(associationConfig, {                            ownerModel: name,                            associatedModel: associationConfig.model                        });                    }                    if (Ext.ModelManager.getModel(associationConfig.model) === undefined) {                        Ext.ModelManager.registerDeferredAssociation(associationConfig);                    } else {                        associationsMixedCollection.add(Ext.data.association.Association.create(associationConfig));                    }                }                data.associations = associationsMixedCollection;                // onBeforeCreated may get called *asynchronously* if any of those required classes caused                // an asynchronous script load. This would mean that the class definition object                // has not been applied to the prototype when the Model definition has returned.                // The Reader constructor does not attempt to buildExtractors if the fields MixedCollection                // has not yet been set. The cls.setProxy call triggers a build of extractor methods.                onBeforeClassCreated.call(me, cls, data, hooks);                cls.setProxy(clsProxy);                // Fire the onModelDefined template method on ModelManager                Ext.ModelManager.onModelDefined(cls);            });        };    },    inheritableStatics: {<span id='Ext-data-Model-static-method-setProxy'>        /**</span>         * 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         * @return {Ext.data.proxy.Proxy}         * @static         * @inheritable         */        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;        },<span id='Ext-data-Model-static-method-getProxy'>        /**</span>         * Returns the configured Proxy for this Model         * @return {Ext.data.proxy.Proxy} The proxy         * @static         * @inheritable         */        getProxy: function() {            return this.proxy;        },<span id='Ext-data-Model-static-method-setFields'>        /**</span>         * Apply a new set of field and/or property definitions to the existing model. This will replace any existing         * fields, including fields inherited from superclasses. Mainly for reconfiguring the         * model based on changes in meta data (called from Reader's onMetaChange method).         * @static         * @inheritable         */        setFields: function(fields, idProperty, clientIdProperty) {            var me = this,                proto = me.prototype,                prototypeFields = proto.fields,                len = fields ? fields.length : 0,                i = 0;            if (idProperty) {                proto.idProperty = idProperty;            }            if (clientIdProperty) {                proto.clientIdProperty = clientIdProperty;            }            if (prototypeFields) {                prototypeFields.clear();            }            else {                prototypeFields = me.prototype.fields = new Ext.util.MixedCollection(false, function(field) {                    return field.name;                });            }            for (; i < len; i++) {                prototypeFields.add(new Ext.data.Field(fields[i]));            }            if (!prototypeFields.get(proto.idProperty)) {                prototypeFields.add(new Ext.data.Field(proto.idProperty));            }            me.fields = prototypeFields;            return prototypeFields;        },<span id='Ext-data-Model-method-getFields'>        /**</span>         * Returns an Array of {@link Ext.data.Field Field} definitions which define this Model's structure         *         * Fields are sorted upon Model class definition. Fields with custom {@link Ext.data.Field#convert convert} functions         * are moved to *after* fields with no convert functions. This is so that convert functions which rely on existing         * field values will be able to read those field values.         *         * @return {Ext.data.Field[]} The defined Fields for this Model.         *         */        getFields: function() {            return this.prototype.fields.items;        },<span id='Ext-data-Model-static-method-load'>        /**</span>         * Asynchronously loads a model instance by id. Sample usage:         *         *     Ext.define('MyApp.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/String} 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) {            config = Ext.apply({}, config);            config = Ext.applyIf(config, {                action: 'read',                id    : id            });            var operation  = new 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: {<span id='Ext-data-Model-static-property-PREFIX'>        /**</span>         * @property         * @static         * @private         */        PREFIX : 'ext-record',<span id='Ext-data-Model-static-property-AUTO_ID'>        /**</span>         * @property         * @static         * @private         */        AUTO_ID: 1,<span id='Ext-data-Model-static-property-EDIT'>        /**</span>         * @property         * @static         * The update operation of type 'edit'. Used by {@link Ext.data.Store#update Store.update} event.         */        EDIT   : 'edit',<span id='Ext-data-Model-static-property-REJECT'>        /**</span>         * @property         * @static         * The update operation of type 'reject'. Used by {@link Ext.data.Store#update Store.update} event.         */        REJECT : 'reject',<span id='Ext-data-Model-static-property-COMMIT'>        /**</span>         * @property         * @static         * The update operation of type 'commit'. Used by {@link Ext.data.Store#update Store.update} event.         */        COMMIT : 'commit',<span id='Ext-data-Model-static-method-id'>        /**</span>         * Generates a sequential id. This method is typically called when a record is {@link Ext#create         * create}d and {@link #constructor no id has been specified}. The id will automatically be assigned to the         * record. The returned id takes the form: {PREFIX}-{AUTO_ID}.         *         * - **PREFIX** : String - Ext.data.Model.PREFIX (defaults to 'ext-record')         * - **AUTO_ID** : String - Ext.data.Model.AUTO_ID (defaults to 1 initially)         *         * @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, `"ext-record-i++"`;         * @static         */        id: function(rec) {            var id = [this.PREFIX, '-', this.AUTO_ID++].join('');            rec.phantom = true;            rec.internalId = id;            return id;        }    },<span id='Ext-data-Model-cfg-idgen'>    /**</span>     * @cfg {String/Object} idgen     * The id generator to use for this model. The default id generator does not generate     * values for the {@link #idProperty}.     *     * This can be overridden at the model level to provide a custom generator for a model.     * The simplest form of this would be:     *     *      Ext.define('MyApp.data.MyModel', {     *          extend: 'Ext.data.Model',     *          requires: ['Ext.data.SequentialIdGenerator'],     *          idgen: 'sequential',     *          ...     *      });     *     * The above would generate {@link Ext.data.SequentialIdGenerator sequential} id's such     * as 1, 2, 3 etc..     *     * Another useful id generator is {@link Ext.data.UuidGenerator}:     *     *      Ext.define('MyApp.data.MyModel', {     *          extend: 'Ext.data.Model',     *          requires: ['Ext.data.UuidGenerator'],     *          idgen: 'uuid',     *          ...     *      });     *     * An id generation can also be further configured:     *     *      Ext.define('MyApp.data.MyModel', {     *          extend: 'Ext.data.Model',     *          idgen: {     *              type: 'sequential',     *              seed: 1000,     *              prefix: 'ID_'     *          }     *      });     *     * The above would generate id's such as ID_1000, ID_1001, ID_1002 etc..     *     * If multiple models share an id space, a single generator can be shared:     *     *      Ext.define('MyApp.data.MyModelX', {     *          extend: 'Ext.data.Model',     *          idgen: {     *              type: 'sequential',     *              id: 'xy'     *          }     *      });     *     *      Ext.define('MyApp.data.MyModelY', {     *          extend: 'Ext.data.Model',     *          idgen: {     *              type: 'sequential',     *              id: 'xy'     *          }     *      });     *     * For more complex, shared id generators, a custom generator is the best approach.     * See {@link Ext.data.IdGenerator} for details on creating custom id generators.     *     * @markdown     */    idgen: {        isGenerator: true,        type: 'default',        generate: function () {            return null;        },        getRecId: function (rec) {            return rec.modelName + '-' + rec.internalId;        }    },<span id='Ext-data-Model-property-editing'>    /**</span>     * @property {Boolean} editing     * Internal flag used to track whether or not the model instance is currently being edited.     * @readonly     */    editing : false,<span id='Ext-data-Model-property-dirty'>    /**</span>     * @property {Boolean} dirty     * True if this Record has been modified.     * @readonly     */    dirty : false,<span id='Ext-data-Model-cfg-persistenceProperty'>    /**</span>     * @cfg {String} persistenceProperty     * The name of the property on this Persistable object that its data is saved to. Defaults to 'data'     * (i.e: all persistable data resides in `this.data`.)     */    persistenceProperty: 'data',    evented: false,<span id='Ext-data-Model-property-isModel'>    /**</span>     * @property {Boolean} isModel     * `true` in this class to identify an object as an instantiated Model, or subclass thereof.     */    isModel: true,<span id='Ext-data-Model-property-phantom'>    /**</span>     * @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,<span id='Ext-data-Model-cfg-idProperty'>    /**</span>     * @cfg {String} idProperty     * The name of the field treated as this Model's unique id. Defaults to 'id'.     */    idProperty: 'id',<span id='Ext-data-Model-cfg-clientIdProperty'>    /**</span>     * @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.     */    clientIdProperty: null,<span id='Ext-data-Model-cfg-defaultProxyType'>    /**</span>     * @cfg {String} defaultProxyType     * The string type of the default Model Proxy. Defaults to 'ajax'.     */    defaultProxyType: 'ajax',    // Fields config and property<span id='Ext-data-Model-cfg-fields'>    /**</span>     * @cfg {Object[]/String[]} fields     * The fields for this model. This is an Array of **{@link Ext.data.Field Field}** definition objects. A Field     * definition may simply be the *name* of the Field, but a Field encapsulates {@link Ext.data.Field#type data type},     * {@link Ext.data.Field#convert custom conversion} of raw data, and a {@link Ext.data.Field#mapping mapping}     * property to specify by name of index, how to extract a field's value from a raw data object, so it is best practice     * to specify a full set of {@link Ext.data.Field Field} config objects.     */<span id='Ext-data-Model-property-fields'>    /**</span>     * @property {Ext.util.MixedCollection} fields     * A {@link Ext.util.MixedCollection Collection} of the fields defined for this Model (including fields defined in superclasses)     *     * This is a collection of {@link Ext.data.Field} instances, each of which encapsulates information that the field was configured with.     * By default, you can specify a field as simply a String, representing the *name* of the field, but a Field encapsulates     * {@link Ext.data.Field#type data type}, {@link Ext.data.Field#convert custom conversion} of raw data, and a {@link Ext.data.Field#mapping mapping}     * property to specify by name of index, how to extract a field's value from a raw data object.     */<span id='Ext-data-Model-cfg-validations'>    /**</span>     * @cfg {Object[]} validations     * An array of {@link Ext.data.validations validations} for this model.     */    // Associations configs and properties<span id='Ext-data-Model-cfg-associations'>    /**</span>     * @cfg {Object[]} associations     * An array of {@link Ext.data.Association associations} for this model.     */<span id='Ext-data-Model-cfg-hasMany'>    /**</span>     * @cfg {String/Object/String[]/Object[]} hasMany     * One or more {@link Ext.data.HasManyAssociation HasMany associations} for this model.     */<span id='Ext-data-Model-cfg-belongsTo'>    /**</span>     * @cfg {String/Object/String[]/Object[]} belongsTo     * One or more {@link Ext.data.BelongsToAssociation BelongsTo associations} for this model.     */<span id='Ext-data-Model-cfg-proxy'>    /**</span>     * @cfg {String/Object/Ext.data.proxy.Proxy} proxy     * The {@link Ext.data.proxy.Proxy proxy} to use for this model.     */<span id='Ext-data-Model-event-idchanged'>    /**</span>     * @event idchanged     * Fired when this model's id changes     * @param {Ext.data.Model} this     * @param {Number/String} oldId The old id     * @param {Number/String} newId The new id     */<span id='Ext-data-Model-method-constructor'>    /**</span>     * Creates new Model instance.     * @param {Object} data An object containing keys corresponding to this model's fields, and their associated values     */    constructor: function(data, id, raw, convertedData) {        // id, raw and convertedData not documented intentionally, meant to be used internally.        // TODO: find where "raw" is used and remove it. The first parameter, "data" is raw, unconverted data. "raw" is redundant.        // The "convertedData" parameter is a converted object hash with all properties corresponding to defined Fields        // and all values of the defined type. It is used directly as this record's data property.        data = data || {};        var me = this,            fields,            length,            field,            name,            value,            newId,            persistenceProperty,            i;<span id='Ext-data-Model-property-internalId'>        /**</span>         * @property {Number/String} internalId         * An internal unique ID for each Model instance, used to identify Models that don't have an ID yet         * @private         */        me.internalId = (id || id === 0) ? id : Ext.data.Model.id(me);<span id='Ext-data-Model-property-raw'>        /**</span>         * @property {Object} raw The raw data used to create this model if created via a reader.         */        me.raw = raw || data; // If created using data in constructor, use data        if (!me.data) {            me.data = {};        }<span id='Ext-data-Model-property-modified'>        /**</span>         * @property {Object} modified Key: value pairs of all fields whose values have changed         */        me.modified = {};        // Deal with spelling error in previous releases        if (me.persistanceProperty) {            //<debug>            Ext.log.warn('Ext.data.Model: persistanceProperty has been deprecated. Use persistenceProperty instead.');            //</debug>            me.persistenceProperty = me.persistanceProperty;        }        me[me.persistenceProperty] = convertedData || {};        me.mixins.observable.constructor.call(me);        if (!convertedData) {            //add default field values if present            fields = me.fields.items;            length = fields.length;            i = 0;            persistenceProperty = me[me.persistenceProperty];            if (Ext.isArray(data)) {                for (; i < length; i++) {                    field = fields[i];                    name  = field.name;                    // Use the original ordinal position at which the Model inserted the field into its collection.                    // Fields are sorted to place fields with a *convert* function last.                    value = data[field.originalIndex];                    if (value === undefined) {                        value = field.defaultValue;                    }                    // 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.                    if (field.convert) {                        value = field.convert(value, me);                    }                    // On instance construction, do not create data properties based on undefined input properties                    if (value !== undefined) {                        persistenceProperty[name] = value;                    }                }            } else {               for (; i < length; i++) {                    field = fields[i];                    name  = field.name;                    value = data[name];                    if (value === undefined) {                        value = field.defaultValue;                    }                    if (field.convert) {                        value = field.convert(value, me);                    }                    // On instance construction, do not create data properties based on undefined input properties                    if (value !== undefined) {                        persistenceProperty[name] = value;                    }               }            }        }<span id='Ext-data-Model-property-stores'>        /**</span>         * @property {Ext.data.Store[]} stores         * The {@link Ext.data.Store Stores} to which this instance is bound.         */        me.stores = [];        if (me.getId()) {            me.phantom = false;        } else if (me.phantom) {            newId = me.idgen.generate();            if (newId !== null) {                me.setId(newId);            }        }        // clear any dirty/modified since we're initializing        me.dirty = false;        me.modified = {};        if (typeof me.init == 'function') {            me.init();        }        me.id = me.idgen.getRecId(me);    },<span id='Ext-data-Model-method-get'>    /**</span>     * Returns the value of the given field     * @param {String} fieldName The field to fetch the value for     * @return {Object} The value     */    get: function(field) {        return this[this.persistenceProperty][field];    },    // This object is used whenever the set() method is called and given a string as the    // first argument. This approach saves memory (and GC costs) since we could be called    // a lot.    _singleProp: {},<span id='Ext-data-Model-method-set'>    /**</span>     * 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} newValue The value to set     * @return {String[]} The array of modified field names or null if nothing was modified.     */    set: function (fieldName, newValue) {        var me = this,            data = me[me.persistenceProperty],            fields = me.fields,            modified = me.modified,            single = (typeof fieldName == 'string'),            currentValue, field, idChanged, key, modifiedFieldNames, name, oldId,            newId, value, values;        if (single) {            values = me._singleProp;            values[fieldName] = newValue;        } else {            values = fieldName;        }        for (name in values) {            if (values.hasOwnProperty(name)) {                value = values[name];                if (fields && (field = fields.get(name)) && field.convert) {                    value = field.convert(value, me);                }                currentValue = data[name];                if (me.isEqual(currentValue, value)) {                    continue; // new value is the same, so no change...                }                data[name] = value;                (modifiedFieldNames || (modifiedFieldNames = [])).push(name);                if (field && field.persist) {                    if (modified.hasOwnProperty(name)) {                        if (me.isEqual(modified[name], value)) {                            // The original value in me.modified equals the new value, so                            // the field is no longer modified:                            delete modified[name];                            // 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;                        modified[name] = currentValue;                    }                }                if (name == me.idProperty) {                    idChanged = true;                    oldId = currentValue;                    newId = value;                }            }        }        if (single) {            // cleanup our reused object for next time... important to do this before            // we fire any events or call anyone else (like afterEdit)!            delete values[fieldName];        }        if (idChanged) {            me.fireEvent('idchanged', me, oldId, newId);        }        if (!me.editing && modifiedFieldNames) {            me.afterEdit(modifiedFieldNames);        }        return modifiedFieldNames || null;    },<span id='Ext-data-Model-method-copyFrom'>    /**</span>     * @private     * Copies data from the passed record into this record. If the passed record is undefined, does nothing.     *     * If this is a phantom record (represented only in the client, with no corresponding database entry), and     * the source record is not a phantom, then this record acquires the id of the source record.     *     * @param {Ext.data.Model} sourceRecord The record to copy data from.     */    copyFrom: function(sourceRecord) {        if (sourceRecord) {            var me = this,                fields = me.fields.items,                fieldCount = fields.length,                field, i = 0,                myData = me[me.persistenceProperty],                sourceData = sourceRecord[sourceRecord.persistenceProperty],                value;            for (; i < fieldCount; i++) {                field = fields[i];                // Do not use setters.                // Copy returned values in directly from the data object.                // Converters have already been called because new Records                // have been created to copy from.                // This is a direct record-to-record value copy operation.                value = sourceData[field.name];                if (value !== undefined) {                    myData[field.name] = value;                }            }            // If this is a phantom record being updated from a concrete record, copy the ID in.            if (me.phantom && !sourceRecord.phantom) {                me.setId(sourceRecord.getId());            }        }    },<span id='Ext-data-Model-method-isEqual'>    /**</span>     * 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 Ext.Date.isEqual(a, b);        }        return a === b;    },<span id='Ext-data-Model-method-beginEdit'>    /**</span>     * 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;            me.dirtySave = me.dirty;            me.dataSave = Ext.apply({}, me[me.persistenceProperty]);            me.modifiedSave = Ext.apply({}, me.modified);        }    },<span id='Ext-data-Model-method-cancelEdit'>    /**</span>     * 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.persistenceProperty] = me.dataSave;            me.dirty = me.dirtySave;            delete me.modifiedSave;            delete me.dataSave;            delete me.dirtySave;        }    },<span id='Ext-data-Model-method-endEdit'>    /**</span>     * 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,            changed;        if (me.editing) {            me.editing = false;            if(!modifiedFieldNames) {                modifiedFieldNames = me.getModifiedFieldNames();            }            changed = me.dirty || modifiedFieldNames.length > 0;            delete me.modifiedSave;            delete me.dataSave;            delete me.dirtySave;            if (changed && silent !== true) {                me.afterEdit(modifiedFieldNames);            }        }    },<span id='Ext-data-Model-method-getModifiedFieldNames'>    /**</span>     * Gets the names of all the fields that were modified during an edit     * @private     * @return {String[]} An array of modified field names     */    getModifiedFieldNames: function(){        var me = this,            saved = me.dataSave,            data = me[me.persistenceProperty],            modified = [],            key;        for (key in data) {            if (data.hasOwnProperty(key)) {                if (!me.isEqual(data[key], saved[key])) {                    modified.push(key);                }            }        }        return modified;     },<span id='Ext-data-Model-method-getChanges'>    /**</span>     * 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;    },<span id='Ext-data-Model-method-isModified'>    /**</span>     * 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);    },<span id='Ext-data-Model-method-setDirty'>    /**</span>     * 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,            fields = me.fields.items,            fLen   = fields.length,            field, name, f;        me.dirty = true;        for (f = 0; f < fLen; f++) {            field = fields[f];            if (field.persist) {                name  = field.name;                me.modified[name] = me.get(name);            }        }    },    //<debug>    markDirty : function() {        Ext.log.warn('Ext.data.Model: markDirty has been deprecated. Use setDirty instead.');        return this.setDirty.apply(this, arguments);    },    //</debug><span id='Ext-data-Model-method-reject'>    /**</span>     * 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#event-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[me.persistenceProperty][field] = modified[field];                }            }        }        me.dirty = false;        me.editing = false;        me.modified = {};        if (silent !== true) {            me.afterReject();        }    },<span id='Ext-data-Model-method-commit'>    /**</span>     * 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#event-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;        me.phantom = me.dirty = me.editing = false;        me.modified = {};        if (silent !== true) {            me.afterCommit();        }    },<span id='Ext-data-Model-method-copy'>    /**</span>     * Creates a copy (clone) of this Model instance.     *     * @param {String} [id] A new id, defaults to the id of the instance being copied.     * See `{@link Ext.data.Model#id id}`. 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;        // Use raw data as the data param.        // Pass a copy iof our converted data in to be used as the new record's convertedData        return new me.self(me.raw, newId, null, Ext.apply({}, me[me.persistenceProperty]));    },<span id='Ext-data-Model-method-setProxy'>    /**</span>     * 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     * @return {Ext.data.proxy.Proxy}     */    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;    },<span id='Ext-data-Model-method-getProxy'>    /**</span>     * Returns the configured Proxy for this Model.     * @return {Ext.data.proxy.Proxy} The proxy     */    getProxy: function() {        return this.proxy;    },<span id='Ext-data-Model-method-validate'>    /**</span>     * Validates the current data against all of its configured {@link #validations}.     * @return {Ext.data.Errors} The errors object     */    validate: function() {        var errors      = new 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;    },<span id='Ext-data-Model-method-isValid'>    /**</span>     * Checks if the model is valid. See {@link #validate}.     * @return {Boolean} True if the model is valid.     */    isValid: function(){        return this.validate().isValid();    },<span id='Ext-data-Model-method-save'>    /**</span>     * 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) {        options = Ext.apply({}, options);        var me     = this,            action = me.phantom ? 'create' : 'update',            scope  = options.scope || me,            stores = me.stores,            i = 0,            storeCount,            store,            args,            operation,            callback;        Ext.apply(options, {            records: [me],            action : action        });        operation = new Ext.data.Operation(options);        callback = function(operation) {            args = [me, operation];            if (operation.wasSuccessful()) {                for(storeCount = stores.length; i < storeCount; i++) {                    store = stores[i];                    store.fireEvent('write', store, operation);                    store.fireEvent('datachanged', store);                    // Not firing refresh here, since it's a single record                }                Ext.callback(options.success, scope, args);            } else {                Ext.callback(options.failure, scope, args);            }            Ext.callback(options.callback, scope, args);        };        me.getProxy()[action](operation, callback, me);        return me;    },<span id='Ext-data-Model-method-destroy'>    /**</span>     * Destroys the model 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     */    destroy: function(options){        options = Ext.apply({}, options);        var me     = this,            scope  = options.scope || me,            stores = me.stores,            i = 0,            storeCount,            store,            args,            operation,            callback;        Ext.apply(options, {            records: [me],            action : 'destroy'        });        operation = new Ext.data.Operation(options);        callback = function(operation) {            args = [me, operation];            if (operation.wasSuccessful()) {                for(storeCount = stores.length; i < storeCount; i++) {                    store = stores[i];                    store.fireEvent('write', store, operation);                    store.fireEvent('datachanged', store);                    // Not firing refresh here, since it's a single record                }                me.clearListeners();                Ext.callback(options.success, scope, args);            } else {                Ext.callback(options.failure, scope, args);            }            Ext.callback(options.callback, scope, args);        };        me.getProxy().destroy(operation, callback, me);        return me;    },<span id='Ext-data-Model-method-getId'>    /**</span>     * Returns the unique ID allocated to this model instance as defined by {@link #idProperty}.     * @return {Number/String} The id     */    getId: function() {        return this.get(this.idProperty);    },<span id='Ext-data-Model-method-getObservableId'>    /**</span>     * @private     */    getObservableId: function() {        return this.id;    },<span id='Ext-data-Model-method-setId'>    /**</span>     * Sets the model instance's id field to the given id.     * @param {Number/String} id The new id     */    setId: function(id) {        this.set(this.idProperty, id);        this.phantom  = !(id || id === 0);    },<span id='Ext-data-Model-method-join'>    /**</span>     * 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.     */    join : function(store) {        Ext.Array.include(this.stores, store);<span id='Ext-data-Model-property-store'>        /**</span>         * @property {Ext.data.Store} store         * The {@link Ext.data.Store Store} to which this instance belongs. NOTE: If this         * instance is bound to multiple stores, this property will reference only the         * first. To examine all the stores, use the {@link #stores} property instead.         */        this.store = this.stores[0]; // compat w/all releases ever    },<span id='Ext-data-Model-method-unjoin'>    /**</span>     * 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.     */    unjoin: function(store) {        Ext.Array.remove(this.stores, store);        this.store = this.stores[0] || null; // compat w/all releases ever    },<span id='Ext-data-Model-method-afterEdit'>    /**</span>     * @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) {        this.callStore('afterEdit', modifiedFieldNames);    },<span id='Ext-data-Model-method-afterReject'>    /**</span>     * @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");    },<span id='Ext-data-Model-method-afterCommit'>    /**</span>     * @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');    },<span id='Ext-data-Model-method-callStore'>    /**</span>     * @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. If this model is joined to      * a Ext.data.NodeStore, then this method calls the given method on the NodeStore and the associated Ext.data.TreeStore     * @param {String} fn The function to call on the store     */    callStore: function(fn) {        var args = Ext.Array.clone(arguments),            stores = this.stores,            i = 0,            len = stores.length,            store, treeStore;        args[0] = this;        for (; i < len; ++i) {            store = stores[i];            if (store && typeof store[fn] == "function") {                store[fn].apply(store, args);            }            // if the record is bound to a NodeStore call the TreeStore's method as well            treeStore = store.treeStore;            if (treeStore && typeof treeStore[fn] == "function") {                treeStore[fn].apply(treeStore, args);            }        }    },<span id='Ext-data-Model-method-getData'>    /**</span>     * Gets all values for each field in this model and returns an object     * containing the current data.     * @param {Boolean} includeAssociated True to also include associated data. Defaults to false.     * @return {Object} An object hash containing all the values in this model     */    getData: function(includeAssociated){        var me     = this,            fields = me.fields.items,            fLen   = fields.length,            data   = {},            name, f;        for (f = 0; f < fLen; f++) {            name = fields[f].name;            data[name] = me.get(name);        }        if (includeAssociated === true) {            Ext.apply(data, me.getAssociatedData());        }        return data;    },<span id='Ext-data-Model-method-getAssociatedData'>    /**</span>     * 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({}, 1);    },<span id='Ext-data-Model-method-prepareAssociatedData'>    /**</span>     * @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 {Object} seenKeys A hash of all the associations we've already seen     * @param {Number} depth The current depth     * @return {Object} The nested data set for the Model's loaded associations     */    prepareAssociatedData: function(seenKeys, depth) {        /*         * In this method we use a breadth first strategy instead of depth         * first. The reason for doing so is that it prevents messy & difficult         * issues when figuring out which associations we've already processed         * & at what depths.         */        var me               = this,            associations     = me.associations.items,            associationCount = associations.length,            associationData  = {},            // We keep 3 lists at the same index instead of using an array of objects.            // The reasoning behind this is that this method gets called a lot            // So we want to minimize the amount of objects we create for GC.            toRead           = [],            toReadKey        = [],            toReadIndex      = [],            associatedStore, associatedRecords, associatedRecord, o, index, result, seenDepth,            associationId, associatedRecordCount, association, i, j, type, name;        for (i = 0; i < associationCount; i++) {            association = associations[i];            associationId = association.associationId;                        seenDepth = seenKeys[associationId];            if (seenDepth && seenDepth !== depth) {                continue;            }            seenKeys[associationId] = depth;            type = association.type;            name = association.name;            if (type == 'hasMany') {                //this is the hasMany store filled with the associated data                associatedStore = me[association.storeName];                //we will use this to contain each associated record's data                associationData[name] = [];                //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. Get                    // all the records so we can process them                    for (j = 0; j < associatedRecordCount; j++) {                        associatedRecord = associatedRecords[j];                        associationData[name][j] = associatedRecord.getData();                        toRead.push(associatedRecord);                        toReadKey.push(name);                        toReadIndex.push(j);                    }                }            } else if (type == 'belongsTo' || type == 'hasOne') {                associatedRecord = me[association.instanceName];                // If we have a record, put it onto our list                if (associatedRecord !== undefined) {                    associationData[name] = associatedRecord.getData();                    toRead.push(associatedRecord);                    toReadKey.push(name);                    toReadIndex.push(-1);                }            }        }                for (i = 0, associatedRecordCount = toRead.length; i < associatedRecordCount; ++i) {            associatedRecord = toRead[i];            o = associationData[toReadKey[i]];            index = toReadIndex[i];            result = associatedRecord.prepareAssociatedData(seenKeys, depth + 1);            if (index === -1) {                Ext.apply(o, result);            } else {                Ext.apply(o[index], result);            }        }        return associationData;    }});</pre></body></html>
 |