+Ext.define('Ext.chart.axis.Gauge', {
+
+ /* Begin Definitions */
+
+ extend: 'Ext.chart.axis.Abstract',
+
+ /* End Definitions */
+
+ /**
+ * @cfg {Number} minimum (required)
+ * The minimum value of the interval to be displayed in the axis.
+ */
+
+ /**
+ * @cfg {Number} maximum (required)
+ * The maximum value of the interval to be displayed in the axis.
+ */
+
+ /**
+ * @cfg {Number} steps (required)
+ * The number of steps and tick marks to add to the interval.
+ */
+
+ /**
+ * @cfg {Number} [margin=10]
+ * The offset positioning of the tick marks and labels in pixels.
+ */
+
+ /**
+ * @cfg {String} title
+ * The title for the Axis.
+ */
+
+ position: 'gauge',
+
+ alias: 'axis.gauge',
+
+ drawAxis: function(init) {
+ var chart = this.chart,
+ surface = chart.surface,
+ bbox = chart.chartBBox,
+ centerX = bbox.x + (bbox.width / 2),
+ centerY = bbox.y + bbox.height,
+ margin = this.margin || 10,
+ rho = Math.min(bbox.width, 2 * bbox.height) /2 + margin,
+ sprites = [], sprite,
+ steps = this.steps,
+ i, pi = Math.PI,
+ cos = Math.cos,
+ sin = Math.sin;
+
+ if (this.sprites && !chart.resizing) {
+ this.drawLabel();
+ return;
+ }
+
+ if (this.margin >= 0) {
+ if (!this.sprites) {
+ //draw circles
+ for (i = 0; i <= steps; i++) {
+ sprite = surface.add({
+ type: 'path',
+ path: ['M', centerX + (rho - margin) * cos(i / steps * pi - pi),
+ centerY + (rho - margin) * sin(i / steps * pi - pi),
+ 'L', centerX + rho * cos(i / steps * pi - pi),
+ centerY + rho * sin(i / steps * pi - pi), 'Z'],
+ stroke: '#ccc'
+ });
+ sprite.setAttributes({
+ hidden: false
+ }, true);
+ sprites.push(sprite);
+ }
+ } else {
+ sprites = this.sprites;
+ //draw circles
+ for (i = 0; i <= steps; i++) {
+ sprites[i].setAttributes({
+ path: ['M', centerX + (rho - margin) * cos(i / steps * pi - pi),
+ centerY + (rho - margin) * sin(i / steps * pi - pi),
+ 'L', centerX + rho * cos(i / steps * pi - pi),
+ centerY + rho * sin(i / steps * pi - pi), 'Z'],
+ stroke: '#ccc'
+ }, true);
+ }
+ }
+ }
+ this.sprites = sprites;
+ this.drawLabel();
+ if (this.title) {
+ this.drawTitle();
+ }
+ },
+
+ drawTitle: function() {
+ var me = this,
+ chart = me.chart,
+ surface = chart.surface,
+ bbox = chart.chartBBox,
+ labelSprite = me.titleSprite,
+ labelBBox;
+
+ if (!labelSprite) {
+ me.titleSprite = labelSprite = surface.add({
+ type: 'text',
+ zIndex: 2
+ });
+ }
+ labelSprite.setAttributes(Ext.apply({
+ text: me.title
+ }, me.label || {}), true);
+ labelBBox = labelSprite.getBBox();
+ labelSprite.setAttributes({
+ x: bbox.x + (bbox.width / 2) - (labelBBox.width / 2),
+ y: bbox.y + bbox.height - (labelBBox.height / 2) - 4
+ }, true);
+ },
+
+ /**
+ * Updates the {@link #title} of this axis.
+ * @param {String} title
+ */
+ setTitle: function(title) {
+ this.title = title;
+ this.drawTitle();
+ },
+
+ drawLabel: function() {
+ var chart = this.chart,
+ surface = chart.surface,
+ bbox = chart.chartBBox,
+ centerX = bbox.x + (bbox.width / 2),
+ centerY = bbox.y + bbox.height,
+ margin = this.margin || 10,
+ rho = Math.min(bbox.width, 2 * bbox.height) /2 + 2 * margin,
+ round = Math.round,
+ labelArray = [], label,
+ maxValue = this.maximum || 0,
+ steps = this.steps, i = 0,
+ adjY,
+ pi = Math.PI,
+ cos = Math.cos,
+ sin = Math.sin,
+ labelConf = this.label,
+ renderer = labelConf.renderer || function(v) { return v; };
+
+ if (!this.labelArray) {
+ //draw scale
+ for (i = 0; i <= steps; i++) {
+ // TODO Adjust for height of text / 2 instead
+ adjY = (i === 0 || i === steps) ? 7 : 0;
+ label = surface.add({
+ type: 'text',
+ text: renderer(round(i / steps * maxValue)),
+ x: centerX + rho * cos(i / steps * pi - pi),
+ y: centerY + rho * sin(i / steps * pi - pi) - adjY,
+ 'text-anchor': 'middle',
+ 'stroke-width': 0.2,
+ zIndex: 10,
+ stroke: '#333'
+ });
+ label.setAttributes({
+ hidden: false
+ }, true);
+ labelArray.push(label);
+ }
+ }
+ else {
+ labelArray = this.labelArray;
+ //draw values
+ for (i = 0; i <= steps; i++) {
+ // TODO Adjust for height of text / 2 instead
+ adjY = (i === 0 || i === steps) ? 7 : 0;
+ labelArray[i].setAttributes({
+ text: renderer(round(i / steps * maxValue)),
+ x: centerX + rho * cos(i / steps * pi - pi),
+ y: centerY + rho * sin(i / steps * pi - pi) - adjY
+ }, true);
+ }
+ }
+ this.labelArray = labelArray;
+ }
+});
+/**
+ * @class Ext.chart.axis.Numeric
+ * @extends Ext.chart.axis.Axis
+ *
+ * An axis to handle numeric values. This axis is used for quantitative data as
+ * opposed to the category axis. You can set mininum and maximum values to the
+ * axis so that the values are bound to that. If no values are set, then the
+ * scale will auto-adjust to the values.
+ *
+ * @example
+ * var store = Ext.create('Ext.data.JsonStore', {
+ * fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
+ * data: [
+ * {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
+ * {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
+ * {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
+ * {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
+ * {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
+ * ]
+ * });
+ *
+ * Ext.create('Ext.chart.Chart', {
+ * renderTo: Ext.getBody(),
+ * width: 500,
+ * height: 300,
+ * store: store,
+ * axes: [{
+ * type: 'Numeric',
+ * grid: true,
+ * position: 'left',
+ * fields: ['data1', 'data2', 'data3', 'data4', 'data5'],
+ * title: 'Sample Values',
+ * grid: {
+ * odd: {
+ * opacity: 1,
+ * fill: '#ddd',
+ * stroke: '#bbb',
+ * 'stroke-width': 1
+ * }
+ * },
+ * minimum: 0,
+ * adjustMinimumByMajorUnit: 0
+ * }, {
+ * type: 'Category',
+ * position: 'bottom',
+ * fields: ['name'],
+ * title: 'Sample Metrics',
+ * grid: true,
+ * label: {
+ * rotate: {
+ * degrees: 315
+ * }
+ * }
+ * }],
+ * series: [{
+ * type: 'area',
+ * highlight: false,
+ * axis: 'left',
+ * xField: 'name',
+ * yField: ['data1', 'data2', 'data3', 'data4', 'data5'],
+ * style: {
+ * opacity: 0.93
+ * }
+ * }]
+ * });
+ *
+ * In this example we create an axis of Numeric type. We set a minimum value so that
+ * even if all series have values greater than zero, the grid starts at zero. We bind
+ * the axis onto the left part of the surface by setting `position` to `left`.
+ * We bind three different store fields to this axis by setting `fields` to an array.
+ * We set the title of the axis to _Number of Hits_ by using the `title` property.
+ * We use a `grid` configuration to set odd background rows to a certain style and even rows
+ * to be transparent/ignored.
+ */
+Ext.define('Ext.chart.axis.Numeric', {
+
+ /* Begin Definitions */
+
+ extend: 'Ext.chart.axis.Axis',
+
+ alternateClassName: 'Ext.chart.NumericAxis',
+
+ /* End Definitions */
+
+ type: 'numeric',
+
+ alias: 'axis.numeric',
+
+ constructor: function(config) {
+ var me = this,
+ hasLabel = !!(config.label && config.label.renderer),
+ label;
+
+ me.callParent([config]);
+ label = me.label;
+ if (me.roundToDecimal === false) {
+ return;
+ }
+ if (!hasLabel) {
+ label.renderer = function(v) {
+ return me.roundToDecimal(v, me.decimals);
+ };
+ }
+ },
+
+ roundToDecimal: function(v, dec) {
+ var val = Math.pow(10, dec || 0);
+ return Math.floor(v * val) / val;
+ },
+
+ /**
+ * The minimum value drawn by the axis. If not set explicitly, the axis
+ * minimum will be calculated automatically.
+ *
+ * @property {Number} minimum
+ */
+ minimum: NaN,
+
+ /**
+ * The maximum value drawn by the axis. If not set explicitly, the axis
+ * maximum will be calculated automatically.
+ *
+ * @property {Number} maximum
+ */
+ maximum: NaN,
+
+ /**
+ * The number of decimals to round the value to.
+ *
+ * @property {Number} decimals
+ */
+ decimals: 2,
+
+ /**
+ * The scaling algorithm to use on this axis. May be "linear" or
+ * "logarithmic". Currently only linear scale is implemented.
+ *
+ * @property {String} scale
+ * @private
+ */
+ scale: "linear",
+
+ /**
+ * Indicates the position of the axis relative to the chart
+ *
+ * @property {String} position
+ */
+ position: 'left',
+
+ /**
+ * Indicates whether to extend maximum beyond data's maximum to the nearest
+ * majorUnit.
+ *
+ * @property {Boolean} adjustMaximumByMajorUnit
+ */
+ adjustMaximumByMajorUnit: false,
+
+ /**
+ * Indicates whether to extend the minimum beyond data's minimum to the
+ * nearest majorUnit.
+ *
+ * @property {Boolean} adjustMinimumByMajorUnit
+ */
+ adjustMinimumByMajorUnit: false,
+
+ // @private apply data.
+ applyData: function() {
+ this.callParent();
+ return this.calcEnds();
+ }
+});
+
+/**
+ * @class Ext.chart.axis.Radial
+ * @extends Ext.chart.axis.Abstract
+ * @ignore
+ */
+Ext.define('Ext.chart.axis.Radial', {
+
+ /* Begin Definitions */
+
+ extend: 'Ext.chart.axis.Abstract',
+
+ /* End Definitions */
+
+ position: 'radial',
+
+ alias: 'axis.radial',
+
+ drawAxis: function(init) {
+ var chart = this.chart,
+ surface = chart.surface,
+ bbox = chart.chartBBox,
+ store = chart.store,
+ l = store.getCount(),
+ centerX = bbox.x + (bbox.width / 2),
+ centerY = bbox.y + (bbox.height / 2),
+ rho = Math.min(bbox.width, bbox.height) /2,
+ sprites = [], sprite,
+ steps = this.steps,
+ i, j, pi2 = Math.PI * 2,
+ cos = Math.cos, sin = Math.sin;
+
+ if (this.sprites && !chart.resizing) {
+ this.drawLabel();
+ return;
+ }
+
+ if (!this.sprites) {
+ //draw circles
+ for (i = 1; i <= steps; i++) {
+ sprite = surface.add({
+ type: 'circle',
+ x: centerX,
+ y: centerY,
+ radius: Math.max(rho * i / steps, 0),
+ stroke: '#ccc'
+ });
+ sprite.setAttributes({
+ hidden: false
+ }, true);
+ sprites.push(sprite);
+ }
+ //draw lines
+ store.each(function(rec, i) {
+ sprite = surface.add({
+ type: 'path',
+ path: ['M', centerX, centerY, 'L', centerX + rho * cos(i / l * pi2), centerY + rho * sin(i / l * pi2), 'Z'],
+ stroke: '#ccc'
+ });
+ sprite.setAttributes({
+ hidden: false
+ }, true);
+ sprites.push(sprite);
+ });
+ } else {
+ sprites = this.sprites;
+ //draw circles
+ for (i = 0; i < steps; i++) {
+ sprites[i].setAttributes({
+ x: centerX,
+ y: centerY,
+ radius: Math.max(rho * (i + 1) / steps, 0),
+ stroke: '#ccc'
+ }, true);
+ }
+ //draw lines
+ store.each(function(rec, j) {
+ sprites[i + j].setAttributes({
+ path: ['M', centerX, centerY, 'L', centerX + rho * cos(j / l * pi2), centerY + rho * sin(j / l * pi2), 'Z'],
+ stroke: '#ccc'
+ }, true);
+ });
+ }
+ this.sprites = sprites;
+
+ this.drawLabel();
+ },
+
+ drawLabel: function() {
+ var chart = this.chart,
+ surface = chart.surface,
+ bbox = chart.chartBBox,
+ store = chart.store,
+ centerX = bbox.x + (bbox.width / 2),
+ centerY = bbox.y + (bbox.height / 2),
+ rho = Math.min(bbox.width, bbox.height) /2,
+ max = Math.max, round = Math.round,
+ labelArray = [], label,
+ fields = [], nfields,
+ categories = [], xField,
+ aggregate = !this.maximum,
+ maxValue = this.maximum || 0,
+ steps = this.steps, i = 0, j, dx, dy,
+ pi2 = Math.PI * 2,
+ cos = Math.cos, sin = Math.sin,
+ display = this.label.display,
+ draw = display !== 'none',
+ margin = 10;
+
+ if (!draw) {
+ return;
+ }
+
+ //get all rendered fields
+ chart.series.each(function(series) {
+ fields.push(series.yField);
+ xField = series.xField;
+ });
+
+ //get maxValue to interpolate
+ store.each(function(record, i) {
+ if (aggregate) {
+ for (i = 0, nfields = fields.length; i < nfields; i++) {
+ maxValue = max(+record.get(fields[i]), maxValue);
+ }
+ }
+ categories.push(record.get(xField));
+ });
+ if (!this.labelArray) {
+ if (display != 'categories') {
+ //draw scale
+ for (i = 1; i <= steps; i++) {
+ label = surface.add({
+ type: 'text',
+ text: round(i / steps * maxValue),
+ x: centerX,
+ y: centerY - rho * i / steps,
+ 'text-anchor': 'middle',
+ 'stroke-width': 0.1,
+ stroke: '#333'
+ });
+ label.setAttributes({
+ hidden: false
+ }, true);
+ labelArray.push(label);
+ }
+ }
+ if (display != 'scale') {
+ //draw text
+ for (j = 0, steps = categories.length; j < steps; j++) {
+ dx = cos(j / steps * pi2) * (rho + margin);
+ dy = sin(j / steps * pi2) * (rho + margin);
+ label = surface.add({
+ type: 'text',
+ text: categories[j],
+ x: centerX + dx,
+ y: centerY + dy,
+ 'text-anchor': dx * dx <= 0.001? 'middle' : (dx < 0? 'end' : 'start')
+ });
+ label.setAttributes({
+ hidden: false
+ }, true);
+ labelArray.push(label);
+ }
+ }
+ }
+ else {
+ labelArray = this.labelArray;
+ if (display != 'categories') {
+ //draw values
+ for (i = 0; i < steps; i++) {
+ labelArray[i].setAttributes({
+ text: round((i + 1) / steps * maxValue),
+ x: centerX,
+ y: centerY - rho * (i + 1) / steps,
+ 'text-anchor': 'middle',
+ 'stroke-width': 0.1,
+ stroke: '#333'
+ }, true);
+ }
+ }
+ if (display != 'scale') {
+ //draw text
+ for (j = 0, steps = categories.length; j < steps; j++) {
+ dx = cos(j / steps * pi2) * (rho + margin);
+ dy = sin(j / steps * pi2) * (rho + margin);
+ if (labelArray[i + j]) {
+ labelArray[i + j].setAttributes({
+ type: 'text',
+ text: categories[j],
+ x: centerX + dx,
+ y: centerY + dy,
+ 'text-anchor': dx * dx <= 0.001? 'middle' : (dx < 0? 'end' : 'start')
+ }, true);
+ }
+ }
+ }
+ }
+ this.labelArray = labelArray;
+ }
+});
+/**
+ * @author Ed Spencer
+ *
+ * AbstractStore is a superclass of {@link Ext.data.Store} and {@link Ext.data.TreeStore}. It's never used directly,
+ * but offers a set of methods used by both of those subclasses.
+ *
+ * We've left it here in the docs for reference purposes, but unless you need to make a whole new type of Store, what
+ * you're probably looking for is {@link Ext.data.Store}. If you're still interested, here's a brief description of what
+ * AbstractStore is and is not.
+ *
+ * AbstractStore provides the basic configuration for anything that can be considered a Store. It expects to be
+ * given a {@link Ext.data.Model Model} that represents the type of data in the Store. It also expects to be given a
+ * {@link Ext.data.proxy.Proxy Proxy} that handles the loading of data into the Store.
+ *
+ * AbstractStore provides a few helpful methods such as {@link #load} and {@link #sync}, which load and save data
+ * respectively, passing the requests through the configured {@link #proxy}. Both built-in Store subclasses add extra
+ * behavior to each of these functions. Note also that each AbstractStore subclass has its own way of storing data -
+ * in {@link Ext.data.Store} the data is saved as a flat {@link Ext.util.MixedCollection MixedCollection}, whereas in
+ * {@link Ext.data.TreeStore TreeStore} we use a {@link Ext.data.Tree} to maintain the data's hierarchy.
+ *
+ * The store provides filtering and sorting support. This sorting/filtering can happen on the client side
+ * or can be completed on the server. This is controlled by the {@link Ext.data.Store#remoteSort remoteSort} and
+ * {@link Ext.data.Store#remoteFilter remoteFilter} config options. For more information see the {@link #sort} and
+ * {@link Ext.data.Store#filter filter} methods.
+ */
+Ext.define('Ext.data.AbstractStore', {
+ requires: ['Ext.util.MixedCollection', 'Ext.data.Operation', 'Ext.util.Filter'],
+
+ mixins: {
+ observable: 'Ext.util.Observable',
+ sortable: 'Ext.util.Sortable'
+ },
+
+ statics: {
+ create: function(store){
+ if (!store.isStore) {
+ if (!store.type) {
+ store.type = 'store';
+ }
+ store = Ext.createByAlias('store.' + store.type, store);
+ }
+ return store;
+ }
+ },
+
+ remoteSort : false,
+ remoteFilter: false,
+
+ /**
+ * @cfg {String/Ext.data.proxy.Proxy/Object} proxy
+ * The Proxy to use for this Store. This can be either a string, a config object or a Proxy instance -
+ * see {@link #setProxy} for details.
+ */
+
+ /**
+ * @cfg {Boolean/Object} autoLoad
+ * If data is not specified, and if autoLoad is true or an Object, this store's load method is automatically called
+ * after creation. If the value of autoLoad is an Object, this Object will be passed to the store's load method.
+ * Defaults to false.
+ */
+ autoLoad: false,
+
+ /**
+ * @cfg {Boolean} autoSync
+ * True to automatically sync the Store with its Proxy after every edit to one of its Records. Defaults to false.
+ */
+ autoSync: false,
+
+ /**
+ * @property {String} batchUpdateMode
+ * Sets the updating behavior based on batch synchronization. 'operation' (the default) will update the Store's
+ * internal representation of the data after each operation of the batch has completed, 'complete' will wait until
+ * the entire batch has been completed before updating the Store's data. 'complete' is a good choice for local
+ * storage proxies, 'operation' is better for remote proxies, where there is a comparatively high latency.
+ */
+ batchUpdateMode: 'operation',
+
+ /**
+ * @property {Boolean} filterOnLoad
+ * If true, any filters attached to this Store will be run after loading data, before the datachanged event is fired.
+ * Defaults to true, ignored if {@link Ext.data.Store#remoteFilter remoteFilter} is true
+ */
+ filterOnLoad: true,
+
+ /**
+ * @property {Boolean} sortOnLoad
+ * If true, any sorters attached to this Store will be run after loading data, before the datachanged event is fired.
+ * Defaults to true, igored if {@link Ext.data.Store#remoteSort remoteSort} is true
+ */
+ sortOnLoad: true,
+
+ /**
+ * @property {Boolean} implicitModel
+ * True if a model was created implicitly for this Store. This happens if a fields array is passed to the Store's
+ * constructor instead of a model constructor or name.
+ * @private
+ */
+ implicitModel: false,
+
+ /**
+ * @property {String} defaultProxyType
+ * The string type of the Proxy to create if none is specified. This defaults to creating a
+ * {@link Ext.data.proxy.Memory memory proxy}.
+ */
+ defaultProxyType: 'memory',
+
+ /**
+ * @property {Boolean} isDestroyed
+ * True if the Store has already been destroyed. If this is true, the reference to Store should be deleted
+ * as it will not function correctly any more.
+ */
+ isDestroyed: false,
+
+ isStore: true,
+
+ /**
+ * @cfg {String} storeId
+ * Unique identifier for this store. If present, this Store will be registered with the {@link Ext.data.StoreManager},
+ * making it easy to reuse elsewhere. Defaults to undefined.
+ */
+
+ /**
+ * @cfg {Object[]} fields
+ * This may be used in place of specifying a {@link #model} configuration. The fields should be a
+ * set of {@link Ext.data.Field} configuration objects. The store will automatically create a {@link Ext.data.Model}
+ * with these fields. In general this configuration option should be avoided, it exists for the purposes of
+ * backwards compatibility. For anything more complicated, such as specifying a particular id property or
+ * assocations, a {@link Ext.data.Model} should be defined and specified for the {@link #model}
+ * config.
+ */
+
+ /**
+ * @cfg {String} model
+ * Name of the {@link Ext.data.Model Model} associated with this store.
+ * The string is used as an argument for {@link Ext.ModelManager#getModel}.
+ */
+
+ sortRoot: 'data',
+
+ //documented above
+ constructor: function(config) {
+ var me = this,
+ filters;
+
+ me.addEvents(
+ /**
+ * @event add
+ * Fired when a Model instance has been added to this Store
+ * @param {Ext.data.Store} store The store
+ * @param {Ext.data.Model[]} records The Model instances that were added
+ * @param {Number} index The index at which the instances were inserted
+ */
+ 'add',
+
+ /**
+ * @event remove
+ * Fired when a Model instance has been removed from this Store
+ * @param {Ext.data.Store} store The Store object
+ * @param {Ext.data.Model} record The record that was removed
+ * @param {Number} index The index of the record that was removed
+ */
+ 'remove',
+
+ /**
+ * @event update
+ * Fires when a Model instance has been updated
+ * @param {Ext.data.Store} this
+ * @param {Ext.data.Model} record The Model instance that was updated
+ * @param {String} operation The update operation being performed. Value may be one of:
+ *
+ * Ext.data.Model.EDIT
+ * Ext.data.Model.REJECT
+ * Ext.data.Model.COMMIT
+ */
+ 'update',
+
+ /**
+ * @event datachanged
+ * Fires whenever the records in the Store have changed in some way - this could include adding or removing
+ * records, or updating the data in existing records
+ * @param {Ext.data.Store} this The data store
+ */
+ 'datachanged',
+
+ /**
+ * @event beforeload
+ * Fires before a request is made for a new data object. If the beforeload handler returns false the load
+ * action will be canceled.
+ * @param {Ext.data.Store} store This Store
+ * @param {Ext.data.Operation} operation The Ext.data.Operation object that will be passed to the Proxy to
+ * load the Store
+ */
+ 'beforeload',
+
+ /**
+ * @event load
+ * Fires whenever the store reads data from a remote data source.
+ * @param {Ext.data.Store} this
+ * @param {Ext.data.Model[]} records An array of records
+ * @param {Boolean} successful True if the operation was successful.
+ */
+ 'load',
+
+ /**
+ * @event write
+ * Fires whenever a successful write has been made via the configured {@link #proxy Proxy}
+ * @param {Ext.data.Store} store This Store
+ * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object that was used in
+ * the write
+ */
+ 'write',
+
+ /**
+ * @event beforesync
+ * Fired before a call to {@link #sync} is executed. Return false from any listener to cancel the synv
+ * @param {Object} options Hash of all records to be synchronized, broken down into create, update and destroy
+ */
+ 'beforesync',
+ /**
+ * @event clear
+ * Fired after the {@link #removeAll} method is called.
+ * @param {Ext.data.Store} this
+ */
+ 'clear'
+ );
+
+ Ext.apply(me, config);
+ // don't use *config* anymore from here on... use *me* instead...
+
+ /**
+ * Temporary cache in which removed model instances are kept until successfully synchronised with a Proxy,
+ * at which point this is cleared.
+ * @private
+ * @property {Ext.data.Model[]} removed
+ */
+ me.removed = [];
+
+ me.mixins.observable.constructor.apply(me, arguments);
+ me.model = Ext.ModelManager.getModel(me.model);
+
+ /**
+ * @property {Object} modelDefaults
+ * @private
+ * A set of default values to be applied to every model instance added via {@link #insert} or created via {@link #create}.
+ * This is used internally by associations to set foreign keys and other fields. See the Association classes source code
+ * for examples. This should not need to be used by application developers.
+ */
+ Ext.applyIf(me, {
+ modelDefaults: {}
+ });
+
+ //Supports the 3.x style of simply passing an array of fields to the store, implicitly creating a model
+ if (!me.model && me.fields) {
+ me.model = Ext.define('Ext.data.Store.ImplicitModel-' + (me.storeId || Ext.id()), {
+ extend: 'Ext.data.Model',
+ fields: me.fields,
+ proxy: me.proxy || me.defaultProxyType
+ });
+
+ delete me.fields;
+
+ me.implicitModel = true;
+ }
+
+ // <debug>
+ if (!me.model) {
+ if (Ext.isDefined(Ext.global.console)) {
+ Ext.global.console.warn('Store defined with no model. You may have mistyped the model name.');
+ }
+ }
+ // </debug>
+
+ //ensures that the Proxy is instantiated correctly
+ me.setProxy(me.proxy || me.model.getProxy());
+
+ if (me.id && !me.storeId) {
+ me.storeId = me.id;
+ delete me.id;
+ }
+
+ if (me.storeId) {
+ Ext.data.StoreManager.register(me);
+ }
+
+ me.mixins.sortable.initSortable.call(me);
+
+ /**
+ * @property {Ext.util.MixedCollection} filters
+ * The collection of {@link Ext.util.Filter Filters} currently applied to this Store
+ */
+ filters = me.decodeFilters(me.filters);
+ me.filters = Ext.create('Ext.util.MixedCollection');
+ me.filters.addAll(filters);
+ },
+
+ /**
+ * Sets the Store's Proxy by string, config object or Proxy instance
+ * @param {String/Object/Ext.data.proxy.Proxy} proxy The new Proxy, which can be either a type string, a configuration object
+ * or an Ext.data.proxy.Proxy instance
+ * @return {Ext.data.proxy.Proxy} The attached Proxy object
+ */
+ setProxy: function(proxy) {
+ var me = this;
+
+ if (proxy instanceof Ext.data.proxy.Proxy) {
+ proxy.setModel(me.model);
+ } else {
+ if (Ext.isString(proxy)) {
+ proxy = {
+ type: proxy
+ };
+ }
+ Ext.applyIf(proxy, {
+ model: me.model
+ });
+
+ proxy = Ext.createByAlias('proxy.' + proxy.type, proxy);
+ }
+
+ me.proxy = proxy;
+
+ return me.proxy;
+ },