/** * @author Ed Spencer * @private * * Represents a mapping between a url and a controller/action pair. May also contain additional params. This is a * private internal class that should not need to be used by end-developer code. Its API and existence are subject to * change so use at your own risk. * * For information on how to use routes we suggest reading the following guides: * * - [Using History Support](#!/guide/history_support) * - [Intro to Applications](#!/guide/apps_intro) * - [Using Controllers](#!/guide/controllers) * */ Ext.define('Ext.app.Route', { config: { /** * @cfg {Object} conditions Optional set of conditions for each token in the url string. Each key should be one * of the tokens, each value should be a regex that the token should accept. For example, if you have a Route * with a url like "files/:fileName" and you want it to match urls like "files/someImage.jpg" then you can set * these conditions to allow the :fileName token to accept strings containing a period ("."): * * conditions: { * ':fileName': "[0-9a-zA-Z\.]+" * } * */ conditions: {}, /** * @cfg {String} url (required) The url regex to match against. */ url: null, /** * @cfg {String} controller The name of the Controller whose {@link #action} will be called if this route is * matched. */ controller: null, /** * @cfg {String} action The name of the action that will be called on the {@link #controller} if this route is * matched. */ action: null, /** * @private * @cfg {Boolean} initialized Indicates whether or not this Route has been initialized. We don't initialize * straight away so as to save unnecessary processing. */ initialized: false }, constructor: function(config) { this.initConfig(config); }, /** * Attempts to recognize a given url string and return controller/action pair for it. * @param {String} url The url to recognize. * @return {Object/Boolean} The matched data, or `false` if no match. */ recognize: function(url) { if (!this.getInitialized()) { this.initialize(); } if (this.recognizes(url)) { var matches = this.matchesFor(url), args = url.match(this.matcherRegex); args.shift(); return Ext.applyIf(matches, { controller: this.getController(), action : this.getAction(), url : url, args : args, // We keep the historyUrl in here for backwards compatibility historyUrl: url }); } }, /** * @private * Sets up the relevant regular expressions used to match against this route. */ initialize: function() { /* * The regular expression we use to match a segment of a route mapping * this will recognize segments starting with a colon, * e.g. on 'namespace/:controller/:action', :controller and :action will be recognized */ this.paramMatchingRegex = new RegExp(/:([0-9A-Za-z\_]*)/g); /* * Converts a route string into an array of symbols starting with a colon. e.g. * ":controller/:action/:id" => [':controller', ':action', ':id'] */ this.paramsInMatchString = this.getUrl().match(this.paramMatchingRegex) || []; this.matcherRegex = this.createMatcherRegex(this.getUrl()); this.setInitialized(true); }, /** * @private * Returns true if this Route matches the given url string * @param {String} url The url to test * @return {Boolean} True if this Route recognizes the url */ recognizes: function(url) { return this.matcherRegex.test(url); }, /** * @private * Returns a hash of matching url segments for the given url. * @param {String} url The url to extract matches for * @return {Object} matching url segments */ matchesFor: function(url) { var params = {}, keys = this.paramsInMatchString, values = url.match(this.matcherRegex), length = keys.length, i; //first value is the entire match so reject values.shift(); for (i = 0; i < length; i++) { params[keys[i].replace(":", "")] = values[i]; } return params; }, /** * @private * Returns an array of matching url segments for the given url. * @param {String} url The url to extract matches for * @return {Array} matching url segments */ argsFor: function(url) { var args = [], keys = this.paramsInMatchString, values = url.match(this.matcherRegex), length = keys.length, i; //first value is the entire match so reject values.shift(); for (i = 0; i < length; i++) { args.push(keys[i].replace(':', "")); params[keys[i].replace(":", "")] = values[i]; } return params; }, /** * @private * Constructs a url for the given config object by replacing wildcard placeholders in the Route's url * @param {Object} config The config object * @return {String} The constructed url */ urlFor: function(config) { var url = this.getUrl(); for (var key in config) { url = url.replace(":" + key, config[key]); } return url; }, /** * @private * Takes the configured url string including wildcards and returns a regex that can be used to match * against a url * @param {String} url The url string * @return {RegExp} The matcher regex */ createMatcherRegex: function(url) { /** * Converts a route string into an array of symbols starting with a colon. e.g. * ":controller/:action/:id" => [':controller', ':action', ':id'] */ var paramsInMatchString = this.paramsInMatchString, length = paramsInMatchString.length, i, cond, matcher; for (i = 0; i < length; i++) { cond = this.getConditions()[paramsInMatchString[i]]; matcher = Ext.util.Format.format("({0})", cond || "[%a-zA-Z0-9\\-\\_\\s,]+"); url = url.replace(new RegExp(paramsInMatchString[i]), matcher); } //we want to match the whole string, so include the anchors return new RegExp("^" + url + "$"); } });