]> git.proxmox.com Git - extjs.git/blame - extjs/packages/core/src/app/route/Route.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / src / app / route / Route.js
CommitLineData
6527f429
DM
1/**\r
2 * Represents a mapping between a url and a controller/action pair. May also contain\r
3 * additional params.\r
4 *\r
5 * This is a private internal class that should not need to be used by end-developer code.\r
6 * Its API and existence are subject to change so use at your own risk.\r
7 *\r
8 * @private\r
9 */\r
10Ext.define('Ext.app.route.Route', {\r
11 /**\r
12 * @cfg {String} action The name of the action that will be called on the\r
13 * {@link #controller} if this route is matched.\r
14 */\r
15 action: null,\r
16\r
17 /**\r
18 * @cfg {Object} conditions Optional set of conditions for each token in the url\r
19 * string. Each key should be one of the tokens, each value should be a regex that the\r
20 * token should accept. For example, if you have a Route with a url like\r
21 * `"files/:fileName"` and you want it to match urls like "files/someImage.jpg" then\r
22 * you can set these conditions to allow the :fileName token to accept strings\r
23 * containing a period ("."):\r
24 *\r
25 * conditions: {\r
26 * ':fileName': "[0-9a-zA-Z\.]+"\r
27 * }\r
28 */\r
29 conditions: null,\r
30\r
31 /**\r
32 * @cfg {String} controller The name of the Controller whose {@link #action} will be\r
33 * called if this route is matched.\r
34 */\r
35 controller: null,\r
36 \r
37 /**\r
38 * @cfg {Boolean} allowInactive `true` to allow this route to be triggered on\r
39 * a controller that is not active.\r
40 */\r
41 allowInactive: false,\r
42\r
43 /**\r
44 * @cfg {String} url (required) The url regex to match against.\r
45 */\r
46 url: null,\r
47\r
48 /**\r
49 * @cfg {Function} before An optional function used to intercept {@link #action}\r
50 * to do perform additional tasks and possibly stop the execution. An example is if the route is\r
51 * for editing a user and you need to verify the current user has permission. You could\r
52 * send an {@link Ext.Ajax} request to a server or some arbitrary code.\r
53 *\r
54 * This function MUST be executed by passing in a Boolean\r
55 * value to allow execution of the configured action on {@link Ext.app.route.Route}.\r
56 *\r
57 * Defaults to `null`\r
58 */\r
59 before: null,\r
60\r
61 /**\r
62 * @cfg {Boolean} caseInsensitive `true` to allow the tokens to be matched with\r
63 * case-insensitive. Defaults to `false` which will force case matching.\r
64 */\r
65 caseInsensitive: false,\r
66\r
67 /**\r
68 * A regular expression to match the token to the configured {@link #url}.\r
69 *\r
70 * @private\r
71 */\r
72 matcherRegex: null,\r
73\r
74 /**\r
75 * A regular expression to check if there are parameters in the configured {@link #url}.\r
76 *\r
77 * @private\r
78 */\r
79 paramMatchingRegex: null,\r
80\r
81 /**\r
82 * An array of parameters in the configured {@link #url}.\r
83 *\r
84 * @private\r
85 */\r
86 paramsInMatchString: null,\r
87\r
88 constructor : function(config) {\r
89 var me = this,\r
90 url;\r
91\r
92 Ext.apply(me, config, {\r
93 conditions : {}\r
94 });\r
95\r
96 url = me.url;\r
97\r
98 me.paramMatchingRegex = new RegExp(/:([0-9A-Za-z\_]*)/g);\r
99 me.paramsInMatchString = url.match(me.paramMatchingRegex) || [];\r
100 me.matcherRegex = me.createMatcherRegex(url);\r
101 },\r
102\r
103 /**\r
104 * Attempts to recognize a given url string and return controller/action pair for it.\r
105 *\r
106 * @param {String} url The url to recognize.\r
107 * @return {Object/Boolean} The matched data, or `false` if no match.\r
108 */\r
109 recognize : function(url) {\r
110 var me = this,\r
111 controller = me.controller,\r
112 matches, args;\r
113\r
114 if ((me.allowInactive || controller.isActive()) && me.recognizes(url)) {\r
115 //find parameter matches\r
116 matches = me.matchesFor(url);\r
117 //find the arguments for the parameters\r
118 args = url.match(me.matcherRegex);\r
119\r
120 //first one is the entire match, remove\r
121 args.shift();\r
122\r
123 return Ext.applyIf(matches, {\r
124 controller : controller,\r
125 action : me.action,\r
126 historyUrl : url,\r
127 args : args\r
128 });\r
129 }\r
130\r
131 return false;\r
132 },\r
133\r
134 /**\r
135 * Returns true if this {@link Ext.app.route.Route} matches the given url string.\r
136 *\r
137 * @private\r
138 * @param {String} url The url to test.\r
139 * @return {Boolean} `true` if this {@link Ext.app.route.Route} recognizes the url.\r
140 */\r
141 recognizes : function (url) {\r
142 return this.matcherRegex.test(url);\r
143 },\r
144\r
145 /**\r
146 * The method to execute the action using the configured before function which will\r
147 * kick off the actual {@link #action} on the {@link #controller}.\r
148 *\r
149 * @private\r
150 * @param {String} token The hash to execute with.\r
151 * @param {Object} argConfig The object from the {@link Ext.app.route.Route}'s\r
152 * recognize method call.\r
153 * @param {Function} callback An optional callback function to execute after the\r
154 * {@link #action} is executed.\r
155 * @param {Object} scope The scope to execute the callback with, defaults to this\r
156 * {@link Ext.app.route.Route}.\r
157 */\r
158 execute : function(token, argConfig, callback, scope) {\r
159 var args = argConfig.args || [],\r
160 before = this.before,\r
161 controller = this.controller,\r
162 beforeCallback = this.createCallback(argConfig, callback, scope);\r
163\r
164 if (before) {\r
165 args.push(beforeCallback);\r
166\r
167 if (Ext.isString(before)) {\r
168 //get method from the controller\r
169 before = this.before = controller[before];\r
170 }\r
171\r
172 if (before) {\r
173 before.apply(controller, args);\r
174 }\r
175 //<debug>\r
176 else {\r
177 Ext.log.warn('The before action: ' + this.before +\r
178 ' was not found on the controller. The action method will not be executed.');\r
179 }\r
180 //</debug>\r
181 } else {\r
182 //If no before was specified, proceed to action\r
183 beforeCallback.resume();\r
184 }\r
185 },\r
186\r
187 /**\r
188 * Returns a hash of matching url segments for the given url.\r
189 *\r
190 * @private\r
191 * @param {String} url The url to extract matches for\r
192 * @return {Object} matching url segments\r
193 */\r
194 matchesFor : function (url) {\r
195 var params = {},\r
196 keys = this.paramsInMatchString,\r
197 values = url.match(this.matcherRegex),\r
198 i = 0,\r
199 len = keys.length;\r
200\r
201 //first value is the entire match so reject\r
202 values.shift();\r
203\r
204 for (; i < len; i++) {\r
205 params[keys[i].replace(':', '')] = values[i];\r
206 }\r
207\r
208 return params;\r
209 },\r
210\r
211 /**\r
212 * Takes the configured url string including wildcards and returns a regex that can be\r
213 * used to match against a url.\r
214 *\r
215 * @private\r
216 * @param {String} url The url string.\r
217 * @return {RegExp} The matcher regex.\r
218 */\r
219 createMatcherRegex : function (url) {\r
220 // Converts a route string into an array of symbols starting with a colon. e.g.\r
221 // ":controller/:action/:id" => [':controller', ':action', ':id']\r
222 //\r
223 var paramsInMatchString = this.paramsInMatchString,\r
224 conditions = this.conditions,\r
225 i = 0,\r
226 len = paramsInMatchString.length,\r
227 format = Ext.util.Format.format,\r
228 modifiers = this.caseInsensitive ? 'i' : '',\r
229 params, cond, matcher;\r
230\r
231 for (; i < len; i++) {\r
232 params = paramsInMatchString[i];\r
233 cond = conditions[params];\r
234 matcher = format('{0}', cond || '([%a-zA-Z0-9\\-\\_\\s,]+)');\r
235\r
236 url = url.replace(new RegExp(params), matcher);\r
237 }\r
238\r
239 //we want to match the whole string, so include the anchors\r
240 return new RegExp('^' + url + '$', modifiers);\r
241 },\r
242\r
243 /**\r
244 * Creates the callback function to execute in the configured {@link #before} function.\r
245 *\r
246 * @private\r
247 * @param {Object} args The arguments found from the {@link Ext.app.route.Route}'s\r
248 * recognize call.\r
249 * @param {Function} callback The function to be executed after the {@link #action}\r
250 * has been executed.\r
251 * @param {Object} scope The scope to execute on the callback function, defaults to\r
252 * the {@link Ext.app.route.Route}.\r
253 * @return {Object} An object with the `resume` and `stop` methods on it to control to continue\r
254 * with the action or not.\r
255 */\r
256 createCallback : function (args, callback, scope) {\r
257 var me = this;\r
258\r
259 scope = scope || me;\r
260\r
261 return {\r
262 resume : function() {\r
263 var controller = me.controller,\r
264 action = me.action,\r
265 resume;\r
266\r
267 if (Ext.isString(action)) {\r
268 //get method from the controller\r
269 action = controller[action];\r
270 }\r
271\r
272 //get the parameter arguments\r
273 args = args && args.args ? args.args : [];\r
274\r
275 //remove the action argument from the before method\r
276 resume = args.pop();\r
277\r
278 if (resume && !Ext.isObject(resume)) {\r
279 args.push(resume);\r
280 }\r
281\r
282 //make sure there is an action\r
283 if (action) {\r
284 me.action = action;\r
285\r
286 //execute the action on the controller scoping to the controller\r
287 action.apply(controller, args);\r
288 }\r
289 //<debug>\r
290 else {\r
291 Ext.log.warn('The action: ' + me.action + ' was not found on the controller.');\r
292 }\r
293 //</debug>\r
294\r
295 if (callback) {\r
296 callback.call(scope);\r
297 }\r
298 },\r
299\r
300 stop : function(all) {\r
301 if (callback) {\r
302 callback.call(scope, all);\r
303 }\r
304 }\r
305 };\r
306 }\r
307});\r