text: /**
   * This class manages arbitrary data and its relationship to data models. Instances of
   * `ViewModel` are associated with some `Component` and then used by their child items
   * for the purposes of Data Binding.
   * 
   * # Binding
   * 
   * The most commonly used aspect of a `ViewModel` is the `bind` method. This method takes
   * a "bind descriptor" (see below) and a callback to call when the data indicated by the
   * bind descriptor either becomes available or changes.
   *
   * The `bind` method, based on the bind descriptor given, will return different types of
   * "binding" objects. These objects maintain the connection between the requested data and
   * the callback. Bindings ultimately derive from `{@link Ext.app.bind.BaseBinding}`
   * which provides several methods to help manage the binding.
   *
   * Perhaps the most important method is `destroy`. When the binding is no longer needed
   * it is important to remember to `destroy` it. Leaking bindings can cause performance
   * problems or worse when callbacks are called at unexpected times.
   *
   * The types of bindings produced by `bind` are:
   *
   *   * `{@link Ext.app.bind.Binding}`
   *   * `{@link Ext.app.bind.Multi}`
   *   * `{@link Ext.app.bind.TemplateBinding}`
   *
   * ## Bind Descriptors
   * 
   * A "bind descriptor" is a value (a String, an Object or an array of these) that describe
   * the desired data. Any piece of data in the `ViewModel` can be described by a bind
   * descriptor.
   * 
   * ### Textual Bind Descriptors
   * 
   * The simplest and most common form of bind descriptors are strings that look like an
   * `Ext.Template` containing text and tokens surrounded by "{}" with dot notation inside
   * to traverse objects and their properties.
   * 
   * For example:
   * 
   *   * `'Hello {user.name}!'`
   *   * `'You have selected "{selectedItem.text}".'`
   *   * `'{!isDisabled}'`
   *   * `'{a > b ? "Bigger" : "Smaller"}'`
   *   * `'{user.groups}'`
   *
   * All except the last are `{@link Ext.app.bind.TemplateBinding template bindings}`
   * which use the familiar `Ext.Template` syntax with some slight differences. For more on
   * templates see `{@link Ext.app.bind.Template}`.
   *
   * The last descriptor is called a "direct bind descriptor". This special form of
   * bind maps one-to-one to some piece of data in the `ViewModel` and is managed by the
   * `{@link Ext.app.bind.Binding}` class.
   *
   * #### Two-Way Descriptors
   *
   * A direct bind descriptor may be able to write back a value to the `ViewModel` as well
   * as retrieve one. When this is the case, they are said to be "two-way". For example:
   *
   *      var binding = viewModel.bind('{s}', function(s) { console.log('s=' + s); });
   *
   *      binding.setValue('abc');
   *
   * Direct use of `ViewModel` in this way is not commonly needed because `Ext.Component`
   * automates this process. For example, a `textfield` component understands when it is
   * given a "two-way" binding and automatically synchronizes its value bidirectionally using
   * the above technique. For example:
   *
   *      Ext.widget({
   *          items: [{
   *              xtype: 'textfield',
   *              bind: '{s}'  // a two-way / direct bind descriptor
   *          }]
   *      });
   *
   * ### Object and Array Descriptors / Multi-Bind
   *
   * With two exceptions (see below) an Object is interpreted as a "shape" to produce by
   * treating each of its properties as individual bind descriptors. An object of the same
   * shape is passed as the value of the bind except that each property is populated with
   * the appropriate value. Of course, this definition is recursive, so these properties
   * may also be objects.
   *
   * For example:
   *
   *      viewModel.bind({
   *              x: '{x}',
   *              foo: {
   *                  bar: 'Hello {foo.bar}'
   *              }
   *          },
   *          function (obj) {
   *              //  obj = {
   *              //      x: 42,
   *              //      foo: {
   *              //          bar: 'Hello foobar'
   *              //      }
   *              //  }
   *          });
   *
   * Arrays are handled in the same way. Each element of the array is considered a bind
   * descriptor (recursively) and the value produced for the binding is an array with each
   * element set to the bound property.
   *
   * ### Bind Options
   *
   * One exception to the "object is a multi-bind" rule is when that object contains a
   * `bindTo` property. When an object contains a `bindTo` property the object is understood
   * to contain bind options and the value of `bindTo` is considered the actual bind
   * descriptor.
   *
   * For example:
   *
   *      viewModel.bind({
   *              bindTo: '{x}',
   *              single: true
   *          },
   *          function (x) {
   *              console.log('x: ' + x); // only called once
   *          });
   *
   * The available bind options depend on the type of binding, but since all bindings
   * derive from `{@link Ext.app.bind.BaseBinding}` its options are always applicable.
   * For a list of the other types of bindings, see above.
   *
   * #### Deep Binding
   *
   * When a direct bind is made and the bound property is an object, by default the binding
   * callback is only called when that reference changes. This is the most efficient way to
   * understand a bind of this type, but sometimes you may need to be notified if any of the
   * properties of that object change.
   *
   * To do this, we create a "deep bind":
   *
   *      viewModel.bind({
   *              bindTo: '{someObject}',
   *              deep: true
   *          },
   *          function (someObject) {
   *              // called when reference changes or *any* property changes
   *          });
   *
   * #### Binding Timings
   *
   * The `ViewModel` has a {@link #scheduler} attached that is used to coordinate the firing of bindings.
   * It serves 2 main purposes:
   * - To coordinate dependencies between bindings. This means bindings will be fired in an order such that
   * the any dependencies for a binding are fired before the binding itself.
   * - To batch binding firings. The scheduler runs on a short timer, so the following code will only trigger
   * a single binding (the last), the changes in between will never be triggered.
   * 
   *     viewModel.bind('{val}', function(v) {
   *         console.log(v);
   *     });
   *     viewModel.set('val', 1);
   *     viewModel.set('val', 2);
   *     viewModel.set('val', 3);
   *     viewModel.set('val', 4);
   *
   * The `ViewModel` can be forced to process by calling `{@link #notify}`, which will force the
   * scheduler to run immediately in the current state.
   * 
   *     viewModel.bind('{val}', function(v) {
   *         console.log(v);
   *     });
   *     viewModel.set('val', 1);
   *     viewModel.notify();
   *     viewModel.set('val', 2);
   *     viewModel.notify();
   *     viewModel.set('val', 3);
   *     viewModel.notify();
   *     viewModel.set('val', 4);
   *     viewModel.notify();
   *  
   *
   * #### Models, Stores and Associations
   *
   * A {@link Ext.data.Session Session} manages model instances and their associations.
   * The `ViewModel` may be used with or without a `Session`. When a `Session` is attached, the
   * `ViewModel` will always consult the `Session` to ask about records and stores. The `Session`
   * ensures that only a single instance of each model Type/Id combination is created. This is 
   * important when tracking changes in models so that we always have the same reference.
   *
   * A `ViewModel` provides functionality to easily consume the built in data package types
   * {@link Ext.data.Model} and {@link Ext.data.Store}, as well as their associations.
   *
   * ### Model Links
   *
   * A model can be described declaratively using {@link #links}. In the example code below,
   * We ask the `ViewModel` to construct a record of type `User` with `id: 17`. The model will be loaded
   * from the server and the bindings will trigger once the load has completed. Similarly, we could also
   * attach a model instance to the `ViewModel` data directly.
   *
   *     Ext.define('MyApp.model.User', {
   *         extend: 'Ext.data.Model',
   *         fields: ['name']
   *     });
   *     
   *     var rec = new MyApp.model.User({
   *         id: 12,
   *         name: 'Foo'
   *     });
   *     
   *     var viewModel = new Ext.app.ViewModel({
   *         links: {
   *             theUser: {
   *                 type: 'User',
   *                 id: 17
   *             }
   *         },
   *         data: {
   *             otherUser: rec
   *         }
   *     });
   *     viewModel.bind('{theUser.name}', function(v) {
   *         console.log(v);
   *     });
   *     viewModel.bind('{otherUser.name}', function(v) {
   *         console.log(v);
   *     });
   *
   * ### Model Fields
   *
   * Bindings have the functionality to inspect the parent values and resolve the underlying
   * value dynamically. This behavior allows model fields to be interrogated as part of a binding.
   *
   *     Ext.define('MyApp.model.User', {
   *         extend: 'Ext.data.Model',
   *         fields: ['name', 'age']
   *     });
   *
   *     var viewModel = new Ext.app.ViewModel({
   *         links: {
   *             theUser: {
   *                 type: 'User',
   *                 id: 22
   *             }
   *         }
   *     });
   *
   *     // Server responds with:
   *     {
   *         "id": 22,
   *         "name": "Foo",
   *         "age": 100
   *     }
   *
   *     viewModel.bind('Hello {name}, you are {age} years old', function(v) {
   *         console.log(v);
   *     });
   *
   * ### Record Properties
   *
   * It is possible to bind to the certain state properties of a record. The available options are:
   * - `{@link Ext.data.Model#property-dirty dirty}`
   * - `{@link Ext.data.Model#property-phantom phantom}`
   * - `{@link #Ext.data.Model#method-isValid valid}`
   *
   *     Ext.define('MyApp.model.User', {
   *         extend: 'Ext.data.Model',
   *         fields: [{
   *             name: 'name',
   *             validators: 'presence'
   *         }, {
   *             name: 'age',
   *             validators: {
   *                type: 'range',
   *                 min: 0
   *              }
   *         }]
   *     });
   *
   *     var rec = new MyApp.model.User();
   *
   *     var viewModel = new Ext.app.ViewModel({
   *         data: {
   *             theUser: rec
   *         }
   *     });
   *
   *     viewModel.bind({
   *         dirty: '{theUser.dirty}',
   *         phantom: '{theUser.phantom}',
   *         valid: '{theUser.valid}'
   *     }, function(v) {
   *         console.log(v.dirty, v.valid);
   *     });
   *
   *     rec.set('name', 'Foo');
   *     viewModel.notify(); // dirty, not valid
   *     rec.set('age', 20);
   *     viewModel.notify(); // dirty, valid
   *     rec.reject();
   *     viewModel.notify(); // not dirty, not valid
   *
   * ### Advanced Record Binding
   *
   * For accessing other record information that is not exposed by the binding API, formulas
   * can be used to achieve more advanced operations:
   *
   *     Ext.define('MyApp.model.User', {
   *         extend: 'Ext.data.Model',
   *         fields: ['name', 'age']
   *     });
   *
   *     var rec = new MyApp.model.User();
   *
   *     var viewModel = new Ext.app.ViewModel({
   *         formulas: {
   *             isNameModified: {
   *                 bind: {
   *                     bindTo: '{theUser}',
   *                     deep: true
   *                 },
   *                 get: function(rec) {
   *                     return rec.isModified('name');
   *                 }
   *             }
   *         },
   *         data: {
   *             theUser: rec
   *         }
   *     });
   *
   *     viewModel.bind('{isNameModified}', function(modified) {
   *         console.log(modified);
   *     });
   *     rec.set('name', 'Foo');
   *
   * ### Associations
   *
   * In the same way as fields, the bindings can also traverse associations in a bind statement.
   * The `ViewModel` will handle the asynchronous loading of data and only present the value once
   * the full path has been loaded. For more information on associations see {@link Ext.data.schema.OneToOne OneToOne} and
   * {@link Ext.data.schema.ManyToOne ManyToOne} associations.
   *
   *     Ext.define('User', {
   *         extend: 'Ext.data.Model',
   *         fields: ['name']
   *     });
   *
   *     Ext.define('Order', {
   *         extend: 'Ext.data.Model',
   *         fields: ['date', {
   *             name: 'userId',
   *             reference: 'User'
   *         }]
   *     });
   *
   *     Ext.define('OrderItem', {
   *         extend: 'Ext.data.Model',
   *         fields: ['price', 'qty', {
   *             name: 'orderId',
   *             reference: 'Order'
   *         }]
   *     });
   *
   *     var viewModel = new Ext.app.ViewModel({
   *         links: {
   *             orderItem: {
   *                 type: 'OrderItem',
   *                 id: 13
   *             }
   *         }
   *     });
   *     // The viewmodel will handle both ways of loading the data:
   *     // a) If the data is loaded inline in a nested fashion it will
   *     //    not make requests for extra data
   *     // b) Only loading a single model at a time. So the Order will be loaded once
   *     //    the OrderItem returns. The User will be loaded once the Order loads.
   *     viewModel.bind('{orderItem.order.user.name}', function(name) {
   *         console.log(name);
   *     });
   *
   * ### Stores
   *
   * Stores can be created as part of the `ViewModel` definition. The definitions are processed
   * like bindings which allows for very powerful dynamic functionality.
   *
   * It is important to ensure that you name viewModel's data keys uniquely. If data is not named  
   * uniquely, binds and formulas may receive information from an unintended data source.  
   * This applies to keys in the viewModel's data block, stores, and links configs.
   *
   *     var viewModel = new Ext.app.ViewModel({
   *         stores: {
   *             users: {
   *                 model: 'User',
   *                 autoLoad: true,
   *                 filters: [{
   *                     property: 'createdDate',
   *                     value: '{createdFilter}',
   *                     operator: '>'
   *                 }]
   *             }
   *         }
   *     });
   *     // Later on in our code, we set the date so that the store is created.
   *     viewModel.set('createdFilter', Ext.Date.subtract(new Date(), Ext.Date.DAY, 7));
   *
   * See {@link #stores} for more detail.
   *
   * ### Store Properties
   *
   * It is possible to bind to the certain state properties of the store. The available options are:
   * - `{@link Ext.data.Store#method-getCount count}`
   * - `{@link #Ext.data.Store#method-first}`
   * - `{@link #Ext.data.Store#method-last}`
   * - `{@link #Ext.data.Store#method-hasPendingLoad loading}`
   * - `{@link #Ext.data.Store#method-getTotalCount totalCount}`
   *
   *     Ext.define('MyApp.model.User', {
   *         extend: 'Ext.data.Model',
   *         fields: ['name']
   *     });
   *
   *     var viewModel = new Ext.app.ViewModel({
   *         stores: {
   *             users: {
   *                 model: 'MyApp.model.User',
   *                 data: [{
   *                     name: 'Foo'
   *                 }, {
   *                     name: 'Bar'
   *                 }]
   *             }
   *         }
   *     });
   *
   *     viewModel.bind('{users.first}', function(first) {
   *         console.log(first ? first.get('name') : 'Nobody');
   *     });
   *
   *     var timer = setInterval(function() {
   *         var store = viewModel.getStore('users');
   *         if (store.getCount()) {
   *             store.removeAt(0);
   *         } else {
   *             clearInterval(timer);
   *         }
   *     }, 100);
   *
   * ### Advanced Store Binding
   *
   * For accessing other store information that is not exposed by the binding API, formulas
   * can be used to achieve more advanced operations:
   *
   *     Ext.define('MyApp.model.User', {
   *         extend: 'Ext.data.Model',
   *         fields: ['name', 'score']
   *     });
   *
   *     var viewModel = new Ext.app.ViewModel({
   *         stores: {
   *             users: {
   *                 model: 'MyApp.model.User',
   *               data: [{
   *                   name: 'Foo',
   *                   score: 100
   *               }, {
   *                   name: 'Bar',
   *                   score: 350
   *               }]
   *           }
   *       },
   *       formulas: {
   *           totalScore: {
   *               bind: {
   *                   bindTo: '{users}',
   *                   deep: true
   *               },
   *               get: function(store) {
   *                   return store.sum('score');
   *               }
   *           }
   *       }
   *   });
   *
   *   viewModel.bind('{totalScore}', function(score) {
   *       console.log(score);
   *   });
   *
   *   viewModel.notify();
   *   viewModel.getStore('users').removeAll();
   *
   * #### Formulas
   *
   * Formulas allow for calculated `ViewModel` data values. The dependencies for these formulas
   * are automatically determined so that the formula will not be processed until the required
   * data is present.
   *
   *     var viewModel = new Ext.app.ViewModel({
   *         formulas: {
   *             fullName: function(get) {
   *                 return get('firstName') + ' ' + get('lastName');
   *             }
   *         },
   *         data: {firstName: 'John', lastName: 'Smith'}
   *     });
   *
   *     viewModel.bind('{fullName}', function(v) {
   *         console.log(v);
   *     });
   *
   * See {@link #formulas} for more detail.
   *
   * #### Inheriting Data With Nesting
   *
   * ViewModels can have a {@link #parent} which allows values to be consumed from
   * a shared base. These values that are available from the {@link #parent} are not copied,
   * rather they are "inherited" in a similar fashion to a javascript closure scope chain. 
   * This is demonstrated in the example below:
   *
   *     var parent = new Ext.app.ViewModel({
   *         data: {
   *             foo: 3
   *         }
   *     });
   *     var child = new Ext.app.ViewModel({
   *         parent: parent
   *     });
   *
   * This is analogous to the following javascript closure:
   *
   *     var foo = 3;
   *     Ext.Ajax.request({
   *         success: function() {
   *             // foo is available here
   *         }
   *     });
   *
   * ### Climbing/Inheriting
   *
   * In line with the above, the default behaviour when setting the value of a child ViewModel (either)
   * through {@link #set} or {@link Ext.app.bind.Binding#method-setValue} is to climb to where the value 
   * is "owned" and set the value there:
   *
   *     var parent = new Ext.app.ViewModel({
   *         data: {
   *             foo: 3
   *         }
   *     });
   *     var child = new Ext.app.ViewModel({
   *         parent: parent
   *     });
   *     
   *     child.set('foo', 100); // Climbs to set the value on parent
   *     console.log(parent.get('foo')); // 100
   *     parent.set('foo', 200);
   *     console.log(child.get('foo')); // 200, inherited from the parent
   *
   * Any subsequent sets are also inherited in the same fashion. The inheriting/climbing behavior 
   * occurs for any arbitrary depth, climbing/inherting can owned by a parent at any level above.
   *
   *     function log() {
   *         console.log([a, b, c, d, e].map(function(vm) {
   *             return vm.get('foo');
   *         }));
   *     }
   *
   *     var a = new Ext.app.ViewModel({data: {foo: 3}}),
   *         b = new Ext.app.ViewModel({parent: a}),
   *         c = new Ext.app.ViewModel({parent: b}),
   *         d = new Ext.app.ViewModel({parent: c}),
   *         e = new Ext.app.ViewModel({parent: d});
   *
   *     log(); // [3, 3, 3, 3, 3]
   *
   *     e.set('foo', 100);
   *     log(); // [100, 100, 100, 100, 100]
   *
   * This same climbing behavior applies when setting a value on a binding. The climbing begins from
   * the ViewModel where the binding was attached:
   *
   *     function log() {
   *         console.log([a, b, c].map(function(vm) {
   *             return vm.get('foo');
   *         }));
   *     }
   *
   *     var a = new Ext.app.ViewModel({data: {foo: 3}}),
   *         b = new Ext.app.ViewModel({parent: a}),
   *         c = new Ext.app.ViewModel({parent: b});
   *
   *     var bind = c.bind('{foo}', function() {});
   *
   *     bind.setValue(100);
   *     log(); // [100, 100, 100]
   *
   * The exception to this rule is when there is nothing above to climb to. If a value is set and there
   * is no parent above to hold it, then the value is set where it was called:
   *
   *     function log() {
   *         console.log([a, b, c].map(function(vm) {
   *             return vm.get('foo');
   *         }));
   *     }
   *
   *     var a = new Ext.app.ViewModel(),
   *         b = new Ext.app.ViewModel({parent: a}),
   *         c = new Ext.app.ViewModel({parent: b});
   *
   *     c.set('foo', 3);
   *     log(); // [null, null, 3]
   *
   *     b.set('foo', 2);
   *     log(); // [null, 2, 3]
   *
   *     a.set('foo', 1);
   *     log(); // [1, 2, 3]
   *
   * These values are called local values, which are discussed below.
   *
   * ### Local Values
   *
   * If the child ViewModel is declared with top level data that also exists in the parent, then that child is
   * considered to own that local value, so no value is inherited from the parent, nor does the climbing
   * behaviour occur.
   *
   *     var parent = new Ext.app.ViewModel({
   *         data: {
   *             foo: 3
   *         }
   *     });
   *     var child = new Ext.app.ViewModel({
   *         parent: parent,
   *         data: {
   *             foo: 5
   *         }
   *     });
   *
   *     console.log(parent.get('foo'), child.get('foo')); // 3, 5
   *     child.set('foo', 100);
   *     console.log(parent.get('foo'), child.get('foo')); // 3, 100
   *     parent.set('foo', 200);
   *     console.log(parent.get('foo'), child.get('foo')); // 200, 100
   *
   * The inheriting/climbing behavior is limited to local values:
   *
   *     function log() {
   *         console.log([a, b, c, d, e].map(function(vm) {
   *             return vm.get('foo');
   *         }));
   *     }
   *
   *     var a = new Ext.app.ViewModel({data: {foo: 1}}),
   *         b = new Ext.app.ViewModel({parent: a}),
   *         c = new Ext.app.ViewModel({parent: b, data: {foo: 2}}),
   *         d = new Ext.app.ViewModel({parent: c}),
   *         e = new Ext.app.ViewModel({parent: d, data: {foo: 3}});
   *
   *     log(); // [1, 1, 2, 2, 3]
   *
   *     e.set('foo', 100);
   *     log(); // [1, 1, 2, 2, 100]
   *
   *     d.set('foo', 200);
   *     log(); // [1, 1, 200, 200, 100]
   *
   *     c.set('foo', 201);
   *     log(); // [1, 1, 201, 201, 100]
   *
   *     b.set('foo', 300);
   *     log(); // [300, 300, 201, 201, 100]
   *
   *     a.set('foo', 301);
   *     log(); // [301, 301, 201, 201, 100]
   *
   * ### Attaching/Clearing Local Values Dynamically
   *
   * To bypass the climbing behaviour and push a value into a particular point
   * in the hierarchy, the {@link #setData} method should be used. Once a local value
   * is set, it will be used as such in the future.
   *
   *     function log() {
   *         console.log([a, b, c, d, e].map(function(vm) {
   *             return vm.get('foo');
   *         }));
   *     }
   *
   *     var a = new Ext.app.ViewModel({data: {foo: 3}}),
   *         b = new Ext.app.ViewModel({parent: a}),
   *         c = new Ext.app.ViewModel({parent: b}),
   *         d = new Ext.app.ViewModel({parent: c}),
   *         e = new Ext.app.ViewModel({parent: d});
   *
   *     log(); // [3, 3, 3, 3, 3]
   *
   *     c.setData({
   *         foo: 100
   *     });
   *
   *     log(); // [3, 3, 100, 100, 100]
   *
   *     d.set('foo', 200); // Climbs to new local value
   *     log(); // [3, 3, 200, 200, 200]
   *
   * Similarly, data can be cleared from being a local value by setting the value to undefined:
   *
   *     function log() {
   *         console.log([a, b, c, d].map(function(vm) {
   *             return vm.get('foo');
   *         }));
   *     }
   *
   *     var a = new Ext.app.ViewModel({data: {foo: 3}}),
   *         b = new Ext.app.ViewModel({parent: a}),
   *         c = new Ext.app.ViewModel({parent: b, data: {foo: 100}}),
   *         d = new Ext.app.ViewModel({parent: c});
   *
   *     log(); // [3, 3, 100, 100]
   *
   *     c.setData({
   *         foo: undefined
   *     });
   *     log([3, 3, 3, 3]);
   *
   */
  Ext.define('Ext.app.ViewModel', {
      mixins: [
          'Ext.mixin.Factoryable',
          'Ext.mixin.Identifiable'
      ],
  
      requires: [
          'Ext.util.Scheduler',
          'Ext.data.Session',
          'Ext.app.bind.RootStub',
          'Ext.app.bind.LinkStub',
          'Ext.app.bind.Multi',
          'Ext.app.bind.Formula',
          'Ext.app.bind.TemplateBinding',
          // TODO: this is an injected dependency in onStoreBind, need to define so 
          // cmd can detect it
          'Ext.data.ChainedStore'
      ],
  
      alias: 'viewmodel.default', // also configures Factoryable
  
      isViewModel: true,
  
      factoryConfig: {
          name: 'viewModel'
      },
  
      collectTimeout: 100,
  
      expressionRe: /^(?:\{(?:(\d+)|([a-z_][\w\.]*))\})$/i,
  
      $configStrict: false, // allow "formulas" to be specified on derived class body
      config: {
          /**
           * @cfg {Object} data
           * This object holds the arbitrary data that populates the `ViewModel` and is
           * then available for binding.
           * @since 5.0.0
           */
          data: true,
  
          /**
           * @cfg {Object} formulas
           * An object that defines named values whose value is managed by function calls.
           * The names of the properties of this object are assigned as values in the
           * ViewModel.
           *
           * For example:
           *
           *      formulas: {
           *          xy: function (get) { return get('x') * get('y'); }
           *      }
           *
           * For more details about defining a formula, see `{@link Ext.app.bind.Formula}`.
           * @since 5.0.0
           */
          formulas: {
              $value: null,
              merge: function (newValue, currentValue, target, mixinClass) {
                  return this.mergeNew(newValue, currentValue, target, mixinClass);
              }
          },
  
          /**
           * @cfg {Object} links
           * Links provide a way to assign a simple name to a more complex bind. The primary
           * use for this is to assign names to records in the data model.
           *
           *      links: {
           *          theUser: {
           *              type: 'User',
           *              id: 12
           *          }
           *      }
           *
           * It is also possible to force a new phantom record to be created by not specifying an
           * id but passing `create: true` as part of the descriptor. This is often useful when
           * creating a new record for a child session.
           *
           *     links: {
           *         newUser: {
           *             type: 'User',
           *             create: true
           *         }
           *     } 
           *
           * `create` can also be an object containing initial data for the record.
           *
           *     links: {
           *         newUser: {
           *             type: 'User',
           *             create: {
           *                 firstName: 'John',
           *                 lastName: 'Smith'
           *             }
           *         }
           *     } 
           *
           * While that is the typical use, the value of each property in `links` may also be
           * a bind descriptor (see `{@link #method-bind}` for the various forms of bind
           * descriptors).
           * @since 5.0.0
           */
          links: null,
  
          /**
           * @cfg {Ext.app.ViewModel} parent
           * The parent `ViewModel` of this `ViewModel`. Once set, this cannot be changed.
           * @readonly
           * @since 5.0.0
           */
          parent: null,
  
          /**
           * @cfg {Ext.app.bind.RootStub} root
           * A reference to the root "stub" (an object that manages bindings).
           * @private
           * @since 5.0.0
           */
          root: true,
  
          /**
           * @cfg {Ext.util.Scheduler} scheduler
           * The scheduler used to schedule and manage the delivery of notifications for
           * all connections to this `ViewModel` and any other attached to it. The normal
           * process to initialize the `scheduler` is to get the scheduler used by the
           * `parent` or `session` and failing either of those, create one.
           * @readonly
           * @private
           * @since 5.0.0
           */
          scheduler: null,
  
          /**
           * @cfg {String/Ext.data.schema.Schema} schema
           * The schema to use for getting information about entities.
           */
          schema: 'default',
  
          /**
           * @cfg {Ext.data.Session} session
           * The session used to manage the data model (records and stores).
           * @since 5.0.0
           */
          session: null,
  
          // @cmd-auto-dependency {isKeyedObject: true, aliasPrefix: "store.", defaultType: "store"}
          /**
           * @cfg {Object} stores
           * A declaration of `Ext.data.Store` configurations that are first processed as
           * binds to produce an effective store configuration.
           *
           * A simple store definition. We can reference this in our bind statements using the
           * `{users}` as we would with other data values.
           *
           *     new Ext.app.ViewModel({
           *         stores: {
           *             users: {
           *                 model: 'User',
           *                 autoLoad: true
           *             }
           *         }
           *     });
           *
           * This store definition contains a dynamic binding. The store will not be created until
           * the initial value for groupId is set. Once that occurs, the store is created with the appropriate
           * filter configuration. Subsequently, once we change the group value, the old filter will be
           * overwritten with the new value.
           *
           *     var viewModel = new Ext.app.ViewModel({
           *         stores: {
           *             users: {
           *                 model: 'User',
           *                 filters: [{
           *                     property: 'groupId',
           *                     value: '{groupId}'
           *                 }]
           *             }
           *         }
           *     });
           *     viewModel.set('groupId', 1); // This will trigger the store creation with the filter.
           *     viewModel.set('groupId', 2); // The filter value will be changed.
           *
           * This store uses {@link Ext.data.ChainedStore store chaining} to create a store backed by the
           * data in another store. By specifying a string as the store, it will bind our creation and backing
           * to the other store. This functionality is especially useful when wanting to display a different "view"
           * of a store, for example a different sort order or different filters.
           *
           *     var viewModel = new Ext.app.ViewModel({
           *         stores: {
           *             allUsers: {
           *                 model: 'User',
           *                 autoLoad: true
           *             },
           *             children: {
           *                 source: '{allUsers}',
           *                 filters: [{
           *                     property: 'age',
           *                     value: 18,
           *                     operator: '<'
           *                 }]
           *             }
           *         }
           *     });
           *
           * @since 5.0.0
           */
          stores: null,
  
          /**
           * @cfg {Ext.container.Container} view
           * The Container that owns this `ViewModel` instance.
           * @since 5.0.0
           */
          view: null
      },
  
      constructor: function (config) {
          // Used to track non-stub bindings
          this.bindings = {};
          /*
           *  me.data = {
           *      foo: {
           *      },
           *          
           *      selectedUser: {
           *          name: null
           *      },
           *  }
           *
           *  me.root = new Ext.app.bind.RootStub({
           *      children: {
           *          foo: new Ext.app.bind.Stub(),
           *          selectedUser: new Ext.app.bind.LinkStub({
           *              binding: session.bind(...),
           *              children: {
           *                  name: : new Ext.app.bind.Stub()
           *              }
           *          }),
           *      }
           *  })
           */
  
          this.initConfig(config);
      },
  
      destroy: function () {
          var me = this,
              scheduler = me._scheduler,
              stores = me.storeInfo,
              parent = me.getParent(),
              task = me.collectTask,
              children = me.children,
              bindings = me.bindings,
              key, store, autoDestroy;
  
          me.destroying = true;
          if (task) {
              task.cancel();
              me.collectTask = null;
          }
  
          // When used with components, they are destroyed bottom up
          // so this scenario is only likely to happen in the case where
          // we're using the VM without any component attachment, in which case
          // we need to clean up here.
          if (children) {
              for (key in children) {
                  children[key].destroy();
              }
          }
  
          if (stores) {
              for (key in stores) {
                  store = stores[key];
                  autoDestroy = store.autoDestroy;
                  if (autoDestroy || (!store.$wasInstance && autoDestroy !== false)) {
                      store.destroy();
                  }
                  Ext.destroy(store.$binding);
              }
          }
  
          if (parent) {
              parent.unregisterChild(me);
          }
  
          
          me.getRoot().destroy();
  
          for (key in bindings) {
              bindings[key].destroy();
          }
  
          if (scheduler && scheduler.$owner === me) {
              scheduler.$owner = null;
              scheduler.destroy();
          }
  
          me.children = me.storeInfo = me._session = me._view = me._scheduler =
                        me.bindings = me._root = me._parent = me.formulaFn = me.$formulaData = null;
  
          me.destroying = false;
          me.callParent();
      },
  
      /**
       * This method requests that data in this `ViewModel` be delivered to the specified
       * `callback`. The data desired is given in a "bind descriptor" which is the first
       * argument.
       *
       * A simple call might look like this:
       *
       *     var binding = vm.bind('{foo}', this.onFoo, this);
       * 
       *     binding.destroy();  // when done with the binding
       *
       * Options for the binding can be provided in the last argument:
       *
       *     var binding = vm.bind('{foo}', this.onFoo, this, {
       *         deep: true
       *     });
       * 
       * Alternatively, bind options can be combined with the bind descriptor using only
       * the first argument:
       *
       *     var binding = vm.bind({
       *         bindTo: '{foo}',  // the presence of bindTo identifies this form
       *         deep: true
       *     }, this.onFoo, this);
       * 
       * See the class documentation for more details on Bind Descriptors and options.
       *
       * @param {String/Object/Array} descriptor The bind descriptor. See class description
       * for details.
       * @param {Function} callback The function to call with the value of the bound property.
       * @param {Object} [scope] The scope (`this` pointer) for the `callback`.
       * @param {Object} [options] Additional options to configure the {@link Ext.app.bind.Binding binding}.
       * If this parameter is provided, the `bindTo` form of combining options and bind descriptor is not
       * recognized.
       * @return {Ext.app.bind.BaseBinding/Ext.app.bind.Binding} The binding.
       */
      bind: function (descriptor, callback, scope, options) {
          var me = this,
              track = true,
              binding;
  
          scope = scope || me;
  
          if (!options && descriptor.bindTo !== undefined && !Ext.isString(descriptor)) {
              options = descriptor;
              descriptor = options.bindTo;
          }
  
          if (!Ext.isString(descriptor)) {
              binding = new Ext.app.bind.Multi(descriptor, me, callback, scope, options);
          } else if (me.expressionRe.test(descriptor)) {
              // If we have '{foo}' alone it is a literal
              descriptor = descriptor.substring(1, descriptor.length - 1);
              binding = me.bindExpression(descriptor, callback, scope, options);
              track = false;
          } else {
              binding = new Ext.app.bind.TemplateBinding(descriptor, me, callback, scope, options);
          }
  
          if (track) {
              me.bindings[binding.id] = binding;
          }
  
          return binding;
      },
  
      /**
       * Gets the session attached to this (or a parent) ViewModel. See the {@link #session} configuration.
       * @return {Ext.data.Session} The session. `null` if no session exists.
       */
      getSession: function () {
          var me = this,
              session = me._session,
              parent;
  
          if (!session && (parent = me.getParent())) {
              me.setSession(session = parent.getSession());
          }
  
          return session || null;
      },
      
      /**
       * Gets a store configured via the {@link #stores} configuration.
       * @param {String} key The name of the store.
       * @return {Ext.data.Store} The store. `null` if no store exists.
       */
      getStore: function(key) {
          var storeInfo = this.storeInfo,
              store;
          
          if (storeInfo) {
              store = storeInfo[key];
          }
          return store || null;
      },
      
      /**
       * @method getStores
       * @hide
       */
  
      /**
       * Create a link to a reference. See the {@link #links} configuration.
       * @param {String} key The name for the link.
       * @param {Object} reference The reference descriptor.
       */
      linkTo: function (key, reference) {
          var me = this,
              stub, create, id, modelType, linkStub, rec;
  
          //<debug>
          if (key.indexOf('.') > -1) {
              Ext.raise('Links can only be at the top-level: "' + key + '"');
          }
          //</debug>
  
          if (reference.isModel) {
              reference = {
                  type: reference.entityName,
                  id: reference.id
              };
          }
          // reference is backwards compat, type is preferred.
          modelType = reference.type || reference.reference;
          create = reference.create;
          if (modelType) {
              // It's a record
              id = reference.id;
              //<debug>
              if (!reference.create && Ext.isEmpty(id)) {
                  Ext.raise('No id specified. To create a phantom model, specify "create: true" as part of the reference.');
              }
              //</debug>
              if (create) {
                  id = undefined;
              }
              rec = me.getRecord(modelType, id);
              if (Ext.isObject(create)) {
                  rec.set(create);
                  rec.commit();
                  rec.phantom = true;
              }
              // Force creation at the root level. If an existing stub is there
              // it will be grafted in place here.
              stub = me.getRoot().createStubChild(key);
              stub.set(rec);
          } else {
              stub = me.getStub(key);
              if (!stub.isLinkStub) {
                  // Pass parent=null since we will graft in this new stub to replace us:
                  linkStub = new Ext.app.bind.LinkStub(me, stub.name);
                  stub.graft(linkStub);
                  stub = linkStub;
              }
              stub.link(reference);
          }
      },
  
      /**
       * Forces all bindings in this ViewModel hierarchy to evaluate immediately. Use this to do a synchronous flush
       * of all bindings.
       */
      notify: function () {
          var scheduler = this.getScheduler();
          if (!scheduler.firing) {
              scheduler.notify();
          }
      },
  
      /**
       * Get a value from the data for this viewmodel.
       * @param {String} path The path of the data to retrieve.
       *
       *    var value = vm.get('theUser.address.city');
       *
       * @return {Object} The data stored at the passed path.
       */
      get: function(path) {
          return this.getStub(path).getValue();
      },
  
      /**
       * Set a value in the data for this viewmodel. This method will climb to set data on
       * a parent view model if appropriate. See "Inheriting Data" in the class introduction for
       * more information.
       * 
       * @param {Object/String} path The path of the value to set, or an object literal to set
       * at the root of the viewmodel.
       * @param {Object} value The data to set at the value. If the value is an object literal,
       * any required paths will be created.
       *
       *     // Set a single property at the root level
       *     viewModel.set('expiry', Ext.Date.add(new Date(), Ext.Date.DAY, 7));
       *     console.log(viewModel.get('expiry'));
       *     // Sets a single property in user.address, does not overwrite any hierarchy.
       *     viewModel.set('user.address.city', 'London');
       *     console.log(viewModel.get('user.address.city'));
       *     // Sets 2 properties of "user". Overwrites any existing hierarchy.
       *     viewModel.set('user', {firstName: 'Foo', lastName: 'Bar'});
       *     console.log(viewModel.get('user.firstName'));
       *     // Sets a single property at the root level. Overwrites any existing hierarchy.
       *     viewModel.set({rootKey: 1});
       *     console.log(viewModel.get('rootKey'));
       */
      set: function (path, value) {
          var me = this,
              obj, stub;
  
          // Force data creation
          me.getData();
  
          if (value === undefined && path && path.constructor === Object) {
              stub = me.getRoot();
              value = path;
          } else if (path && path.indexOf('.') < 0) {
              obj = {};
              obj[path] = value;
              value = obj;
              stub = me.getRoot();
          } else {
              stub = me.getStub(path);
          }
  
          stub.set(value);
      },
  
      /**
       * Sets data directly at the level of this viewmodel. This method does not climb
       * to set data on parent view models. Passing `undefined` will clear the value
       * in this viewmodel, which means that this viewmodel is free to inherit data
       * from a parent. See "Inheriting Data" in the class introduction for more information.
       * @param {Object} data The new data to set.
       * @method setData
       */
  
      //=========================================================================
      privates: {
          registerChild: function(child) {
              var children = this.children;
              if (!children) {
                  this.children = children = {};
              }
              children[child.getId()] = child;
          },
          
          unregisterChild: function(child) {
              var children = this.children;
              // If we're destroying we'll be wiping this collection shortly, so
              // just ignore it here
              if (!this.destroying && children) {
                  delete children[child.getId()];
              }
          },
  
          /**
           * Get a record instance given a reference descriptor. Will ask
           * the session if one exists.
           * @param {String/Ext.Class} type The model type.
           * @param {Object} id The model id.
           * @return {Ext.data.Model} The model instance.
           * @private
           */
           getRecord: function(type, id) {
              var session = this.getSession(),
                  Model = type,
                  hasId = id !== undefined,
                  record;
  
              if (session) {
                  if (hasId) {
                      record = session.getRecord(type, id);
                  } else {
                      record = session.createRecord(type);
                  }
              } else {
                  if (!Model.$isClass) {
                      Model = this.getSchema().getEntity(Model);
                      //<debug>
                      if (!Model) {
                          Ext.raise('Invalid model name: ' + type);
                      }
                      //</debug>
                  }
                  if (hasId) {
                      record = Model.createWithId(id);
                      record.load();
                  } else {
                      record = new Model();
                  }
              }
              return record;
          },
  
          bindExpression: function (descriptor, callback, scope, options) {
              var stub = this.getStub(descriptor);
              return stub.bind(callback, scope, options);
          },
  
          applyScheduler: function (scheduler) {
              if (scheduler && !scheduler.isInstance) {
                  if (scheduler === true) {
                      scheduler = {};
                  }
                  if (!('preSort' in scheduler)) {
                      scheduler = Ext.apply({
                          preSort: 'kind,-depth'
                      }, scheduler);
                  }
                  scheduler = new Ext.util.Scheduler(scheduler);
                  scheduler.$owner = this;
              }
              return scheduler;
          },
  
          getScheduler: function () {
              var me = this,
                  scheduler = me._scheduler,
                  parent;
  
              if (!scheduler) {
                  if (!(parent = me.getParent())) {
                      scheduler = new Ext.util.Scheduler({
                          // See Session#scheduler
                          preSort: 'kind,-depth'
                      });
                      scheduler.$owner = me;
                  } else {
                      scheduler = parent.getScheduler();
                  }
  
                  me.setScheduler(scheduler);
              }
  
              return scheduler;
          },
  
          /**
           * This method looks up the `Stub` for a single bind descriptor.
           * @param {String/Object} bindDescr The bind descriptor.
           * @return {Ext.app.bind.AbstractStub} The `Stub` associated to the bind descriptor.
           * @private
           */
          getStub: function (bindDescr) {
              var root = this.getRoot();
              return bindDescr ? root.getChild(bindDescr) : root;
          },
  
          collect: function() {
              var me = this,
                  parent = me.getParent(),
                  task = me.collectTask;
  
              if (parent) {
                  parent.collect();
                  return;
              }
  
              if (!task) {
                  task = me.collectTask = new Ext.util.DelayedTask(me.doCollect, me);
              }
  
              // Useful for testing
              if (me.collectTimeout === 0) {
                  me.doCollect();
              } else {
                  task.delay(me.collectTimeout);
              }
          },
  
          doCollect: function() {
              var children = this.children,
                  key;
              
              // We need to loop over the children first, since they may have link stubs
              // that create bindings inside our VM. Attempt to clean them up first.
              if (children) {
                  for (key in children) {
                      children[key].doCollect();
                  }
              }
              this.getRoot().collect();
          },
  
          invalidateChildLinks: function(name, clear) {
              var children = this.children,
                  key;
  
              if (children) {
                  for (key in children) {
                      children[key].getRoot().invalidateChildLink(name, clear);
                  }
              }
          },
  
          onBindDestroy: function(binding, fromChild) {
              var me = this,
                  parent;
  
              if (me.destroying) {
                  return;
              }
  
              if (!fromChild) {
                  delete me.bindings[binding.id];
              }
  
              parent = me.getParent();
              if (parent) {
                  parent.onBindDestroy(binding, true);
              } else {
                  me.collect();
              }
          },
  
          //-------------------------------------------------------------------------
          // Config
          // <editor-fold>
  
          applyData: function (newData, data) {
              var me = this,
                  linkData, parent;
  
              // Force any session to be invoked so we can access it
              me.getSession();
              if (!data) {
                  parent = me.getParent();
  
                  /**
                   * @property {Object} linkData
                   * This object is used to hold the result of a linked value. This is done
                   * so that the data object hasOwnProperty equates to whether or not this
                   * property is owned by this instance or inherited.
                   * @private
                   * @readonly
                   * @since 5.0.0
                   */
                  me.linkData = linkData = parent ? Ext.Object.chain(parent.getData()) : {};
  
                  /**
                   * @property {Object} data
                   * This object holds all of the properties of this `ViewModel`. It is
                   * prototype chained to the `linkData` which is, in turn, prototype chained
                   * to (if present) the `data` object of the parent `ViewModel`.
                   * @private
                   * @readonly
                   * @since 5.0.0
                   */
                  me.data = me._data = Ext.Object.chain(linkData);
              }
  
              if (newData && newData.constructor === Object) {
                  me.getRoot().set(newData, true);
              }
          },
  
          applyParent: function(parent) {
              if (parent) {
                  parent.registerChild(this);
              }
              return parent;
          },
          
          applyStores: function(stores) {
              var me = this,
                  root = me.getRoot(),
                  key, cfg, storeBind, stub, listeners;
              
              me.storeInfo = {};
              me.listenerScopeFn = function() {
                  return me.getView().getInheritedConfig('defaultListenerScope');
              };
              for (key in stores) {
                  cfg = stores[key];
                  if (cfg.isStore) {
                      cfg.$wasInstance = true;
                      me.setupStore(cfg, key);
                      continue;
                  } else if (Ext.isString(cfg)) {
                      cfg = {
                          source: cfg
                      };
                  } else {
                      cfg = Ext.apply({}, cfg);
                  }
                  // Get rid of listeners so they don't get considered as a bind
                  listeners = cfg.listeners;
                  delete cfg.listeners;
                  storeBind = me.bind(cfg, me.onStoreBind, me, {trackStatics: true});
                  if (storeBind.isStatic()) {
                      // Everything is static, we don't need to wait, so remove the
                      // binding because it will only fire the first time.
                      storeBind.destroy();
                      me.createStore(key, cfg, listeners);
                  } else {
                      storeBind.$storeKey = key;
                      storeBind.$listeners = listeners;
                      stub = root.createStubChild(key);
                      stub.setStore(storeBind);
                  }
              }
          },
          
          onStoreBind: function(cfg, oldValue, binding) {
              var info = this.storeInfo,
                  key = binding.$storeKey,
                  store = info[key],
                  proxy;
  
              if (!store) {
                  this.createStore(key, cfg, binding.$listeners, binding);
              } else {
                  cfg = Ext.merge({}, binding.pruneStaticKeys());
                  proxy = cfg.proxy;
                  delete cfg.type;
                  delete cfg.model;
                  delete cfg.fields;
                  delete cfg.proxy;
                  delete cfg.listeners;
                  
                  // TODO: possibly optimize this so we can figure out what has changed
                  // instead of smashing the whole lot
                  if (proxy) {
                      delete proxy.reader;
                      delete proxy.writer;
                      store.getProxy().setConfig(proxy);
                  }
                  store.setConfig(cfg);
              }
          },
  
          createStore: function(key, cfg, listeners, binding) {
              var session = this.getSession(),
                  store;
  
              cfg = Ext.apply({}, cfg);
  
              if (cfg.session) {
                  cfg.session = session;
              }
              if (cfg.source) {
                  cfg.type = cfg.type || 'chained';
              }
  
              // Restore the listeners from applyStores here
              cfg.listeners = listeners;
              // Ensure events fired by ctor can find their target:
              cfg.resolveListenerScope = this.listenerScopeFn;
  
              store = Ext.Factory.store(cfg);
              store.$binding = binding;
  
              this.setupStore(store, key);
          },
  
          setupStore: function (store, key) {
              // May have been given a store instance
              store.resolveListenerScope = this.listenerScopeFn;
              this.storeInfo[key] = store;
              this.set(key, store);
          },
  
          applyFormulas: function (formulas) {
              var me = this,
                  root = me.getRoot(),
                  name, stub;
  
              me.getData(); // make sure our data is setup first
  
              for (name in formulas) {
                  //<debug>
                  if (name.indexOf('.') >= 0) {
                      Ext.raise('Formula names cannot contain dots: ' + name);
                  }
                  //</debug>
                  
                  // Force a stub to be created
                  root.createStubChild(name);
  
                  stub = me.getStub(name);
                  stub.setFormula(formulas[name]);
              }
              return formulas;
          },
  
          applyLinks: function (links) {
              for (var link in links) {
                  this.linkTo(link, links[link]);
              }
          },
  
          applySchema: function (schema) {
              return Ext.data.schema.Schema.get(schema);
          },
  
          applyRoot: function () {
              var root = new Ext.app.bind.RootStub(this),
                  parent = this.getParent();
  
              if (parent) {
                  // We are assigning the root of a child VM such that its bindings will be
                  // pre-sorted after the bindings of the parent VM.
                  root.depth = parent.getRoot().depth - 1000;
              }
  
              return root;
          },
  
          getFormulaFn: function(data) {
              var me = this,
                  fn = me.formulaFn;
  
              if (!fn) {
                  fn = me.formulaFn = function(name) {
                      // Note that the `this` pointer here is the view model because
                      // the VM calls it in the VM scope.
                      return me.$formulaData[name];
                  };
              }
              me.$formulaData = data;
              return fn;
          }
  
          // </editor-fold>
      }
  });
  