diff --git a/controller/CrudController.js b/controller/CrudController.js index d6e2b89..944923f 100644 --- a/controller/CrudController.js +++ b/controller/CrudController.js @@ -166,17 +166,22 @@ ExtMVC.controller.CrudController = Ext.extend(ExtMVC.controller.Controller, { * Renders the custom Edit view if present, otherwise falls back to the default scaffold Edit form * @param {Mixed} instance The model instance to edit. If not given an ExtMVC.model.Base * instance, a findById() will be called on this controller's associated model + * @param {Object} viewConfig Optional config object to pass to the view class constructor */ - edit: function(instance) { + edit: function(instance, viewConfig) { + viewConfig = viewConfig || {}; + if (instance instanceof Ext.data.Record) { - var editView = this.render('Edit', { + Ext.applyIf(viewConfig, { model : this.model, controller : this, listeners : this.getEditViewListeners(), viewsPackage: this.viewsPackage, - id : String.format("{0}_edit_{1}", this.name, instance.get(instance.primaryKey)) + id : String.format("{0}_edit_{1}", this.name, instance.get(instance.primaryKey)) }); + var editView = this.render('Edit', viewConfig); + editView.loadRecord(instance); this.onEdit(editView, instance); diff --git a/ext-mvc-all-min.js b/ext-mvc-all-min.js index df6ec52..4d85fa2 100644 --- a/ext-mvc-all-min.js +++ b/ext-mvc-all-min.js @@ -1 +1 @@ -ExtMVC=Ext.extend(Ext.util.Observable,{version:"0.6b1",constructor:function(){ExtMVC.superclass.constructor.apply(this,arguments);this.addEvents("environment-changed");this.getEnvSettings=this.getCurrentEnvironmentSettings},setup:function(A){this.app=new ExtMVC.App(A);this.name=this.app.name},controllers:{},registerController:function(A,B){this.controllers[A]=B},getController:function(A){var B=this.controllers[A];if(B){if(typeof B==="function"){this.controllers[A]=new this.controllers[A]()}return this.controllers[A]}else{return null}},currentEnvironment:"production",environments:{production:{}},setCurrentEnvironment:function(A){if(this.getEnvironmentSettings(A)){this.currentEnvironment=A;this.fireEvent("environment-changed",A,this.getEnvironmentSettings(A))}},getCurrentEnvironment:function(){return ExtMVC.currentEnvironment},getCurrentEnvironmentSettings:function(){return this.getEnvironmentSettings(this.getCurrentEnvironment())},addEnvironmentSettings:function(B,A){ExtMVC.environments[B]=ExtMVC.environments[B]||{};Ext.apply(ExtMVC.environments[B],A)},getEnvironmentSettings:function(A){A=A||ExtMVC.environment;return ExtMVC.environments[A]}});ExtMVC=new ExtMVC();Ext.ns("ExtMVC.router","ExtMVC.plugin","ExtMVC.controller","ExtMVC.view","ExtMVC.view.scaffold","ExtMVC.lib");ExtMVC.App=Ext.extend(Ext.util.Observable,{constructor:function(A){ExtMVC.App.superclass.constructor.apply(this,arguments);Ext.apply(this,A||{});window[this.name]=this;this.initializeNamespaces();Ext.onReady(function(){if(this.fireEvent("before-launch",this)){this.initializeRouter();this.initializeEvents();if(this.usesHistory===true){this.initializeHistory()}this.launch();this.fireEvent("launched",this);if(this.usesHistory){if(this.dispatchHistoryOnLoad===true){Ext.History.init(function(C){var B=document.location.hash.replace("#","");var D=this.router.recognise(B);if(D){this.dispatch(D)}},this)}else{Ext.History.init()}}}},this)},name:"MyApp",usesHistory:false,dispatchHistoryOnLoad:true,launch:Ext.emptyFn,params:{},dispatch:function(B,C,A){var B=B||{};Ext.applyIf(B,{action:"index"});this.params=B;var E=ExtMVC.getController(B.controller);if(E!=undefined){var D=E[B.action];if(typeof D=="function"){D.apply(C||E,A||[])}else{throw new Error(String.format("Action '{0}' not found on Controller '{1}'",B.action,B.controller))}}},initializeRouter:function(){if(this.router==undefined){this.router=new ExtMVC.router.Router();ExtMVC.router.Router.defineRoutes(this.router)}},initializeNamespaces:function(A){var A=A||this.name;if(A){Ext.ns(A,A+".controllers",A+".models",A+".views")}},initializeHistory:function(){this.historyForm=Ext.getBody().createChild({tag:"form",action:"#",cls:"x-hidden",id:"history-form",children:[{tag:"div",children:[{tag:"input",id:"x-history-field",type:"hidden"},{tag:"iframe",id:"x-history-frame"}]}]});Ext.History.on("change",this.onHistoryChange,this)},onHistoryChange:function(B){var A=this.router.recognise(B);if(A){this.dispatch(A,null,[{url:B}])}},initializeEvents:function(){this.addEvents("before-launch","launched")}});ExtMVC.Inflector={Inflections:{plural:[[(/(quiz)$/i),"$1zes"],[(/^(ox)$/i),"$1en"],[(/([m|l])ouse$/i),"$1ice"],[(/(matr|vert|ind)ix|ex$/i),"$1ices"],[(/(x|ch|ss|sh)$/i),"$1es"],[(/([^aeiouy]|qu)y$/i),"$1ies"],[(/(hive)$/i),"$1s"],[(/(?:([^f])fe|([lr])f)$/i),"$1$2ves"],[(/sis$/i),"ses"],[(/([ti])um$/i),"$1a"],[(/(buffal|tomat)o$/i),"$1oes"],[(/(bu)s$/i),"$1ses"],[(/(alias|status)$/i),"$1es"],[(/(octop|vir)us$/i),"$1i"],[(/(ax|test)is$/i),"$1es"],[(/s$/i),"s"],[(/$/),"s"]],singular:[[(/(quiz)zes$/i),"$1"],[(/(matr)ices$/i),"$1ix"],[(/(vert|ind)ices$/i),"$1ex"],[(/^(ox)en/i),"$1"],[(/(alias|status)es$/i),"$1"],[(/(octop|vir)i$/i),"$1us"],[(/(cris|ax|test)es$/i),"$1is"],[(/(shoe)s$/i),"$1"],[(/(o)es$/i),"$1"],[(/(bus)es$/i),"$1"],[(/([m|l])ice$/i),"$1ouse"],[(/(x|ch|ss|sh)es$/i),"$1"],[(/(m)ovies$/i),"$1ovie"],[(/(s)eries$/i),"$1eries"],[(/([^aeiouy]|qu)ies$/i),"$1y"],[(/([lr])ves$/i),"$1f"],[(/(tive)s$/i),"$1"],[(/(hive)s$/i),"$1"],[(/([^f])ves$/i),"$1fe"],[(/(^analy)ses$/i),"$1sis"],[(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i),"$1$2sis"],[(/([ti])a$/i),"$1um"],[(/(n)ews$/i),"$1ews"],[(/s$/i),""]],irregular:[["move","moves"],["sex","sexes"],["child","children"],["man","men"],["person","people"]],uncountable:["sheep","fish","series","species","money","rice","information","equipment"]},ordinalize:function(A){if(11<=parseInt(A,10)%100&&parseInt(A,10)%100<=13){return A+"th"}else{switch(parseInt(A,10)%10){case 1:return A+"st";case 2:return A+"nd";case 3:return A+"rd";default:return A+"th"}}},pluralize:function(C){var E=ExtMVC.Inflector.uncountableOrIrregular(C);if(E){return E}for(var A=0;A/g,">").replace(/=0;C--){E[D[C].replace(":","")]=A[C]}for(option in this.options){E[option]=this.options[option]}return E},urlForNamed:function(A){var A=A||{};return this.urlFor(Ext.applyIf(A,this.options))},urlFor:function(C){var B=this.mappingString;for(var F in C){if(C[F]&&this.options[F]&&C[F]!=this.options[F]){return false}}var E=[];for(var F in C){E.push(":"+F)}E=E.sort();var A=this.paramsInStringWithOptions.sort();if(A.length!=E.length){return false}for(var D=0;D=0;B--){var C=this.conditions[E[B]];var D=String.format("({0})",C||"[a-zA-Z0-9_,]+");A=A.replace(new RegExp(E[B]),D)}return new RegExp("^"+A+"$")}};ExtMVC.lib.Dependencies=Ext.extend(Ext.util.Observable,{constructor:function(){this.dependencies={};ExtMVC.lib.Dependencies.superclass.constructor.apply(this,arguments)},get:function(A){return this.dependencies[A]||[]},add:function(D,C,B){var A=this.dependencies[D]||[];A.push({name:C,config:B});this.dependencies[D]=A}});Ext.extend=function(){var B=function(D){for(var C in D){this[C]=D[C]}};var A=Object.prototype.constructor;return function(J,G,I){if(Ext.isObject(G)){I=G;G=J;J=I.constructor!=A?I.constructor:function(){G.apply(this,arguments)}}var E=function(){},H,D=G.prototype;E.prototype=D;H=J.prototype=new E();H.constructor=J;J.superclass=D;if(D.constructor==A){D.constructor=G}J.override=function(F){Ext.override(J,F)};H.superclass=H.supr=(function(){return D});H.override=B;Ext.override(J,I);J.extend=function(F){Ext.extend(J,F)};var C=J.prototype.onExtended;if(C){C.call(J.prototype)}return J}}();ExtMVC.controller.Controller=Ext.extend(Ext.util.Observable,{name:null,onExtended:function(){if(this.name!=null){this.viewsPackage=Ext.ns(String.format("{0}.views.{1}",ExtMVC.name,this.name));ExtMVC.registerController(this.name,this.constructor)}},constructor:function(A){ExtMVC.controller.Controller.superclass.constructor.apply(this,arguments);Ext.apply(this,A||{});this.initEvents();this.initListeners()},initEvents:function(){},initListeners:function(){},showNotice:function(A){},getViewClass:function(A){return this.viewsPackage[A]},addTo:null,render:function(D,B){var C=this.getViewClass(D);if(typeof C=="function"){var A=new C(B);if(this.addTo){this.renderViaAddTo(A)}return A}else{throw new Error(String.format("View '{0}' not found",D))}},renderViaAddTo:function(A){if(this.addTo!=undefined){this.addTo.removeAll();this.addTo.doLayout();this.addTo.add(A);this.addTo.doLayout()}}});Ext.reg("controller",ExtMVC.controller.Controller);ExtMVC.controller.CrudController=Ext.extend(ExtMVC.controller.Controller,{model:null,create:function(C,B){var A=new this.model(C);A.save({scope:this,success:this.onCreateSuccess,failure:this.onCreateFailure})},read:function(A){this.model.findById(A,{scope:this,success:function(B){this.fireEvent("read",B)},failure:function(){this.fireEvent("read-failed",A)}})},update:function(A,C){for(var B in C){A.set(B,C[B])}A.save({scope:this,success:function(D){this.onUpdateSuccess(D,C)},failure:function(){this.onUpdateFailure(A,C)}})},destroy:function(A){if(A.destroy==undefined){var B={};B[this.model.prototype.primaryKey]=parseInt(A,10);var A=new (this.model)(B)}A.destroy({scope:this,success:this.onDestroySuccess,failure:this.onDestroyFailure})},index:function(){var A=this.render("Index",{model:this.model,controller:this,listeners:this.getIndexViewListeners(),viewsPackage:this.viewsPackage});this.fireEvent("index");return A},build:function(){return this.render("New",{model:this.model,controller:this,listeners:this.getBuildViewListeners(),viewsPackage:this.viewsPackage})},edit:function(A){if(A instanceof Ext.data.Record){var C=this.render("Edit",{model:this.model,controller:this,listeners:this.getEditViewListeners(),viewsPackage:this.viewsPackage,id:String.format("{0}_edit_{1}",this.name,A.get(A.primaryKey))});C.loadRecord(A);this.onEdit(C,A);this.fireEvent("edit",A);return C}else{var B=A;this.model.find(parseInt(B,10),{scope:this,success:function(D){this.edit(D)}})}},getIndexViewListeners:function(){return{scope:this,"delete":this.destroy,"new":this.build,edit:this.edit}},getEditViewListeners:function(){return{scope:this,cancel:this.index,save:this.update}},getBuildViewListeners:function(){return{scope:this,cancel:this.index,save:this.create}},showCreatedNotice:function(){this.showNotice(String.format("{0} successfully created",this.model.prototype.singularHumanName))},showUpdatedNotice:function(){this.showNotice(String.format("{0} successfully updated",this.model.prototype.singularHumanName))},showDestroyedNotice:function(){this.showNotice(String.format("{0} successfully deleted",this.model.prototype.singularHumanName))},onCreateSuccess:function(A){if(this.fireEvent("create",A)!==false){this.showCreatedNotice();this.index()}},onCreateFailure:function(A){this.fireEvent("create-failed",A)},onUpdateSuccess:function(A,B){if(this.fireEvent("update",A,B)!==false){this.showUpdatedNotice();this.index()}},onUpdateFailure:function(A,B){this.fireEvent("update-failed",A,B)},onDestroySuccess:function(A){this.fireEvent("delete",A);this.showDestroyedNotice()},onDestroyFailure:function(A){this.fireEvent("delete-failed",A)},onEdit:function(A){},initEvents:function(){this.addEvents("create","create-failed","read","read-failed","update","update-failed","delete","delete-failed","index","edit")},getViewClass:function(B){var A=ExtMVC.controller.CrudController.superclass.getViewClass.call(this,B);return(A==undefined)?this.scaffoldViewName(B):A},scaffoldViewName:function(A){return ExtMVC.view.scaffold[A.titleize()]}});ExtMVC.model={pendingCreation:{},getModelsPendingDefinitionOf:function(A){return this.pendingCreation[A]||[]},setModelPendingDefinitionOf:function(B,D,C){var A=this.pendingCreation[B]||[];A.push({name:D,config:C});this.pendingCreation[B]=A},strictMode:false,modelNamespace:window,define:function(A,B){var D=true,B=B||{};if(typeof B.extend!="undefined"){var C=this.modelNamespace[B.extend];if(typeof C=="undefined"){D=false;this.setModelPendingDefinitionOf(B.extend,A,B)}}if(D){this.create.apply(this,arguments)}},create:function(B,E){E=E||{};if(this.isAlreadyDefined(B)){if(this.strictMode){throw new Error(B+" is already defined")}return false}var F=this.modelNamespace[E.extend];var A=this.buildFields(E.fields,F);delete E.fields;var D=this.modelNamespace[B]=Ext.data.Record.create(A);var G=E.classMethods||{};delete E.classMethods;Ext.apply(D.prototype,E);if(typeof F!="undefined"){Ext.applyIf(G,F);Ext.applyIf(D.prototype,F.prototype)}D.prototype.modelName=B;this.setupNames(D);for(var C in G){if(C!="prototype"){D[C]=G[C]}}this.initializePlugins(D);this.afterCreate(B)},afterCreate:function(A){var B=this.getModelsPendingDefinitionOf(A);if(B){Ext.each(B,function(C){this.create(C.name,C.config)},this)}},isAlreadyDefined:function(A){if(typeof this.modelNamespace[A]!="undefined"){return true}var C=false;for(superclass in this.pendingCreation){var B=this.pendingCreation[superclass];Ext.each(B,function(D){if(D.name==A){C=true}},this)}return C},buildFields:function(B,C){B=B||[];var A=new Ext.util.MixedCollection(false,function(D){return D.name});A.addAll(B);if(typeof C!="undefined"){C.prototype.fields.each(function(D){if(typeof A.get(D.name)=="undefined"){A.add(D)}})}return A.items},setupNames:function(A){var C=A.prototype,B=ExtMVC.Inflector;Ext.applyIf(A.prototype,{tableName:B.pluralize(C.modelName.underscore()),foreignKeyName:B.singularize(C.modelName.underscore())+"_id",singularHumanName:C.modelName.humanize().titleize(),pluralHumanName:B.pluralize(C.modelName.humanize().titleize())})},plugins:[],addPlugin:function(A){this.plugins.push(A)},initializePlugins:function(A){Ext.each(this.plugins,function(B){B.initialize(A)},this)}};Ext.ns("ExtMVC.model.plugin");ExtMVC.model.Base=function(){};ExtMVC.model.Base.prototype={primaryKey:"id",newRecord:function(){var A=this.get(this.primaryKey);return typeof A=="undefined"||A==""},MVCModelId:function(){return String.format("{0}-{1}",this.tableName,this.get(this.primaryKey))},getReader:function(){if(!this.reader){this.reader=new Ext.data.JsonReader({totalProperty:"results",root:this.tableName},this.constructor)}return this.reader},initialize:Ext.emptyFn};Ext.apply(Ext.data.Record.prototype,new ExtMVC.model.Base());ExtMVC.model.plugin.adapter={initialize:function(B){var A=new this.RESTJSONAdapter();Ext.override(Ext.data.Record,A.instanceMethods());Ext.apply(B,A.classMethods());try{Ext.override(ExtMVC.model.plugin.association.HasMany,A.hasManyAssociationMethods());Ext.override(ExtMVC.model.plugin.association.BelongsTo,A.belongsToAssociationMethods())}catch(C){}}};ExtMVC.model.addPlugin(ExtMVC.model.plugin.adapter);ExtMVC.model.plugin.adapter.Abstract=function(A){};ExtMVC.model.plugin.adapter.Abstract.prototype={doSave:Ext.emptyFn,doFind:Ext.emptyFn,doDestroy:Ext.emptyFn,instanceMethods:function(){return{adapter:this,save:function(A){A=A||{};if(A.skipValidation===true||this.isValid()){return this.adapter.doSave(this,A)}else{if(typeof A.failure=="function"){return A.failure.call(A.scope||this,this)}}},destroy:function(A){return this.adapter.doDestroy(this,A)},update:function(B,A){this.setValues(B);this.save(A)},loaded:function(){}}},classMethods:function(){return{adapter:this,create:function(C,B){var A=new this(C);A.save(B);return A},build:function(A){return new this(A)},find:function(B,A){if(typeof(B)=="number"){B={primaryKey:B}}return this.adapter.doFind(B,A,this)},destroy:function(B,A){return this.adapter.doDestroy(B,A,this)}}},hasManyAssociationMethods:function(){return{adapter:this,create:function(C,B){var A=new this.associatedClass(C)},build:function(B,A){},find:function(A){},loaded:function(){},destroy:function(A){}}},belongsToAssociationMethods:function(){return{adapter:this,find:function(A){},loaded:function(){},destroy:function(A){}}}};ExtMVC.model.plugin.adapter.RESTAdapter=Ext.extend(ExtMVC.model.plugin.adapter.Abstract,{createMethod:"POST",readMethod:"GET",updateMethod:"PUT",destroyMethod:"DELETE",proxyType:Ext.data.HttpProxy,doSave:function(B,D){if(typeof B=="undefined"){throw new Error("No instance provided to REST Adapter save")}D=D||{};var A=D.success||Ext.emptyFn,C=D.failure||Ext.emptyFn;delete D.success;delete D.failure;Ext.Ajax.request(Ext.apply({url:this.instanceUrl(B),method:B.newRecord()?this.createMethod:this.updateMethod,params:this.buildPostData(B),success:function(E,F,G){G=G||this;return function(H,I){var L=E.modelName.underscore(),K=Ext.decode(H.responseText)[L];for(var J in K){E.set(J,K[J])}F.call(this,E)}}(B,A,D.scope)},D))},afterSave:function(){},doFind:function(E,B,C){E=E||{};B=B||{};var F=(E.primaryKey!==undefined),A=B.url||this.findUrl(E,C);Ext.applyIf(B,{conditions:E,scope:this});var D=F?this.doSingleFind:this.doCollectionFind;return D.call(this,A,B,C)},doDestroy:function(B,C,D){var C=C||{};if(typeof B=="undefined"){throw new Error("No instance provided to REST Adapter destroy")}if(!(B instanceof Ext.data.Record)){var E=parseInt(B,10);B=new D();B.set(D.prototype.primaryKey,E)}var A=C.success||Ext.emptyFn;delete C.success;return Ext.Ajax.request(Ext.applyIf(C,{method:this.destroyMethod,url:this.instanceUrl(B),success:function(){A.call(C.scope||this,B)}}))},doSingleFind:function(D,C,F){var H=C.callback,A=C.success,G=C.failure;delete C.callback;delete C.success;delete C.failure;var E=this.decodeSingleLoadResponse;var B=function(J,I){if(typeof J=="function"){J.apply(C.scope,I)}};Ext.Ajax.request(Ext.apply(C,{callback:function(K,L,J){if(L===true){var I=new F(E(J.responseText,F));B(A,[I,K,J])}else{B(G,arguments)}B(H,arguments)}},this.buildProxyConfig(D)))},storeConfig:{autoLoad:true,remoteSort:false},doCollectionFind:function(B,A,C){Ext.applyIf(A,this.storeConfig);if(A.conditions!=undefined){Ext.applyIf(A,{baseParams:A.conditions})}return new Ext.data.Store(Ext.applyIf(A,{reader:C.prototype.getReader(),proxy:new this.proxyType(this.buildProxyConfig(B))}))},instanceUrl:function(A){if(A.newRecord()){return String.format("/{0}",A.tableName)}else{return String.format("/{0}/{1}",A.tableName,A.get(A.primaryKey))}},collectionUrl:function(A){return String.format("/{0}",A.prototype.tableName)},buildProxyConfig:function(A){return{url:A,method:this.readMethod}},buildPostData:function(A){var C={},B=A.modelName.underscore();for(key in A.data){C[B+"["+key+"]"]=A.data[key]}return C},decodeSingleLoadResponse:function(C,A){var B=A.prototype.tableName;return Ext.decode(C)[B]},findUrl:function(C,B){if(typeof(C)=="object"&&C.primaryKey){var A=new B({});A.set(A.primaryKey,C.primaryKey);delete C.primaryKey;return this.instanceUrl(A)}else{return this.collectionUrl(B)}}});ExtMVC.model.plugin.adapter.RESTJSONAdapter=Ext.extend(ExtMVC.model.plugin.adapter.RESTAdapter,{doSave:function(A,B){if(typeof A=="undefined"){throw new Error("No instance provided to REST Adapter save")}Ext.applyIf(B||{},{jsonData:A.data,params:{},headers:{"Content-Type":"application/json"}});ExtMVC.model.plugin.adapter.RESTJSONAdapter.superclass.doSave.apply(this,arguments)},doDestroy:function(A,B,C){B=B||{};Ext.applyIf(B,{headers:{"Content-type":"application/json"}});ExtMVC.model.plugin.adapter.RESTJSONAdapter.superclass.doDestroy.call(this,A,B,C)},decodeSingleLoadResponse:function(C,A){var B=ExtMVC.Inflector.singularize(A.prototype.tableName);return Ext.decode(C)[B]},buildProxyConfig:function(A){var B=ExtMVC.model.plugin.adapter.RESTJSONAdapter.superclass.buildProxyConfig.apply(this,arguments);return Ext.apply(B,{headers:{"Content-Type":"application/json"}})}});Ext.ns("ExtMVC.model.plugin.validation");ExtMVC.model.plugin.validation.AbstractValidation=function(A,C,B){this.ownerClass=A;this.field=C;Ext.apply(this,B)};ExtMVC.model.plugin.validation.AbstractValidation.prototype={getValue:function(A){return A.get(this.field)},isValid:function(A){return true}};ExtMVC.model.plugin.validation.ValidatesPresenceOf=Ext.extend(ExtMVC.model.plugin.validation.AbstractValidation,{message:"must be present",isValid:function(A){var C=this.getValue(A),B=false;switch(typeof C){case"object":if(C!=null){B=true}break;case"string":if(C.length!=0){B=true}break}return B}});ExtMVC.model.plugin.validation.ValidatesLengthOf=Ext.extend(ExtMVC.model.plugin.validation.AbstractValidation,{tooShortMessage:"is too short",tooLongMessage:"is too long",message:"",isValid:function(A){var B=this.getValue(A);if(typeof B=="undefined"){return true}if(this.minimum&&B.lengththis.maximum){this.message=this.tooLongMessage;return false}return true}});ExtMVC.model.plugin.validation.ValidatesNumericalityOf=Ext.extend(ExtMVC.model.plugin.validation.AbstractValidation,{message:"must be a number",isValid:function(A){return"number"==typeof this.getValue(A)}});ExtMVC.model.plugin.validation.ValidatesInclusionOf=Ext.extend(ExtMVC.model.plugin.validation.AbstractValidation,{constructor:function(A,C,B){B=B||{};Ext.applyIf(B,{allowed:[]});ExtMVC.model.plugin.validation.ValidatesInclusionOf.superclass.constructor.call(this,A,C,B);Ext.applyIf(this,{message:"must be one of "+this.allowed.toSentence("or")})},isValid:function(A){var C=this.getValue(A);for(var B=0;B-1){E.push(this.buildColumn(F.name))}},this);Ext.each(A,function(F){if(this.preferredColumns.indexOf(F.name)==-1&&this.ignoreColumns.indexOf(F.name)==-1){E.push(this.buildColumn(F.name))}if(this.wideColumns.indexOf(F.name)){wideColumns.push(F.name)}},this);for(var D=E.length-1;D>=0;D--){var B=E[D];if(this.narrowColumns.indexOf(B.id)>-1){Ext.applyIf(B,{width:this.narrowColumnWidth})}else{if(this.wideColumns.indexOf(B.id)>-1){Ext.applyIf(B,{width:this.wideColumnWidth})}else{Ext.applyIf(B,{width:this.normalColumnWidth})}}}}return E},getFields:function(){if(this.useColumns===undefined){return this.model.prototype.fields.items}else{var A=[];Ext.each(this.useColumns,function(B){A.push({name:B})},this);return A}},buildColumn:function(A){var A=A||{};if(typeof(A)=="string"){A={name:A}}return Ext.applyIf(A,{id:A.name,header:A.name.replace(/_/g," ").titleize(),sortable:true,dataIndex:A.name})},hasAddButton:true,hasEditButton:true,hasDeleteButton:true,buildAddButton:function(A){return new Ext.Button(Ext.applyIf(A||{},{text:"New "+this.model.prototype.singularHumanName,scope:this,iconCls:"add",handler:this.onAdd}))},buildEditButton:function(A){return new Ext.Button(Ext.applyIf(A||{},{text:"Edit selected",scope:this,iconCls:"edit",disabled:true,handler:this.onEdit}))},buildDeleteButton:function(A){return new Ext.Button(Ext.applyIf(A||{},{text:"Delete selected",disabled:true,scope:this,iconCls:"delete",handler:this.onDelete}))},buildTopToolbar:function(){var A=[];if(this.hasAddButton===true){this.addButton=this.buildAddButton();A.push(this.addButton,"-")}if(this.hasEditButton===true){this.editButton=this.buildEditButton();A.push(this.editButton,"-")}if(this.hasDeleteButton===true){this.deleteButton=this.buildDeleteButton();A.push(this.deleteButton,"-")}if(this.hasSearchField===true){this.searchField=this.buildSearchField();A.push(this.searchField,"-")}this.getSelectionModel().on("selectionchange",function(B){if(B.getCount()>0){if(this.deleteButton!=undefined){this.deleteButton.enable()}if(this.editButton!=undefined){this.editButton.enable()}}else{if(this.deleteButton!=undefined){this.deleteButton.disable()}if(this.editButton!=undefined){this.editButton.disable()}}},this);return A},pageSize:25,buildBottomToolbar:function(A){return new Ext.PagingToolbar({store:A,displayInfo:true,pageSize:this.pageSize,emptyMsg:String.format("No {0} to display",this.model.prototype.pluralHumanName)})},hasSearchField:false,searchParamName:"q",buildSearchField:function(){this.searchField=new Ext.form.TwinTriggerField({width:200,validationEvent:false,validateOnBlur:false,hideTrigger1:true,onTrigger1Click:this.clearSearchField.createDelegate(this,[]),onTrigger2Click:this.onSearch.createDelegate(this,[]),trigger1Class:"x-form-clear-trigger",trigger2Class:"x-form-search-trigger"});this.searchField.on("specialkey",function(B,A){if(A.getKey()===A.ESC){this.clearSearchField()}A.stopEvent();if(A.getKey()===A.ENTER){this.onSearch()}},this);return this.searchField},clearSearchField:function(){var A=this.searchField;A.el.dom.value="";A.triggers[0].hide();this.doSearch()},onSearch:function(){var B=this.searchField,A=B.getRawValue();if(A.length<1){this.clearSearchField()}else{B.triggers[0].show();this.doSearch(A)}},doSearch:function(A){A=A||this.searchField.getRawValue()||"";var B={start:0};this.store.baseParams=this.store.baseParams||{};this.store.baseParams[this.searchParamName]=A;this.store.reload({params:B})},onAdd:function(){this.fireEvent("new")},onEdit:function(B){var A=this.getSelectionModel().getSelected();if(A){this.fireEvent("edit",A)}},onDelete:function(){Ext.Msg.confirm("Are you sure?",String.format("Are you sure you want to delete this {0}? This cannot be undone.",this.model.prototype.modelName.titleize()),function(A){if(A=="yes"){var B=this.getSelectionModel().getSelected();if(B){this.fireEvent("delete",B)}}},this)}});Ext.reg("scaffold_index",ExtMVC.view.scaffold.Index);ExtMVC.view.scaffold.New=Ext.extend(ExtMVC.view.scaffold.ScaffoldFormPanel,{initComponent:function(){Ext.applyIf(this,{title:"New "+this.model.prototype.singularHumanName});ExtMVC.view.scaffold.New.superclass.initComponent.apply(this,arguments)}});Ext.reg("scaffold_new",ExtMVC.view.scaffold.New);ExtMVC.view.scaffold.Edit=Ext.extend(ExtMVC.view.scaffold.ScaffoldFormPanel,{initComponent:function(){Ext.applyIf(this,{title:"Edit "+this.model.prototype.singularHumanName});ExtMVC.view.scaffold.Edit.superclass.initComponent.apply(this,arguments)},loadRecord:function(A){this.instance=A;this.getForm().loadRecord(A)},onSave:function(){this.fireEvent("save",this.instance,this.getFormValues(),this)}});Ext.reg("scaffold_edit",ExtMVC.view.scaffold.Edit);ExtMVC.view.HasManyEditorGridPanel=Ext.extend(Ext.grid.EditorGridPanel,{initComponent:function(){Ext.applyIf(this,{autoScroll:true,store:this.association.findAll(),viewConfig:{forceFit:true}});if(this.hasTopToolbar){this.addTopToolbar()}ExtMVC.view.HasManyEditorGridPanel.superclass.initComponent.apply(this,arguments);this.on("afteredit",function(A){A.record.save({success:function(){A.record.commit()}})},this);this.getSelectionModel().on("selectionchange",function(A,B){if(this.deleteButton){this.deleteButton.enable()}},this)},hasTopToolbar:true,hasNewButton:true,hasDeleteButton:true,addTopToolbar:function(B){var A=[];if(this.hasNewButton){this.newButton=new Ext.Toolbar.Button({iconCls:"add",text:"Add",scope:this,handler:this.onAdd});A.push(this.newButton);A.push("-")}if(this.hasDeleteButton){this.deleteButton=new Ext.Toolbar.Button({text:"Remove selected",disabled:true,iconCls:"delete",scope:this,handler:this.onDelete});A.push(this.deleteButton)}Ext.applyIf(this,{tbar:A})},windowConfig:{},onAdd:function(A){if(!this.addWindow){this.addWindow=new Ext.Window(Ext.applyIf(this.windowConfig,{title:"New",layout:"fit",modal:true,height:300,width:400,items:[this.form],closeAction:"hide",buttons:[{text:"Save",iconCls:"save",scope:this,handler:this.onSaveNew},{text:"Cancel",iconCls:"cancel",scope:this,handler:this.onCancelNew}]}))}this.addWindow.show()},onDelete:function(B){var A=this.getSelectionModel().selection.record;if(A){A.destroy({scope:this,success:function(){this.store.reload()},failure:function(){Ext.Msg.alert("Delete failed","Something went wrong while trying to delete - please try again");this.store.reload()}})}this.deleteButton.disable()},onSaveNew:function(){this.association.create(this.form.getForm().getValues(),{scope:this,success:function(B,A){this.store.reload();this.addWindow.hide()},failure:function(B,A){this.form.getForm().clearInvalid();this.form.getForm().markInvalid(B.errors.forForm())}})},onCancelNew:function(A){this.addWindow.hide()}});Ext.reg("hasmany_editorgrid",ExtMVC.view.HasManyEditorGridPanel);ExtMVC.view.FormWindow=Ext.extend(Ext.Window,{modal:true,height:230,width:400,initComponent:function(){this.form=this.buildForm();Ext.apply(this,{items:[this.form],buttons:[{text:"Save",iconCls:"save",scope:this,handler:this.onSave},{text:"Cancel",iconCls:"cancel",scope:this,handler:this.onCancel}],layout:"fit",closeAction:"hide"});ExtMVC.view.FormWindow.superclass.initComponent.apply(this,arguments)},buildForm:function(){return new Ext.form.FormPanel({})},loadRecord:function(A){this.instance=A;this.form.form.loadRecord(A)},onSave:function(){this.fireEvent("save",this.getFormValues())},onCancel:function(){this.hide()},getFormValues:function(){var B=this.form.getForm(),A={};B.items.each(function(D){var C=(typeof D.getSubmitValue=="function")?"getSubmitValue":"getValue";A[D.getName()]=D[C]()},this);return A}});Ext.reg("formwindow",ExtMVC.view.FormWindow); \ No newline at end of file +ExtMVC=Ext.extend(Ext.util.Observable,{version:"0.6b1",constructor:function(){ExtMVC.superclass.constructor.apply(this,arguments);this.addEvents("environment-changed");this.getEnvSettings=this.getCurrentEnvironmentSettings},setup:function(A){this.app=new ExtMVC.App(A);this.name=this.app.name},controllers:{},registerController:function(A,B){this.controllers[A]=B},getController:function(A){var B=this.controllers[A];if(B){if(typeof B==="function"){this.controllers[A]=new this.controllers[A]()}return this.controllers[A]}else{return null}},currentEnvironment:"production",environments:{production:{}},setCurrentEnvironment:function(A){if(this.getEnvironmentSettings(A)){this.currentEnvironment=A;this.fireEvent("environment-changed",A,this.getEnvironmentSettings(A))}},getCurrentEnvironment:function(){return ExtMVC.currentEnvironment},getCurrentEnvironmentSettings:function(){return this.getEnvironmentSettings(this.getCurrentEnvironment())},addEnvironmentSettings:function(B,A){ExtMVC.environments[B]=ExtMVC.environments[B]||{};Ext.apply(ExtMVC.environments[B],A)},getEnvironmentSettings:function(A){A=A||ExtMVC.environment;return ExtMVC.environments[A]}});ExtMVC=new ExtMVC();Ext.ns("ExtMVC.router","ExtMVC.plugin","ExtMVC.controller","ExtMVC.view","ExtMVC.view.scaffold","ExtMVC.lib");ExtMVC.App=Ext.extend(Ext.util.Observable,{constructor:function(A){ExtMVC.App.superclass.constructor.apply(this,arguments);Ext.apply(this,A||{});window[this.name]=this;this.initializeNamespaces();Ext.onReady(function(){if(this.fireEvent("before-launch",this)){this.initializeRouter();this.initializeEvents();if(this.usesHistory===true){this.initializeHistory()}this.launch();this.fireEvent("launched",this);if(this.usesHistory){if(this.dispatchHistoryOnLoad===true){Ext.History.init(function(C){var B=document.location.hash.replace("#","");var D=this.router.recognise(B);if(D){this.dispatch(D)}},this)}else{Ext.History.init()}}}},this)},name:"MyApp",usesHistory:false,dispatchHistoryOnLoad:true,launch:Ext.emptyFn,params:{},dispatch:function(B,C,A){var B=B||{};Ext.applyIf(B,{action:"index"});this.params=B;var E=ExtMVC.getController(B.controller);if(E!=undefined){var D=E[B.action];if(typeof D=="function"){D.apply(C||E,A||[])}else{throw new Error(String.format("Action '{0}' not found on Controller '{1}'",B.action,B.controller))}}},initializeRouter:function(){if(this.router==undefined){this.router=new ExtMVC.router.Router();ExtMVC.router.Router.defineRoutes(this.router)}},initializeNamespaces:function(A){var A=A||this.name;if(A){Ext.ns(A,A+".controllers",A+".models",A+".views")}},initializeHistory:function(){this.historyForm=Ext.getBody().createChild({tag:"form",action:"#",cls:"x-hidden",id:"history-form",children:[{tag:"div",children:[{tag:"input",id:"x-history-field",type:"hidden"},{tag:"iframe",id:"x-history-frame"}]}]});Ext.History.on("change",this.onHistoryChange,this)},onHistoryChange:function(B){var A=this.router.recognise(B);if(A){this.dispatch(A,null,[{url:B}])}},initializeEvents:function(){this.addEvents("before-launch","launched")}});ExtMVC.Inflector={Inflections:{plural:[[(/(quiz)$/i),"$1zes"],[(/^(ox)$/i),"$1en"],[(/([m|l])ouse$/i),"$1ice"],[(/(matr|vert|ind)ix|ex$/i),"$1ices"],[(/(x|ch|ss|sh)$/i),"$1es"],[(/([^aeiouy]|qu)y$/i),"$1ies"],[(/(hive)$/i),"$1s"],[(/(?:([^f])fe|([lr])f)$/i),"$1$2ves"],[(/sis$/i),"ses"],[(/([ti])um$/i),"$1a"],[(/(buffal|tomat)o$/i),"$1oes"],[(/(bu)s$/i),"$1ses"],[(/(alias|status)$/i),"$1es"],[(/(octop|vir)us$/i),"$1i"],[(/(ax|test)is$/i),"$1es"],[(/s$/i),"s"],[(/$/),"s"]],singular:[[(/(quiz)zes$/i),"$1"],[(/(matr)ices$/i),"$1ix"],[(/(vert|ind)ices$/i),"$1ex"],[(/^(ox)en/i),"$1"],[(/(alias|status)es$/i),"$1"],[(/(octop|vir)i$/i),"$1us"],[(/(cris|ax|test)es$/i),"$1is"],[(/(shoe)s$/i),"$1"],[(/(o)es$/i),"$1"],[(/(bus)es$/i),"$1"],[(/([m|l])ice$/i),"$1ouse"],[(/(x|ch|ss|sh)es$/i),"$1"],[(/(m)ovies$/i),"$1ovie"],[(/(s)eries$/i),"$1eries"],[(/([^aeiouy]|qu)ies$/i),"$1y"],[(/([lr])ves$/i),"$1f"],[(/(tive)s$/i),"$1"],[(/(hive)s$/i),"$1"],[(/([^f])ves$/i),"$1fe"],[(/(^analy)ses$/i),"$1sis"],[(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i),"$1$2sis"],[(/([ti])a$/i),"$1um"],[(/(n)ews$/i),"$1ews"],[(/s$/i),""]],irregular:[["move","moves"],["sex","sexes"],["child","children"],["man","men"],["person","people"]],uncountable:["sheep","fish","series","species","money","rice","information","equipment"]},ordinalize:function(A){if(11<=parseInt(A,10)%100&&parseInt(A,10)%100<=13){return A+"th"}else{switch(parseInt(A,10)%10){case 1:return A+"st";case 2:return A+"nd";case 3:return A+"rd";default:return A+"th"}}},pluralize:function(C){var E=ExtMVC.Inflector.uncountableOrIrregular(C);if(E){return E}for(var A=0;A/g,">").replace(/=0;C--){E[D[C].replace(":","")]=A[C]}for(option in this.options){E[option]=this.options[option]}return E},urlForNamed:function(A){var A=A||{};return this.urlFor(Ext.applyIf(A,this.options))},urlFor:function(C){var B=this.mappingString;for(var F in C){if(C[F]&&this.options[F]&&C[F]!=this.options[F]){return false}}var E=[];for(var F in C){E.push(":"+F)}E=E.sort();var A=this.paramsInStringWithOptions.sort();if(A.length!=E.length){return false}for(var D=0;D=0;B--){var C=this.conditions[E[B]];var D=String.format("({0})",C||"[a-zA-Z0-9_,]+");A=A.replace(new RegExp(E[B]),D)}return new RegExp("^"+A+"$")}};ExtMVC.lib.Dependencies=Ext.extend(Ext.util.Observable,{constructor:function(){this.dependencies={};ExtMVC.lib.Dependencies.superclass.constructor.apply(this,arguments)},get:function(A){return this.dependencies[A]||[]},add:function(D,C,B){var A=this.dependencies[D]||[];A.push({name:C,config:B});this.dependencies[D]=A}});Ext.extend=function(){var B=function(D){for(var C in D){this[C]=D[C]}};var A=Object.prototype.constructor;return function(J,G,I){if(Ext.isObject(G)){I=G;G=J;J=I.constructor!=A?I.constructor:function(){G.apply(this,arguments)}}var E=function(){},H,D=G.prototype;E.prototype=D;H=J.prototype=new E();H.constructor=J;J.superclass=D;if(D.constructor==A){D.constructor=G}J.override=function(F){Ext.override(J,F)};H.superclass=H.supr=(function(){return D});H.override=B;Ext.override(J,I);J.extend=function(F){Ext.extend(J,F)};var C=J.prototype.onExtended;if(C){C.call(J.prototype)}return J}}();ExtMVC.controller.Controller=Ext.extend(Ext.util.Observable,{name:null,onExtended:function(){if(this.name!=null){this.viewsPackage=Ext.ns(String.format("{0}.views.{1}",ExtMVC.name,this.name));ExtMVC.registerController(this.name,this.constructor)}},constructor:function(A){ExtMVC.controller.Controller.superclass.constructor.apply(this,arguments);Ext.apply(this,A||{});this.initEvents();this.initListeners()},initEvents:function(){},initListeners:function(){},showNotice:function(A){},getViewClass:function(A){return this.viewsPackage[A]},addTo:null,render:function(D,B){var C=this.getViewClass(D);if(typeof C=="function"){var A=new C(B);if(this.addTo){this.renderViaAddTo(A)}return A}else{throw new Error(String.format("View '{0}' not found",D))}},renderViaAddTo:function(A){if(this.addTo!=undefined){this.addTo.removeAll();this.addTo.doLayout();this.addTo.add(A);this.addTo.doLayout()}}});Ext.reg("controller",ExtMVC.controller.Controller);ExtMVC.controller.CrudController=Ext.extend(ExtMVC.controller.Controller,{model:null,create:function(C,B){var A=new this.model(C);A.save({scope:this,success:this.onCreateSuccess,failure:this.onCreateFailure})},read:function(A){this.model.findById(A,{scope:this,success:function(B){this.fireEvent("read",B)},failure:function(){this.fireEvent("read-failed",A)}})},update:function(A,C){for(var B in C){A.set(B,C[B])}A.save({scope:this,success:function(D){this.onUpdateSuccess(D,C)},failure:function(){this.onUpdateFailure(A,C)}})},destroy:function(A){if(A.destroy==undefined){var B={};B[this.model.prototype.primaryKey]=parseInt(A,10);var A=new (this.model)(B)}A.destroy({scope:this,success:this.onDestroySuccess,failure:this.onDestroyFailure})},index:function(){var A=this.render("Index",{model:this.model,controller:this,listeners:this.getIndexViewListeners(),viewsPackage:this.viewsPackage});this.fireEvent("index");return A},build:function(){return this.render("New",{model:this.model,controller:this,listeners:this.getBuildViewListeners(),viewsPackage:this.viewsPackage})},edit:function(A,B){B=B||{};if(A instanceof Ext.data.Record){Ext.applyIf(B,{model:this.model,controller:this,listeners:this.getEditViewListeners(),viewsPackage:this.viewsPackage,id:String.format("{0}_edit_{1}",this.name,A.get(A.primaryKey))});var D=this.render("Edit",B);D.loadRecord(A);this.onEdit(D,A);this.fireEvent("edit",A);return D}else{var C=A;this.model.find(parseInt(C,10),{scope:this,success:function(E){this.edit(E)}})}},getIndexViewListeners:function(){return{scope:this,"delete":this.destroy,"new":this.build,edit:this.edit}},getEditViewListeners:function(){return{scope:this,cancel:this.index,save:this.update}},getBuildViewListeners:function(){return{scope:this,cancel:this.index,save:this.create}},showCreatedNotice:function(){this.showNotice(String.format("{0} successfully created",this.model.prototype.singularHumanName))},showUpdatedNotice:function(){this.showNotice(String.format("{0} successfully updated",this.model.prototype.singularHumanName))},showDestroyedNotice:function(){this.showNotice(String.format("{0} successfully deleted",this.model.prototype.singularHumanName))},onCreateSuccess:function(A){if(this.fireEvent("create",A)!==false){this.showCreatedNotice();this.index()}},onCreateFailure:function(A){this.fireEvent("create-failed",A)},onUpdateSuccess:function(A,B){if(this.fireEvent("update",A,B)!==false){this.showUpdatedNotice();this.index()}},onUpdateFailure:function(A,B){this.fireEvent("update-failed",A,B)},onDestroySuccess:function(A){this.fireEvent("delete",A);this.showDestroyedNotice()},onDestroyFailure:function(A){this.fireEvent("delete-failed",A)},onEdit:function(A){},initEvents:function(){this.addEvents("create","create-failed","read","read-failed","update","update-failed","delete","delete-failed","index","edit")},getViewClass:function(B){var A=ExtMVC.controller.CrudController.superclass.getViewClass.call(this,B);return(A==undefined)?this.scaffoldViewName(B):A},scaffoldViewName:function(A){return ExtMVC.view.scaffold[A.titleize()]}});ExtMVC.model={pendingCreation:{},getModelsPendingDefinitionOf:function(A){return this.pendingCreation[A]||[]},setModelPendingDefinitionOf:function(B,D,C){var A=this.pendingCreation[B]||[];A.push({name:D,config:C});this.pendingCreation[B]=A},strictMode:false,modelNamespace:window,define:function(A,B){var D=true,B=B||{};if(typeof B.extend!="undefined"){var C=this.modelNamespace[B.extend];if(typeof C=="undefined"){D=false;this.setModelPendingDefinitionOf(B.extend,A,B)}}if(D){this.create.apply(this,arguments)}},create:function(B,E){E=E||{};if(this.isAlreadyDefined(B)){if(this.strictMode){throw new Error(B+" is already defined")}return false}var F=this.modelNamespace[E.extend];var A=this.buildFields(E.fields,F);delete E.fields;var D=this.modelNamespace[B]=Ext.data.Record.create(A);var G=E.classMethods||{};delete E.classMethods;Ext.apply(D.prototype,E);if(typeof F!="undefined"){Ext.applyIf(G,F);Ext.applyIf(D.prototype,F.prototype)}D.prototype.modelName=B;this.setupNames(D);for(var C in G){if(C!="prototype"){D[C]=G[C]}}this.initializePlugins(D);this.afterCreate(B)},afterCreate:function(A){var B=this.getModelsPendingDefinitionOf(A);if(B){Ext.each(B,function(C){this.create(C.name,C.config)},this)}},isAlreadyDefined:function(A){if(typeof this.modelNamespace[A]!="undefined"){return true}var C=false;for(superclass in this.pendingCreation){var B=this.pendingCreation[superclass];Ext.each(B,function(D){if(D.name==A){C=true}},this)}return C},buildFields:function(B,C){B=B||[];var A=new Ext.util.MixedCollection(false,function(D){return D.name});A.addAll(B);if(typeof C!="undefined"){C.prototype.fields.each(function(D){if(typeof A.get(D.name)=="undefined"){A.add(D)}})}return A.items},setupNames:function(A){var C=A.prototype,B=ExtMVC.Inflector;Ext.applyIf(A.prototype,{tableName:B.pluralize(C.modelName.underscore()),foreignKeyName:B.singularize(C.modelName.underscore())+"_id",singularHumanName:C.modelName.humanize().titleize(),pluralHumanName:B.pluralize(C.modelName.humanize().titleize())})},plugins:[],addPlugin:function(A){this.plugins.push(A)},initializePlugins:function(A){Ext.each(this.plugins,function(B){B.initialize(A)},this)}};Ext.ns("ExtMVC.model.plugin");ExtMVC.model.Base=function(){};ExtMVC.model.Base.prototype={primaryKey:"id",newRecord:function(){var A=this.get(this.primaryKey);return typeof A=="undefined"||A==""},MVCModelId:function(){return String.format("{0}-{1}",this.tableName,this.get(this.primaryKey))},getReader:function(){if(!this.reader){this.reader=new Ext.data.JsonReader({totalProperty:"results",root:this.tableName},this.constructor)}return this.reader},initialize:Ext.emptyFn};Ext.apply(Ext.data.Record.prototype,new ExtMVC.model.Base());ExtMVC.model.plugin.adapter={initialize:function(B){var A=new this.RESTJSONAdapter();Ext.override(Ext.data.Record,A.instanceMethods());Ext.apply(B,A.classMethods());try{Ext.override(ExtMVC.model.plugin.association.HasMany,A.hasManyAssociationMethods());Ext.override(ExtMVC.model.plugin.association.BelongsTo,A.belongsToAssociationMethods())}catch(C){}}};ExtMVC.model.addPlugin(ExtMVC.model.plugin.adapter);ExtMVC.model.plugin.adapter.Abstract=function(A){};ExtMVC.model.plugin.adapter.Abstract.prototype={doSave:Ext.emptyFn,doFind:Ext.emptyFn,doDestroy:Ext.emptyFn,instanceMethods:function(){return{adapter:this,save:function(A){A=A||{};if(A.skipValidation===true||this.isValid()){return this.adapter.doSave(this,A)}else{if(typeof A.failure=="function"){return A.failure.call(A.scope||this,this)}}},destroy:function(A){return this.adapter.doDestroy(this,A)},update:function(B,A){this.setValues(B);this.save(A)},loaded:function(){}}},classMethods:function(){return{adapter:this,create:function(C,B){var A=new this(C);A.save(B);return A},build:function(A){return new this(A)},find:function(B,A){if(typeof(B)=="number"){B={primaryKey:B}}return this.adapter.doFind(B,A,this)},destroy:function(B,A){return this.adapter.doDestroy(B,A,this)}}},hasManyAssociationMethods:function(){return{adapter:this,create:function(C,B){var A=new this.associatedClass(C)},build:function(B,A){},find:function(A){},loaded:function(){},destroy:function(A){}}},belongsToAssociationMethods:function(){return{adapter:this,find:function(A){},loaded:function(){},destroy:function(A){}}}};ExtMVC.model.plugin.adapter.RESTAdapter=Ext.extend(ExtMVC.model.plugin.adapter.Abstract,{createMethod:"POST",readMethod:"GET",updateMethod:"PUT",destroyMethod:"DELETE",proxyType:Ext.data.HttpProxy,doSave:function(B,D){if(typeof B=="undefined"){throw new Error("No instance provided to REST Adapter save")}D=D||{};var A=D.success||Ext.emptyFn,C=D.failure||Ext.emptyFn;delete D.success;delete D.failure;Ext.Ajax.request(Ext.apply({url:this.instanceUrl(B),method:B.newRecord()?this.createMethod:this.updateMethod,params:this.buildPostData(B),success:function(E,F,G){G=G||this;return function(H,I){var L=E.modelName.underscore(),K=Ext.decode(H.responseText)[L];for(var J in K){E.set(J,K[J])}F.call(this,E)}}(B,A,D.scope)},D))},afterSave:function(){},doFind:function(E,B,C){E=E||{};B=B||{};var F=(E.primaryKey!==undefined),A=B.url||this.findUrl(E,C);Ext.applyIf(B,{conditions:E,scope:this});var D=F?this.doSingleFind:this.doCollectionFind;return D.call(this,A,B,C)},doDestroy:function(B,C,D){var C=C||{};if(typeof B=="undefined"){throw new Error("No instance provided to REST Adapter destroy")}if(!(B instanceof Ext.data.Record)){var E=parseInt(B,10);B=new D();B.set(D.prototype.primaryKey,E)}var A=C.success||Ext.emptyFn;delete C.success;return Ext.Ajax.request(Ext.applyIf(C,{method:this.destroyMethod,url:this.instanceUrl(B),success:function(){A.call(C.scope||this,B)}}))},doSingleFind:function(D,C,F){var H=C.callback,A=C.success,G=C.failure;delete C.callback;delete C.success;delete C.failure;var E=this.decodeSingleLoadResponse;var B=function(J,I){if(typeof J=="function"){J.apply(C.scope,I)}};Ext.Ajax.request(Ext.apply(C,{callback:function(K,L,J){if(L===true){var I=new F(E(J.responseText,F));B(A,[I,K,J])}else{B(G,arguments)}B(H,arguments)}},this.buildProxyConfig(D)))},storeConfig:{autoLoad:true,remoteSort:false},doCollectionFind:function(B,A,C){Ext.applyIf(A,this.storeConfig);if(A.conditions!=undefined){Ext.applyIf(A,{baseParams:A.conditions})}return new Ext.data.Store(Ext.applyIf(A,{reader:C.prototype.getReader(),proxy:new this.proxyType(this.buildProxyConfig(B))}))},instanceUrl:function(A){if(A.newRecord()){return String.format("/{0}",A.tableName)}else{return String.format("/{0}/{1}",A.tableName,A.get(A.primaryKey))}},collectionUrl:function(A){return String.format("/{0}",A.prototype.tableName)},buildProxyConfig:function(A){return{url:A,method:this.readMethod}},buildPostData:function(A){var C={},B=A.modelName.underscore();for(key in A.data){C[B+"["+key+"]"]=A.data[key]}return C},decodeSingleLoadResponse:function(C,A){var B=A.prototype.tableName;return Ext.decode(C)[B]},findUrl:function(C,B){if(typeof(C)=="object"&&C.primaryKey){var A=new B({});A.set(A.primaryKey,C.primaryKey);delete C.primaryKey;return this.instanceUrl(A)}else{return this.collectionUrl(B)}}});ExtMVC.model.plugin.adapter.RESTJSONAdapter=Ext.extend(ExtMVC.model.plugin.adapter.RESTAdapter,{doSave:function(A,B){if(typeof A=="undefined"){throw new Error("No instance provided to REST Adapter save")}Ext.applyIf(B||{},{jsonData:A.data,params:{},headers:{"Content-Type":"application/json"}});ExtMVC.model.plugin.adapter.RESTJSONAdapter.superclass.doSave.apply(this,arguments)},doDestroy:function(A,B,C){B=B||{};Ext.applyIf(B,{headers:{"Content-type":"application/json"}});ExtMVC.model.plugin.adapter.RESTJSONAdapter.superclass.doDestroy.call(this,A,B,C)},decodeSingleLoadResponse:function(C,A){var B=ExtMVC.Inflector.singularize(A.prototype.tableName);return Ext.decode(C)[B]},buildProxyConfig:function(A){var B=ExtMVC.model.plugin.adapter.RESTJSONAdapter.superclass.buildProxyConfig.apply(this,arguments);return Ext.apply(B,{headers:{"Content-Type":"application/json"}})}});Ext.ns("ExtMVC.model.plugin.validation");ExtMVC.model.plugin.validation.AbstractValidation=function(A,C,B){this.ownerClass=A;this.field=C;Ext.apply(this,B)};ExtMVC.model.plugin.validation.AbstractValidation.prototype={getValue:function(A){return A.get(this.field)},isValid:function(A){return true}};ExtMVC.model.plugin.validation.ValidatesPresenceOf=Ext.extend(ExtMVC.model.plugin.validation.AbstractValidation,{message:"must be present",isValid:function(A){var C=this.getValue(A),B=false;switch(typeof C){case"object":if(C!=null){B=true}break;case"string":if(C.length!=0){B=true}break}return B}});ExtMVC.model.plugin.validation.ValidatesLengthOf=Ext.extend(ExtMVC.model.plugin.validation.AbstractValidation,{tooShortMessage:"is too short",tooLongMessage:"is too long",message:"",isValid:function(A){var B=this.getValue(A);if(typeof B=="undefined"){return true}if(this.minimum&&B.lengththis.maximum){this.message=this.tooLongMessage;return false}return true}});ExtMVC.model.plugin.validation.ValidatesNumericalityOf=Ext.extend(ExtMVC.model.plugin.validation.AbstractValidation,{message:"must be a number",isValid:function(A){return"number"==typeof this.getValue(A)}});ExtMVC.model.plugin.validation.ValidatesInclusionOf=Ext.extend(ExtMVC.model.plugin.validation.AbstractValidation,{constructor:function(A,C,B){B=B||{};Ext.applyIf(B,{allowed:[]});ExtMVC.model.plugin.validation.ValidatesInclusionOf.superclass.constructor.call(this,A,C,B);Ext.applyIf(this,{message:"must be one of "+this.allowed.toSentence("or")})},isValid:function(A){var C=this.getValue(A);for(var B=0;B-1){E.push(this.buildColumn(F.name))}},this);Ext.each(A,function(F){if(this.preferredColumns.indexOf(F.name)==-1&&this.ignoreColumns.indexOf(F.name)==-1){E.push(this.buildColumn(F.name))}if(this.wideColumns.indexOf(F.name)){wideColumns.push(F.name)}},this);for(var D=E.length-1;D>=0;D--){var B=E[D];if(this.narrowColumns.indexOf(B.id)>-1){Ext.applyIf(B,{width:this.narrowColumnWidth})}else{if(this.wideColumns.indexOf(B.id)>-1){Ext.applyIf(B,{width:this.wideColumnWidth})}else{Ext.applyIf(B,{width:this.normalColumnWidth})}}}}return E},getFields:function(){if(this.useColumns===undefined){return this.model.prototype.fields.items}else{var A=[];Ext.each(this.useColumns,function(B){A.push({name:B})},this);return A}},buildColumn:function(A){var A=A||{};if(typeof(A)=="string"){A={name:A}}return Ext.applyIf(A,{id:A.name,header:A.name.replace(/_/g," ").titleize(),sortable:true,dataIndex:A.name})},hasAddButton:true,hasEditButton:true,hasDeleteButton:true,buildAddButton:function(A){return new Ext.Button(Ext.applyIf(A||{},{text:"New "+this.model.prototype.singularHumanName,scope:this,iconCls:"add",handler:this.onAdd}))},buildEditButton:function(A){return new Ext.Button(Ext.applyIf(A||{},{text:"Edit selected",scope:this,iconCls:"edit",disabled:true,handler:this.onEdit}))},buildDeleteButton:function(A){return new Ext.Button(Ext.applyIf(A||{},{text:"Delete selected",disabled:true,scope:this,iconCls:"delete",handler:this.onDelete}))},buildTopToolbar:function(){var A=[];if(this.hasAddButton===true){this.addButton=this.buildAddButton();A.push(this.addButton,"-")}if(this.hasEditButton===true){this.editButton=this.buildEditButton();A.push(this.editButton,"-")}if(this.hasDeleteButton===true){this.deleteButton=this.buildDeleteButton();A.push(this.deleteButton,"-")}if(this.hasSearchField===true){this.searchField=this.buildSearchField();A.push(this.searchField,"-")}this.getSelectionModel().on("selectionchange",function(B){if(B.getCount()>0){if(this.deleteButton!=undefined){this.deleteButton.enable()}if(this.editButton!=undefined){this.editButton.enable()}}else{if(this.deleteButton!=undefined){this.deleteButton.disable()}if(this.editButton!=undefined){this.editButton.disable()}}},this);return A},pageSize:25,buildBottomToolbar:function(A){return new Ext.PagingToolbar({store:A,displayInfo:true,pageSize:this.pageSize,emptyMsg:String.format("No {0} to display",this.model.prototype.pluralHumanName)})},hasSearchField:false,searchParamName:"q",buildSearchField:function(){this.searchField=new Ext.form.TwinTriggerField({width:200,validationEvent:false,validateOnBlur:false,hideTrigger1:true,onTrigger1Click:this.clearSearchField.createDelegate(this,[]),onTrigger2Click:this.onSearch.createDelegate(this,[]),trigger1Class:"x-form-clear-trigger",trigger2Class:"x-form-search-trigger"});this.searchField.on("specialkey",function(B,A){if(A.getKey()===A.ESC){this.clearSearchField()}A.stopEvent();if(A.getKey()===A.ENTER){this.onSearch()}},this);return this.searchField},clearSearchField:function(){var A=this.searchField;A.el.dom.value="";A.triggers[0].hide();this.doSearch()},onSearch:function(){var B=this.searchField,A=B.getRawValue();if(A.length<1){this.clearSearchField()}else{B.triggers[0].show();this.doSearch(A)}},doSearch:function(A){A=A||this.searchField.getRawValue()||"";var B={start:0};this.store.baseParams=this.store.baseParams||{};this.store.baseParams[this.searchParamName]=A;this.store.reload({params:B})},onAdd:function(){this.fireEvent("new")},onEdit:function(B){var A=this.getSelectionModel().getSelected();if(A){this.fireEvent("edit",A)}},onDelete:function(){Ext.Msg.confirm("Are you sure?",String.format("Are you sure you want to delete this {0}? This cannot be undone.",this.model.prototype.modelName.titleize()),function(A){if(A=="yes"){var B=this.getSelectionModel().getSelected();if(B){this.fireEvent("delete",B)}}},this)}});Ext.reg("scaffold_index",ExtMVC.view.scaffold.Index);ExtMVC.view.scaffold.New=Ext.extend(ExtMVC.view.scaffold.ScaffoldFormPanel,{initComponent:function(){Ext.applyIf(this,{title:"New "+this.model.prototype.singularHumanName});ExtMVC.view.scaffold.New.superclass.initComponent.apply(this,arguments)}});Ext.reg("scaffold_new",ExtMVC.view.scaffold.New);ExtMVC.view.scaffold.Edit=Ext.extend(ExtMVC.view.scaffold.ScaffoldFormPanel,{initComponent:function(){Ext.applyIf(this,{title:"Edit "+this.model.prototype.singularHumanName});ExtMVC.view.scaffold.Edit.superclass.initComponent.apply(this,arguments)},loadRecord:function(A){this.instance=A;this.getForm().loadRecord(A)},onSave:function(){this.fireEvent("save",this.instance,this.getFormValues(),this)}});Ext.reg("scaffold_edit",ExtMVC.view.scaffold.Edit);ExtMVC.view.HasManyEditorGridPanel=Ext.extend(Ext.grid.EditorGridPanel,{initComponent:function(){Ext.applyIf(this,{autoScroll:true,store:this.association.findAll(),viewConfig:{forceFit:true}});if(this.hasTopToolbar){this.addTopToolbar()}ExtMVC.view.HasManyEditorGridPanel.superclass.initComponent.apply(this,arguments);this.on("afteredit",function(A){A.record.save({success:function(){A.record.commit()}})},this);this.getSelectionModel().on("selectionchange",function(A,B){if(this.deleteButton){this.deleteButton.enable()}},this)},hasTopToolbar:true,hasNewButton:true,hasDeleteButton:true,addTopToolbar:function(B){var A=[];if(this.hasNewButton){this.newButton=new Ext.Toolbar.Button({iconCls:"add",text:"Add",scope:this,handler:this.onAdd});A.push(this.newButton);A.push("-")}if(this.hasDeleteButton){this.deleteButton=new Ext.Toolbar.Button({text:"Remove selected",disabled:true,iconCls:"delete",scope:this,handler:this.onDelete});A.push(this.deleteButton)}Ext.applyIf(this,{tbar:A})},windowConfig:{},onAdd:function(A){if(!this.addWindow){this.addWindow=new Ext.Window(Ext.applyIf(this.windowConfig,{title:"New",layout:"fit",modal:true,height:300,width:400,items:[this.form],closeAction:"hide",buttons:[{text:"Save",iconCls:"save",scope:this,handler:this.onSaveNew},{text:"Cancel",iconCls:"cancel",scope:this,handler:this.onCancelNew}]}))}this.addWindow.show()},onDelete:function(B){var A=this.getSelectionModel().selection.record;if(A){A.destroy({scope:this,success:function(){this.store.reload()},failure:function(){Ext.Msg.alert("Delete failed","Something went wrong while trying to delete - please try again");this.store.reload()}})}this.deleteButton.disable()},onSaveNew:function(){this.association.create(this.form.getForm().getValues(),{scope:this,success:function(B,A){this.store.reload();this.addWindow.hide()},failure:function(B,A){this.form.getForm().clearInvalid();this.form.getForm().markInvalid(B.errors.forForm())}})},onCancelNew:function(A){this.addWindow.hide()}});Ext.reg("hasmany_editorgrid",ExtMVC.view.HasManyEditorGridPanel);ExtMVC.view.FormWindow=Ext.extend(Ext.Window,{modal:true,height:230,width:400,initComponent:function(){this.form=this.buildForm();Ext.apply(this,{items:[this.form],buttons:[{text:"Save",iconCls:"save",scope:this,handler:this.onSave},{text:"Cancel",iconCls:"cancel",scope:this,handler:this.onCancel}],layout:"fit",closeAction:"hide"});ExtMVC.view.FormWindow.superclass.initComponent.apply(this,arguments)},buildForm:function(){return new Ext.form.FormPanel({})},loadRecord:function(A){this.instance=A;this.form.form.loadRecord(A)},onSave:function(){this.fireEvent("save",this.getFormValues(),this)},onCancel:function(){this.hide()},getFormValues:function(){var B=this.form.getForm(),A={};B.items.each(function(D){var C=(typeof D.getSubmitValue=="function")?"getSubmitValue":"getValue";A[D.getName()]=D[C]()},this);return A}});Ext.reg("formwindow",ExtMVC.view.FormWindow); \ No newline at end of file diff --git a/ext-mvc-all.js b/ext-mvc-all.js index 4908a22..9280ca1 100644 --- a/ext-mvc-all.js +++ b/ext-mvc-all.js @@ -1497,17 +1497,22 @@ ExtMVC.controller.CrudController = Ext.extend(ExtMVC.controller.Controller, { * Renders the custom Edit view if present, otherwise falls back to the default scaffold Edit form * @param {Mixed} instance The model instance to edit. If not given an ExtMVC.model.Base * instance, a findById() will be called on this controller's associated model + * @param {Object} viewConfig Optional config object to pass to the view class constructor */ - edit: function(instance) { + edit: function(instance, viewConfig) { + viewConfig = viewConfig || {}; + if (instance instanceof Ext.data.Record) { - var editView = this.render('Edit', { + Ext.applyIf(viewConfig, { model : this.model, controller : this, listeners : this.getEditViewListeners(), viewsPackage: this.viewsPackage, - id : String.format("{0}_edit_{1}", this.name, instance.get(instance.primaryKey)) + id : String.format("{0}_edit_{1}", this.name, instance.get(instance.primaryKey)) }); + var editView = this.render('Edit', viewConfig); + editView.loadRecord(instance); this.onEdit(editView, instance); @@ -4528,24 +4533,27 @@ ExtMVC.view.HasManyEditorGridPanel = Ext.extend(Ext.grid.EditorGridPanel, { Ext.reg('hasmany_editorgrid', ExtMVC.view.HasManyEditorGridPanel); -/** - * @class ExtMVC.view.FormWindow - * @extends Ext.Window +/** + * @class ExtMVC.view.FormWindow + * @extends Ext.Window * Convenience class for creating a window with a default form. Example: *
-MyApp.views.MyFormWindow = Ext.extend(ExtMVC.view.FormWindow, {
-
 height: 200,
-
 width : 400,
-
 
+MyApp.views.MyFormWindow = Ext.extend(ExtMVC.view.FormWindow, {
+
+ height: 200,
+
+ width : 400,
+
+ 
   buildForm: function() {
     //return your Ext.form.FormPanel here
   }
 });
 
* - */ -ExtMVC.view.FormWindow = Ext.extend(Ext.Window, { + */ +ExtMVC.view.FormWindow = Ext.extend(Ext.Window, { modal : true, height : 230, width : 400, @@ -4610,7 +4618,7 @@ ExtMVC.view.FormWindow = Ext.extend(Ext.Window, { * Called when the user clicks the save button */ onSave: function() { - this.fireEvent('save', this.getFormValues()); + this.fireEvent('save', this.getFormValues(), this); }, /** @@ -4637,7 +4645,7 @@ ExtMVC.view.FormWindow = Ext.extend(Ext.Window, { return values; } -}); - +}); + Ext.reg('formwindow', ExtMVC.view.FormWindow); diff --git a/scripts/stats.rb b/scripts/stats.rb index 3d1a858..46c3b7e 100644 --- a/scripts/stats.rb +++ b/scripts/stats.rb @@ -110,7 +110,7 @@ def column_order # The lines to show, in order def line_order - [:controller_files, :model_files, :view_files, :lib_files, :controller_specs, :model_specs] + [:controller_files, :model_files, :view_files, :lib_files, :controller_specs, :model_specs, :view_specs] end # The arrays to use when calculating totals for project LOC etc @@ -120,7 +120,7 @@ def project_code_arrays # The arrays to use when calculating totals for project spec LOC etc def project_spec_arrays - [:controller_specs, :model_specs] + [:controller_specs, :model_specs, :view_specs] end # Mappings between method and human names for line headings @@ -132,7 +132,8 @@ def line_headings :lib_files => "Libraries", :plugin_files => "Plugins", :controller_specs => "Controller Specs", - :model_specs => "Model Specs" + :model_specs => "Model Specs", + :view_specs => "View Specs" } end @@ -176,6 +177,10 @@ def controller_specs def model_specs files_in('spec/models') end + + def view_specs + files_in('spec/views') + end private def files_in(directory) diff --git a/view/FormWindow.js b/view/FormWindow.js index c622039..3162568 100644 --- a/view/FormWindow.js +++ b/view/FormWindow.js @@ -1,21 +1,24 @@ -/** - * @class ExtMVC.view.FormWindow - * @extends Ext.Window +/** + * @class ExtMVC.view.FormWindow + * @extends Ext.Window * Convenience class for creating a window with a default form. Example: *
-MyApp.views.MyFormWindow = Ext.extend(ExtMVC.view.FormWindow, {
-
 height: 200,
-
 width : 400,
-
 
+MyApp.views.MyFormWindow = Ext.extend(ExtMVC.view.FormWindow, {
+
+ height: 200,
+
+ width : 400,
+
+ 
   buildForm: function() {
     //return your Ext.form.FormPanel here
   }
 });
 
* - */ -ExtMVC.view.FormWindow = Ext.extend(Ext.Window, { + */ +ExtMVC.view.FormWindow = Ext.extend(Ext.Window, { modal : true, height : 230, width : 400, @@ -80,7 +83,7 @@ ExtMVC.view.FormWindow = Ext.extend(Ext.Window, { * Called when the user clicks the save button */ onSave: function() { - this.fireEvent('save', this.getFormValues()); + this.fireEvent('save', this.getFormValues(), this); }, /** @@ -107,6 +110,6 @@ ExtMVC.view.FormWindow = Ext.extend(Ext.Window, { return values; } -}); - +}); + Ext.reg('formwindow', ExtMVC.view.FormWindow); \ No newline at end of file