Add ICU message format support
[chromium-blink-merge.git] / third_party / polymer / v1_0 / components-chromium / more-routing / route-extracted.js
blob6f6e6fa1a85524775e726550d0c15add0a51713b
2 (function(scope) {
3 var MoreRouting = scope.MoreRouting = scope.MoreRouting || {};
4 MoreRouting.Route = Route;
6 // Note that this can differ from the part separator defined by the driver. The
7 // driver's separator is used when parsing/generating URLs given to the client,
8 // whereas this one is for route definitions.
9 var PART_SEPARATOR    = '/';
10 var PARAM_SENTINEL    = ':';
11 var SEPARATOR_CLEANER = /\/\/+/g;
13 /**
14  * TODO(nevir): Docs.
15  */
16 function Route(path, parent) {
17   // For `MoreRouting.Emitter`; Emits changes for `active`.
18   this.__listeners = [];
20   this.path     = path;
21   this.parent   = parent;
22   this.fullPath = path;
23   this.compiled = this._compile(this.path);
24   this.active   = false;
25   this.driver   = null;
27   var params = MoreRouting.Params(namedParams(this.compiled), this.parent && this.parent.params);
28   params.__subscribe(this._navigateToParams.bind(this));
29   Object.defineProperty(this, 'params', {
30     get: function() { return params; },
31     set: function() { throw new Error('Route#params cannot be overwritten'); },
32   });
34   this.parts    = [];
35   this.children = [];
37   // Param values matching the current URL, or an empty object if not `active`.
38   //
39   // To make data "binding" easy, `Route` guarantees that `params` will always
40   // be the same object; just make a reference to it.
41   if (this.parent) {
42     this.parent.children.push(this);
43     this.fullPath  = this.parent.fullPath + this.fullPath;
44     this.depth     = this.parent.depth + this.compiled.length;
45     this.numParams = this.parent.numParams + countParams(this.compiled);
46   } else {
47     this.depth     = this.compiled.length;
48     this.numParams = countParams(this.compiled);
49   }
51 Route.prototype = Object.create(MoreRouting.Emitter);
53 Object.defineProperty(Route.prototype, 'active', {
54   get: function() {
55     return this._active;
56   },
57   set: function(value) {
58     if (value !== this._active);
59     this._active = value;
60     this.__notify('active', value);
61   },
62 });
64 Route.isPath = function isPath(pathOrName) {
65   return pathOrName.indexOf(PART_SEPARATOR) === 0;
68 Route.joinPath = function joinPath(paths) {
69   var joined = Array.prototype.join.call(arguments, PART_SEPARATOR);
70   joined = joined.replace(SEPARATOR_CLEANER, PART_SEPARATOR);
72   var minLength = joined.length - PART_SEPARATOR.length;
73   if (joined.substr(minLength) === PART_SEPARATOR) {
74     joined = joined.substr(0, minLength);
75   }
77   return joined;
80 Route.prototype.urlFor = function urlFor(params) {
81   return this.driver.urlForParts(this.partsForParams(params));
84 Route.prototype.navigateTo = function navigateTo(params) {
85   return this.driver.navigateToParts(this.partsForParams(params));
88 Route.prototype.isCurrentUrl = function isCurrentUrl(params) {
89   if (!this.active) return false;
90   var currentKeys = Object.keys(this.params);
91   for (var i = 0, key; key = currentKeys[i]; i++) {
92     if (this.params[key] !== String(params[key])) {
93       return false;
94     }
95   }
96   return true;
99 // Driver Interface
101 Route.prototype.partsForParams = function partsForParams(params, silent) {
102   var parts = this.parent && this.parent.partsForParams(params, silent) || [];
103   for (var i = 0, config; config = this.compiled[i]; i++) {
104     if (config.type === 'static') {
105       parts.push(config.part);
106     } else if (config.type === 'param') {
107       var value
108       if (params && config.name in params) {
109         value = params[config.name];
110       } else {
111         value = this.params[config.name];
112       }
113       if (value === undefined) {
114         if (silent) {
115           return null;
116         } else {
117           throw new Error('Missing param "' + config.name + '" for route ' + this);
118         }
119       }
120       parts.push(value);
121     }
122   }
123   return parts;
127  * Called by the driver whenever it has detected a change to the URL.
129  * @param {Array.<String>|null} parts The parts of the URL, or null if the
130  *     route should be disabled.
131  */
132 Route.prototype.processPathParts = function processPathParts(parts) {
133   this.parts  = parts;
134   this.active = this.matchesPathParts(parts);
136   // We don't want to notify of these changes; they'd be no-op noise.
137   this.params.__silent = true;
139   if (this.active) {
140     var keys = Object.keys(this.params);
141     for (var i = 0; i < keys.length; i++) {
142       delete this.params[keys[i]];
143     }
144     for (var i = 0, config; config = this.compiled[i]; i++) {
145       if (config.type === 'param') {
146         this.params[config.name] = parts[i];
147       }
148     }
149   } else {
150     for (key in this.params) {
151       this.params[key] = undefined;
152     }
153   }
155   delete this.params.__silent;
158 Route.prototype.matchesPathParts = function matchesPathParts(parts) {
159   if (!parts) return false;
160   if (parts.length < this.compiled.length) return false;
161   for (var i = 0, config; config = this.compiled[i]; i++) {
162     if (config.type === 'static' && parts[i] !== config.part) {
163       return false;
164     }
165   }
166   return true;
169 Route.prototype.toString = function toString() {
170   return this.path;
173 // Internal Implementation
175 Route.prototype._compile = function _compile(rawPath) {
176   // Not strictly required, but helps us stay consistent w/ `getRoute`, etc.
177   if (rawPath.indexOf(PART_SEPARATOR) !== 0) {
178     throw new Error('Route paths must begin with a path separator; got: "' + rawPath + '"');
179   }
180   var path = rawPath.substr(PART_SEPARATOR.length);
181   if (path === '') return [];
183   return path.split(PART_SEPARATOR).map(function(part) {
184     // raw fragment.
185     if (part.substr(0, 1) == PARAM_SENTINEL) {
186       return {type: 'param', name: part.substr(1)};
187     } else {
188       return {type: 'static', part: part};
189     }
190   });
193 Route.prototype._navigateToParams = function _navigateToParams() {
194   var parts = this.partsForParams(this.params, true);
195   if (!parts) return;
196   this.driver.navigateToParts(parts);
199 function countParams(compiled) {
200   return compiled.reduce(function(count, part) {
201     return count + (part.type === 'param' ? 1 : 0);
202   }, 0);
205 function namedParams(compiled) {
206   var result = [];
207   compiled.forEach(function(part) {
208     if (part.type === 'static') return;
209     result.push(part.name);
210   });
211   return result;
214 })(window);