]> git.proxmox.com Git - extjs.git/blame - extjs/packages/core/src/app/Controller.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / src / app / Controller.js
CommitLineData
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
153Ext.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