This is an example of the TodoMVC application using [Sencha's ExtJS](http://www.sencha.com/) enhanced with the [DeftJS library](http://deftjs.org/). DeftJS is an MVC framework and inversion of control (IoC) container, and provides many other features such as a Promises API, live event listeners, etc.
Instead of the singleton-based, stateless controllers promoted by Sencha's built-in MVC system, DeftJS uses ViewControllers which are dynamically attached to each view. This is essentially an implementation of the [Passive View pattern](http://martinfowler.com/eaaDev/PassiveScreen.html), similar to Cocoa's UIViewControllers. The source code and full documentation can be found via the [DeftJS home page](http://deftjs.org/).
Because the application is quite small, the benefits of ViewControllers and IoC aren't very obvious. The advantages become pronounced on larger applications.
It's also worth noting that the TodoMVC application is **not** very typical of an ExtJS-based application. ExtJS is more applicable to large-scale [rich internet application (RIA)](http://en.wikipedia.org/wiki/Rich_Internet_application) development. The XTemplate approach used here to more easily leverage the mandated CSS and layout is not a very common sight.
ExtJS has a very advanced layout and component system, along with full-blown SASS-based themes. So while this example looks and functions exactly as the TodoMVC App Specification mandates, this isn't really how views in a "normal" ExtJS or Sencha Touch application would be set up. Sencha has [many examples of the available components and layouts](http://docs.sencha.com/ext-js/4-1/#!/example), if you'd like to see the more typical options.
* Returns the configuration object to pass to Deft.Injector.configure(). Override in subclasses to alter the Injector configuration before returning the config object.
* @return {Object} The Injector configuration object.
*/
buildInjectorConfiguration:function(){
varconfig;
config={
templateLoader:'TodoDeftJS.util.TemplateLoader',
todoStore:'TodoDeftJS.store.TodoStore'
};
returnconfig;
},
/**
* @protected
* Runs at the start of the init() method. Override in subclasses if needed.
*/
beforeInit:function(){},
/**
* @protected
* Runs at the end of the init() method. Useful to create initial Viewport, start Jasmine tests, etc.
Open source under the [MIT License](http://en.wikipedia.org/wiki/MIT_License).
*/
/**
* Used by Deft.ioc.Injector.
* @private
*/
Ext.define('Deft.ioc.DependencyProvider',{
requires:['Deft.log.Logger'],
config:{
identifier:null,
/**
Class to be instantiated, by either full name, alias or alternate name, to resolve this dependency.
*/
className:null,
/**
Optional arguments to pass to the class' constructor when instantiating a class to resolve this dependency.
*/
parameters:null,
/**
Factory function to be executed to obtain the corresponding object instance or value to resolve this dependency.
NOTE: For lazily instantiated dependencies, this function will be passed the object instance for which the dependency is being resolved.
*/
fn:null,
/**
Value to use to resolve this dependency.
*/
value:void0,
/**
Indicates whether this dependency should be resolved as a singleton, or as a transient value for each resolution request.
*/
singleton:true,
/**
Indicates whether this dependency should be 'eagerly' instantiated when this provider is defined, rather than 'lazily' instantiated when later requested.
NOTE: Only valid when either a factory function or class is specified as a singleton.
msg:"Error while configuring rule for '"+(this.getIdentifier())+"': singleton classes cannot be configured for injection as a prototype. Consider removing 'singleton: true' from the class definition."
});
}
}
if(this.getValue()!=null){
Ext.Error.raise({
msg:"Error while configuring '"+(this.getIdentifier())+"': a 'value' can only be configured as a singleton."
msg:"Error while configuring rule for '"+(this.getIdentifier())+"': parameters cannot be applied to singleton classes. Consider removing 'singleton: true' from the class definition."
});
}
}
}
returnthis;
},
/**
Resolve a target instance's dependency with an object instance or value generated by this dependency provider.
In the IoC configuration above, we have created two **dependency providers**, one named `companyStore` and one named `companyService`. By default, DeftJS uses lazy instantiation to create singleton instances of the `CompanyStore` and `CompanyService` classes. This means that a singleton won't be created until an object in your application specifies one of these dependency providers as an injected dependency.
## <u>[Singleton vs. Prototype Dependency Providers](https://github.com/deftjs/DeftJS/wiki/Singleton-vs.-Prototype-Dependency-Providers)</u>
By default, the dependency providers set up with the DeftJS `Injector` are singletons. This means that only one instance of that dependency will be created, and the same instance will be injected into all objects that request that dependency.
For cases where this is not desired, you can create non-singleton (prototype) dependency providers like this:
Deft.Injector.configure({
editHistory: {
className: "MyApp.util.EditHistory",
singleton: false
}
});
## <u>[Lazy vs. Eager Dependency Creation](https://github.com/deftjs/DeftJS/wiki/Eager-vs.-Lazy-Instantiation)</u>
By default, dependency providers are created **lazily**. This means that the dependency will not be created by DeftJS until another object is created which specifies that dependency as an injection.
In cases where lazy instantiation is not desired, you can set up a dependency provider to be created immediately upon application startup by using the `eager` configuration:
Deft.Injector.configure({
notificationService: {
className: "MyApp.service.NotificationService",
eager: true
}
});
> **NOTE: Only singleton dependency providers can be eagerly instantiated.** This means that specifying `singleton: false` and `eager: true` for a dependency provider won't work. The reason may be obvious: DeftJS can't do anything with a prototype object that is eagerly created, since by definition each injection of a prototype dependency must be a new instance!
If needed, constructor parameters can be specified for a dependency provider. These parameters will be passed into the constructor of the target object when it is created. Constructor parameters can be configured in the following way:
Deft.Injector.configure({
contactStore: {
className: 'MyApp.store.ContactStore',
// Specify an array of params to pass into ContactStore constructor
When the Injector is called to resolve dependencies for these identifiers, the factory function is called and the dependency is resolved with the return value.
As shown above, a lazily instantiated factory function can optionally accept a parameter, corresponding to the instance for which the Injector is currently injecting dependencies.
Factory function dependency providers can be configured as singletons or prototypes and can be eagerly or lazily instantiated.
> **NOTE: Only singleton factory functions can be eagerly instantiated.** This means that specifying `singleton: false` and `eager: true` for a dependency provider won't work. The reason may be obvious: DeftJS can't do anything with a prototype object that is eagerly created, since by definition each injection of a prototype dependency must be a new instance!
## Define <u>[references to view components](https://github.com/deftjs/DeftJS/wiki/Accessing-Views)</u> and <u>[add view listeners](https://github.com/deftjs/DeftJS/wiki/Handling-View-Events)</u> with the `control` property:
// Most common configuration, using an itemId and listener
manufacturingFilter: {
change: "onFilterChange"
},
// Reference only, with no listeners
serviceIndustryFilter: true,
// Configuration using selector, listeners, and event listener options
salesFilter: {
selector: "toolbar > checkbox",
listeners: {
change: {
fn: "onFilterChange",
buffer: 50,
single: true
}
}
}
},
init: function() {
return this.callParent(arguments);
}
// Event handlers or other methods here...
});
## Dynamically monitor view to attach listeners to added components with <u>[live selectors](https://github.com/deftjs/DeftJS/wiki/ViewController-Live-Selectors)</u>:
control: {
manufacturingFilter: {
live: true,
listeners: {
change: "onFilterChange"
}
}
};
## Observe events on injected objects with the <u>[`observe` property](https://github.com/deftjs/DeftJS/wiki/ViewController-Observe-Configuration)</u>:
Deft.Logger.deprecate('Deft.mixin.Controllable has been deprecated and can now be omitted - simply use the \'controller\' class annotation on its own.');
Deft.Logger.warn("Error initializing view controller: an error occurred while creating an instance of the specified controller: '"+this.controller+"'.");
Deft.Logger.warn("Error initializing view controller: an error occurred while creating an instance of the specified controller: '"+this.controller+"'.");
Open source under the [MIT License](http://en.wikipedia.org/wiki/MIT_License).
Promise.when(), all(), any(), some(), map() and reduce() methods adapted from:
[when.js](https://github.com/cujojs/when)
Copyright (c) B Cavalier & J Hann
Open source under the [MIT License](http://en.wikipedia.org/wiki/MIT_License).
*/
/**
* A Promise represents the result of a future value that has not been defined yet, typically because it is created asynchronously. Used in conjunction with Deft.promise.Deferred.
*/
Ext.define('Deft.promise.Promise',{
alternateClassName:['Deft.Promise'],
statics:{
/**
* Returns a new {@link Deft.promise.Promise} that:
* - resolves immediately for the specified value, or
* - resolves, rejects, updates or cancels when the specified {@link Deft.promise.Deferred} or {@link Deft.promise.Promise} is resolved, rejected, updated or cancelled.
* Returns a new {@link Deft.promise.Promise} with the specified callbacks registered to be called when this {@link Deft.promise.Promise} is resolved, rejected, updated or cancelled.
* Returns a new {@link Deft.promise.Promise} with the specified callback registered to be called when this {@link Deft.promise.Promise} is resolved, rejected or cancelled.
* Adds a log message if {Deft.promise.Deferred.enableLogging} is set to true.
*/
logMessage:function(message){
if(Deft.promise.Deferred.enableLogging){
returnDeft.Logger.log(message);
}
}
},
id:null,
constructor:function(config){
if(config==null){
config={};
}
this.id=config.id;
this.state='pending';
this.progress=void0;
this.value=void0;
this.progressCallbacks=[];
this.successCallbacks=[];
this.failureCallbacks=[];
this.cancelCallbacks=[];
this.promise=Ext.create('Deft.Promise',{
id:this.id?"of "+this.id:null,
deferred:this
});
returnthis;
},
/**
* Returns a new {@link Deft.promise.Promise} with the specified callbacks registered to be called when this {@link Deft.promise.Deferred} is resolved, rejected, updated or cancelled.
* Returns a new {@link Deft.promise.Promise} with the specified callback registered to be called when this {@link Deft.promise.Deferred} is rejected.
*/
otherwise:function(callback,scope){
var_ref;
if(Ext.isObject(callback)){
_ref=callback,callback=_ref.fn,scope=_ref.scope;
}
returnthis.then({
failure:callback,
scope:scope
});
},
/**
* Returns a new {@link Deft.promise.Promise} with the specified callback registered to be called when this {@link Deft.promise.Deferred} is either resolved, rejected, or cancelled.
*/
always:function(callback,scope){
var_ref;
if(Ext.isObject(callback)){
_ref=callback,callback=_ref.fn,scope=_ref.scope;
}
returnthis.then({
success:callback,
failure:callback,
cancel:callback,
scope:scope
});
},
/**
* Update progress for this {@link Deft.promise.Deferred} and notify relevant callbacks.
*/
update:function(progress){
Deft.promise.Deferred.logMessage(""+this+" updated with progress: "+progress);
if(this.state==='pending'){
this.progress=progress;
this.notify(this.progressCallbacks,progress);
}else{
if(this.state!=='cancelled'){
Ext.Error.raise({
msg:"Error: this "+this+" has already been completed and cannot be modified."
});
}
}
},
/**
* Resolve this {@link Deft.promise.Deferred} and notify relevant callbacks.
*/
resolve:function(value){
Deft.promise.Deferred.logMessage(""+this+" resolved with value: "+value);
Deft.promise.Deferred.logMessage("Registering progress callback for "+self+".");
}
returnfunction(value){
varresult;
if(Ext.isFunction(callback)){
try{
Deft.promise.Deferred.logMessage("Calling progress callback registered for "+self+".");
result=callback.call(scope,value);
Deft.promise.Deferred.logMessage(""+(deferred.getPromise())+" updated with progress returned by the progress callback: "+result+".");
deferred.update(result);
}catch(error){
Deft.Logger.error("Error: progress callback registered for "+self+" threw: "+(error.stack!=null?error.stack:error));
}
}else{
Deft.promise.Deferred.logMessage(""+(deferred.getPromise())+" updated with progress: "+value);
deferred.update(value);
}
};
},
/**
* Register a callback for this {@link Deft.promise.Deferred} for the specified callbacks and state, immediately notifying with the specified value (if applicable).
* Execute an Array (or Deferred/Promise of an Array) of functions in parallel.
* The specified functions may optionally return their results as Promises.
* Returns a Promise of an Array of results for each function call (in the same order).
*/
parallel:function(fns,scope){
returnDeft.Promise.map(fns,function(fn){
returnfn.call(scope);
});
},
/**
* Execute an Array (or Deferred/Promise of an Array) of functions as a pipeline, where each function's result is passed to the subsequent function as input.
* The specified functions may optionally return their results as Promises.
* Returns a Promise of the result value for the final function in the pipeline.
Open source under the [MIT License](http://en.wikipedia.org/wiki/MIT_License).
*/
Ext.define("Deft.core.Class",{alternateClassName:["Deft.Class"],statics:{registerPreprocessor:function(b,d,a,c){if(Ext.getVersion("extjs")&&Ext.getVersion("core").isLessThan("4.1.0")){Ext.Class.registerPreprocessor(b,function(e,f,g){returnd.call(this,e,f,f,g)}).setDefaultPreprocessorPosition(b,a,c)}else{Ext.Class.registerPreprocessor(b,function(f,g,e,h){returnd.call(this,f,g,e,h)},[b],a,c)}},hookOnClassCreated:function(a,b){if(Ext.getVersion("extjs")&&Ext.getVersion("core").isLessThan("4.1.0")){Ext.Function.interceptBefore(a,"onClassCreated",b)}else{Ext.Function.interceptBefore(a,"onCreated",b)}},hookOnClassExtended:function(c,b){vara;if(Ext.getVersion("extjs")&&Ext.getVersion("core").isLessThan("4.1.0")){a=function(d,e){returnb.call(this,d,e,e)}}else{a=b}if(c.onClassExtended!=null){Ext.Function.interceptBefore(c,"onClassExtended",a)}else{c.onClassExtended=a}},extendsClass:function(c,b){try{if(Ext.getClassName(b)===c){returntrue}if(b!=null?b.superclass:void0){if(Ext.getClassName(b.superclass)===c){returntrue}else{returnDeft.Class.extendsClass(c,Ext.getClass(b.superclass))}}else{returnfalse}}catch(a){returnfalse}}}});Ext.define("Deft.log.Logger",{alternateClassName:["Deft.Logger"],singleton:true,log:function(b,a){if(a==null){a="info"}},error:function(a){this.log(a,"error")},info:function(a){this.log(a,"info")},verbose:function(a){this.log(a,"verbose")},warn:function(a){this.log(a,"warn")},deprecate:function(a){this.log(a,"deprecate")}},function(){vara;if(Ext.getVersion("extjs")!=null){this.log=function(c,b){if(b==null){b="info"}if(b==="verbose"){b==="info"}if(b==="deprecate"){b="warn"}Ext.log({msg:c,level:b})}}else{if(Ext.isFunction((a=Ext.Logger)!=null?a.log:void0)){this.log=Ext.bind(Ext.Logger.log,Ext.Logger)}}});Ext.define("Deft.util.Function",{alternateClassName:["Deft.Function"],statics:{spread:function(b,a){returnfunction(c){if(!Ext.isArray(c)){Ext.Error.raise({msg:"Error spreading passed Array over target function arguments: passed a non-Array."})}returnb.apply(a,c)}},memoize:function(d,c,a){varb;b={};returnfunction(f){vare;e=Ext.isFunction(a)?a.apply(c,arguments):f;if(!(einb)){b[e]=d.apply(c,arguments)}returnb[e]}},extract:function(a,b){varc;c=a[b];deletea[b];returnc}}});Ext.define("Deft.event.LiveEventListener",{alternateClassName:["Deft.LiveEventListener"],requires:["Ext.ComponentQuery"],constructor:function(c){varb,d,e,a;Ext.apply(this,c);this.components=[];d=Ext.ComponentQuery.query(this.selector,this.container);for(e=0,a=d.length;e<a;e++){b=d[e];this.components.push(b);b.on(this.eventName,this.fn,this.scope,this.options)}},destroy:function(){varb,d,a,c;c=this.components;for(d=0,a=c.length;d<a;d++){b=c[d];b.un(this.eventName,this.fn,this.scope)}this.components=null},register:function(a){if(this.matches(a)){this.components.push(a);a.on(this.eventName,this.fn,this.scope,this.options)}},unregister:function(b){vara;a=Ext.Array.indexOf(this.components,b);if(a!==-1){b.un(this.eventName,this.fn,this.scope);Ext.Array.erase(this.components,a,1)}},matches:function(a){if(this.selector===null&&this.container===a){returntrue}if(this.container===null&&Ext.Array.contains(Ext.ComponentQuery.query(this.selector),a)){returntrue}if(a.isDescendantOf(this.container)&&Ext.Array.contains(this.container.query(this.selector),a)){returntrue}returnfalse}});Ext.define("Deft.event.LiveEventBus",{alternateClassName:["Deft.LiveEventBus"],requires:["Ext.Component","Ext.ComponentManager","Deft.event.LiveEventListener"],singleton:true,constructor:function(){this.listeners=[]},destroy:function(){vard,c,a,b;b=this.listeners;for(c=0,a=b.length;c<a;c++){d=b[c];d.destroy()}this.listeners=null},addListener:function(b,a,c,f,e,d){varg;g=Ext.create("Deft.event.LiveEventListener",{container:b,selector:a,eventName:c,fn:f,scope:e,options:d});this.listeners.push(g)},removeListener:function(b,a,c,e,d){varf;f=this.findListener(b,a,c,e,d);if(f!=null){Ext.Array.remove(this.listeners,f);f.destroy()}},on:function(b,a,c,f,e,d){returnthis.addListener(b,a,c,f,e,d)},un:function(b,a,c,e,d){returnthis.removeListener(b,a,c,e,d)},findListener:function(a,c,f,g,i){varb,d,h,e;e=this.listeners;for(d=0,h=e.length;d<h;d++){b=e[d];if(b.container===a&&b.selector===c&&b.eventName===f&&b.fn===g&&b.scope===i){returnb}}returnnull},register:function(a){a.on("added",this.onComponentAdded,this);a.on("removed",this.onComponentRemoved,this)},unregister:function(a){a.un("added",this.onComponentAdded,this);a.un("removed",this.onComponentRemoved,this)},onComponentAdded:function(c,b,d){varg,f,a,e;e=this.listeners;for(f=0,a=e.length;f<a;f++){g=e[f];g.register(c)}},onComponentRemoved:function(c,b,d){varg,f,a,e;e=this.listeners;for(f=0,a=e.length;f<a;f++){g=e[f];g.unregister(c)}}},function(){if(Ext.getVersion("touch")!=null){Ext.define("Deft.Component",{override:"Ext.Component",setParent:function(c){varb,a;b=this.getParent();a=this.callParent(arguments);if(b===null&&c!==null){this.fireEvent("added",this,c)}else{if(b!==null&&c!==null){this.fireEvent("removed",this,b);this.fireEvent("added",this,c)}else{if(b!==null&&c===null){this.fireEvent("removed",this,b)}}}returna},isDescendantOf:function(a){varb;b=this.getParent();while(b!=null){if(b===a){returntrue}b=b.getParent()}returnfalse}})}Ext.Function.interceptAfter(Ext.ComponentManager,"register",function(a){Deft.event.LiveEventBus.register(a)});Ext.Function.interceptAfter(Ext.ComponentManager,"unregister",function(a){Deft.event.LiveEventBus.unregister(a)})});Ext.define("Deft.ioc.DependencyProvider",{requires:["Deft.log.Logger"],config:{identifier:null,className:null,parameters:null,fn:null,value:void0,singleton:true,eager:false},constructor:function(b){vara;this.initConfig(b);if((b.value!=null)&&b.value.constructor===Object){this.setValue(b.value)}if(this.getEager()){if(this.getValue()!=null){Ext.Error.raise({msg:"Error while configuring '"+(this.getIdentifier())+"': a 'value' cannot be created eagerly."})}if(!this.getSingleton()){Ext.Error.raise({msg:"Error while configuring '"+(this.getIdentifier())+"': only singletons can be created eagerly."})}}if(this.getClassName()!=null){a=Ext.ClassManager.get(this.getClassName());if(!(a!=null)){Deft.Logger.warn("Synchronously loading '"+(this.getClassName())+"'; consider adding Ext.require('"+(this.getClassName())+"') above Ext.onReady.");Ext.syncRequire(this.getClassName());a=Ext.ClassManager.get(this.getClassName())}if(!(a!=null)){Ext.Error.raise({msg:"Error while configuring rule for '"+(this.getIdentifier())+"': unrecognized class name or alias: '"+(this.getClassName())+"'"})}}if(!this.getSingleton()){if(this.getClassName()!=null){if(Ext.ClassManager.get(this.getClassName()).singleton){Ext.Error.raise({msg:"Error while configuring rule for '"+(this.getIdentifier())+"': singleton classes cannot be configured for injection as a prototype. Consider removing 'singleton: true' from the class definition."})}}if(this.getValue()!=null){Ext.Error.raise({msg:"Error while configuring '"+(this.getIdentifier())+"': a 'value' can only be configured as a singleton."})}}else{if((this.getClassName()!=null)&&(this.getParameters()!=null)){if(Ext.ClassManager.get(this.getClassName()).singleton){Ext.Error.raise({msg:"Error while configuring rule for '"+(this.getIdentifier())+"': parameters cannot be applied to singleton classes. Consider removing 'singleton: true' from the class definition."})}}}returnthis},resolve:function(c){vara,b;Deft.Logger.log("Resolving '"+(this.getIdentifier())+"'.");if(this.getValue()!==void0){returnthis.getValue()}a=null;if(this.getFn()!=null){Deft.Logger.log("Executing factory function.");a=this.getFn().call(null,c)}else{if(this.getClassName()!=null){if(Ext.ClassManager.get(this.getClassName()).singleton){Deft.Logger.log("Using existing singleton instance of '"+(this.getClassName())+"'.");a=Ext.ClassManager.get(this.getClassName())}else{Deft.Logger.log("Creating instance of '"+(this.getClassName())+"'.");b=this.getParameters()!=null?[this.getClassName()].concat(this.getParameters()):[this.getClassName()];a=Ext.create.apply(this,b)}}else{Ext.Error.raise({msg:"Error while configuring rule for '"+(this.getIdentifier())+"': no 'value', 'fn', or 'className' was specified."})}}if(this.getSingleton()){this.setValue(a)}returna}});Ext.define("Deft.ioc.Injector",{alternateClassName:["Deft.Injector"],requires:["Ext.Component","Deft.log.Logger","Deft.ioc.DependencyProvider"],singleton:true,constructor:function(){this.providers={};this.injectionStack=[];returnthis},configure:function(b){vara;Deft.Logger.log("Configuring the injector.");a={};Ext.Object.each(b,function(d,c){vare;Deft.Logger.log("Configuring dependency provider for '"+d+"'.");if(Ext.isString(c)){e=Ext.create("Deft.ioc.DependencyProvider",{identifier:d,className:c})}else{e=Ext.create("Deft.ioc.DependencyProvider",Ext.apply({identifier:d},c))}this.providers[d]=e;a[d]=e},this);Ext.Object.each(a,function(c,d){if(d.getEager()){Deft.Logger.log("Eagerly creating '"+(d.getIdentifier())+"'.");d.resolve()}},this)},reset:function(){Deft.Logger.log("Resetting the injector.");this.providers={}},canResolve:function(a){varb;b=this.providers[a];returnb!=null},resolve:function(a,b){varc;c=this.providers[a];if(c!=null){returnc.resolve(b)}else{Ext.Error.raise({msg:"Error while resolving value to inject: no dependency provider found for '"+a+"'."})}},inject:function(e,f,j){varh,a,b,c,d,i,g;if(j==null){j=true}i=Ext.getClassName(f);if(Ext.Array.contains(this.injectionStack,i)){d=this.injectionStack.join(" -> ");this.injectionStack=[];Ext.Error.raise({msg:"Error resolving dependencies for '"+i+"'. A circular dependency exists in its injections: "+d+" -> *"+i+"*"});returnnull}this.injectionStack.push(i);h={};if(Ext.isString(e)){e=[e]}Ext.Object.each(e,function(n,o){varm,k,l;l=Ext.isArray(e)?o:n;m=o;k=this.resolve(m,f);if(linf.config){Deft.Logger.log("Injecting '"+m+"' into '"+l+"' config.");h[l]=k}else{Deft.Logger.log("Injecting '"+m+"' into '"+l+"' property.");f[l]=k}},this);this.injectionStack=[];if(j){for(ainh){g=h[a];c="set"+Ext.String.capitalize(a);f[c].call(f,g)}}else{if((Ext.getVersion("extjs")!=null)&&finstanceofExt.ClassManager.get("Ext.Component")){f.injectConfig=h}else{if(Ext.isFunction(f.initConfig)){b=f.initConfig;f.initConfig=function(l){vark;k=b.call(this,Ext.Object.merge({},l||{},h));returnk}}}}returnf}},function(){if(Ext.getVersion("extjs")!=null){if(Ext.getVersion("core").isLessThan("4.1.0")){Ext.Component.override({constructor:function(a){a=Ext.Object.merge({},a||{},this.injectConfig||{});deletethis.injectConfig;returnthis.callOverridden([a])}})}else{Ext.define("Deft.InjectableComponent",{override:"Ext.Component",constructor:function(a){a=Ext.Object.merge({},a||{},this.injectConfig||{});deletethis.injectConfig;returnthis.callParent([a])}})}}});Ext.define("Deft.mixin.Injectable",{requires:["Deft.core.Class","Deft.ioc.Injector","Deft.log.Logger"],onClassMixedIn:function(a){Deft.Logger.deprecate("Deft.mixin.Injectable has been deprecated and can now be omitted - simply use the 'inject' class annotation on its own.")}},function(){vara;if(Ext.getVersion("extjs")&&Ext.getVersion("core").isLessThan("4.1.0")){a=function(){returnfunction(){if(!this.$injected){Deft.Injector.inject(this.inject,this,false);this.$injected=true}returnthis.callOverridden(arguments)}}}else{a=function(){returnfunction(){if(!this.$injected){Deft.Injector.inject(this.inject,this,false);this.$injected=true}returnthis.callParent(arguments)}}}Deft.Class.registerPreprocessor("inject",function(b,e,j,i){varg,f,c,h,d;if(Ext.isString(e.inject)){e.inject=[e.inject]}if(Ext.isArray(e.inject)){g={};d=e.inject;for(c=0,h=d.length;c<h;c++){f=d[c];g[f]=f}e.inject=g}Deft.Class.hookOnClassCreated(j,function(k){k.override({constructor:a()})});Deft.Class.hookOnClassExtended(e,function(m,n,l){vark;Deft.Class.hookOnClassCreated(l,function(o){o.override({constructor:a()})});if((k=n.inject)==null){n.inject={}}Ext.applyIf(n.inject,m.superclass.inject)})},"before","extend")});Ext.define("Deft.mvc.Observer",{requires:["Deft.core.Class","Ext.util.Observable","Deft.util.Function"],statics:{mergeObserve:function(b,l){vard,q,m,r,g,k,o,i,n,p,e,f,a,j,h,c;if(!Ext.isObject(b)){a={}}else{a=Ext.clone(b)}if(!Ext.isObject(l)){g={}}else{g=Ext.clone(l)}i=["buffer","single","delay","element","target","destroyable"];o=function(A){varD,z,u,C,t,v,y,x,B,s,w;w=[];for(CinA){u=A[C];if(Ext.isArray(u)){z={};for(y=0,B=u.length;y<B;y++){v=u[y];if(Ext.Object.getSize(v)===1){Ext.apply(z,v)}else{D={};if((v!=null?v.fn:void0)!=null){D.fn=v.fn}if((v!=null?v.scope:void0)!=null){D.scope=v.scope}for(x=0,s=i.length;x<s;x++){t=i[x];if((v!=null?v[t]:void0)!=null){D[t]=v[t]}}z[v.event]=[D]}}w.push(A[C]=z)}else{w.push(void0)}}returnw};o(a);o(g);for(king){q=g[k];for(dinq){m=q[d];if(Ext.isString(m)){g[k][d]=m.replace("","").split(",")}if(!(a!=null?a[k]:void0)){a[k]={}}if(!(a!=null?(h=a[k])!=null?h[d]:void0:void0)){a[k][d]=g[k][d];deleteg[k][d]}}}for(jina){p=a[j];for(ninp){e=p[n];if(Ext.isString(e)){a[j][n]=e.split(",")}if(g!=null?(c=g[j])!=null?c[n]:void0:void0){r=g[j][n];f=a[j][n];a[j][n]=Ext.Array.unique(Ext.Array.insert(f,0,r))}}}returna}},constructor:function(a){varc,k,j,f,i,l,g,h,d,b,e;this.listeners=[];i=a!=null?a.host:void0;d=a!=null?a.target:void0;k=a!=null?a.events:void0;if(i&&d&&(this.isPropertyChain(d)||this.isTargetObservable(i,d))){for(cink){f=k[c];if(Ext.isString(f)){f=f.replace("","").split(",")}for(b=0,e=f.length;b<e;b++){j=f[b];h=i;l=null;if(Ext.isObject(j)){l=Ext.clone(j);if(l!=null?l.event:void0){c=Deft.util.Function.extract(l,"event")}if(l!=null?l.fn:void0){j=Deft.util.Function.extract(l,"fn")}if(l!=null?l.scope:void0){h=Deft.util.Function.extract(l,"scope")}}g=this.locateReferences(i,d,j);if(g){g.target.on(c,g.handler,h,l);this.listeners.push({targetName:d,target:g.target,event:c,handler:g.handler,scope:h});Deft.Logger.log("Created observer on '"+d+"' for event '"+c+"'.")}else{Deft.Logger.warn("Could not create observer on '"+d+"' for event '"+c+"'.")}}}}else{Deft.Logger.warn("Could not create observers on '"+d+"' because '"+d+"' is not an Ext.util.Observable")}returnthis},isTargetObservable:function(b,e){vara,d,c;a=this.locateTarget(b,e);if(!(a!=null)){returnfalse}if((a.isObservable!=null)||(((c=a.mixins)!=null?c.observable:void0)!=null)){returntrue}else{d=Ext.ClassManager.getClass(a);returnDeft.Class.extendsClass("Ext.util.Observable",d)||Deft.Class.extendsClass("Ext.mixin.Observable",d)}},locateTarget:function(b,c){vara;if(Ext.isFunction(b["get"+Ext.String.capitalize(c)])){a=b["get"+Ext.String.capitalize(c)].call(b);returna}else{if((b!=null?b[c]:void0)!=null){a=b[c];returna}else{returnnull}}},isPropertyChain:function(a){returnExt.isString(a)&&a.indexOf(".")>-1},locateReferences:function(c,d,a){varb,e;b=c;if(this.isPropertyChain(d)){e=this.parsePropertyChain(c,d);if(!e){returnnull}c=e.host;d=e.target}if(Ext.isFunction(a)){return{target:this.locateTarget(c,d),handler:a}}else{if(Ext.isFunction(b[a])){return{target:this.locateTarget(c,d),handler:b[a]}}else{returnnull}}},parsePropertyChain:function(a,b){varc;if(Ext.isString(b)){c=b.split(".")}else{if(Ext.isArray(b)){c=b}else{returnnull}}if(c.length>1&&(this.locateTarget(a,c[0])!=null)){returnthis.parsePropertyChain(this.locateTarget(a,c[0]),c.slice(1))}else{if(this.isTargetObservable(a,c[0])){return{host:a,target:c[0]}}else{returnnull}}},destroy:function(){vard,c,a,b;b=this.listeners;for(c=0,a=b.length;c<a;c++){d=b[c];Deft.Logger.log("Removing observer on '"+d.targetName+"' for event '"+d.event+"'.");d.target.un(d.event,d.handler,d.scope)}this.listeners=[]}});Ext.define("Deft.mvc.ComponentSelectorListener",{requires:["Deft.event.LiveEventBus"],constructor:function(c){varb,e,a,d;Ext.apply(this,c);if(this.componentSelector.live){Deft.LiveEventBus.addListener(this.componentSelector.view,this.componentSelector.selector,this.eventName,this.fn,this.scope,this.options)}else{d=this.componentSelector.components;for(e=0,a=d.length;e<a;e++){b=d[e];b.on(this.eventName,this.fn,this.scope,this.options)}}returnthis},destroy:function(){varb,d,a,c;if(this.componentSelector.live){Deft.LiveEventBus.removeListener(this.componentSelector.view,this.componentSelector.selector,this.eventName,this.fn,this.scope)}else{c=this.componentSelector.components;for(d=0,a=c.length;d<a;d++){b=c[d];b.un(this.eventName,this.fn,this.scope)}}}});Ext.define("Deft.mvc.ComponentSelector",{requires:["Ext.ComponentQuery","Deft.log.Logger","Deft.mvc.ComponentSelectorListener"],constructor:function(c){vara,e,g,b,d,f;Ext.apply(this,c);if(!this.live){this.components=this.selector!=null?Ext.ComponentQuery.query(this.selector,this.view):[this.view]}this.selectorListeners=[];if(Ext.isObject(this.listeners)){f=this.listeners;for(ainf){g=f[a];e=g;d=this.scope;b=null;if(Ext.isObject(g)){b=Ext.apply({},g);if(b.fn!=null){e=b.fn;deleteb.fn}if(b.scope!=null){d=b.scope;deleteb.scope}}if(Ext.isString(e)&&Ext.isFunction(d[e])){e=d[e]}if(!Ext.isFunction(e)){Ext.Error.raise({msg:"Error adding '"+a+"' listener: the specified handler '"+e+"' is not a Function or does not exist."})}this.addListener(a,e,d,b)}}returnthis},destroy:function(){vard,c,a,b;b=this.selectorListeners;for(c=0,a=b.length;c<a;c++){d=b[c];d.destroy()}this.selectorListeners=[]},addListener:function(a,d,c,b){vare;if(this.findListener(a,d,c)!=null){Ext.Error.raise({msg:"Error adding '"+a+"' listener: an existing listener for the specified function was already registered for '"+this.selector+"."})}Deft.Logger.log("Adding '"+a+"' listener to '"+this.selector+"'.");e=Ext.create("Deft.mvc.ComponentSelectorListener",{componentSelector:this,eventName:a,fn:d,scope:c,options:b});this.selectorListeners.push(e)},removeListener:function(a,c,b){vard;d=this.findListener(a,c,b);if(d!=null){Deft.Logger.log("Removing '"+a+"' listener from '"+this.selector+"'.");d.destroy();Ext.Array.remove(this.selectorListeners,d)}},findListener:function(b,d,c){varg,f,a,e;e=this.selectorListeners;for(f=0,a=e.length;f<a;f++){g=e[f];if(g.eventName===b&&g.fn===d&&g.scope===c){returng}}returnnull}});Ext.define("Deft.mvc.ViewController",{alternateClassName:["Deft.ViewController"],requires:["Deft.core.Class","Deft.log.Logger","Deft.mvc.ComponentSelector","Deft.mvc.Observer"],config:{view:null},observe:{},constructor:function(a){if(a==null){a={}}if(a.view){this.controlView(a.view)}this.initConfig(a);if(Ext.Object.getSize(this.observe)>0){this.createObservers()}returnthis},controlView:function(a){if(ainstanceofExt.ClassManager.get("Ext.Component")){this.setView(a);this.registeredComponentReferences={};this.registeredComponentSelectors={};if(Ext.getVersion("extjs")!=null){if(this.getView().rendered){this.onViewInitialize()}else{this.getView().on("afterrender",this.onViewInitialize,this,{single:true})}}else{if(this.getView().initialized){this.onViewInitialize()}else{this.getView().on("initialize",this.onViewInitialize,this,{single:true})}}}else{Ext.Error.raise({msg:"Error constructing ViewController: the configured 'view' is not an Ext.Component."})}},init:function(){},destroy:function(){varb,a;for(binthis.registeredComponentReferences){this.removeComponentReference(b)}for(ainthis.registeredComponentSelectors){this.removeComponentSelector(a)}this.removeObservers();returntrue},onViewInitialize:function(){vard,h,e,f,c,a,b,g;if(Ext.getVersion("extjs")!=null){this.getView().on("beforedestroy",this.onViewBeforeDestroy,this)}else{b=this;c=this.getView().destroy;this.getView().destroy=function(){if(b.destroy()){c.call(this)}}}g=this.control;for(hing){d=g[h];a=null;if(h!=="view"){if(Ext.isString(d)){a=d}else{if(d.selector!=null){a=d.selector}else{a="#"+h}}}e=null;if(Ext.isObject(d.listeners)){e=d.listeners}else{if(!((d.selector!=null)||(d.live!=null))){e=d}}f=(d.live!=null)&&d.live;this.addComponentReference(h,a,f);this.addComponentSelector(a,e,f)}this.init()},onViewBeforeDestroy:function(){if(this.destroy()){this.getView().un("beforedestroy",this.onViewBeforeDestroy,this);returntrue}returnfalse},addComponentReference:function(e,a,c){varb,d;if(c==null){c=false}Deft.Logger.log("Adding '"+e+"' component reference for selector: '"+a+"'.");if(this.registeredComponentReferences[e]!=null){Ext.Error.raise({msg:"Error adding component reference: an existing component reference was already registered as '"+e+"'."})}if(e!=="view"){b="get"+Ext.String.capitalize(e);if(this[b]==null){if(c){this[b]=Ext.Function.pass(this.getViewComponent,[a],this)}else{d=this.getViewComponent(a);if(d==null){Ext.Error.raise({msg:"Error locating component: no component(s) found matching '"+a+"'."})}this[b]=function(){returnd}}this[b].generated=true}}this.registeredComponentReferences[e]=true},removeComponentReference:function(b){vara;Deft.Logger.log("Removing '"+b+"' component reference.");if(this.registeredComponentReferences[b]==null){Ext.Error.raise({msg:"Error removing component reference: no component reference is registered as '"+b+"'."})}if(b!=="view"){a="get"+Ext.String.capitalize(b);if(this[a].generated){this[a]=null}}deletethis.registeredComponentReferences[b]},getViewComponent:function(a){varb;if(a!=null){b=Ext.ComponentQuery.query(a,this.getView());if(b.length===0){returnnull}else{if(b.length===1){returnb[0]}else{returnb}}}else{returnthis.getView()}},addComponentSelector:function(a,b,c){vard,e;if(c==null){c=false}Deft.Logger.log("Adding component selector for: '"+a+"'.");e=this.getComponentSelector(a);if(e!=null){Ext.Error.raise({msg:"Error adding component selector: an existing component selector was already registered for '"+a+"'."})}d=Ext.create("Deft.mvc.ComponentSelector",{view:this.getView(),selector:a,listeners:b,scope:this,live:c});this.registeredComponentSelectors[a]=d},removeComponentSelector:function(a){varb;Deft.Logger.log("Removing component selector for '"+a+"'.");b=this.getComponentSelector(a);if(b==null){Ext.Error.raise({msg:"Error removing component selector: no component selector registered for '"+a+"'."})}b.destroy();deletethis.registeredComponentSelectors[a]},getComponentSelector:function(a){returnthis.registeredComponentSelectors[a]},createObservers:function(){vara,c,b;this.registeredObservers={};b=this.observe;for(cinb){a=b[c];this.addObserver(c,a)}},addObserver:function(c,b){vara;a=Ext.create("Deft.mvc.Observer",{host:this,target:c,events:b});returnthis.registeredObservers[c]=a},removeObservers:function(){vara,c,b;b=this.registeredObservers;for(cinb){a=b[c];a.destroy();deletethis.registeredObservers[c]}}},function(){returnDeft.Class.registerPreprocessor("observe",function(b,c,a,d){Deft.Class.hookOnClassExtended(c,function(f,h,e){varg;if(f.superclass&&((g=f.superclass)!=null?g.observe:void0)&&Deft.Class.extendsClass("Deft.mvc.ViewController",f)){h.observe=Deft.mvc.Observer.mergeObserve(f.superclass.observe,h.observe)}})},"before","extend")});Ext.define("Deft.mvc.Application",{alternateClassName:["Deft.Application"],initialized:false,constructor:function(a){if(a==null){a={}}this.initConfig(a);Ext.onReady(function(){this.init();this.initialized=true},this);returnthis},init:function(){}});Ext.define("Deft.mixin.Controllable",{requires:["Ext.Component","Deft.core.Class","Deft.log.Logger"],onClassMixedIn:function(a){Deft.Logger.deprecate("Deft.mixin.Controllable has been deprecated and can now be omitted - simply use the 'controller' class annotation on its own.")}},function(){vara;if(Ext.getVersion("extjs")&&Ext.getVersion("core").isLessThan("4.1.0")){a=function(){returnfunction(d){varb;if(d==null){d={}}if(thisinstanceofExt.ClassManager.get("Ext.Component")&&!this.$controlled){try{b=Ext.create(this.controller,d.controllerConfig||this.controllerConfig||{})}catch(c){Deft.Logger.warn("Error initializing view controller: an error occurred while creating an instance of the specified controller: '"+this.controller+"'.");throwc}if(this.getController===void0){this.getController=function(){returnb}}this.$controlled=true;this.callOverridden(arguments);b.controlView(this);returnthis}returnthis.callOverridden(arguments)}}}else{a=function(){returnfunction(d){varb;if(d==null){d={}}if(thisinstanceofExt.ClassManager.get("Ext.Component")&&!this.$controlled){try{b=Ext.create(this.controller,d.controllerConfig||this.controllerConfig||{})}catch(c){Deft.Logger.warn("Error initializing view controller: an error occurred while creating an instance of the specified controller: '"+this.controller+"'.");throwc}if(this.getController===void0){this.getController=function(){returnb}}this.$controlled=true;this.callParent(arguments);b.controlView(this);returnthis}returnthis.callParent(arguments)}}}Deft.Class.registerPreprocessor("controller",function(d,e,b,f){varc;Deft.Class.hookOnClassCreated(b,function(g){g.override({constructor:a()})});Deft.Class.hookOnClassExtended(e,function(h,i,g){Deft.Class.hookOnClassCreated(g,function(j){j.override({constructor:a()})})});c=this;Ext.require([e.controller],function(){if(f!=null){f.call(c,d,e,b)}});returnfalse},"before","extend")});Ext.define("Deft.promise.Promise",{alternateClassName:["Deft.Promise"],statics:{when:function(a){varb;if(ainstanceofExt.ClassManager.get("Deft.promise.Promise")||ainstanceofExt.ClassManager.get("Deft.promise.Deferred")){returna.then()}else{if(Ext.isObject(a)&&Ext.isFunction(a.then)){b=Ext.create("Deft.promise.Deferred");a.then(function(c){b.resolve(c)},function(c){b.reject(c)});returnb.then()}else{b=Ext.create("Deft.promise.Deferred");b.resolve(a);returnb.then()}}},all:function(a){returnthis.when(a).then({success:function(n){varq,l,i,h,r,d,e,j,p,k,m,c,f,s,g,b,o;r=Ext.create("Deft.promise.Deferred");s=n.length;c=newArray(n);m=0;g=function(t){r.update(t);returnt};f=function(t,u){c[t]=u;m++;if(m===s){i();r.resolve(c)}returnu};k=function(t){i();r.reject(t);returnt};l=function(t){i();r.cancel(t);returnt};i=function(){returng=f=k=l=Ext.emptyFn};h=function(t){returnfunction(u){returnf(t,u)}};d=function(t){returnk(t)};j=function(t){returng(t)};q=function(t){returnl(t)};for(e=b=0,o=n.length;b<o;e=++b){p=n[e];if(einn){this.when(p).then({success:h(e),failure:d,progress:j,cancel:q})}}returnr.getPromise()},scope:this})},any:function(a){returnthis.some(a,1).then({success:function(b){returnb[0]}})},some:function(b,a){returnthis.when(b).then({success:function(p){vart,n,k,u,o,f,g,l,s,m,i,q,h,e,j,d,c,r;d=[];q=a;i=(p.length-q)+1;u=Ext.create("Deft.promise.Deferred");if(p.length<a){u.reject(newError("Too few Promises or values were supplied to obtain the requested number of resolved values."))}else{o=a===1?"No Promises were resolved.":"Too few Promises were resolved.";j=function(v){u.update(v);returnv};h=function(v){d.push(v);q--;if(q===0){k();u.resolve(d)}returnv};m=function(v){i--;if(i===0){k();u.reject(newError(o))}returnv};n=function(v){i--;if(i===0){k();u.reject(newError(o))}returnv};k=function(){returnj=h=m=n=Ext.emptyFn};e=function(v){returnh(v)};f=function(v){returnm(v)};l=function(v){returnj(v)};t=function(v){returnn(v)};for(g=c=0,r=p.length;c<r;g=++c){s=p[g];if(ginp){this.when(s).then({success:e,failure:f,progress:l,cancel:t})}}}returnu.getPromise()},scope:this})},memoize:function(d,c,a){varb;b=Deft.util.Function.memoize(d,c,a);returnExt.bind(function(){returnthis.all(Ext.Array.toArray(arguments)).then(function(e){returnb.apply(c,e)})},this)},map:function(c,b){vara;a=function(d){returnfunction(e){returnb(e,d,c)}};returnthis.when(c).then({success:function(i){varf,d,g,h,e;g=newArray(i.length);for(f=h=0,e=i.length;h<e;f=++h){d=i[f];if(fini){g[f]=this.when(d).then(a(f))}}returnthis.reduce(g,this.reduceIntoArray,g)},scope:this})},reduce:function(c,b,a){vard;d=arguments.length===3;returnthis.when(c).then({success:function(f){vare,g;g=this.when;e=[function(i,j,h){returng(i).then(function(k){returng(j).then(function(l){returnb(k,l,h,f)})})}];if(d){e.push(a)}returnthis.when(this.reduceArray.apply(f,e))},scope:this})},reduceArray:function(b,a){vare,g,d,f,c;d=0;g=Object(this);f=g.length>>>0;e=arguments;if(e.length<=1){while(true){if(ding){c=g[d++];break}if(++d>=f){thrownewTypeError()}}}else{c=e[1]}while(d<f){if(ding){c=b(c,g[d],d,g)}d++}returnc},reduceIntoArray:function(b,c,a){b[a]=c;returnb}},id:null,constructor:function(a){this.id=a.id;this.deferred=a.deferred;returnthis},then:function(a){returnthis.deferred.then.apply(this.deferred,arguments)},otherwise:function(b,a){returnthis.deferred.otherwise.apply(this.deferred,arguments)},always:function(b,a){returnthis.deferred.always.apply(this.deferred,arguments)},cancel:function(a){returnthis.deferred.cancel(a)},getState:function(){returnthis.deferred.getState()},toString:function(){if(this.id!=null){return"Promise "+this.id}return"Promise"}},function(){if(Array.prototype.reduce!=null){this.reduceArray=Array.prototype.reduce}});Ext.define("Deft.promise.Deferred",{alternateClassName:["Deft.Deferred"],requires:["Deft.log.Logger","Deft.promise.Promise"],statics:{enableLogging:true,logMessage:function(a){if(Deft.promise.Deferred.enableLogging){returnDeft.Logger.log(a)}}},id:null,constructor:function(a){if(a==null){a={}}this.id=a.id;this.state="pending";this.progress=void0;this.value=void0;this.progressCallbacks=[];this.successCallbacks=[];this.failureCallbacks=[];this.cancelCallbacks=[];this.promise=Ext.create("Deft.Promise",{id:this.id?"of "+this.id:null,deferred:this});returnthis},then:function(f){varh,i,k,a,c,j,b,d,g,e;if(Ext.isObject(f)){b=f.success,a=f.failure,c=f.progress,i=f.cancel,j=f.scope}else{b=arguments[0],a=arguments[1],c=arguments[2],i=arguments[3],j=arguments[4]}e=[b,a,c,i];for(d=0,g=e.length;d<g;d++){h=e[d];if(!(Ext.isFunction(h)||h===null||h===void0)){Ext.Error.raise({msg:"Error while registering callback with "+this+": a non-function specified."})}}k=Ext.create("Deft.promise.Deferred",{id:"transformed result of "+this});this.register(this.wrapCallback(k,b,j,"success","resolve"),this.successCallbacks,"resolved",this.value);this.register(this.wrapCallback(k,a,j,"failure","reject"),this.failureCallbacks,"rejected",this.value);this.register(this.wrapCallback(k,i,j,"cancel","cancel"),this.cancelCallbacks,"cancelled",this.value);this.register(this.wrapProgressCallback(k,c,j),this.progressCallbacks,"pending",this.progress);Deft.promise.Deferred.logMessage("Returning "+(k.getPromise())+".");returnk.getPromise()},otherwise:function(c,a){varb;if(Ext.isObject(c)){b=c,c=b.fn,a=b.scope}returnthis.then({failure:c,scope:a})},always:function(c,a){varb;if(Ext.isObject(c)){b=c,c=b.fn,a=b.scope}returnthis.then({success:c,failure:c,cancel:c,scope:a})},update:function(a){Deft.promise.Deferred.logMessage(""+this+" updated with progress: "+a);if(this.state==="pending"){this.progress=a;this.notify(this.progressCallbacks,a)}else{if(this.state!=="cancelled"){Ext.Error.raise({msg:"Error: this "+this+" has already been completed and cannot be modified."})}}},resolve:function(a){Deft.promise.Deferred.logMessage(""+this+" resolved with value: "+a);this.complete("resolved",a,this.successCallbacks)},reject:function(a){Deft.promise.Deferred.logMessage(""+this+" rejected with error: "+a);this.complete("rejected",a,this.failureCallbacks)},cancel:function(a){Deft.promise.Deferred.logMessage(""+this+" cancelled with reason: "+a);this.complete("cancelled",a,this.cancelCallbacks)},getPromise:function(){returnthis.promise},getState:function(){returnthis.state},toString:function(){if(this.id!=null){return"Deferred "+this.id}return"Deferred"},wrapCallback:function(b,f,c,e,d){vara;a=this;if(f!=null){Deft.promise.Deferred.logMessage("Registering "+e+" callback for "+a+".")}returnfunction(i){varg;if(Ext.isFunction(f)){try{Deft.promise.Deferred.logMessage("Calling "+e+" callback registered for "+a+".");g=f.call(c,i);if(ginstanceofExt.ClassManager.get("Deft.promise.Promise")||ginstanceofExt.ClassManager.get("Deft.promise.Deferred")){Deft.promise.Deferred.logMessage(""+(b.getPromise())+" will be completed based on the "+g+" returned by the "+e+" callback.");g.then(Ext.bind(b.resolve,b),Ext.bind(b.reject,b),Ext.bind(b.update,b),Ext.bind(b.cancel,b))}else{Deft.promise.Deferred.logMessage(""+(b.getPromise())+" resolved with the value returned by the "+e+" callback: "+g+".");b.resolve(g)}}catch(h){if(Ext.Array.contains(["RangeError","ReferenceError","SyntaxError","TypeError"],h.name)){Deft.Logger.error("Error: "+e+" callback for "+a+" threw: "+(h.stack!=null?h.stack:h))}else{Deft.promise.Deferred.logMessage(""+(b.getPromise())+" rejected with the Error returned by the "+e+" callback: "+h)}b.reject(h)}}else{Deft.promise.Deferred.logMessage(""+(b.getPromise())+" resolved with the value: "+i+".");b[d](i)}}},wrapProgressCallback:function(b,d,c){vara;a=this;if(d!=null){Deft.promise.Deferred.logMessage("Registering progress callback for "+a+".")}returnfunction(g){vare;if(Ext.isFunction(d)){try{Deft.promise.Deferred.logMessage("Calling progress callback registered for "+a+".");e=d.call(c,g);Deft.promise.Deferred.logMessage(""+(b.getPromise())+" updated with progress returned by the progress callback: "+e+".");b.update(e)}catch(f){Deft.Logger.error("Error: progress callback registered for "+a+" threw: "+(f.stack!=null?f.stack:f))}}else{Deft.promise.Deferred.logMessage(""+(b.getPromise())+" updated with progress: "+g);b.update(g)}}},register:function(d,a,c,b){if(Ext.isFunction(d)){if(this.state==="pending"){a.push(d);if(this.state===c&&b!==void0){this.notify([d],b)}}else{if(this.state===c){this.notify([d],b)}}}},complete:function(c,b,a){if(this.state==="pending"){this.state=c;this.value=b;this.notify(a,b);this.releaseCallbacks()}else{if(this.state!=="cancelled"){Ext.Error.raise({msg:"Error: this "+this+" has already been completed and cannot be modified."})}}},notify:function(b,d){vare,c,a;for(c=0,a=b.length;c<a;c++){e=b[c];e(d)}},releaseCallbacks:function(){this.progressCallbacks=null;this.successCallbacks=null;this.failureCallbacks=null;this.cancelCallbacks=null}});Ext.define("Deft.promise.Chain",{alternateClassName:["Deft.Chain"],requires:["Deft.promise.Promise"],statics:{sequence:function(a,b){returnDeft.Promise.reduce(a,function(c,d){returnDeft.Promise.when(d.call(b)).then(function(e){c.push(e);returnc})},[])},parallel:function(a,b){returnDeft.Promise.map(a,function(c){returnc.call(b)})},pipeline:function(b,c,a){returnDeft.Promise.reduce(b,function(e,d){returnd.call(c,e)},a)}}});
<p><ahref="http://www.sencha.com"muse_scanned="true">ExtJS</a> with <ahref="http://www.deftjs.com"muse_scanned="true">DeftJS</a> version created by <ahref="http://www.briankotek.com"muse_scanned="true">Brian Kotek</a></p>
<p>Part of <ahref="http://todomvc.com"muse_scanned="true">TodoMVC</a></p>