extjs-mvc 0.4.0.j → 0.4.0.k

Sign up to get free protection for your applications and to get access to all the features.
Files changed (169) hide show
  1. data/Rakefile +1 -1
  2. data/VERSION +1 -1
  3. data/lib/extjs-mvc.rb +0 -14
  4. data/lib/extjs-mvc/api.rb +14 -27
  5. data/lib/extjs-mvc/src/App.js +219 -0
  6. data/lib/extjs-mvc/src/MVC.js +260 -0
  7. data/lib/extjs-mvc/src/Presenter.js +52 -0
  8. data/lib/extjs-mvc/src/README.rdoc +69 -0
  9. data/lib/extjs-mvc/src/controller/Controller.js +278 -0
  10. data/lib/extjs-mvc/src/controller/CrudController.js +460 -0
  11. data/lib/extjs-mvc/src/lib/Array.js +26 -0
  12. data/lib/extjs-mvc/src/lib/Booter.js +417 -0
  13. data/lib/extjs-mvc/src/lib/ClassManager.js +191 -0
  14. data/lib/extjs-mvc/src/lib/ControllerClassManager.js +95 -0
  15. data/lib/extjs-mvc/src/lib/Dependencies.js +44 -0
  16. data/lib/extjs-mvc/src/lib/DispatchMatcher.js +98 -0
  17. data/lib/extjs-mvc/src/lib/Dispatcher.js +129 -0
  18. data/lib/extjs-mvc/src/lib/Environment.js +43 -0
  19. data/lib/extjs-mvc/src/lib/Inflector.js +155 -0
  20. data/lib/extjs-mvc/src/lib/ModelClassManager.js +19 -0
  21. data/lib/extjs-mvc/src/lib/Route.js +139 -0
  22. data/lib/extjs-mvc/src/lib/Router.js +282 -0
  23. data/lib/extjs-mvc/src/lib/String.js +94 -0
  24. data/lib/extjs-mvc/src/lib/ViewClassManager.js +229 -0
  25. data/lib/extjs-mvc/src/lib/notes.txt +32 -0
  26. data/lib/extjs-mvc/src/model/AdapterManager.js +30 -0
  27. data/lib/extjs-mvc/src/model/Association.js +26 -0
  28. data/lib/extjs-mvc/src/model/Base.js +63 -0
  29. data/lib/extjs-mvc/src/model/BelongsToAssociation.js +116 -0
  30. data/lib/extjs-mvc/src/model/Cache.js +131 -0
  31. data/lib/extjs-mvc/src/model/HasManyAssociation.js +160 -0
  32. data/lib/extjs-mvc/src/model/Model.js +331 -0
  33. data/lib/extjs-mvc/src/model/UrlBuilder.js +106 -0
  34. data/lib/extjs-mvc/src/model/adapters/AbstractAdapter.js +296 -0
  35. data/lib/extjs-mvc/src/model/adapters/MemoryAdapter.js +103 -0
  36. data/lib/extjs-mvc/src/model/adapters/RESTAdapter.js +345 -0
  37. data/lib/extjs-mvc/src/model/adapters/RESTJSONAdapter.js +68 -0
  38. data/lib/extjs-mvc/src/model/adapters/notes.txt +42 -0
  39. data/lib/extjs-mvc/src/model/associations/Association.js +192 -0
  40. data/lib/extjs-mvc/src/model/associations/notes.txt +87 -0
  41. data/lib/extjs-mvc/src/model/validations/Errors.js +136 -0
  42. data/lib/extjs-mvc/src/model/validations/Plugin.js +139 -0
  43. data/lib/extjs-mvc/src/model/validations/Validations.js +276 -0
  44. data/lib/extjs-mvc/src/notes/Charts.graffle +0 -0
  45. data/lib/extjs-mvc/src/overrides/Ext.Component.js +21 -0
  46. data/lib/extjs-mvc/src/overrides/Ext.extend.js +142 -0
  47. data/lib/extjs-mvc/src/spec/Array.spec.js +15 -0
  48. data/lib/extjs-mvc/src/spec/ExtMVC.spec.js +65 -0
  49. data/lib/extjs-mvc/src/spec/Model.spec.js +370 -0
  50. data/lib/extjs-mvc/src/spec/OS.spec.js +83 -0
  51. data/lib/extjs-mvc/src/spec/Router.spec.js +99 -0
  52. data/lib/extjs-mvc/src/spec/SpecHelper.js +106 -0
  53. data/lib/extjs-mvc/src/spec/String.spec.js +83 -0
  54. data/lib/extjs-mvc/src/spec/model/AbstractAdapter.spec.js +49 -0
  55. data/lib/extjs-mvc/src/spec/model/Associations.spec.js +99 -0
  56. data/lib/extjs-mvc/src/spec/model/Cache.spec.js +5 -0
  57. data/lib/extjs-mvc/src/spec/model/RESTAdapter.spec.js +19 -0
  58. data/lib/extjs-mvc/src/spec/model/ValidationErrors.spec.js +64 -0
  59. data/lib/extjs-mvc/src/spec/model/Validations.spec.js +166 -0
  60. data/lib/extjs-mvc/src/spec/model/ValidationsPlugin.spec.js +108 -0
  61. data/lib/extjs-mvc/src/spec/suite.html +60 -0
  62. data/lib/extjs-mvc/src/specs-old/JSSpec.css +216 -0
  63. data/lib/extjs-mvc/src/specs-old/JSSpec.js +1512 -0
  64. data/lib/extjs-mvc/src/specs-old/all.html +66 -0
  65. data/lib/extjs-mvc/src/specs-old/base.js +14 -0
  66. data/lib/extjs-mvc/src/specs-old/controller.js +17 -0
  67. data/lib/extjs-mvc/src/specs-old/diff_match_patch.js +1 -0
  68. data/lib/extjs-mvc/src/specs-old/model.js +70 -0
  69. data/lib/extjs-mvc/src/specs-old/route.js +38 -0
  70. data/lib/extjs-mvc/src/specs-old/router.js +59 -0
  71. data/lib/extjs-mvc/src/specs-old/string.js +22 -0
  72. data/lib/extjs-mvc/src/testrunner/JSpecFormatter.js +111 -0
  73. data/lib/extjs-mvc/src/testrunner/TestClient.js +181 -0
  74. data/lib/extjs-mvc/src/testrunner/TestGrid.js +351 -0
  75. data/lib/extjs-mvc/src/testrunner/TestRunner.js +110 -0
  76. data/lib/extjs-mvc/src/testrunner/TestViewport.js +94 -0
  77. data/lib/extjs-mvc/src/vendor.yml +30 -0
  78. data/lib/extjs-mvc/src/vendor/ext-3.1.1/vendor.yml +16 -0
  79. data/lib/extjs-mvc/src/view/FormWindow.js +184 -0
  80. data/lib/extjs-mvc/src/view/HasManyEditorGridPanel.js +211 -0
  81. data/lib/extjs-mvc/src/view/scaffold/Edit.js +46 -0
  82. data/lib/extjs-mvc/src/view/scaffold/Index.js +561 -0
  83. data/lib/extjs-mvc/src/view/scaffold/New.js +20 -0
  84. data/lib/extjs-mvc/src/view/scaffold/ScaffoldFormPanel.js +255 -0
  85. data/lib/{vendor.yml → extjs-mvc/vendor.yml} +0 -0
  86. data/test/app/vendor/extjs-mvc/App.js +219 -0
  87. data/test/app/vendor/extjs-mvc/MVC.js +260 -0
  88. data/test/app/vendor/extjs-mvc/Presenter.js +52 -0
  89. data/test/app/vendor/extjs-mvc/README.rdoc +69 -0
  90. data/test/app/vendor/extjs-mvc/controller/Controller.js +278 -0
  91. data/test/app/vendor/extjs-mvc/controller/CrudController.js +460 -0
  92. data/test/app/vendor/extjs-mvc/lib/Array.js +26 -0
  93. data/test/app/vendor/extjs-mvc/lib/Booter.js +417 -0
  94. data/test/app/vendor/extjs-mvc/lib/ClassManager.js +191 -0
  95. data/test/app/vendor/extjs-mvc/lib/ControllerClassManager.js +95 -0
  96. data/test/app/vendor/extjs-mvc/lib/Dependencies.js +44 -0
  97. data/test/app/vendor/extjs-mvc/lib/DispatchMatcher.js +98 -0
  98. data/test/app/vendor/extjs-mvc/lib/Dispatcher.js +129 -0
  99. data/test/app/vendor/extjs-mvc/lib/Environment.js +43 -0
  100. data/test/app/vendor/extjs-mvc/lib/Inflector.js +155 -0
  101. data/test/app/vendor/extjs-mvc/lib/ModelClassManager.js +19 -0
  102. data/test/app/vendor/extjs-mvc/lib/Route.js +139 -0
  103. data/test/app/vendor/extjs-mvc/lib/Router.js +282 -0
  104. data/test/app/vendor/extjs-mvc/lib/String.js +94 -0
  105. data/test/app/vendor/extjs-mvc/lib/ViewClassManager.js +229 -0
  106. data/test/app/vendor/extjs-mvc/lib/notes.txt +32 -0
  107. data/test/app/vendor/extjs-mvc/model/AdapterManager.js +30 -0
  108. data/test/app/vendor/extjs-mvc/model/Association.js +26 -0
  109. data/test/app/vendor/extjs-mvc/model/Base.js +63 -0
  110. data/test/app/vendor/extjs-mvc/model/BelongsToAssociation.js +116 -0
  111. data/test/app/vendor/extjs-mvc/model/Cache.js +131 -0
  112. data/test/app/vendor/extjs-mvc/model/HasManyAssociation.js +160 -0
  113. data/test/app/vendor/extjs-mvc/model/Model.js +331 -0
  114. data/test/app/vendor/extjs-mvc/model/UrlBuilder.js +106 -0
  115. data/test/app/vendor/extjs-mvc/model/adapters/AbstractAdapter.js +296 -0
  116. data/test/app/vendor/extjs-mvc/model/adapters/MemoryAdapter.js +103 -0
  117. data/test/app/vendor/extjs-mvc/model/adapters/RESTAdapter.js +345 -0
  118. data/test/app/vendor/extjs-mvc/model/adapters/RESTJSONAdapter.js +68 -0
  119. data/test/app/vendor/extjs-mvc/model/adapters/notes.txt +42 -0
  120. data/test/app/vendor/extjs-mvc/model/associations/Association.js +192 -0
  121. data/test/app/vendor/extjs-mvc/model/associations/notes.txt +87 -0
  122. data/test/app/vendor/extjs-mvc/model/validations/Errors.js +136 -0
  123. data/test/app/vendor/extjs-mvc/model/validations/Plugin.js +139 -0
  124. data/test/app/vendor/extjs-mvc/model/validations/Validations.js +276 -0
  125. data/test/app/vendor/extjs-mvc/notes/Charts.graffle +0 -0
  126. data/test/app/vendor/extjs-mvc/overrides/Ext.Component.js +21 -0
  127. data/test/app/vendor/extjs-mvc/overrides/Ext.extend.js +142 -0
  128. data/test/app/vendor/extjs-mvc/spec/Array.spec.js +15 -0
  129. data/test/app/vendor/extjs-mvc/spec/ExtMVC.spec.js +65 -0
  130. data/test/app/vendor/extjs-mvc/spec/Model.spec.js +370 -0
  131. data/test/app/vendor/extjs-mvc/spec/OS.spec.js +83 -0
  132. data/test/app/vendor/extjs-mvc/spec/Router.spec.js +99 -0
  133. data/test/app/vendor/extjs-mvc/spec/SpecHelper.js +106 -0
  134. data/test/app/vendor/extjs-mvc/spec/String.spec.js +83 -0
  135. data/test/app/vendor/extjs-mvc/spec/model/AbstractAdapter.spec.js +49 -0
  136. data/test/app/vendor/extjs-mvc/spec/model/Associations.spec.js +99 -0
  137. data/test/app/vendor/extjs-mvc/spec/model/Cache.spec.js +5 -0
  138. data/test/app/vendor/extjs-mvc/spec/model/RESTAdapter.spec.js +19 -0
  139. data/test/app/vendor/extjs-mvc/spec/model/ValidationErrors.spec.js +64 -0
  140. data/test/app/vendor/extjs-mvc/spec/model/Validations.spec.js +166 -0
  141. data/test/app/vendor/extjs-mvc/spec/model/ValidationsPlugin.spec.js +108 -0
  142. data/test/app/vendor/extjs-mvc/spec/suite.html +60 -0
  143. data/test/app/vendor/extjs-mvc/specs-old/JSSpec.css +216 -0
  144. data/test/app/vendor/extjs-mvc/specs-old/JSSpec.js +1512 -0
  145. data/test/app/vendor/extjs-mvc/specs-old/all.html +66 -0
  146. data/test/app/vendor/extjs-mvc/specs-old/base.js +14 -0
  147. data/test/app/vendor/extjs-mvc/specs-old/controller.js +17 -0
  148. data/test/app/vendor/extjs-mvc/specs-old/diff_match_patch.js +1 -0
  149. data/test/app/vendor/extjs-mvc/specs-old/model.js +70 -0
  150. data/test/app/vendor/extjs-mvc/specs-old/route.js +38 -0
  151. data/test/app/vendor/extjs-mvc/specs-old/router.js +59 -0
  152. data/test/app/vendor/extjs-mvc/specs-old/string.js +22 -0
  153. data/test/app/vendor/extjs-mvc/testrunner/JSpecFormatter.js +111 -0
  154. data/test/app/vendor/extjs-mvc/testrunner/TestClient.js +181 -0
  155. data/test/app/vendor/extjs-mvc/testrunner/TestGrid.js +351 -0
  156. data/test/app/vendor/extjs-mvc/testrunner/TestRunner.js +110 -0
  157. data/test/app/vendor/extjs-mvc/testrunner/TestViewport.js +94 -0
  158. data/test/app/vendor/extjs-mvc/vendor.yml +30 -0
  159. data/test/app/vendor/extjs-mvc/vendor/ext-3.1.1/vendor.yml +16 -0
  160. data/test/app/vendor/extjs-mvc/view/FormWindow.js +184 -0
  161. data/test/app/vendor/extjs-mvc/view/HasManyEditorGridPanel.js +211 -0
  162. data/test/app/vendor/extjs-mvc/view/scaffold/Edit.js +46 -0
  163. data/test/app/vendor/extjs-mvc/view/scaffold/Index.js +561 -0
  164. data/test/app/vendor/extjs-mvc/view/scaffold/New.js +20 -0
  165. data/test/app/vendor/extjs-mvc/view/scaffold/ScaffoldFormPanel.js +255 -0
  166. data/test/helper.rb +7 -1
  167. data/test/test_extjs-mvc.rb +46 -0
  168. metadata +167 -7
  169. data/test/test_extjs-mvc-gem.rb +0 -7
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @class ExtMVC.Presenter
3
+ * @extends Ext.util.Observable
4
+ * Used as an interface between a controller and its views
5
+ */
6
+ ExtMVC.Presenter = Ext.extend(Ext.util.Observable, {
7
+
8
+ constructor: function(config) {
9
+ ExtMVC.Presenter.superclass.constructor.apply(this, arguments);
10
+
11
+ this.addEvents(
12
+ /**
13
+ * @event load
14
+ * Fires when all items in the Presenter have been loaded
15
+ */
16
+ 'load'
17
+ );
18
+
19
+ /**
20
+ * @property loaded
21
+ * @type Boolean
22
+ * True if all items that must be loaded before rendering have been
23
+ */
24
+ this.loaded = false;
25
+
26
+ /**
27
+ * @property loading
28
+ * @type Boolean
29
+ * True while the loader is loading
30
+ */
31
+ this.loading = false;
32
+ },
33
+
34
+ load: function() {
35
+ if (this.loaded || this.loading) return;
36
+
37
+ this.each(function(item, index, length) {
38
+ var callback = function(index) {
39
+ return function() {
40
+ if (index == length) {
41
+ this.loaded = true;
42
+ this.loading = false;
43
+
44
+ this.fireEvent('load');
45
+ }
46
+ };
47
+ }(index);
48
+
49
+ item.on('load', callback, this, {single: true});
50
+ }, this);
51
+ }
52
+ });
@@ -0,0 +1,69 @@
1
+ = Ext MVC
2
+
3
+ EXT MVC is a complete MVC stack on top of Ext JS (http://extjs.com).
4
+
5
+ == Base App
6
+
7
+ You are advised to use the base app to get up and running with Ext MVC as it sets everything up for you.
8
+
9
+ If you're comfortable with git:
10
+
11
+ 1. git clone git://github.com/extmvc/baseapp.git myapp
12
+ 2. cd myapp
13
+ 3. rm -rf .git/
14
+ 4. git init
15
+
16
+ If not:
17
+
18
+ 1. Download the latest zip file from Github at http://github.com/extmvc/baseapp/zipball/master and unzip.
19
+
20
+ == Getting started
21
+
22
+ Read the readme file inside the base app and refer to http://extmvc.com
23
+
24
+ == Building
25
+
26
+ Ext MVC comes with a set of build tools for itself and your applications, written in Ruby. You need to be using
27
+ the Baseapp or a structure similar to it for these to work effectively.
28
+
29
+ === Building your Ext MVC application
30
+
31
+ Build any app started from the base app by running this from the app's root directory:
32
+
33
+ ruby script/build all
34
+
35
+ This will examine the contents of index.html, pull out all of the javascript includes and concatenate them into
36
+ public/application-all.js. If you have the yui-compressor in your vendor directory (this is included by default)
37
+ and you have java installed, the build process will also minify your application-all.js into application-all-min.js.
38
+
39
+ Building your app also concatenates any CSS includes in index.html into public/application-all.css.
40
+
41
+ *NOTE*: Building looks at your index.html, NOT public/index.html. index.html is your development version,
42
+ public/index.html should simply include those concatenated/minified files.
43
+
44
+ === Autobuild
45
+
46
+ If you need your app to be built every time you change any of the source files for whatever reason, use:
47
+
48
+ ruby script/build auto
49
+
50
+ This will again look at index.html and will force a rebuild any time any of those files are changed.
51
+
52
+ === Other app build options
53
+
54
+ There are several other build commands, most of which are used by the two above:
55
+
56
+ - ruby script/build css - only build CSS files, no JS concatenation or minification
57
+ - ruby script/build js - only builds JS files, not CSS
58
+ - ruby script/build concatenate_js - concatenates but does not attempt to minify your JS
59
+ - ruby script/build minify_js - minifies an already concatenated public/application-all.js file
60
+
61
+ === Building Ext MVC
62
+
63
+ If you are changing Ext MVC files and need to build Ext MVC itself, use:
64
+
65
+ - ruby script/build mvc
66
+ - ruby script/build mvc_auto
67
+
68
+ Again run from the app root directory. Like with ruby script/build auto, mvc_auto automatically rebuilds whenever
69
+ any Ext MVC file is changed.
@@ -0,0 +1,278 @@
1
+ /**
2
+ * @class ExtMVC.controller.Controller
3
+ * @extends Ext.util.Observable
4
+ * <h1>Controllers in Ext MVC</h1>
5
+ * <p>Controllers are the glue that stick applications together. They listen to events emitted by the UI as the user
6
+ * clicks interface elements, and take actions as appropriate. The relevant action may be to create or save a model
7
+ * instance, to render another view, to perform AJAX requests, or any other action.</p>
8
+ *
9
+ * <p>Controllers should be kept skinny as far as possible - receive an event, then hand any processing off to the
10
+ * appropriate model. This ensures we can keep the code as DRY as possible and makes refactoring easier.</p>
11
+ *
12
+ * <h2>Example Controller</h2>
13
+ * Here is a simple example controller which renders a couple of views and listens to events:
14
+ <pre><code>
15
+ //simple controller which manages the Page model within our application
16
+ MyApp.controllers.PagesController = Ext.extend(ExtMVC.controller.Controller, {
17
+ name: 'pages',
18
+
19
+ //renders the 'Index' template and sets up listeners
20
+ index: function() {
21
+ this.render('Index', {
22
+ listeners: {
23
+ scope : this,
24
+ 'edit' : this.edit,
25
+ 'delete': this.destroy
26
+ }
27
+ });
28
+ },
29
+
30
+ //renders the 'Edit' template (let's say it's a FormPanel), and loads the instance
31
+ edit: function(instance) {
32
+ this.render('Edit', {
33
+ listeners: {
34
+ scope : this,
35
+ save : this.update,
36
+ cancel : function() {
37
+ alert("You cancelled the update!");
38
+ }
39
+ }
40
+ }).loadRecord(instance);
41
+ },
42
+
43
+ //when the 'delete' event is fired by our 'Index' template (see the index action), this method is called.
44
+ //In this fictional example, we assume that the templates 'delete' event was called with the single argument
45
+ //of the Page instance the user wishes to destroy
46
+ destroy: function(instance) {
47
+ instance.destroy({
48
+ success: function() {
49
+ alert("The Page was deleted");
50
+ //at this point we might render another page for the user to look at
51
+ },
52
+ failure: function() {
53
+ alert('Curses! The Page could not be deleted');
54
+ }
55
+ });
56
+ },
57
+
58
+ //when the 'save' event is fired by our 'Edit' template, this method is called.
59
+ //Again, we assume that our template fired the event with the Page instance, and also an object with updates
60
+ update: function(instance, updates) {
61
+ //this applies the updates to the model instance and saves
62
+ instance.update(updates, {
63
+ success: function(updatedInstance) {
64
+ alert('Success! It saved');
65
+ //at this point we might render another page for the user to look at
66
+ },
67
+ failure: function(updatedInstance) {
68
+ alert('Darn it. Did not save');
69
+
70
+ //here we're firing the controller's update-failed event, which the view can pick up on
71
+ //The view can simply listen to our Pages controller and add errors from this instance to the form
72
+ //using form.markInvalid(instance.errors.forForm())
73
+ this.fireEvent('update-failed', instance);
74
+ };
75
+ });
76
+ },
77
+
78
+ //Sets up events emitted by this controller. Controllers are expected to fire events, so this method is called
79
+ //automatically when a controller is instantiated. Don't forget to call super here
80
+ initEvents: function() {
81
+ this.addEvents(
82
+ //this event will be fired when the controller can't update a Page instance
83
+ 'update-failed'
84
+ );
85
+
86
+ MyApp.controllers.PagesController.superclass.initEvents.apply(this, arguments);
87
+ }
88
+ })
89
+ </code></pre>
90
+ * Note that many of the methods above are provided by the {@link ExtMVC.controller.CrudController CrudController}
91
+ *
92
+ * <h2>Rendering Views</h2>
93
+ * Each controller can automatically render view classes inside its views package. In the Pages controller above the
94
+ * views package is MyApp.views.pages - the application itself is called MyApp, and the 'pages' segment comes from the
95
+ * controller's 'name' property
96
+ * <br />
97
+ * <br />
98
+ * In the example above, the line: <pre><code>this.render('Edit', {})</code></pre> will automatically find the
99
+ * MyApp.views.pages.Edit class, with the second argument to this.render being a config argument passed to the view's constructor.
100
+ *
101
+ * <br />
102
+ * <h4>Rendering strategies</h4>
103
+ * Not all applications will render views in the same way
104
+ */
105
+ // ExtMVC.controller.Controller = Ext.extend(Ext.util.Observable,
106
+ ExtMVC.registerController('controller', {
107
+
108
+ constructor: function(config) {
109
+ Ext.util.Observable.prototype.constructor.apply(this, arguments);
110
+
111
+ Ext.apply(this, {
112
+ /**
113
+ * @property renderStrategies
114
+ * @type Object
115
+ * An object of the form {xtype: function} which keys a container's xtype to the function to use
116
+ * when rendering a view to that container (see registerRenderStrategy)
117
+ */
118
+ renderStrategies: {}
119
+ }, config || {});
120
+
121
+ this.registerDefaultRenderStrategies();
122
+
123
+ this.initEvents();
124
+ this.initListeners();
125
+ },
126
+
127
+ /**
128
+ * Registers a rendering function for a given container xtype. When a view is rendered via this.render,
129
+ * the xtype of the container it is being rendered to is compared to the registered strategy xtypes, and
130
+ * the most specific match will be used to perform the rendering.
131
+ * @param {String} xtype The container xtype to register
132
+ * @param {Function} strategy The function to call when rendering to a container of the given xtype
133
+ */
134
+ registerRenderStrategy: function(xtype, strategy) {
135
+ this.renderStrategies[xtype] = strategy;
136
+ },
137
+
138
+ /**
139
+ * Returns the strategy function to use when rendering to the given container instance.
140
+ * @param {Ext.Container} container The container to add to
141
+ * @return {Function} The rendering strategy to use
142
+ */
143
+ getRenderStrategy: function(container) {
144
+ var xtypes = container.getXTypes().split("/");
145
+
146
+ for (var i = xtypes.length - 1; i >= 0; i--){
147
+ var strategy = this.renderStrategies[xtypes[i]];
148
+
149
+ if (strategy != undefined) return strategy;
150
+ };
151
+
152
+ throw new Ext.Error("No render strategy could be found for the container you specified");
153
+ },
154
+
155
+ /**
156
+ * @private
157
+ * Adds the default strategies for panel and tabpanel
158
+ */
159
+ registerDefaultRenderStrategies: function() {
160
+ this.registerRenderStrategy('panel', this.panelRenderStrategy);
161
+ this.registerRenderStrategy('tabpanel', this.tabPanelRenderStrategy);
162
+ },
163
+
164
+ /**
165
+ * Sets up events emitted by this controller. This defaults to an empty function and is
166
+ * called automatically when the controller is constructed so can simply be overridden
167
+ */
168
+ initEvents: function() {},
169
+
170
+ /**
171
+ * Sets up events this controller listens to, and the actions the controller should take
172
+ * when each event is received. This defaults to an empty function and is called when
173
+ * the controller is constructed so can simply be overridden
174
+ */
175
+ initListeners: function() {},
176
+
177
+ /**
178
+ * Shows the user a notification message. Usually used to inform user of a successful save, deletion, etc
179
+ * This is an empty function which you must implement yourself
180
+ * @param {String} notice The string notice to display
181
+ */
182
+ showNotice: function(notice) {},
183
+
184
+ /**
185
+ * @property addTo
186
+ * @type Ext.Container
187
+ * The container to add views to using the 'add' renderMethod. Usually set to an Ext.TabPanel instance or similar
188
+ */
189
+ addTo: null,
190
+
191
+ /**
192
+ * Renders a given view name in the way set up by the controller. For this to work you must have passed a
193
+ * 'name' property when creating the controller, which is automatically used to find the view namespace for
194
+ * this controller. For example, in an application called MyApp, and a controller with a name of 'users',
195
+ * the view namespace would be MyApp.views.users, and render('Index') would search for a class called
196
+ * MyApp.views.users.Index and instantiate it with the passed config.
197
+ * An error is thrown if the view could not be found.
198
+ * @param {String} viewName The name of the view class within the view namespace used by this controller
199
+ * @param {Object} config Configuration options passed through to the view class' constructor
200
+ * @return {Ext.Component} The view object that was just created
201
+ */
202
+ render: function render() {
203
+ //handle both method signatures
204
+ switch(arguments.length) {
205
+ case 1:
206
+ //this just falls through into case 2, which provides a config {} if one is not supplied
207
+ case 2:
208
+ var namespace = this.name,
209
+ viewName = arguments[0],
210
+ config = arguments[1] || {};
211
+ break;
212
+ case 3:
213
+ var namespace = arguments[0],
214
+ viewName = arguments[1],
215
+ config = arguments[2] || {};
216
+ break;
217
+ }
218
+
219
+ //we also use this constructor object to define whether or not the view should be added to the default
220
+ //container or not
221
+ Ext.applyIf(config, {
222
+ autoAdd: true,
223
+ addTo : ExtMVC.app.main
224
+ });
225
+
226
+ //NOTE: ExtMVC.getView will throw an error if the view hasn't been defined anywhere yet. At the moment this
227
+ //error will just propagate up as it's probably pretty clear, but we could provide a custom Error message here instead
228
+ var view = new (this.getView(namespace, viewName))(config);
229
+
230
+ if (config.autoAdd === true) {
231
+ if (view.isXType('window')) {
232
+ view.show();
233
+ } else {
234
+ this.getRenderStrategy(config.addTo)(config.addTo, view);
235
+ }
236
+ }
237
+
238
+ return view;
239
+ },
240
+
241
+ /**
242
+ * Just calls ExtMVC.getView and returns. This is here because we override it in Crud Controller
243
+ * @param {String} namespace The view namespace
244
+ * @param {String} name The view name
245
+ * @return {Function} The view constructor function
246
+ */
247
+ getView: function(namespace, name) {
248
+ return ExtMVC.getView(namespace, name);
249
+ },
250
+
251
+ /**
252
+ * @private
253
+ * The tabpanel render strategy
254
+ */
255
+ tabPanelRenderStrategy: function(container, view) {
256
+ var existing = container.getItem(view.id);
257
+
258
+ //don't add a tab with the same id as an existing one
259
+ if (existing == undefined) {
260
+ container.add(view);
261
+ container.doLayout();
262
+ container.activate(view);
263
+ } else {
264
+ container.setActiveTab(view.id);
265
+ view.destroy();
266
+ }
267
+ },
268
+
269
+ /**
270
+ * @private
271
+ * The panel render strategy
272
+ */
273
+ panelRenderStrategy: function(container, view) {
274
+ container.removeAll();
275
+ container.add(view);
276
+ container.doLayout();
277
+ }
278
+ });
@@ -0,0 +1,460 @@
1
+ /**
2
+ * @class ExtMVC.controller.CrudController
3
+ * @extends ExtMVC.controller.Controller
4
+ * <h1>CRUD Controller</h1>
5
+ * <p>The CRUD Controller is an extension of Controller which provides the generic CRUD actions (Create, Read, Update and Delete).
6
+ * Although CRUD Controller provides sensible default options for most cases, it is also highly extensible. Here's an example for
7
+ * managing CRUD operations on a fictional 'Page' model in our app. Note that the name and model are the only required properties
8
+ * for a CRUD controller with default behaviour:</p>
9
+ <pre><code>
10
+ MyApp.controllers.PagesController = Ext.extend(ExtMVC.controller.CrudController, {
11
+ //the name of the controller (see {@link ExtMVC.controller.Controller})
12
+ name : 'pages',
13
+
14
+ //The model that this controller will be providing CRUD services for
15
+ model: MyApp.models.Page,
16
+
17
+ //override the default behaviour that occurs when the 'create' action was successfully completed
18
+ onCreateSuccess: function(instance) {
19
+ alert('new Page successfully created!');
20
+ },
21
+
22
+ //override the listeners that are attached to the Index view. The Index view is usually an instance of
23
+ //{@link ExtMVC.view.scaffold.Index} or a subclass of it.
24
+ getIndexViewListeners: function() {
25
+ return {
26
+ scope : this,
27
+ 'edit': function(instance) {
28
+ alert('inside the Index view (usually a scaffold grid), the user wants to edit an instance');
29
+ }
30
+ };
31
+ },
32
+
33
+ //provide our own implementation for destroy
34
+ destroy: function(instance) {
35
+ alert('user wants to destroy an instance');
36
+ }
37
+ });
38
+ </code></pre>
39
+ *
40
+ * <p>The 3 CRUD Controller methods that take action are create, update and destroy<p>
41
+ * <p>The 3 CRUD Controller methods that render views are index, new and edit</p>
42
+ * <p>By default, CRUD Controllers render the scaffolding views, which provide sensible default views.
43
+ * The index action renders a {@link ExtMVC.view.scaffold.Index Scaffold Grid}, edit renders a
44
+ * {@link ExtMVC.view.scaffold.Edit Scaffold Edit Form} and new renders a {@link ExtMVC.view.scaffold.New Scaffold New Form}</p>
45
+ * <p>To make CRUD controller render a customised view instead of the scaffold, simply define the relevant view and ensure it is
46
+ * available within your code. For example, if you want to show a customised Ext.Panel instead of the Scaffold Grid on index,
47
+ * simply define:</p>
48
+ <pre><code>
49
+ MyApp.views.pages.Index = Ext.extend(Ext.Panel, {
50
+ title: 'My Specialised Index view - replaces the scaffold grid'
51
+ });
52
+ </code></pre>
53
+ * <p>The same applies with Edit and New classes within the appropriate views namespace. See more on view namespaces in
54
+ * {@link ExtMVC.controller.Controller Controller}.</p>
55
+ */
56
+ ExtMVC.registerController('crud', {
57
+ extend: "controller",
58
+
59
+ /**
60
+ * @property model
61
+ * @type Function/Null
62
+ * Defaults to null. If set to a reference to an ExtMVC.model subclass, renderView will attempt to dynamically
63
+ * scaffold any missing views, if the corresponding view is defined in the ExtMVC.view.scaffold package
64
+ */
65
+ model: null,
66
+
67
+ /**
68
+ * Attempts to create a new instance of this controller's associated model
69
+ * @param {Object} data A fields object (e.g. {title: 'My instance'})
70
+ */
71
+ create: function create(data, form) {
72
+ var instance = new this.model(data);
73
+
74
+ instance.save({
75
+ scope: this,
76
+ success: this.onCreateSuccess,
77
+ failure: this.onCreateFailure
78
+ });
79
+ },
80
+
81
+ /**
82
+ * Attempts to find (read) a single model instance by ID
83
+ * @param {Number} id The Id of the instance to read
84
+ */
85
+ read: function read(id) {
86
+ this.model.findById(id, {
87
+ scope: this,
88
+ success: function(instance) {
89
+ this.fireEvent('read', instance);
90
+ },
91
+ failure: function() {
92
+ this.fireEvent('read-failed', id);
93
+ }
94
+ });
95
+ },
96
+
97
+ /**
98
+ * Attempts to update an existing instance with new values. If the update was successful the controller fires
99
+ * the 'update' event and then shows a default notice to the user (this.showUpdatedNotice()) and calls this.index().
100
+ * To cancel this default behaviour, return false from any listener on the 'update' event.
101
+ * @param {ExtMVC.model.Base} instance The existing instance object
102
+ * @param {Object} updates An object containing updates to apply to the instance
103
+ */
104
+ update: function update(instance, updates) {
105
+ for (var key in updates) {
106
+ instance.set(key, updates[key]);
107
+ }
108
+
109
+ instance.save({
110
+ scope: this,
111
+ success: function(instance) {
112
+ this.onUpdateSuccess(instance, updates);
113
+ },
114
+ failure: function() {
115
+ this.onUpdateFailure(instance, updates);
116
+ }
117
+ });
118
+ },
119
+
120
+ /**
121
+ * Attempts to delete an existing instance
122
+ * @param {Mixed} instance The ExtMVC.model.Base subclass instance to delete. Will also accept a string/number ID
123
+ */
124
+ destroy: function destroy(instance) {
125
+ if (instance.destroy == undefined) {
126
+ //if we're passed an ID instead of an instance, make a fake model instance
127
+ var config = {};
128
+ config[this.model.prototype.primaryKey] = parseInt(instance, 10);
129
+ var instance = new (this.model)(config);
130
+ }
131
+
132
+ instance.destroy({
133
+ scope: this,
134
+ success: this.onDestroySuccess,
135
+ failure: this.onDestroyFailure
136
+ });
137
+ },
138
+
139
+ /**
140
+ * Renders the custom Index view if present, otherwise falls back to the default scaffold grid
141
+ * @param {Object} config Optional config object to be passed to the view's constructor
142
+ */
143
+ index: function index(config) {
144
+ config = config || {};
145
+
146
+ Ext.applyIf(config, {
147
+ model : this.model,
148
+ controller : this,
149
+ listeners : this.getIndexViewListeners(),
150
+ viewsPackage: this.viewsPackage
151
+ });
152
+
153
+ var index = this.render('index', config);
154
+
155
+ this.fireEvent('index');
156
+
157
+ return index;
158
+ },
159
+
160
+ /**
161
+ * Renders the custom New view if present, otherwise falls back to the default scaffold New form
162
+ */
163
+ build: function build() {
164
+ var buildView = this.render('new', {
165
+ model : this.model,
166
+ controller : this,
167
+ listeners : this.getBuildViewListeners()
168
+ // items : ExtMVC.getFields(this.name)
169
+ });
170
+
171
+ this.onBuild(buildView);
172
+
173
+ return buildView;
174
+ },
175
+
176
+ /**
177
+ * Renders the custom Edit view if present, otherwise falls back to the default scaffold Edit form
178
+ * @param {Mixed} instance The model instance to edit. If not given an ExtMVC.model.Base
179
+ * instance, a findById() will be called on this controller's associated model
180
+ * @param {Object} viewConfig Optional config object to pass to the view class constructor
181
+ */
182
+ edit: function edit(instance, viewConfig) {
183
+ viewConfig = viewConfig || {};
184
+
185
+ if (instance instanceof Ext.data.Record) {
186
+ Ext.applyIf(viewConfig, {
187
+ model : this.model,
188
+ controller : this,
189
+ listeners : this.getEditViewListeners(),
190
+ // items : ExtMVC.getFields(this.name),
191
+ id : String.format("{0}_edit_{1}", this.name, instance.get(instance.primaryKey))
192
+ });
193
+
194
+ var editView = this.render('edit', viewConfig);
195
+
196
+ editView.loadRecord(instance);
197
+
198
+ this.onEdit(editView, instance);
199
+ this.fireEvent('edit', instance);
200
+
201
+ return editView;
202
+ } else {
203
+ var id = instance;
204
+
205
+ this.model.find(parseInt(id, 10), {
206
+ scope : this,
207
+ success: function(instance) {
208
+ this.edit(instance);
209
+ }
210
+ });
211
+ }
212
+ },
213
+
214
+ /**
215
+ * Returns a listeners object passed to the Index view when rendered inside the index action. This contains
216
+ * default listeners, but can be overridden in subclasses to provide custom behaviour
217
+ * @return {Object} The listeners object
218
+ */
219
+ getIndexViewListeners: function getIndexViewListeners() {
220
+ return {
221
+ scope : this,
222
+ 'delete': this.destroy,
223
+ 'new' : this.build,
224
+ 'edit' : this.edit
225
+ };
226
+ },
227
+
228
+ /**
229
+ * Returns a listeners object passed to the Edit view when rendered inside the edit action. This contains
230
+ * default listeners, but can be overridden in subclasses to provide custom behaviour
231
+ * @return {Object} The listeners object
232
+ */
233
+ getEditViewListeners: function getEditViewListeners() {
234
+ return {
235
+ scope : this,
236
+ cancel: this.index,
237
+ save : this.update
238
+ };
239
+ },
240
+
241
+ /**
242
+ * Returns a listeners object passed to the New view when rendered inside the build action. This contains
243
+ * default listeners, but can be overridden in subclasses to provide custom behaviour
244
+ * @return {Object} The listeners object
245
+ */
246
+ getBuildViewListeners: function getBuildViewListeners() {
247
+ return {
248
+ scope : this,
249
+ cancel: this.index,
250
+ save : this.create
251
+ };
252
+ },
253
+
254
+ /**
255
+ * Called when an instance has been successfully created. This just calls this.showNotice
256
+ * with a default message for this model. Overwrite to provide your own implementation
257
+ */
258
+ showCreatedNotice: function() {
259
+ this.showNotice(String.format("{0} successfully created", this.model.prototype.singularHumanName));
260
+ },
261
+
262
+ /**
263
+ * Called when an instance has been successfully updated. This just calls this.showNotice
264
+ * with a default message for this model. Overwrite to provide your own implementation
265
+ */
266
+ showUpdatedNotice: function() {
267
+ this.showNotice(String.format("{0} successfully updated", this.model.prototype.singularHumanName));
268
+ },
269
+
270
+ /**
271
+ * Called when an instance has been successfully destroyed. This just calls this.showNotice
272
+ * with a default message for this model. Overwrite to provide your own implementation
273
+ */
274
+ showDestroyedNotice: function() {
275
+ this.showNotice(String.format("{0} successfully deleted", this.model.prototype.singularHumanName));
276
+ },
277
+
278
+ /**
279
+ * Called after a successful update. By default this calls showUpdatedNotice and then this.index()
280
+ * @param {ExtMVC.model.Base} instance The newly updated instance
281
+ * @param {Object} updates The updates that were made
282
+ */
283
+ onCreateSuccess: function(instance) {
284
+ if(this.fireEvent('create', instance) !== false) {
285
+ this.showCreatedNotice();
286
+ this.index();
287
+ }
288
+ },
289
+
290
+ /**
291
+ * Called after an unsuccessful update. By default this simply fires the 'update-failed' event
292
+ * @param {ExtMVC.model.Base} instance The instance that could not be updated
293
+ * @param {Object} updates The updates that were attempted to be made
294
+ */
295
+ onCreateFailure: function(instance) {
296
+ this.fireEvent('create-failed', instance);
297
+ },
298
+
299
+ /**
300
+ * Called after a successful update. By default this calls showUpdatedNotice and then this.index()
301
+ * @param {ExtMVC.model.Base} instance The newly updated instance
302
+ * @param {Object} updates The updates that were made
303
+ */
304
+ onUpdateSuccess: function(instance, updates) {
305
+ if (this.fireEvent('update', instance, updates) !== false) {
306
+ this.showUpdatedNotice();
307
+ this.index();
308
+ }
309
+ },
310
+
311
+ /**
312
+ * Called after an unsuccessful update. By default this simply fires the 'update-failed' event
313
+ * @param {ExtMVC.model.Base} instance The instance that could not be updated
314
+ * @param {Object} updates The updates that were attempted to be made
315
+ */
316
+ onUpdateFailure: function(instance, updates) {
317
+ this.fireEvent('update-failed', instance, updates);
318
+ },
319
+
320
+ /**
321
+ * Called after successful destruction of a model instance. By default simply fires the 'delete' event
322
+ * with the instance as a single argument
323
+ * @param {ExtMVC.model.Base} instance The instane that was just destroyed
324
+ */
325
+ onDestroySuccess: function(instance) {
326
+ this.fireEvent('delete', instance);
327
+
328
+ this.showDestroyedNotice();
329
+ },
330
+
331
+ /**
332
+ * Called after unsuccessful destruction of a model instance. By default simply fires the 'delete-failed' event
333
+ * with the instance as a single argument
334
+ * @param {ExtMVC.model.Base} instance The instance that could not be destroyed
335
+ */
336
+ onDestroyFailure: function(instance) {
337
+ this.fireEvent('delete-failed', instance);
338
+ },
339
+
340
+ /**
341
+ * Called whenever the 'New' form has been rendered for a given instance. This is an empty function by default,
342
+ * which you can override to provide your own logic if needed
343
+ * @param {Ext.Component} form The rendered 'New' form
344
+ */
345
+ onBuild: function(form) {},
346
+
347
+ /**
348
+ * Called whenever the Edit form has been rendered for a given instance. This is an empty function by default,
349
+ * which you can override to provide your own logic if needed
350
+ * @param {Ext.Component} form The rendered Edit form
351
+ * @param {ExtMVC.model.Base} instance The instance loaded into the form
352
+ */
353
+ onEdit: function(form) {},
354
+
355
+ /**
356
+ * Sets up events emitted by the CRUD Controller's actions
357
+ */
358
+ initEvents: function() {
359
+ this.addEvents(
360
+ /**
361
+ * @event create
362
+ * Fired when a new instance has been successfully created
363
+ * @param {ExtMVC.model.Base} instance The newly created model instance
364
+ */
365
+ 'create',
366
+
367
+ /**
368
+ * @event create-failed
369
+ * Fired when an attempt to create a new instance failed
370
+ * @param {ExtMVC.model.Base} instance The instance object which couldn't be saved
371
+ */
372
+ 'create-failed',
373
+
374
+ /**
375
+ * @event read
376
+ * Fired when a single instance has been loaded from the database
377
+ * @param {ExtMVC.model.Base} instance The instance instance that was loaded
378
+ */
379
+ 'read',
380
+
381
+ /**
382
+ * @event read-failed
383
+ * Fired when an attempt to load a single instance failed
384
+ * @param {Number} id The ID of the instance we were attempting to find
385
+ */
386
+ 'read-failed',
387
+
388
+ /**
389
+ * @event update
390
+ * Fired when an existing instance has been successfully created
391
+ * @param {ExtMVC.model.Base} instance The updated instance
392
+ * @param {Object} updates The updates object that was supplied
393
+ */
394
+ 'update',
395
+
396
+ /**
397
+ * @event update-failed
398
+ * Fired when an attempty to update an existing instance failed
399
+ * @param {ExtMVC.model.Base} instance The instance we were attempting to update
400
+ * @param {Object} updates The object of updates we were trying to apply
401
+ */
402
+ 'update-failed',
403
+
404
+ /**
405
+ * @event delete
406
+ * Fired when an existing instance has been successfully deleteed
407
+ * @param {ExtMVC.model.Base} instance The instance that was deleteed
408
+ */
409
+ 'delete',
410
+
411
+ /**
412
+ * @event delete-failed
413
+ * Fired when an attempt to delete an existing instance failed
414
+ * @param {ExtMVC.model.Base} instance The instance we were trying to delete
415
+ */
416
+ 'delete-failed',
417
+
418
+ /**
419
+ * @event index
420
+ * Fired when an instances collection has been loaded from the database
421
+ */
422
+ 'index',
423
+
424
+ /**
425
+ * @event edit
426
+ * Fired when an instance is being edited
427
+ * @param {ExtMVC.model.Base} instance The instance being edited
428
+ */
429
+ 'edit'
430
+ );
431
+ },
432
+
433
+ /**
434
+ * If a view of the given viewName is defined in this controllers viewPackage, a reference to its
435
+ * constructor is defined. If not, a reference to the default scaffold for the viewName is returned
436
+ * @param {String} namespace The view namesapce
437
+ * @param {String} name The name of the view to return a constructor function for
438
+ * @return {Function} A reference to the custom view, or the scaffold fallback
439
+ */
440
+ getView: function getView(namespace, name) {
441
+ var view;
442
+
443
+ try {
444
+ view = ExtMVC.getController("controller").getView.apply(this, arguments);
445
+ } catch(e) {
446
+ view = this.scaffoldViewName(name);
447
+ }
448
+
449
+ return view;
450
+ },
451
+
452
+ /**
453
+ * Returns a reference to the Scaffold view class for a given viewName
454
+ * @param {String} viewName The name of the view to return a class for (index, new, edit or show)
455
+ * @return {Function} A reference to the view class to instantiate to render this scaffold view
456
+ */
457
+ scaffoldViewName: function scaffoldViewName(viewName) {
458
+ return ExtMVC.getView('scaffold', viewName);
459
+ }
460
+ });