]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * Controllers are the glue that binds an application together. That said, their main \r | |
3 | * purpose is to listen for events (usually from views) and take some action. Here's how \r | |
4 | * we might create a Controller to manage Users:\r | |
5 | *\r | |
6 | * Ext.define('MyApp.controller.Users', {\r | |
7 | * extend: 'Ext.app.Controller',\r | |
8 | *\r | |
9 | * init: function() {\r | |
10 | * console.log('Initialized Users! This happens before ' +\r | |
11 | * 'the Application launch() function is called');\r | |
12 | * }\r | |
13 | * });\r | |
14 | *\r | |
15 | * The init function is a special method that is called when your application boots. It is \r | |
16 | * called before the {@link Ext.app.Application Application}'s launch function is executed. \r | |
17 | * This creates an area you can run code prior to Viewport creation.\r | |
18 | *\r | |
19 | * The controller's {@link #method-control} function\r | |
20 | * makes it easy to listen to events on your view classes and take some action with a \r | |
21 | * handler function. Let's update our Users controller to tell us when the panel is \r | |
22 | * rendered:\r | |
23 | *\r | |
24 | * Ext.define('MyApp.controller.Users', {\r | |
25 | * extend: 'Ext.app.Controller',\r | |
26 | *\r | |
27 | * control: {\r | |
28 | * 'viewport > panel': {\r | |
29 | * render: 'onPanelRendered'\r | |
30 | * }\r | |
31 | * }\r | |
32 | *\r | |
33 | * onPanelRendered: function() {\r | |
34 | * console.log('The panel was rendered');\r | |
35 | * }\r | |
36 | * });\r | |
37 | *\r | |
38 | * The {@link Ext.app.BaseController#method-control control method} has now set up \r | |
39 | * listeners on views in our application. The control method uses the ComponentQuery \r | |
40 | * engine to quickly and easily get references to components on the page. If you are not \r | |
41 | * familiar with ComponentQuery yet, be sure to check out the \r | |
42 | * {@link Ext.ComponentQuery documentation}. In brief, it allows us to pass a \r | |
43 | * CSS-like selector that will find every matching component on the page.\r | |
44 | *\r | |
45 | * In our init function above, we supplied 'viewport > panel', which translates to "find me \r | |
46 | * every Panel that is a direct child of a Viewport". We then supplied an object that maps \r | |
47 | * event names (just 'render' in this case) to handler functions. In short, whenever \r | |
48 | * a component that matches our selector fires a 'render' event, our\r | |
49 | * onPanelRendered function is called.\r | |
50 | *\r | |
51 | * ## Event domains\r | |
52 | *\r | |
53 | * In Ext JS 4.2, we introduced the concept of event domains. In terms of MVC, an event \r | |
54 | * domain is one or more base classes that fire events to which a Controller wants to \r | |
55 | * listen. Besides Component event domain that encompass {@link Ext.Component}-descended \r | |
56 | * Views, Controllers now can listen to events from data Stores, Ext Direct Providers, \r | |
57 | * other Controllers, and Ext.GlobalEvents. This feature provides a way to communicate \r | |
58 | * between parts of the whole application without the need to bind controllers together \r | |
59 | * tightly, and allows to develop and test application parts in isolation.\r | |
60 | *\r | |
61 | * See usage examples in {@link #method-listen} method documentation.\r | |
62 | *\r | |
63 | * ## Using refs\r | |
64 | *\r | |
65 | * One of the most useful parts of Controllers is the ref system. These use \r | |
66 | * {@link Ext.ComponentQuery} to make it really easy to get references to Views on your \r | |
67 | * page. Let's look at an example of this now:\r | |
68 | *\r | |
69 | * Ext.define('MyApp.controller.Users', {\r | |
70 | * extend: 'Ext.app.Controller',\r | |
71 | * \r | |
72 | * refs: [{\r | |
73 | * ref: 'list',\r | |
74 | * selector: 'grid'\r | |
75 | * }],\r | |
76 | * \r | |
77 | * control: {\r | |
78 | * 'button': {\r | |
79 | * click: 'refreshGrid'\r | |
80 | * }\r | |
81 | * },\r | |
82 | * \r | |
83 | * refreshGrid: function() {\r | |
84 | * this.getList().store.load();\r | |
85 | * }\r | |
86 | * });\r | |
87 | *\r | |
88 | * This example assumes the existence of a {@link Ext.grid.Panel Grid} on the page, which \r | |
89 | * contains a single button to refresh the Grid when clicked. In our refs array, we set up \r | |
90 | * a reference to the grid. There are two parts to this - the 'selector', which is a \r | |
91 | * {@link Ext.ComponentQuery ComponentQuery} selector which finds any grid on the page and\r | |
92 | * assigns it to the reference 'list'.\r | |
93 | *\r | |
94 | * By giving the reference a name, we get a number of things for free. The first is the \r | |
95 | * getList function that we use in the refreshGrid method above. This is generated \r | |
96 | * automatically by the Controller based on the name of our ref, which was capitalized and \r | |
97 | * prepended with get to go from 'list' to 'getList'.\r | |
98 | *\r | |
99 | * The way this works is that the first time getList is called by your code, the \r | |
100 | * ComponentQuery selector is run and the first component that matches the selector \r | |
101 | * ('grid' in this case) will be returned. All future calls to getList will use a cached \r | |
102 | * reference to that grid. Usually it is advised to use a specific ComponentQuery selector \r | |
103 | * that will only match a single View in your application (in the case above our selector \r | |
104 | * will match any grid on the page).\r | |
105 | *\r | |
106 | * Bringing it all together, we configure control\r | |
107 | * to listen to any click on a {@link Ext.button.Button button} and call our refreshGrid \r | |
108 | * function (again, this will match any button on the page so we advise a more specific \r | |
109 | * selector than just 'button', but have left it this way for simplicity). When the button \r | |
110 | * is clicked we use out getList function to refresh the grid.\r | |
111 | *\r | |
112 | * You can create any number of refs and control any number of components this way, simply \r | |
113 | * adding more functions to your Controller as you go. For an example of real-world usage \r | |
114 | * of Controllers see the Feed Viewer example in the examples/app/feed-viewer folder in \r | |
115 | * the SDK download.\r | |
116 | *\r | |
117 | * ## Generated getter methods\r | |
118 | *\r | |
119 | * Refs aren't the only thing that generate convenient getter methods. Controllers often \r | |
120 | * have to deal with Models and Stores so the framework offers a couple of easy ways to \r | |
121 | * get access to those too. Let's look at another example:\r | |
122 | *\r | |
123 | * Ext.define('MyApp.controller.Users', {\r | |
124 | * extend: 'Ext.app.Controller',\r | |
125 | *\r | |
126 | * models: ['User'],\r | |
127 | * stores: ['AllUsers', 'AdminUsers'],\r | |
128 | *\r | |
129 | * init: function() {\r | |
130 | * var User, allUsers, ed;\r | |
131 | * \r | |
132 | * User = this.getUserModel();\r | |
133 | * allUsers = this.getAllUsersStore();\r | |
134 | *\r | |
135 | * ed = new User({ name: 'Ed' });\r | |
136 | * allUsers.add(ed);\r | |
137 | * }\r | |
138 | * });\r | |
139 | *\r | |
140 | * By specifying Models and Stores that the Controller cares about, it again dynamically \r | |
141 | * loads them from the appropriate locations (app/model/User.js, app/store/AllUsers.js and \r | |
142 | * app/store/AdminUsers.js in this case) and creates getter functions for them all. The \r | |
143 | * example above will create a new User model instance and add it to the AllUsers Store.\r | |
144 | * Of course, you could do anything in this function but in this case we just did \r | |
145 | * something simple to demonstrate the functionality.\r | |
146 | *\r | |
147 | * ## Further Reading\r | |
148 | *\r | |
149 | * For more information about writing Ext JS 5 applications, please see the\r | |
150 | * [Application Architecture](../../../application_architecture/application_architecture.html). \r | |
151 | * Also see the {@link Ext.app.Application} documentation.\r | |
152 | */\r | |
153 | Ext.define('Ext.app.Controller', {\r | |
154 | extend: 'Ext.app.BaseController',\r | |
155 | requires: [\r | |
156 | 'Ext.app.Util',\r | |
157 | 'Ext.data.StoreManager',\r | |
158 | 'Ext.ComponentManager',\r | |
159 | 'Ext.app.domain.Component',\r | |
160 | 'Ext.app.domain.Store',\r | |
161 | 'Ext.app.route.Router'\r | |
162 | ],\r | |
163 | \r | |
164 | statics: {\r | |
165 | strings: {\r | |
166 | model: {\r | |
167 | getter: 'getModel',\r | |
168 | upper: 'Model'\r | |
169 | },\r | |
170 | \r | |
171 | view: {\r | |
172 | getter: 'getView',\r | |
173 | upper: 'View'\r | |
174 | },\r | |
175 | \r | |
176 | controller: {\r | |
177 | getter: 'getController',\r | |
178 | upper: 'Controller'\r | |
179 | },\r | |
180 | \r | |
181 | store: {\r | |
182 | getter: 'getStore',\r | |
183 | upper: 'Store'\r | |
184 | },\r | |
185 | \r | |
186 | profile: {\r | |
187 | getter: 'getProfile',\r | |
188 | upper: 'Profiles'\r | |
189 | }\r | |
190 | },\r | |
191 | \r | |
192 | controllerRegex: /^(.*)\.controller\./,\r | |
193 | profileRegex: /^(.*)\.profile\./,\r | |
194 | \r | |
195 | createGetter: function(baseGetter, name) {\r | |
196 | return function () {\r | |
197 | return this[baseGetter](name);\r | |
198 | };\r | |
199 | },\r | |
200 | \r | |
201 | getGetterName: function(name, kindUpper) {\r | |
202 | var fn = 'get',\r | |
203 | parts = name.split('.'),\r | |
204 | numParts = parts.length,\r | |
205 | index;\r | |
206 | \r | |
207 | // Handle namespaced class names. E.g. feed.Add becomes getFeedAddView etc.\r | |
208 | for (index = 0; index < numParts; index++) {\r | |
209 | fn += Ext.String.capitalize(parts[index]);\r | |
210 | }\r | |
211 | \r | |
212 | fn += kindUpper;\r | |
213 | \r | |
214 | return fn;\r | |
215 | },\r | |
216 | \r | |
217 | resolveNamespace: function(cls, data) {\r | |
218 | var Controller = Ext.app.Controller,\r | |
219 | namespaceRe = cls.prototype.isProfile ? Controller.profileRegex : Controller.controllerRegex,\r | |
220 | className, namespace, match;\r | |
221 | /*\r | |
222 | * Namespace resolution is tricky business: we should know what namespace\r | |
223 | * this Controller descendant belongs to, or model/store/view dependency\r | |
224 | * resolution will be either ambiguous or plainly not possible. To avoid\r | |
225 | * guessing games we try to look for a forward hint ($namespace) that\r | |
226 | * Application class sets when its onClassExtended gets processed; if that\r | |
227 | * fails we try to deduce namespace from class name.\r | |
228 | *\r | |
229 | * Note that for Ext.app.Application, Controller.onClassExtended gets executed\r | |
230 | * *before* Application.onClassExtended so we have to delay namespace handling\r | |
231 | * until after Application.onClassExtended kicks in, hence it is done in this hook.\r | |
232 | */\r | |
233 | className = Ext.getClassName(cls);\r | |
234 | namespace = data.$namespace || data.namespace ||\r | |
235 | Ext.app.getNamespace(className) ||\r | |
236 | ((match = namespaceRe.exec(className)) && match[1]);\r | |
237 | \r | |
238 | //<debug>\r | |
239 | if (!namespace) {\r | |
240 | Ext.log.warn("Missing namespace for " + className + ", please define it "+\r | |
241 | "in namespaces property of your Application class.");\r | |
242 | }\r | |
243 | //</debug>\r | |
244 | \r | |
245 | return namespace;\r | |
246 | },\r | |
247 | \r | |
248 | /**\r | |
249 | * This method is called like so:\r | |
250 | *\r | |
251 | * Ext.app.Controller.processDependencies(proto, requiresArray, 'MyApp', 'model', [\r | |
252 | * 'User',\r | |
253 | * 'Item',\r | |
254 | * 'Foo@Common.model',\r | |
255 | * 'Bar.Baz@Common.model'\r | |
256 | * ]);\r | |
257 | *\r | |
258 | * Required dependencies are added to requiresArray.\r | |
259 | *\r | |
260 | * @private\r | |
261 | */\r | |
262 | processDependencies: function(cls, requires, namespace, kind, names, profileName) {\r | |
263 | if (!names || !names.length) {\r | |
264 | return;\r | |
265 | }\r | |
266 | \r | |
267 | var me = this,\r | |
268 | strings = me.strings[kind],\r | |
269 | o, absoluteName, shortName, name, j, subLn, getterName, getter;\r | |
270 | \r | |
271 | if (!Ext.isArray(names)) {\r | |
272 | names = [names];\r | |
273 | }\r | |
274 | \r | |
275 | for (j = 0, subLn = names.length; j < subLn; j++) {\r | |
276 | name = names[j];\r | |
277 | o = me.getFullName(name, kind, namespace, profileName);\r | |
278 | // Update the name in the array to be the absolute name\r | |
279 | names[j] = absoluteName = o.absoluteName;\r | |
280 | shortName = o.shortName;\r | |
281 | \r | |
282 | requires.push(absoluteName);\r | |
283 | getterName = me.getGetterName(shortName, strings.upper);\r | |
284 | \r | |
285 | if (!cls[getterName]) {\r | |
286 | cls[getterName] = getter = me.createGetter(strings.getter, name);\r | |
287 | }\r | |
288 | //<debug>\r | |
289 | else if (getterName === 'getMainView') {\r | |
290 | Ext.log.warn('Cannot have a view named \'Main\' - getter conflicts with mainView config.')\r | |
291 | }\r | |
292 | //</debug>\r | |
293 | \r | |
294 | // Application class will init the controller getters\r | |
295 | if (getter && kind !== 'controller') {\r | |
296 | // This marker allows the constructor to easily/cheaply identify the\r | |
297 | // generated getter methods since they all need to be called to get\r | |
298 | // things initialized. We use a property name that deliberately does\r | |
299 | // not work with dot-access to reduce any chance of collision.\r | |
300 | getter['Ext.app.getter'] = true;\r | |
301 | }\r | |
302 | }\r | |
303 | },\r | |
304 | \r | |
305 | getFullName: function(name, kind, namespace, profileName) {\r | |
306 | var shortName = name,\r | |
307 | sep, absoluteName;\r | |
308 | \r | |
309 | if ((sep = name.indexOf('@')) > 0) {\r | |
310 | // The unambiguous syntax is Model@Name.space (or "space.Model@Name")\r | |
311 | // which contains both the short name ("Model" or "space.Model") and\r | |
312 | // the full name (Name.space.Model).\r | |
313 | //\r | |
314 | shortName = name.substring(0, sep); // "Model"\r | |
315 | absoluteName = name.substring(sep + 1) + '.' + shortName; // ex: "Name.space.Model"\r | |
316 | }\r | |
317 | // Deciding if a class name must be qualified:\r | |
318 | //\r | |
319 | // 1 - if the name doesn't contain a dot, we must qualify it\r | |
320 | //\r | |
321 | // 2 - the name may be a qualified name of a known class, but:\r | |
322 | //\r | |
323 | // 2.1 - in runtime, the loader may not know the class - specially in\r | |
324 | // production - so we must check the class manager\r | |
325 | //\r | |
326 | // 2.2 - in build time, the class manager may not know the class, but\r | |
327 | // the loader does, so we check the second one (the loader check\r | |
328 | // assures it's really a class, and not a namespace, so we can\r | |
329 | // have 'Books.controller.Books', and requesting a controller\r | |
330 | // called Books will not be underqualified)\r | |
331 | //\r | |
332 | else if (name.indexOf('.') > 0 && (Ext.ClassManager.isCreated(name) ||\r | |
333 | this.hasRegisteredPrefix(name))) {\r | |
334 | absoluteName = name;\r | |
335 | }\r | |
336 | else {\r | |
337 | //<debug>\r | |
338 | if (!namespace) {\r | |
339 | Ext.log.warn("Cannot find namespace for " + kind + " " + name + ", " +\r | |
340 | "assuming it is fully qualified class name");\r | |
341 | }\r | |
342 | //</debug>\r | |
343 | \r | |
344 | if (namespace) {\r | |
345 | absoluteName = namespace + '.' + kind + '.' +\r | |
346 | (profileName ? profileName + '.' + name : name);\r | |
347 | shortName = name;\r | |
348 | }\r | |
349 | else {\r | |
350 | absoluteName = name;\r | |
351 | }\r | |
352 | }\r | |
353 | \r | |
354 | return {\r | |
355 | absoluteName: absoluteName,\r | |
356 | shortName: shortName\r | |
357 | };\r | |
358 | },\r | |
359 | \r | |
360 | hasRegisteredPrefix: function (className) {\r | |
361 | var inventory = Ext.ClassManager,\r | |
362 | prefix = inventory.getPrefix(className);\r | |
363 | \r | |
364 | // It's a class if className is not equal to any known namespace\r | |
365 | return prefix && prefix !== className;\r | |
366 | }\r | |
367 | },\r | |
368 | \r | |
369 | // @cmd-auto-dependency {aliasPrefix : "model.", mvc : true, blame: "all"}\r | |
370 | /**\r | |
371 | * @cfg {String/String[]} models\r | |
372 | * Array of models to require from AppName.model namespace. For example:\r | |
373 | *\r | |
374 | * Ext.define("MyApp.controller.Foo", {\r | |
375 | * extend: "Ext.app.Controller",\r | |
376 | * models: ['User', 'Vehicle']\r | |
377 | * });\r | |
378 | *\r | |
379 | * This is equivalent to:\r | |
380 | *\r | |
381 | * Ext.define("MyApp.controller.Foo", {\r | |
382 | * extend: "Ext.app.Controller",\r | |
383 | * requires: ['MyApp.model.User', 'MyApp.model.Vehicle'],\r | |
384 | * \r | |
385 | * getUserModel: function() {\r | |
386 | * return this.getModel("User");\r | |
387 | * },\r | |
388 | * \r | |
389 | * getVehicleModel: function() {\r | |
390 | * return this.getModel("Vehicle");\r | |
391 | * }\r | |
392 | * });\r | |
393 | *\r | |
394 | * **Note:** If the model has a different namespace than that of the \r | |
395 | * application you will need to specify the full class name as well as define a path \r | |
396 | * in the {@link Ext.Loader#cfg-paths Loader's paths} config or \r | |
397 | * {@link Ext.Loader#method-setPath setPath} method.\r | |
398 | */\r | |
399 | models: null,\r | |
400 | \r | |
401 | // @cmd-auto-dependency {aliasPrefix: "view.", mvc: true, blame: "all"}\r | |
402 | /**\r | |
403 | * @cfg {String/String[]} views\r | |
404 | * Array of views to require from AppName.view namespace and to generate getter methods for.\r | |
405 | * For example:\r | |
406 | *\r | |
407 | * Ext.define("MyApp.controller.Foo", {\r | |
408 | * extend: "Ext.app.Controller",\r | |
409 | * views: ['List', 'Detail']\r | |
410 | * });\r | |
411 | *\r | |
412 | * This is equivalent to:\r | |
413 | *\r | |
414 | * Ext.define("MyApp.controller.Foo", {\r | |
415 | * extend: "Ext.app.Controller",\r | |
416 | * requires: ['MyApp.view.List', 'MyApp.view.Detail'],\r | |
417 | * \r | |
418 | * getListView: function() {\r | |
419 | * return this.getView("List");\r | |
420 | * },\r | |
421 | * \r | |
422 | * getDetailView: function() {\r | |
423 | * return this.getView("Detail");\r | |
424 | * }\r | |
425 | * });\r | |
426 | * \r | |
427 | * **Note:** If the view has a different namespace than that of the \r | |
428 | * application you will need to specify the full class name as well as define a path \r | |
429 | * in the {@link Ext.Loader#cfg-paths Loader's paths} config or \r | |
430 | * {@link Ext.Loader#method-setPath setPath} method.\r | |
431 | */\r | |
432 | views: null,\r | |
433 | \r | |
434 | // @cmd-auto-dependency {aliasPrefix: "store.", mvc: true, blame: "all"}\r | |
435 | /**\r | |
436 | * @cfg {String/String[]} stores\r | |
437 | * Array of stores to require from AppName.store namespace and to generate getter methods for.\r | |
438 | * For example:\r | |
439 | *\r | |
440 | * Ext.define("MyApp.controller.Foo", {\r | |
441 | * extend: "Ext.app.Controller",\r | |
442 | * stores: ['Users', 'Vehicles']\r | |
443 | * });\r | |
444 | *\r | |
445 | * This is equivalent to:\r | |
446 | *\r | |
447 | * Ext.define("MyApp.controller.Foo", {\r | |
448 | * extend: "Ext.app.Controller",\r | |
449 | * \r | |
450 | * requires: [\r | |
451 | * 'MyApp.store.Users',\r | |
452 | * 'MyApp.store.Vehicles'\r | |
453 | * ]\r | |
454 | * \r | |
455 | * getUsersStore: function() {\r | |
456 | * return this.getStore("Users");\r | |
457 | * },\r | |
458 | *\r | |
459 | * getVehiclesStore: function() {\r | |
460 | * return this.getStore("Vehicles");\r | |
461 | * }\r | |
462 | * });\r | |
463 | * \r | |
464 | * **Note:** If the store has a different namespace than that of the \r | |
465 | * application you will need to specify the full class name as well as define a path \r | |
466 | * in the {@link Ext.Loader#cfg-paths Loader's paths} config or \r | |
467 | * {@link Ext.Loader#method-setPath setPath} method.\r | |
468 | */\r | |
469 | stores: null,\r | |
470 | \r | |
471 | // @cmd-auto-dependency {aliasPrefix: "controller.", mvc: true, blame: "all"}\r | |
472 | controllers: null,\r | |
473 | \r | |
474 | config : {\r | |
475 | /**\r | |
476 | * @cfg {Ext.app.Application} application The {@link Ext.app.Application} for this controller accessible via the getApplication method.\r | |
477 | * @accessor\r | |
478 | * @readonly\r | |
479 | */\r | |
480 | application: null,\r | |
481 | \r | |
482 | /**\r | |
483 | * @cfg {Object/Object[]} refs\r | |
484 | * @accessor\r | |
485 | *\r | |
486 | * The refs config creates a getter method on the controller that internally \r | |
487 | * uses Ext.ComponentQuery to fetch the component instance using the configured \r | |
488 | * selector. The following example will add the `getList` method to \r | |
489 | * the controller and will return the first component in the application \r | |
490 | * hierarchy with an xtype of "grid". By default, *undefined* will be returned \r | |
491 | * when the query does not locate the target component.\r | |
492 | *\r | |
493 | * Ext.define('MyApp.controller.Foo', {\r | |
494 | * extend: 'Ext.app.Controller',\r | |
495 | * \r | |
496 | * refs: [{\r | |
497 | * ref: 'list',\r | |
498 | * selector: 'grid'\r | |
499 | * }]\r | |
500 | * });\r | |
501 | *\r | |
502 | * The following fields may be used in the ref definition:\r | |
503 | *\r | |
504 | * - `ref` - name of the reference.\r | |
505 | * - `selector` - Ext.ComponentQuery selector to access the component.\r | |
506 | * - `autoCreate` - True to create the component automatically if not found on \r | |
507 | * page.\r | |
508 | * - `forceCreate` - True to force the creation of the component every time \r | |
509 | * reference is accessed (when `get<REFNAME>` is called).\r | |
510 | * - `xtype` - Used to create the component by its xtype with `autoCreate` or \r | |
511 | * `forceCreate`. If you don't provide `xtype`, an Ext.Component instance will \r | |
512 | * be created.\r | |
513 | * \r | |
514 | * The following example will create a `getList` and `getUser` method on the \r | |
515 | * controller.\r | |
516 | * \r | |
517 | * Ext.define('MyApp.controller.Foo', {\r | |
518 | * extend: 'Ext.app.Controller',\r | |
519 | * \r | |
520 | * refs: [{\r | |
521 | * list: 'grid',\r | |
522 | * user: {\r | |
523 | * autoCreate: true,\r | |
524 | * selector: 'form',\r | |
525 | * xtype: 'form'\r | |
526 | * }\r | |
527 | * }]\r | |
528 | * });\r | |
529 | */\r | |
530 | refs: null,\r | |
531 | \r | |
532 | active: true,\r | |
533 | \r | |
534 | /**\r | |
535 | * @private\r | |
536 | */\r | |
537 | moduleClassName: null\r | |
538 | },\r | |
539 | \r | |
540 | onClassExtended: function(cls, data, hooks) {\r | |
541 | var onBeforeClassCreated = hooks.onBeforeCreated;\r | |
542 | \r | |
543 | hooks.onBeforeCreated = function(cls, data) {\r | |
544 | var Controller = Ext.app.Controller,\r | |
545 | requires = [],\r | |
546 | namespace, proto;\r | |
547 | \r | |
548 | proto = cls.prototype;\r | |
549 | \r | |
550 | namespace = Controller.resolveNamespace(cls, data);\r | |
551 | \r | |
552 | if (namespace) {\r | |
553 | proto.$namespace = namespace;\r | |
554 | }\r | |
555 | \r | |
556 | Controller.processDependencies(proto, requires, namespace, 'model', data.models);\r | |
557 | Controller.processDependencies(proto, requires, namespace, 'view', data.views);\r | |
558 | Controller.processDependencies(proto, requires, namespace, 'store', data.stores);\r | |
559 | Controller.processDependencies(proto, requires, namespace, 'controller', data.controllers);\r | |
560 | \r | |
561 | Ext.require(requires, Ext.Function.pass(onBeforeClassCreated, arguments, this));\r | |
562 | };\r | |
563 | },\r | |
564 | \r | |
565 | /**\r | |
566 | * Creates new Controller.\r | |
567 | *\r | |
568 | * @param {Object} [config] Configuration object.\r | |
569 | */\r | |
570 | constructor: function(config) {\r | |
571 | this.initAutoGetters();\r | |
572 | this.callParent(arguments);\r | |
573 | },\r | |
574 | \r | |
575 | /**\r | |
576 | * @private\r | |
577 | * Takes either an object and transforms it into an array. The following are valid refs values:\r | |
578 | *\r | |
579 | * refs: {\r | |
580 | * myComponent: 'container'\r | |
581 | * }\r | |
582 | *\r | |
583 | * refs: {\r | |
584 | * myComponent: {\r | |
585 | * selector: 'container'\r | |
586 | * }\r | |
587 | * }\r | |
588 | *\r | |
589 | * refs: [\r | |
590 | * {\r | |
591 | * ref: 'myComponent',\r | |
592 | * selector: 'container'\r | |
593 | * }\r | |
594 | * ]\r | |
595 | *\r | |
596 | * @param {Array|Object} refs The refs to normalize\r | |
597 | * @param {Array} newRefs An array to place the normalized refs on to\r | |
598 | * @return {Array} The normalized array of refs\r | |
599 | */\r | |
600 | normalizeRefs: function(refs) {\r | |
601 | var me = this,\r | |
602 | newRefs = [];\r | |
603 | \r | |
604 | if (refs) {\r | |
605 | if (Ext.isObject(refs)) {\r | |
606 | Ext.Object.each(refs, function(key, value) {\r | |
607 | if (Ext.isString(value)) {\r | |
608 | value = {\r | |
609 | selector : value\r | |
610 | };\r | |
611 | }\r | |
612 | \r | |
613 | value.ref = key;\r | |
614 | \r | |
615 | newRefs.push(value);\r | |
616 | });\r | |
617 | } else if (Ext.isArray(refs)) {\r | |
618 | newRefs = Ext.Array.merge(newRefs, refs);\r | |
619 | }\r | |
620 | }\r | |
621 | \r | |
622 | refs = me.refs;\r | |
623 | \r | |
624 | if (refs) {\r | |
625 | me.refs = null;\r | |
626 | \r | |
627 | refs = me.normalizeRefs(refs);\r | |
628 | \r | |
629 | if (refs) {\r | |
630 | newRefs = Ext.Array.merge(newRefs, refs);\r | |
631 | }\r | |
632 | }\r | |
633 | \r | |
634 | return newRefs;\r | |
635 | },\r | |
636 | \r | |
637 | /**\r | |
638 | * Returns a map of reference names to selectors\r | |
639 | * @private\r | |
640 | */\r | |
641 | getRefMap: function() {\r | |
642 | var me = this,\r | |
643 | refMap = me._refMap,\r | |
644 | refs, ref, ln, i;\r | |
645 | \r | |
646 | if (!refMap) {\r | |
647 | refs = me.getRefs();\r | |
648 | refMap = me._refMap = {};\r | |
649 | \r | |
650 | if (refs) {\r | |
651 | for (i = 0, ln = refs.length; i < ln; i++) {\r | |
652 | ref = refs[i];\r | |
653 | refMap[ref.ref] = ref.selector;\r | |
654 | }\r | |
655 | }\r | |
656 | }\r | |
657 | \r | |
658 | return refMap;\r | |
659 | },\r | |
660 | \r | |
661 | applyRefs: function(refs) {\r | |
662 | return this.normalizeRefs(Ext.clone(refs));\r | |
663 | },\r | |
664 | \r | |
665 | /**\r | |
666 | * @param {Object} refs The refs to pass to the {@link #ref} method.\r | |
667 | * @private\r | |
668 | */\r | |
669 | updateRefs: function(refs) {\r | |
670 | if (refs) {\r | |
671 | this.ref(refs);\r | |
672 | }\r | |
673 | },\r | |
674 | \r | |
675 | initAutoGetters: function() {\r | |
676 | var proto = this.self.prototype,\r | |
677 | prop, fn;\r | |
678 | \r | |
679 | for (prop in proto) {\r | |
680 | fn = proto[prop];\r | |
681 | \r | |
682 | // Look for the marker placed on the getters by processDependencies so that\r | |
683 | // we can know what to call cheaply:\r | |
684 | if (fn && fn['Ext.app.getter']) {\r | |
685 | fn.call(this);\r | |
686 | }\r | |
687 | }\r | |
688 | },\r | |
689 | \r | |
690 | doInit: function(app) {\r | |
691 | var me = this;\r | |
692 | \r | |
693 | if (!me._initialized) {\r | |
694 | me.init(app);\r | |
695 | me._initialized = true;\r | |
696 | }\r | |
697 | },\r | |
698 | \r | |
699 | finishInit: function(app) {\r | |
700 | var me = this,\r | |
701 | controllers = me.controllers,\r | |
702 | controller, i, l;\r | |
703 | \r | |
704 | if (me._initialized && controllers && controllers.length) {\r | |
705 | for (i = 0, l = controllers.length; i < l; i++) {\r | |
706 | controller = me.getController(controllers[i]);\r | |
707 | controller.finishInit(app);\r | |
708 | }\r | |
709 | }\r | |
710 | },\r | |
711 | \r | |
712 | /**\r | |
713 | * @method\r | |
714 | *\r | |
715 | * A template method that is called when your application boots. It is called before the\r | |
716 | * {@link Ext.app.Application Application}'s launch function is executed so gives a hook point\r | |
717 | * to run any code before your Viewport is created.\r | |
718 | *\r | |
719 | * @param {Ext.app.Application} application\r | |
720 | *\r | |
721 | * @template\r | |
722 | */\r | |
723 | init: Ext.emptyFn,\r | |
724 | \r | |
725 | /**\r | |
726 | * @method\r | |
727 | *\r | |
728 | * A template method like {@link #init}, but called after the viewport is created.\r | |
729 | * This is called after the {@link Ext.app.Application#launch launch} method of Application\r | |
730 | * is executed.\r | |
731 | *\r | |
732 | * @param {Ext.app.Application} application\r | |
733 | *\r | |
734 | * @template\r | |
735 | */\r | |
736 | onLaunch: Ext.emptyFn,\r | |
737 | \r | |
738 | /**\r | |
739 | * Allow the controller to resume receiving events from the event bus.\r | |
740 | * Routes will also be able to begin firing on this controller.\r | |
741 | * Also see {@link #deactivate}.\r | |
742 | */\r | |
743 | activate: function() {\r | |
744 | this.setActive(true);\r | |
745 | },\r | |
746 | \r | |
747 | /**\r | |
748 | * Prevent this controller from receiving events from the event bus.\r | |
749 | * Routes will also not be triggered on inactive controllers unless\r | |
750 | * the {@link Ext.app.route.Route#allowInactive} flag is set.\r | |
751 | * Also see {@link #activate}.\r | |
752 | */\r | |
753 | deactivate: function() {\r | |
754 | this.setActive(false);\r | |
755 | },\r | |
756 | \r | |
757 | /**\r | |
758 | * Checks if this controller is active. See {@link #activate} & \r | |
759 | * {@link #deactivate}.\r | |
760 | * @return {Boolean} `true` if this controller is active.\r | |
761 | */\r | |
762 | isActive: function() {\r | |
763 | return this.getActive();\r | |
764 | },\r | |
765 | \r | |
766 | ref: function(refs) {\r | |
767 | var me = this,\r | |
768 | i = 0,\r | |
769 | length = refs.length,\r | |
770 | info, ref, fn;\r | |
771 | \r | |
772 | refs = Ext.Array.from(refs);\r | |
773 | \r | |
774 | me.references = me.references || [];\r | |
775 | \r | |
776 | for (; i < length; i++) {\r | |
777 | info = refs[i];\r | |
778 | ref = info.ref;\r | |
779 | fn = 'get' + Ext.String.capitalize(ref);\r | |
780 | \r | |
781 | if (!me[fn]) {\r | |
782 | me[fn] = Ext.Function.pass(me.getRef, [ref, info], me);\r | |
783 | }\r | |
784 | me.references.push(ref.toLowerCase());\r | |
785 | }\r | |
786 | },\r | |
787 | \r | |
788 | /**\r | |
789 | * Registers one or more {@link #refs references}.\r | |
790 | *\r | |
791 | * @param {Object/Object[]} refs\r | |
792 | */\r | |
793 | addRef: function(refs) {\r | |
794 | this.ref(refs);\r | |
795 | },\r | |
796 | \r | |
797 | getRef: function(ref, info, config) {\r | |
798 | var me = this,\r | |
799 | refCache = me.refCache || (me.refCache = {}),\r | |
800 | cached = refCache[ref];\r | |
801 | \r | |
802 | info = info || {};\r | |
803 | config = config || {};\r | |
804 | \r | |
805 | Ext.apply(info, config);\r | |
806 | \r | |
807 | if (info.forceCreate) {\r | |
808 | return Ext.ComponentManager.create(info, 'component');\r | |
809 | }\r | |
810 | \r | |
811 | if (!cached) {\r | |
812 | if (info.selector) {\r | |
813 | refCache[ref] = cached = Ext.ComponentQuery.query(info.selector)[0];\r | |
814 | }\r | |
815 | \r | |
816 | if (!cached && info.autoCreate) {\r | |
817 | refCache[ref] = cached = Ext.ComponentManager.create(info, 'component');\r | |
818 | }\r | |
819 | \r | |
820 | if (cached) {\r | |
821 | cached.on('beforedestroy', function() {\r | |
822 | refCache[ref] = null;\r | |
823 | });\r | |
824 | }\r | |
825 | }\r | |
826 | \r | |
827 | return cached;\r | |
828 | },\r | |
829 | \r | |
830 | /**\r | |
831 | * Returns `true` if a {@link #refs reference} is registered.\r | |
832 | *\r | |
833 | * @param {String} ref The name of the ref to check for.\r | |
834 | * @return {Boolean}\r | |
835 | */\r | |
836 | hasRef: function(ref) {\r | |
837 | var references = this.references;\r | |
838 | return references && Ext.Array.indexOf(references, ref.toLowerCase()) !== -1;\r | |
839 | },\r | |
840 | \r | |
841 | /**\r | |
842 | * Returns instance of a {@link Ext.app.Controller Controller} with the given id.\r | |
843 | * When controller doesn't exist yet, it's created. Note that this method depends\r | |
844 | * on Application instance and will return undefined when Application is not\r | |
845 | * accessible. The only exception is when this Controller instance's id is requested;\r | |
846 | * in that case we always return the instance even if Application is no available.\r | |
847 | *\r | |
848 | * @param {String} id\r | |
849 | *\r | |
850 | * @return {Ext.app.Controller} controller instance or undefined.\r | |
851 | */\r | |
852 | getController: function(id) {\r | |
853 | var app = this.getApplication();\r | |
854 | \r | |
855 | if (id === this.getId()) {\r | |
856 | return this;\r | |
857 | }\r | |
858 | \r | |
859 | return app && app.getController(id);\r | |
860 | },\r | |
861 | \r | |
862 | /**\r | |
863 | * Returns instance of a {@link Ext.data.Store Store} with the given name.\r | |
864 | * When store doesn't exist yet, it's created.\r | |
865 | *\r | |
866 | * @param {String} name\r | |
867 | *\r | |
868 | * @return {Ext.data.Store} a store instance.\r | |
869 | */\r | |
870 | getStore: function(name) {\r | |
871 | var storeId, store;\r | |
872 | \r | |
873 | storeId = (name.indexOf('@') === -1) ? name : name.split('@')[0];\r | |
874 | store = Ext.StoreManager.get(storeId);\r | |
875 | \r | |
876 | if (!store) {\r | |
877 | name = Ext.app.Controller.getFullName(name, 'store', this.$namespace);\r | |
878 | \r | |
879 | if (name) {\r | |
880 | store = Ext.create(name.absoluteName, {\r | |
881 | // Use id here. If the store has a configured storeId, \r | |
882 | // that will take precedence\r | |
883 | id: storeId\r | |
884 | });\r | |
885 | }\r | |
886 | }\r | |
887 | \r | |
888 | return store;\r | |
889 | },\r | |
890 | \r | |
891 | /**\r | |
892 | * Returns a {@link Ext.data.Model Model} class with the given name.\r | |
893 | *\r | |
894 | * @param {String} name\r | |
895 | * @return {Ext.Class} A class ultimately derived from `Ext.data.Model`.\r | |
896 | */\r | |
897 | getModel: function(model) {\r | |
898 | var name = Ext.app.Controller.getFullName(model, 'model', this.$namespace),\r | |
899 | ret = Ext.ClassManager.get(name.absoluteName);\r | |
900 | \r | |
901 | if (!ret) {\r | |
902 | ret = Ext.data.schema.Schema.lookupEntity(model);\r | |
903 | }\r | |
904 | \r | |
905 | return ret;\r | |
906 | },\r | |
907 | \r | |
908 | /**\r | |
909 | * Returns instance of a {@link Ext.app.Profile Profile} with the given name.\r | |
910 | *\r | |
911 | * @param {String} name\r | |
912 | *\r | |
913 | * @return {String} a profile instance.\r | |
914 | */\r | |
915 | getProfile: function(name) {\r | |
916 | name = Ext.app.Controller.getFullName(name, 'profile', this.$namespace);\r | |
917 | return name;\r | |
918 | },\r | |
919 | \r | |
920 | /**\r | |
921 | * Returns a View class with the given name. To create an instance of the view,\r | |
922 | * you can use it like it's used by Application to create the Viewport:\r | |
923 | *\r | |
924 | * this.getView('Viewport').create();\r | |
925 | *\r | |
926 | * @param {String} view\r | |
927 | *\r | |
928 | * @return {Ext.Base} a view class.\r | |
929 | */\r | |
930 | getView: function(view) {\r | |
931 | var name = Ext.app.Controller.getFullName(view, 'view', this.$namespace);\r | |
932 | return name && Ext.ClassManager.get(name.absoluteName);\r | |
933 | },\r | |
934 | \r | |
935 | ensureId: function() {\r | |
936 | var id = this.getId();\r | |
937 | \r | |
938 | if (!id) {\r | |
939 | this.setId(this.getModuleClassName(this.$className, 'controller'));\r | |
940 | } \r | |
941 | },\r | |
942 | \r | |
943 | destroy: function(destroyRefs, /* private */ fromApp) {\r | |
944 | var me = this,\r | |
945 | app = me.application,\r | |
946 | refCache, ref;\r | |
947 | \r | |
948 | if (!fromApp && app) {\r | |
949 | app.unregister(me);\r | |
950 | }\r | |
951 | \r | |
952 | me.application = null;\r | |
953 | \r | |
954 | if (destroyRefs) {\r | |
955 | // Possible destroy stores here too?\r | |
956 | refCache = me.refCache;\r | |
957 | for (ref in refCache) {\r | |
958 | if (refCache.hasOwnProperty(ref)) {\r | |
959 | Ext.destroy(refCache[ref]);\r | |
960 | }\r | |
961 | }\r | |
962 | }\r | |
963 | me.callParent();\r | |
964 | }\r | |
965 | });\r |